12 KiB
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.goand 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.goplus 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:
- 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.
- A list click changes focus/selection, and double-click invokes the existing selected action exactly once.
- A table click selects the expected row/cell, wheel events change the visible offset, and double-click invokes the row action exactly once.
- Form button, input field, checkbox, and dropdown clicks match their keyboard callbacks.
- A modal button click acts on the modal and cannot activate the underlying page; Escape still cancels.
--no-mouseleaves keyboard selection/activation unchanged and mouse injection has no application effect.- 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 --helpdocuments--no-mouseand 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, andgit diff --checkpass (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.