Expanded editor for a single VarInput field — a wide modal that pairs a
multi-line draft textarea with an always-populated suggestion panel above
it, for authoring longer or more intricate {{path}} templates than the
inline popover comfortably supports.
Shares useTemplateToken (../templateToken.ts, Task 1) with VarInput
rather than re-implementing token detection/filtering/insertion: this
component only adds the modal chrome (Dialog, draft state, the panel's
browse-vs-filtered switch) around the same hook. See VarInput.tsx for
the popover this modal is the "expand" counterpart of, and its "Expand
editor" button / Ctrl+Space shortcut that open this modal.
Browse mode
The suggestion panel is always populated: filtered (trigger + mode +
substring filtered against the in-progress token) while a token is active,
every suggestion (mode-filtered only) otherwise, so the user can browse the
full catalog before typing a token at all. Clicking a row in browse mode
must insert at the bare caret rather than reuse tokenStart, which could
be stale (left over from a token that's no longer active) or simply 0 (a
modal that just opened never had one) — useTemplateToken.insert was
extended with an explicit insertAtCaretWhenNoToken option for exactly
this (see its doc comment in templateToken.ts) rather than duplicating
the splice/caret-restore logic here.
Mounting contract
VarInput mounts this component conditionally ({modalOpen && <TemplateEditorModal .../>}) rather than always rendering it with open
toggling — so there's never an idle useTemplateToken instance (and a
full suggestion-list filter pass) running per closed field. A consequence
this component relies on: every mount already has open true from the
very first render, so the [open]-keyed effect below (draft
(re)initialization + focus) always finds its own <textarea> already in
the DOM the first time it runs — refs are attached during the commit
phase, which completes before any effect fires, regardless of whether
that commit is this component's first one (conditional mount) or a
later one (a hypothetical caller that keeps it mounted and toggles open
instead — the same effect covers that case too, unchanged).
Expanded editor for a single
VarInputfield — a wide modal that pairs a multi-line draft textarea with an always-populated suggestion panel above it, for authoring longer or more intricate{{path}}templates than the inline popover comfortably supports.Shares
useTemplateToken(../templateToken.ts, Task 1) withVarInputrather than re-implementing token detection/filtering/insertion: this component only adds the modal chrome (Dialog, draft state, the panel's browse-vs-filtered switch) around the same hook. SeeVarInput.tsxfor the popover this modal is the "expand" counterpart of, and its "Expand editor" button / Ctrl+Space shortcut that open this modal.Browse mode
The suggestion panel is always populated:
filtered(trigger + mode + substring filtered against the in-progress token) while a token is active, every suggestion (mode-filtered only) otherwise, so the user can browse the full catalog before typing a token at all. Clicking a row in browse mode must insert at the bare caret rather than reusetokenStart, which could be stale (left over from a token that's no longer active) or simply 0 (a modal that just opened never had one) —useTemplateToken.insertwas extended with an explicitinsertAtCaretWhenNoTokenoption for exactly this (see its doc comment intemplateToken.ts) rather than duplicating the splice/caret-restore logic here.Mounting contract
VarInputmounts this component conditionally ({modalOpen && <TemplateEditorModal .../>}) rather than always rendering it withopentoggling — so there's never an idleuseTemplateTokeninstance (and a full suggestion-list filter pass) running per closed field. A consequence this component relies on: every mount already hasopentrue from the very first render, so the[open]-keyed effect below (draft (re)initialization + focus) always finds its own<textarea>already in the DOM the first time it runs — refs are attached during the commit phase, which completes before any effect fires, regardless of whether that commit is this component's first one (conditional mount) or a later one (a hypothetical caller that keeps it mounted and togglesopeninstead — the same effect covers that case too, unchanged).