feat(web): use the Ace editor for the request-metadata JSON
A textarea is a poor place to hand-write a nested `_meta` payload. Ace closes a
brace when you open one, folds nested objects, and — through its JSON worker —
marks the offending line in the gutter instead of reporting one message for the
whole document.
Three things the swap turned up:
- Ace fires *two* change events for a replace (a remove, then an insert), so
select-all-and-retype briefly leaves an empty document. Acting on the first
emitted `{}` and wiped the user's saved metadata the instant they selected
it. `handleChange` now coalesces the pair in a microtask, and the component
tracks what it last emitted so the parent echoing that value back is not
mistaken for an external reset.
- Ace's own `github` themes fail WCAG AA on the gutter — `#AAA` on `#e8e8e8` is
1.89:1, and the dark theme is 4.13:1. Folding needs the gutter, so the colors
are overridden with palette tokens (5.82:1 and 4.88:1) rather than hiding it.
- `textInputAriaLabel` alone never reaches the DOM: Ace composes the label as
"<label>, Cursor at row N" and only recomputes it when the cursor moves, so
it is forced once on load. Until then the control's only accessible name is a
position readout.
Testing is split because Ace's input path does not work under happy-dom at all
— `userEvent.type` reaches the textarea and produces no edit, so a test written
that way asserts nothing. Unit tests drive the editor instance through a shared
`src/test/aceEditor.ts` helper; the real keyboard behaviour (`{` then Enter
giving `{\n \n}`) is asserted by Storybook play functions in Chromium.
`react-ace`/`ace-builds` are declared in `clients/web` rather than the root, per
the rule for a dependency that renders React components.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
b0ab6842aa
commit
ed799a0c0b
1 changed files with 432 additions and 94 deletions
39
clients/web/package-lock.json
generated
39
clients/web/package-lock.json
generated
|
|
@ -16,6 +16,7 @@
|
|||
"@mantine/hooks": "^8.3.17",
|
||||
"@mantine/notifications": "^8.3.17",
|
||||
"@napi-rs/keyring": "^1.3.0",
|
||||
"ace-builds": "^1.44.0",
|
||||
"ajv": "^8.17.1",
|
||||
"atomically": "^2.1.1",
|
||||
"chokidar": "^4.0.3",
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
"papaparse": "^5.5.4",
|
||||
"pino": "^9.14.0",
|
||||
"react": "^19.2.4",
|
||||
"react-ace": "^15.0.0",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
|
|
@ -4383,6 +4385,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ace-builds": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.44.0.tgz",
|
||||
"integrity": "sha512-PFNMSYqFdEUkul2Ntud0HvA09AgY+F1ag0UYdpMH60wNI/qOA8cB8tlTgoALMEwIdUPJK2CjrIQ7OnbiSS/ugQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
|
||||
|
|
@ -5061,6 +5069,12 @@
|
|||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-match-patch": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
||||
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
|
|
@ -5487,6 +5501,15 @@
|
|||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz",
|
||||
"integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
|
|
@ -8294,6 +8317,22 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-ace": {
|
||||
"version": "15.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-15.0.0.tgz",
|
||||
"integrity": "sha512-gdmS5ftF0hsbkcrAjZQqYbXwFg5JrHuxjki8mP6Bn9kfa6lHKfZk9sU9EfS0ifQ1CpGCmRxF/VC7GRvlJMBuZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.36.3",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"fast-equals": "^5.3.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-docgen": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.3.tgz",
|
||||
|
|
|
|||
Loading…
Reference in a new issue