Merge pull request 'docs(ui): plan TUI mouse support' (#53) from issue-46-mouse-support-plan into master

Reviewed-on: #53
This commit was merged in pull request #53.
This commit is contained in:
2026-10-03 18:12:04 +00:00
+212
View File
@@ -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.