experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

polish(permissions): per-permission emoji glyphs + test-coupling cleanup

Two more polishes on the web-permissions surface:

1. **Per-permission emoji glyphs** — visual scan helper rendered next
to the label in BOTH the page-host prompt and each Settings →
Permissions row. Helps the user tell "this is geolocation" 📍
from "this is camera/mic" 🎤 at a glance instead of reading the
words. Same glyph set used in both places (defined as
`PERMISSION_GLYPHS` in app/page/page.js and mirrored in
app/settings/settings.js — keep in sync). Glyphs:
📍 geolocation 🎤 media 🎹 midi/midiSysex
📺 display-capture 🔔 notifications 📋 clipboard-*
🖱️ pointerLock ⤢ fullscreen 🔗 openExternal
Falls back to 🔒 for unknown permissions.

2. **Test-coupling cleanup** — added a `test.beforeEach` to the
Permission Prompt suite that calls
`app.permissions.forgetAll()` on bgWindow before every test.
Previously each test that needed clean state sprinkled its own
reset; now the hygiene lives in one place and individual tests
don't accidentally depend on whether a prior test happened to
persist a decision. Removed the per-test resets that were added
in the prior commit.

Tests:
* permission-prompt 6/6 — same coverage, cleaner setup.
* permissions-settings 5/5 — unchanged, glyph addition is
cosmetic (label text gains a leading emoji).

No backend changes. Renderer + test polish only.

+55 -19
+24 -1
app/page/page.js
··· 2330 2330 } 2331 2331 } 2332 2332 2333 + // Visual scan helper — emoji glyph by permission type, shown next to the 2334 + // label so the user can recognize at-a-glance which sense the page is 2335 + // asking for. Falls back to a generic lock for unknown permissions. 2336 + const PERMISSION_GLYPHS = { 2337 + geolocation: '\u{1F4CD}', // 📍 2338 + media: '\u{1F3A4}', // 🎤 2339 + midi: '\u{1F3B9}', // 🎹 2340 + midiSysex: '\u{1F3B9}', 2341 + 'display-capture': '\u{1F4FA}', // 📺 2342 + notifications: '\u{1F514}', // 🔔 2343 + 'clipboard-read': '\u{1F4CB}', // 📋 2344 + 'clipboard-sanitized-write': '\u{1F4CB}', 2345 + pointerLock: '\u{1F5B1}', // 🖱️ 2346 + fullscreen: '\u{2922}', // ⤢ 2347 + openExternal: '\u{1F517}', // 🔗 2348 + }; 2349 + function glyphFor(permission) { 2350 + return PERMISSION_GLYPHS[permission] || '\u{1F512}'; // 🔒 fallback 2351 + } 2352 + 2333 2353 function renderActivePermissionPrompt() { 2334 2354 // Removes any prior DOM and renders the head of the queue (if any). 2335 2355 if (activePermissionPrompt) { ··· 2379 2399 text.appendChild(badge); 2380 2400 2381 2401 text.appendChild(document.createElement('br')); 2382 - text.appendChild(document.createTextNode(`wants to ${req.label || req.permission}.`)); 2402 + // Emoji glyph for the requested permission type — visual scan helper 2403 + // so the user can tell "this is geolocation" vs "this is camera/mic" 2404 + // at a glance. 2405 + text.appendChild(document.createTextNode(`wants to ${glyphFor(req.permission)} ${req.label || req.permission}.`)); 2383 2406 message.appendChild(text); 2384 2407 2385 2408 // "Remember this decision" checkbox — when checked, the response includes
+19 -1
app/settings/settings.js
··· 1108 1108 openExternal: 'Open external apps', 1109 1109 }; 1110 1110 1111 + // Visual scan helper — emoji glyph by permission type. Same set as the 1112 + // page-host prompt overlay (app/page/page.js PERMISSION_GLYPHS) so the 1113 + // visual identity is consistent across both surfaces. 1114 + const PERMISSION_GLYPHS = { 1115 + geolocation: '\u{1F4CD}', // 📍 1116 + media: '\u{1F3A4}', // 🎤 1117 + midi: '\u{1F3B9}', // 🎹 1118 + midiSysex: '\u{1F3B9}', 1119 + 'display-capture': '\u{1F4FA}', // 📺 1120 + notifications: '\u{1F514}', // 🔔 1121 + 'clipboard-read': '\u{1F4CB}', // 📋 1122 + 'clipboard-sanitized-write': '\u{1F4CB}', 1123 + pointerLock: '\u{1F5B1}', // 🖱️ 1124 + fullscreen: '\u{2922}', // ⤢ 1125 + openExternal: '\u{1F517}', // 🔗 1126 + }; 1127 + 1111 1128 // Globe-emoji SVG fallback used when the favicon service is unreachable 1112 1129 // for an origin (offline, blocked, weird URL). Same data-URI used in the 1113 1130 // page-host prompt + cards. ··· 1153 1170 meta.className = 'help-text'; 1154 1171 meta.style.fontSize = '12px'; 1155 1172 const label = PERMISSION_LABELS[decision.permission] || decision.permission; 1173 + const glyph = PERMISSION_GLYPHS[decision.permission] || '\u{1F512}'; 1156 1174 const verdict = decision.allowed ? 'Allowed' : 'Denied'; 1157 1175 const when = decision.timestamp ? new Date(decision.timestamp).toLocaleString() : ''; 1158 - meta.textContent = `${label} · ${verdict}${when ? ' · ' + when : ''}`; 1176 + meta.textContent = `${glyph} ${label} · ${verdict}${when ? ' · ' + when : ''}`; 1159 1177 textCol.appendChild(meta); 1160 1178 left.appendChild(textCol); 1161 1179
+12 -17
tests/desktop/permission-prompt.spec.ts
··· 74 74 if (app) await app.close(); 75 75 }); 76 76 77 + // Reset stored decisions between tests so each is hermetic — no test 78 + // should depend on whether a previous test happened to run before it. 79 + // (The Remember-this-decision test persists state that would otherwise 80 + // auto-resolve geolocation prompts in subsequent tests.) 81 + test.beforeEach(async () => { 82 + await bgWindow.evaluate(async () => { 83 + await (window as any).app.permissions.forgetAll(); 84 + }); 85 + }); 86 + 77 87 async function openPageHost(slug: string): Promise<Page> { 78 88 const url = `http://127.0.0.1:${serverPort}/${slug}`; 79 89 const result = await bgWindow.evaluate(async (u: string) => { ··· 186 196 }); 187 197 188 198 test('Remember-this-decision (allow path): clicking Allow + checkbox persists, second request auto-allows', async () => { 189 - // Symmetric to the Deny+Remember test above. Start clean — earlier 190 - // tests may have stored a 127.0.0.1 deny. 191 - await bgWindow.evaluate(async () => { 192 - await (window as any).app.permissions.forgetAll(); 193 - }); 194 - 199 + // Symmetric to the Deny+Remember test above. (beforeEach has already 200 + // cleared any stored decisions.) 195 201 const first = await openPageHost('perm-allow-remember-1'); 196 202 const wv1 = await first.evaluateHandle(() => document.getElementById('content')); 197 203 await first.evaluate((wv: any) => { ··· 227 233 }); 228 234 229 235 test('Phase 5 — queueing: synthetic concurrent requests show one prompt + pending badge; resolving advances queue', async () => { 230 - // Earlier "Remember-this-decision" test persisted a deny for 231 - // 127.0.0.1 geolocation. Clear it so this test gets a fresh prompt. 232 - await bgWindow.evaluate(async () => { 233 - await (window as any).app.permissions.forgetAll(); 234 - }); 235 - 236 236 const pageWindow = await openPageHost('perm-queue'); 237 237 const myWindowId = await pageWindow.evaluate(async () => { 238 238 const r = await (window as any).app.window.getWindowId(); ··· 303 303 }); 304 304 305 305 test('Phase 5 — favicon img renders with origin-derived src in the prompt', async () => { 306 - // Same reason as above — clear any stored decisions so the prompt fires. 307 - await bgWindow.evaluate(async () => { 308 - await (window as any).app.permissions.forgetAll(); 309 - }); 310 - 311 306 const pageWindow = await openPageHost('perm-favicon'); 312 307 const webview = await pageWindow.evaluateHandle(() => document.getElementById('content')); 313 308 await pageWindow.evaluate((wv: any) => {