feat: paste overrides through a reviewable dialog, any origin (REQ-190)
navigator.clipboard.readText() only exists in a secure context, so on plain http over a LAN address it is absent, not merely refused — the previous one-click paste could never work there. A keyboard paste into a focused textarea is an ordinary user gesture and is not gated, so paste now opens a dialog: the YAML goes in a textarea, prefilled only when the browser allows the read. Nothing is written until Apply, and Apply stays disabled while the text does not parse, so a typo cannot be clicked through. A live line names the classes the block matched, the container flags it would flip and the classes it would ignore. Esc closes and returns focus to the button; Enter is left to the textarea, because YAML needs newlines. planPaste() produces that review line and applyPasted() does the writing, keeping the selected-classes-only rule and the single batched container PATCH.
This commit is contained in:
1 parent
4575c46f4c
commit
3eeb529e3d
6 files changed
+229
-84
No files matched your search
+21
-9
@@ -468,10 +468,10 @@ Two columns inside one dialog (920 px wide, max 96 vw / 90 vh, scrollable).
|
||||
with revert on rejection — preview and batch job read the same stored flag.
|
||||
- **Copy / Paste YAML (REQ-186, REQ-190)** — a small right-aligned toolbar above the overrides
|
||||
block: two real buttons, `Copy YAML` and `Paste YAML`, each an inline SVG icon (Lucide copy /
|
||||
clipboard-paste, no emoji) plus its label, `btn` styling at `0.76rem`, both disabled while a
|
||||
paste is in flight. A `role="status"` line beside them carries the outcome — `Copied`,
|
||||
`Pasted 3 classes`, `Pasted 2 classes — ignored crane`, or the failure in `--danger` — success
|
||||
text clears itself after 2 s, a failure stays until the next action.
|
||||
clipboard-paste, no emoji) plus its label, `btn` styling at `0.76rem`. A `role="status"` line
|
||||
beside them carries the outcome — `Copied`, `Pasted 3 classes`, `Pasted 2 classes — ignored
|
||||
crane`, or the failure in `--danger` — success text clears itself after 2 s, a failure stays
|
||||
until the next action.
|
||||
Copy puts one YAML block per selected class on the clipboard:
|
||||
|
||||
```yaml
|
||||
@@ -487,8 +487,21 @@ Two columns inside one dialog (920 px wide, max 96 vw / 90 vh, scrollable).
|
||||
Field names are the row labels as written on screen; a name is quoted only when YAML would
|
||||
misread it (`"weird: name":`). An empty input copies the global value, so the block is a
|
||||
complete configuration, not a diff.
|
||||
Paste reads the clipboard and fills the overrides for the classes **selected in this modal**;
|
||||
an unselected class is named in the status line and never written. Numbers merge per field.
|
||||
Paste opens a dialog and never applies blind. A dark overlay card (`rgba(0,0,0,0.85)`, above
|
||||
the auto-annotate modal in z-order, `role="dialog" aria-modal="true"`), the title *Paste
|
||||
per-class overrides*, a monospace `textarea` focused on open, and a live review line under it
|
||||
(`role="status"`, recomputed per keystroke, no debounce): the matched classes, the `container`
|
||||
flags it would flip, the classes it would ignore — or the parse error in `--danger`.
|
||||
**Apply stays disabled while the text does not parse**, so a typo cannot be clicked through;
|
||||
*Cancel* is a ghost button and a backdrop click cancels. Keyboard paste works everywhere: the
|
||||
dialog prefills from the clipboard only when the browser permits a read (localhost or https)
|
||||
and otherwise says so and waits for Ctrl+V — no permission prompt and no https requirement.
|
||||
`Esc` closes and returns focus to the *Paste YAML* button; **Enter is never bound to Apply**,
|
||||
because YAML needs newlines. Focus moves into the textarea on open; no focus trap (nothing in
|
||||
this app has one).
|
||||
Apply fills the overrides for the classes **selected in this modal**; an unselected class is
|
||||
named in the review line and the status line, and never written. Numbers merge per field — the
|
||||
fields the block mentions are overwritten, the ones it omits keep their value.
|
||||
`container` is saved to the server like ticking the box by hand (one PATCH for all changed
|
||||
classes, reverted together on rejection, and the status line says the flags were not saved).
|
||||
Parsing is strict and **atomic**: `#` comments (whole lines, or trailing on a value), `class:`
|
||||
@@ -496,9 +509,7 @@ Two columns inside one dialog (920 px wide, max 96 vw / 90 vh, scrollable).
|
||||
(`0.5abc` is a typo, not `0.5`), `container` `true|false|yes|no|1|0`; a list item, a nested
|
||||
map, a top-level scalar, an unknown key, a malformed number, an out-of-range value or a
|
||||
key before any class throws `line N: …` and applies **nothing**. A class repeated in the same
|
||||
document merges rather than replacing. Where the browser blocks the clipboard read — the
|
||||
app on plain http over a LAN address, or the permission prompt refused — the status line says
|
||||
so rather than doing nothing.
|
||||
document merges rather than replacing.
|
||||
Both buttons live in the shared per-class table, so they exist in the engine-chooser modal
|
||||
**and** the mass modal.
|
||||
- `ClassPromptPanel`:
|
||||
@@ -1161,6 +1172,7 @@ Handlers are attached in the **capture** phase on `document`, and:
|
||||
| hold `S` | SAM3 assist mode while held | — |
|
||||
| `Enter` | apply the exemplar preview (panel open) | — |
|
||||
| `Esc` | discard the exemplar preview (panel open) | — |
|
||||
| `Esc` | cancel the Paste YAML dialog, focus back on its trigger | — |
|
||||
|
||||
**In select mode the marquee owns `Delete` and the digits.** Otherwise a 40-box selection is
|
||||
thrown away by one keystroke meant for it.
|
||||
|
||||
Reference in new issue
Block a user