* fix(cli): restore OpenTUI dialog editing controls * fix(cli): preserve dialog Ctrl+C editing ownership * fix(cli): clear OpenTUI Source fields on Ctrl+C * fix(cli): flatten pasted breaks and read live focus in OpenTUI dialogs * test(cli): pin column-exact dialog rows and the second-Ctrl+C exit * test(cli): correct the row-oracle comment to the measured geometry * test(cli): name the measured phantom-column shape in the Sources oracle --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: Shaojin Wen <[email protected]>
4.3 KiB
Source field Ctrl+C parity
Status: the installed Ink baseline passed the complete public Source scenario, including Ctrl+C empty field and global exit hint, Undo, acquisition and cold state. The unchanged strict OpenTUI candidate failed at the empty-field oracle: its field retained RbZ after Ctrl+C. The whole component RED run registered 17 tests: four intended clear/Undo cases failed and 13 passed, including both shifted Ctrl+C controls and all 11 existing cases. The explicit owner branch passes all 17 component tests, normal build, typecheck and lint. Node+Ink, Bun+Ink and strict Bun+OpenTUI each pass the complete Source case plus both Permissions regressions: nine executions, three unique cases, twelve recorded natural exits, model HTTP zero. These results cover the precommit source tree and its bound split bundle, not a postcommit package or final-main installation.
Ink SourcesTab uses the shared TextInput for both Add Marketplace and Install Extension. TextInput handles Command.CLEAR_INPUT by clearing nonempty text. OpenTUI ExtensionSourcesTab uses a single useLineEdit instance for those same two views, and before this fix delegated directly to line.handleKey, which has no Ctrl+C owner. Existing Auth and Permissions fields already explicitly call line.clear().
The coherent fix is one explicit Command.CLEAR_INPUT branch in the shared Source input handler, calling the existing line.clear(). Keep the existing current-owner, active, visible, not-busy and not-settled gates. Escape and submission retain their present ownership. Do not add Ctrl+C to the shared line-edit default: Discover search, sensitive acquisition/password fields and AskUserQuestion have separate contracts. No Core, flag, schema or generic reducer changes are needed.
The matcher requires shift:false. The handler must neither stop propagation nor prevent the global broadcast path: first Ctrl+C still arms global quit while clearing the local field; second Ctrl+C retains the existing global exit contract. This local unit patch cannot prove global arbitration. It must not relabel shifted Ctrl+C, Ctrl+D or all modifiers as qualified.
The existing whole ExtensionSourcesTab test file adds three behavioral tests for each actual view: clear plus Enter within one synchronous read must not submit stale text, then replacement submits exactly once with the original cwd/user scope; clear followed by another empty clear and Undo restores the exact Unicode value and commits it; shifted Ctrl+C leaves the value unchanged. These tests use the existing catalog and public component callbacks, with no new driver or injected private state. The second case proves empty clear is a no-op history transaction, not an extra undo entry.
Public test plan: use the existing Source editor scenario, owned source/store/settings and zero-model fixture. The Add Marketplace edit stage sends Ctrl+C after its existing edit states, observes the exact empty field and first-C exit hint, sends Undo and observes the exact restored RbZ. It then preserves the original cancel and real consent/catalog acquisition route. Assert the original fixture's persisted truth, unchanged source fixture, hot/cold state, zero model HTTP and natural CLI exit. Replacement submission is independently checked by the component cases. Keep a separate first-arm/second-exit observation: local clearing alone is not evidence of global quit parity. The installed Ink baseline and strict OpenTUI reproduction are complete; retain the original failure at its actual scope. Direct installation shares the same production input owner and is covered by its separate exact-callback unit cases.
After the reproduced public mismatch, run the whole existing component test file from packages/cli. With the owner branch implemented, verify the whole affected unit file plus existing shared clear/history/global-quit regressions, build/typecheck/lint, and run the public scenario under the supported renderer profiles. Run local validation serially to avoid concurrent application or build processes. Async submit ownership remains unchanged: once settled or busy, late clear/paste/Enter cannot change or duplicate the submitted value. Existing busy/stale-owner cases remain required regression coverage; no new concurrency framework is proposed.