Skip to content

Ask in the app's own voice, not the browser's - #36

Merged
ibrahimweng merged 1 commit into
mainfrom
claude/ask-in-app
Aug 30, 2026
Merged

Ask in the app's own voice, not the browser's#36
ibrahimweng merged 1 commit into
mainfrom
claude/ask-in-app

Conversation

@ibrahimweng

Copy link
Copy Markdown
Owner

Found by clicking every control in the app and recording what changed. Of about a hundred and fifty, none was dead — but three left the app entirely: window.prompt to name a sound, and window.confirm to remove a layer with sounds on it or to start again.

They were the only places the app dropped out of its own language: system chrome in a system font, in the middle of a dark editor, placed by the operating system.

That is the visible half, and the smaller one. A native dialog cannot say which of its two answers is the one that destroys something, cannot put focus on the safe one, and is suppressed outright in some embedded contexts — where a confirm returns false and the thing somebody asked for silently does not happen, which is this app's oldest fault wearing a different hat.

What replaces them

src/ui/ask.ts — one modal, two shapes, following the help panel's dimmed backdrop and the same cards and chips as everything else.

question now reads
Name this sound field focused with the sound's own name selected; Return saves
Remove Impacts? "The one sound on it goes with it. Undo brings them back." — Cancel focused
Start a new project? "This throws away the clip, and the kept copy with it. It cannot be undone." — Cancel focused

Escape and a press on the dimmed area are ways out, Return agrees, Tab stays inside, and focus goes back where it came from. A field takes focus with its contents ready to be replaced; a yes-or-no question opens on Cancel, because these are the presses that lose work and a stray Return should not be what throws away a project.

Checking

Eleven tests, each failing without the thing it names. Every one also watches for a native dialog appearing, since that is the real regression: not that the question stops working, but that it goes back to being asked by the browser.

Writing them found two of my own:

  • The first version would not close. It compared the open dialog against its own closer by identity and registered a wrapper instead of the closer, so the comparison never matched, every answer returned early, and Escape left an overlay on top of an app it had made unreachable. A boolean cannot be got wrong that way.
  • A gap I had backwards. Blocking the letter keys matters for the yes-or-no shape and not for the prompt: the app's own key handler already ignores anything typed into an input, but a confirm has no input and focus sits on a button, so c would still pick up the blade underneath. That case now has its own test, and it is the only thing that fails when the blocking goes.

Reverting either dialog to window.confirm fails three tests; removing the focus trap fails one; focusing the destructive button instead of Cancel fails one.

236 unit tests, 77 browser tests (was 66), build clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ux1kydvUkLRoMbHp82ofDi


Generated by Claude Code

Found by clicking every control in the app and recording what changed.
Of about a hundred and fifty, none was dead — but three left the app
entirely: window.prompt to name a sound, and window.confirm to remove a
layer with sounds on it or to start again.

They were the only places the app dropped out of its own language:
system chrome in a system font, in the middle of a dark editor, placed
by the operating system. That is the visible half. The rest matters
more. A native dialog cannot say which of its two answers is the one
that destroys something, cannot put focus on the safe one, and is
suppressed outright in some embedded contexts — where a confirm
returns false and the thing somebody asked for silently does not
happen, which is this app's oldest fault wearing a different hat.

So src/ui/ask.ts: one modal, two shapes, following the help panel's
dimmed backdrop and the same cards and chips as everything else.
Escape and a press outside are ways out, Return agrees, Tab stays
inside, and focus goes back where it came from. A field takes focus
with its contents ready to be replaced; a yes-or-no question opens on
Cancel, because these are the presses that lose work and a stray
Return should not be what throws away a project.

Eleven tests, each failing without the thing it names. Writing them
found two of my own:

The first version compared the open dialog against its own closer by
identity and registered a wrapper instead of the closer, so the
comparison never matched, every answer returned early, and Escape left
an overlay on top of an app it had made unreachable. A boolean cannot
be got wrong that way.

The second was a gap rather than a bug. Blocking the letters matters
for the yes-or-no shape and not for the prompt, which is the opposite
of what I assumed: the app's own key handler already ignores anything
typed into an input, but a confirm has no input and focus sits on a
button, so c would still pick up the blade underneath. That case now
has its own test, and it is the only thing that fails when the
blocking goes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ux1kydvUkLRoMbHp82ofDi
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beat-studio Ready Ready Preview Aug 30, 2026 10:01pm

@ibrahimweng
ibrahimweng merged commit 0f30856 into main Aug 30, 2026
5 checks passed
@ibrahimweng
ibrahimweng deleted the claude/ask-in-app branch August 31, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant