From df980a3434bfbb5cfa69d02550b1f8fdc7835440 Mon Sep 17 00:00:00 2001 From: SG Command Date: Sat, 3 Oct 2026 13:24:46 +0200 Subject: [PATCH] docs(ui): plan TUI mouse support --- docs/TUI_MOUSE_SUPPORT_PLAN.md | 212 +++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 docs/TUI_MOUSE_SUPPORT_PLAN.md diff --git a/docs/TUI_MOUSE_SUPPORT_PLAN.md b/docs/TUI_MOUSE_SUPPORT_PLAN.md new file mode 100644 index 0000000..886c47f --- /dev/null +++ b/docs/TUI_MOUSE_SUPPORT_PLAN.md @@ -0,0 +1,212 @@ +# TUI mouse support plan + +Issue: #46 +Status: design only; this document does not implement mouse input. + +## 1. Current implementation and scope + +The editor is created in `cmd/relspec/edit.go` by +`ui.NewSchemaEditorWithConfigs(...).Run()`. `pkg/ui/editor.go` owns the +`tview.Application`, `tview.Pages`, and application lifecycle. The current +code never calls `Application.EnableMouse`, so tcell mouse reporting is off. +The module uses tview v0.42.0 and tcell/v2 v2.13.9. + +The first implementation should add `--no-mouse` to the `edit` Cobra command +only. The flag is a local boolean, defaulting to false, and should be passed +explicitly into the editor (prefer an options/config field rather than a +package-global or environment variable). It must not affect convert, inspect, +merge, or other commands. There is no environment-variable or persistent +configuration setting in this issue: a command-line opt-out is predictable, +visible in `edit --help`, and avoids adding configuration precedence rules. + +At startup, the editor should call `app.EnableMouse(!noMouse)` before +`Run()`. `--no-mouse` must mean that the application does not enable terminal +mouse reporting and that no custom mouse handlers are relied upon. Keyboard +behavior must remain identical in both modes. + +Likely implementation files are `cmd/relspec/edit.go`, +`pkg/ui/editor.go`, focused TUI mouse helpers/tests under `pkg/ui`, and a +short user-facing note in the command help or TUI documentation. Do not +refactor unrelated screens or data operations. + +## 2. Widget and screen coverage + +The application composes `Pages`, `Flex`, `TextView`, `List`, `Table`, `Form`, +`Button`, `InputField`, `DropDown`, `TextArea`, `CheckBox`, and `Modal`. +Vendored tview confirms mouse handlers exist for all of those relevant +primitives, including focus on left-down, list/table selection, button clicks, +form child dispatch, dropdown opening/drag selection, text-area cursor and +scrolling, and modal button dispatch. `Pages`, `Flex`, and `Form` forward events +to their children. + +The coverage plan is: + +* Main menu (`pkg/ui/main_menu.go`): left click focuses/selects a list entry; + second activation opens it; buttons and exit confirmation remain reachable. +* Schema, table, domain, object, relation, and database screens: click a row + to select it; double-click the row to perform the same action as the + keyboard Enter/selected callback where opening is meaningful; scroll lists + and tables; click each action button. +* Tables (`schema_screens.go`, `table_screens.go`, and object/relation tables): + tview's table handler provides selection and scrolling, but it does not + provide application-specific double-click activation. Add a small reusable + wrapper/helper for the table instances that need it. It must preserve the + existing selected row/column behavior and invoke the same callback as Enter, + not duplicate mutation logic. +* Forms (`load_save_screens.go` and the form-building screen files): click an + input to focus it, click buttons to activate them, click a dropdown to open + it and choose an option, scroll multiline help/text areas, and retain all + existing keyboard Tab/Shift-Tab, shortcut, Enter, and Escape behavior. +* Dialogs (`pkg/ui/dialogs.go` plus confirmation/error/success modals): modal + buttons are clickable and the modal keeps focus above the underlying page. + Clicking outside a modal must not activate the hidden page or dismiss a + destructive confirmation. Escape and the existing button-key behavior stay + authoritative. +* The planned file browser and connection-string builder from issue #44 must + use the same contracts: clickable entries/buttons and scrolling, with + keyboard navigation and explicit cancel/accept paths. #46 should not + implement #44's widgets; it should define the integration point and test + them when #44 lands. + +Do not promise drag semantics for every widget. Drag is appropriate for text +selection/cursor movement and dropdown selection where tview already supports +it. For ordinary list/table navigation, a click selects and the wheel scrolls; +row dragging should not mutate data. + +## 3. Exact mouse action contract + +| Widget/type | Left down/click | Double click | Wheel/drag | Keyboard fallback | +| --- | --- | --- | --- | --- | +| Main/list menu | focus and select row | invoke row selected callback | scroll list | arrows, Enter, shortcuts | +| Data table | focus and select cell/row | invoke the screen's existing open/edit action for the selected row | vertical/horizontal scroll as supported by tview | arrows, PageUp/PageDown, Enter, existing shortcuts | +| Button | focus | same as one activation, never duplicate the callback | none | Tab/Shift-Tab, Enter/Space and existing shortcut | +| Input field | focus; place cursor if supported | no destructive action | text-area behavior if provided by tview | typing, arrows, Home/End, Tab, Escape | +| Text area/help | focus and position cursor | select word only where tview supports it; no application action | scroll; drag text selection if supported | arrows, PageUp/PageDown, standard editing keys | +| Dropdown | focus/open and choose the hit option | same as click; no duplicate selection | drag through options only while open | arrows, Enter, Escape, Tab | +| Checkbox | toggle on click | no second toggle | none | Space and existing form navigation | +| Modal | focus/click visible button | same button action once | no underlying-page scrolling | Tab/Shift-Tab, Enter, Escape, existing button keys | +| Blank/border/title area | focus containing primitive where useful | none | no mutation | current screen shortcuts | + +Right and middle clicks should have no application action in the first +release. Wheel events should be consumed only by the scrollable primitive +under the pointer. Double-click timing/translation should come from tview/ +tcell; custom code must not fire the action once for both the click and the +double-click. Any custom table wrapper needs a small state machine or tview +mouse action handling that is tested for this property. + +## 4. tview gaps and implementation boundaries + +Enabling mouse support is not sufficient for the desired behavior. tview's +built-in Table handler selects cells and scrolls but has no repository-specific +row-open callback on double click. Existing screen code also wires keyboard +input captures directly on individual widgets, so mouse actions must call the +same screen callbacks rather than route through synthetic key events. + +Use tview's `MouseHandler`/`WrapMouseHandler` contracts and `setFocus` rather +than reading terminal coordinates in each screen. A reusable table adapter +may embed `*tview.Table`, delegate ordinary actions to the original table +handler, and add the screen's double-click callback. Keep the adapter in +`pkg/ui` and use it only where a row-opening action exists. Do not modify the +vendored tview copy. + +The `Pages`/`Modal` dispatch order must be verified: a visible modal consumes +its click before the page below it. Page transitions should happen only in the +existing callbacks, so a stale hidden page cannot receive a click. + +## 5. Keyboard, terminal, and copy/paste behavior + +Mouse is an enhancement, never a requirement. Every acceptance path must be +reachable with the existing keyboard controls, including load/save, navigation, +editing, confirmations, cancel, and exit. `--no-mouse` is the regression mode +for proving this contract. + +Mouse reporting is terminal capability dependent. On local terminals it is +negotiated by tcell; tmux and SSH can suppress, translate, or fail to pass +mouse reporting depending on their configuration. The application must still +start and remain keyboard usable if mouse reporting is unavailable or broken. +Documentation should state that terminal/tmux configuration may be required, +and that SSH behavior depends on the remote terminal path. Windows Terminal and +other Windows console hosts should be treated as supported only insofar as the +selected tcell backend reports mouse events; the CLI must not assume POSIX +escape sequences or add platform-specific code in this issue. + +Enabling mouse capture normally prevents terminal-native selection/copy from +seeing ordinary button-drag events. Document the standard workaround: hold the +terminal's bypass modifier (commonly Shift, terminal-dependent) for selection, +or use `--no-mouse` when native copy/paste is the priority. Do not implement a +second clipboard protocol. Input-field/text-area copy/paste must continue to +use tview/tcell paste handling and keyboard shortcuts; verify that enabling +mouse does not intercept paste events. + +## 6. Test strategy using tcell simulation + +Add focused tests rather than attempting a full interactive end-to-end test. +Use `tcell.NewSimulationScreen("")`, `screen.Init()`, construct the editor or +an isolated primitive tree, and inject events with the actual API: +`SimulationScreen.InjectMouse(x, y, buttons, mod)` and `InjectKey(...)`. +Coordinates must be derived from the primitive's drawn rectangle or fixed by a +small deterministic test layout; do not use arbitrary coordinates without +checking the rendered screen. + +Minimum cases: + +1. Default editor configuration enables mouse; the explicit disabled option + leaves it disabled. If the Application API is not observable directly, + test through the simulation screen's event path plus a constructor-level + option assertion. +2. A list click changes focus/selection, and double-click invokes the existing + selected action exactly once. +3. A table click selects the expected row/cell, wheel events change the visible + offset, and double-click invokes the row action exactly once. +4. Form button, input field, checkbox, and dropdown clicks match their + keyboard callbacks. +5. A modal button click acts on the modal and cannot activate the underlying + page; Escape still cancels. +6. `--no-mouse` leaves keyboard selection/activation unchanged and mouse + injection has no application effect. +7. Existing dialogs and screen transitions do not leave a stale mouse capture + after a page is removed. + +Prefer callback counters and selected-index assertions over screen-text-only +assertions. Run the relevant `pkg/ui` tests with `go test -race ./pkg/ui` and +run the full repository test suite if time/resources permit. + +## 7. Rollout and acceptance criteria + +Implementation is ready for review when: + +* `relspec edit --help` documents `--no-mouse` and mouse is enabled by default. +* Only the edit TUI is affected; non-TUI commands have no changed behavior. +* Main screens, tables, lists, forms, dropdowns, buttons, text areas, and + visible dialogs support the action contract above. +* Keyboard-only operation is complete and verified with `--no-mouse`. +* Modal clicks cannot fall through to an underlying page. +* Table double-click behavior is explicit, tested, and does not duplicate + activation. +* tcell simulation tests cover default-on, opt-out, selection, scrolling, + activation, dialog focus, and keyboard fallback. +* `go test -race ./pkg/ui`, appropriate command tests, `go test ./...`, + formatting, and `git diff --check` pass (or any limitation is recorded). +* User-facing docs explain tmux/SSH/Windows variability and the terminal + modifier workaround for native copy/paste. + +Roll out in two implementation slices if needed: first application option, +standard tview handlers, tests, and documentation; second only the reusable +table double-click adapter and screen wiring. Do not block the first slice on +issue #44, but do not claim #44's future widgets are covered until they use the +same contract. + +## 8. Open decisions and dependencies + +* Confirm whether the project wants a public editor options type or a small + `SetMouseEnabled`/constructor parameter; avoid a global flag. +* Confirm the preferred terminal copy modifier in project documentation, since + tmux, SSH clients, and Windows Terminal differ. +* Decide whether horizontal wheel events should be supported where tview/table + exposes them; vertical scrolling is mandatory, horizontal is optional. +* Decide whether double-click opens every data table or only tables with an + unambiguous row action. The plan recommends the latter. +* Confirm #44's file-browser and connection-builder primitive choices before + wiring their mouse tests. +* Confirm CI has a stable non-terminal environment for simulation-screen tests; + no real terminal, tmux session, database, or network should be required. -- 2.54.0