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
+16
-11
@@ -160,22 +160,27 @@ changes.
|
||||
review assist filter), surfaces as `MaxBox` in the params table, and is included in the
|
||||
REQ-186 copy line. Size bounds are ordinary per-class tuning: the `container` flag
|
||||
(REQ-184) stays purely the NMS containment carve-out and gains no size semantics.
|
||||
- **REQ-190** — The same table has a **Paste YAML** button that reads the clipboard and fills
|
||||
the per-class overrides from it, so a tuned configuration moves between projects without
|
||||
retyping. It applies only the classes currently **selected in this modal**; a pasted class
|
||||
that is not selected is reported as ignored rather than written, because `buildClassParams`
|
||||
walks every entry and an unseen override would otherwise ride along in `class_params`.
|
||||
Numbers merge per field (a pasted block overwrites the four numbers, leaves other state
|
||||
alone); `container` goes out as one `PATCH /projects/{id} {containers: {classId: bool}}`
|
||||
covering every flag that changed, reverted together with a message if the save is rejected.
|
||||
- **REQ-190** — The same table has a **Paste YAML** button that opens a **dialog** holding the
|
||||
YAML in a textarea, so a tuned configuration moves between projects without retyping. The
|
||||
textarea is the real path and must work **anywhere the app is served** — no https, no
|
||||
localhost, no permission prompt: `navigator.clipboard.readText()` only exists on a secure
|
||||
origin, but a keyboard paste into a focused textarea is an ordinary user gesture and is not
|
||||
gated. The dialog therefore *prefills* from the clipboard when the browser allows it and
|
||||
otherwise just waits for Ctrl+V. Before anything is written, a live line reports what the
|
||||
text would do — the classes it matched, the `container` flags it would flip, the classes it
|
||||
would ignore — and any parse error, with **Apply disabled** while the text does not parse.
|
||||
It applies only the classes currently **selected in this modal**; a pasted class that is not
|
||||
selected is reported as ignored rather than written, because `buildClassParams` walks every
|
||||
entry and an unseen override would otherwise ride along in `class_params`. Numbers merge per
|
||||
field (only the fields the block mentions are overwritten, omitted ones keep their value);
|
||||
`container` goes out as one `PATCH /projects/{id} {containers: {classId: bool}}` covering
|
||||
every flag that changed, reverted together with a message if the save is rejected.
|
||||
Parsing is strict on purpose — a subset reader for exactly what Copy emits (`#` comments,
|
||||
`class:` at the left margin, `key: value` indented under it) rather than a general YAML
|
||||
engine, and **any** malformed line throws with its line number and applies **nothing**, so a
|
||||
half-understood paste cannot quietly set wrong parameters. A value must *look* like a number,
|
||||
not merely start like one (`0.5abc` is rejected rather than read as `0.5`), and values
|
||||
outside 0–1 are rejected the same way. Clipboard **read** has no legacy fallback, so a blocked or denied read (the app
|
||||
served over plain http on a LAN address, or the browser prompt refused) is reported in the
|
||||
status line instead of failing silently. Paste never touches the global sliders.
|
||||
outside 0–1 are rejected the same way. Paste never touches the global sliders.
|
||||
|
||||
## E. Review & correction
|
||||
|
||||
|
||||
Reference in new issue
Block a user