Ask in the app's own voice, not the browser's - #36
Merged
Conversation
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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.promptto name a sound, andwindow.confirmto 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
falseand 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.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:
cwould 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.confirmfails 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