cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Document the UI's hosts and the packages components moved to

Dami committed fd19d91
architecture.md lists the new packages, each host and its size, and
what stays in package ui and why; extending.md's pattern 2 says how a
component gets its host and when it moves out, and the gap narrows to
the overlays that still keep the model.
commit
fd19d9117fd0553d288da4850531d2b4d2387da6
parent
b4931c8dae918e1d4991a2f8ebc12e8c643ddc67
author
Dami <dami.etoile@gmail.com>
3 files changed, +78 -23
Mdocs/architecture.md71+++++++++++++++++++++++-------
Mdocs/extending.md22++++++++-
Minternal/ui/model.go8++--
M docs/architecture.md +55 -16
@@ -22,6 +22,14 @@ internal/ui the Bubble Tea model: modes, menus, overlays, rendering
2222 theme style roles, color schemes and the widgets drawn with them (frames, key chips)2323 rowtext laying out a row of cell text across the columns on screen2424 formula reading the formula being typed (F4, the word and call at the caret)25+ overlay the contract of what takes over input: Overlay, boxes, mouse events, lists26+ lineedit the one-line editor every text field shares27+ picker the searchable list behind the palette and every picker28+ cmdline the : command line and its completions29+ findbar find and replace, a bar on the context line30+ themepicker File > Settings > Theme, previewing as it moves31+ tabstrip the sheet tabs' layout and where each sheet was left32+ transfer imports running in the background and their progress2533 e2e/ end-to-end tests through libghostty (separate module, cgo)2634 oracle/ differential tests against excelize's calculation (separate module)2735 ```
@@ -168,23 +176,50 @@ draw. The components:
168176 | Component | Type | Owns |169177 |---|---|---|170178 | grid (embedded) | `grid` | the sheet shown, active cell, scroll, window size, selection; mapping rows and columns to the screen, frozen panes, moving and selecting (`grid.go`, `panes.go`, `selection.go`) |171-| edit line | `lineEdit` | the one-line editor shared by cell entries, prompts and search fields (`line.go`) |179+| edit line | `lineedit.Line` | the one-line editor shared by cell entries, prompts and search fields (package `lineedit`) |172180 | cell entry | `entry`, `assist` | typing into a cell, pointing at references, other sheets while pointing, formula suggestions and signatures (`entry.go`, `assist.go`) |173181 | prompt | `prompt` | a question on the context line, typed or pointed at (`prompt.go`) |174-| overlays | `overlay` | whatever has taken over input: menus (`menuoverlay.go`), the palette and pickers (`palette.go`, `names.go`), the filter picker, the find, sort and choice bars, the chart editor and selection, the pivot editor (`pivoteditor.go`, `pivotactions.go`), the shortcuts |175-| sheet tabs | `tabStrip` | where each sheet was left, the tab strip's scroll, layout and clicks (`tabstrip.go`) |182+| overlays | `overlay.Overlay` | whatever has taken over input: menus (`menuoverlay.go`), the palette and pickers (package `picker`, with `palette.go`, `names.go`), the command line (package `cmdline`), the theme picker (package `themepicker`), the find bar (package `findbar`), the filter picker, the sort and choice bars, the chart editor and selection, the pivot editor (`pivoteditor.go`, `pivotactions.go`), the shortcuts |183+| sheet tabs | `tabstrip.Strip` | where each sheet was left, the tab strip's scroll and layout (package `tabstrip`); what clicks on it do (`tabstrip.go`) |176184 | mouse | `mouseState` | drags, hover, double clicks, the fill handle (`mouse.go`, `fill.go`) |177-| import | `transfer` | the import in progress, its progress display and cancelling (`transfer.go`) |185+| import | `transfer.Transfer` | the import in progress, its progress display and cancelling (package `transfer`); choosing and placing imports (`transfer.go`, `importplace.go`) |178186 | macros | `recorder`, `macroState` | a recording in progress (`macrorec.go`); a macro running, trust in the file's macros (`macrorun.go`); what scripts act on (`macrohost.go`, `macrohostnav.go`); Data > Macros and the manager (`macro.go`, `macromanage.go`) |179187 | others | `clipboard`, `trace`, `chartState`, `jevRunner`, `terminal` | what Ctrl+V pastes, a trace being shown, chart commands' target, JEV questions in flight, what the terminal supports and the chart images sent to it |180188 181-Overlays implement the `overlay` interface (`overlay.go`): an indicator182-for the mode, `key` and `mouse` handlers, a `layout` of boxes to draw, and183-the status line while open; bars on the context line add a184-`contextLine`, and those with a text field a `cursor`. Like `assist` and185-`prompt`, they are handed the model when they handle input, since acting186-on it is their job. A new overlay is a new type; `Model` needs no new187-fields, only a way to open it (usually a command).189+Overlays implement `overlay.Overlay` (package `overlay`): an indicator for190+the mode, `Key` and `Mouse` handlers, a `Layout` of boxes to draw, and191+the status line while open; bars on the context line add `ContextLine`192+(`overlay.Liner`), and those with a text field `Cursor` and `Changed`193+(`overlay.Text`). The model routes to them without handing itself over:194+each overlay is built with the host it acts on and keeps it. A new195+overlay is a new type; `Model` needs no new fields, only a way to open it196+(usually a command).197+198+**Hosts.** A component names what it needs of the model in a small199+interface, its host, and is tested against a fake of it. Components in200+packages of their own declare an exported `Host`, which the model201+implements through `host` (`hosts.go`), an adapter that keeps those202+methods off `ui.Model`'s exported API:203+204+| Package | Host | Methods |205+|---|---|---|206+| `picker` | `picker.Host` | theme, size, the edit line, close, record the answer to a command's question (5) |207+| `cmdline` | `cmdline.Host` | theme, size, the edit line, close, the commands to complete, run a line, fail (7) |208+| `themepicker` | `themepicker.Host` | a picker's host, and the current theme, the themes directory, preview, keep (9) |209+| `findbar` | `findbar.Host` | theme, size, the edit line, the workbook, the sheet and cell shown, show a cell, note, mark modified, leave keeping the search, pass a click to the grid (10) |210+| `tabstrip`, `transfer` | none | they're handed a view or messages and draw what they're given |211+212+Components that stay in package `ui` declare an unexported host the model213+implements itself: `menuHost` for menus (9 methods; their items are the214+command registry and the menu bar's definitions), `macrosHost` for the215+macro manager (9; it's a few keys over a picker, and what they do is the216+macro machinery), and `pivotHost` for the pivot editor and its field217+picker (14; it opens the model's pickers, filter values and range prompt218+and comes back from them). The rest (the filter picker, the sort and219+choice bars, the chart editor and selection, the shortcuts, the named220+ranges picker) still keep the model itself. Commands still run in one221+place: a host's way to run one (`runFromOverlay`, `cmdline.Host.Run`)222+goes through `runCommand`, so macros record them and pivots guard them.188223 189224 **Drawing.** `View` (`panel.go`) stacks the control panel, the header,190225 the grid rows and the status line, then composites the floating layers
@@ -196,11 +231,15 @@ of roles with dark and light variants on the terminal's 16 ANSI colors,
196231 so the user's palette applies; its widgets (framed boxes, key chips, key197232 hints) are what every overlay is drawn with.198233 199-**Packages.** `theme`, `rowtext` and `formula` depend on nothing in200-`ui`, so they can be tested and measured alone. The components stay in201-package `ui` because they act on the model; moving them out would mean202-exporting most of it. New leaf packages are split off the same way when203-a part needs only the sheet or the theme.234+**Packages.** `theme`, `rowtext`, `formula`, `overlay` and `lineedit`235+depend on nothing in `ui`, so they can be tested and measured alone. The236+components in `picker`, `cmdline`, `themepicker`, `findbar`, `tabstrip`237+and `transfer` build on them and reach the model only through their238+hosts, with unit tests of their own. A component moves out of package239+`ui` when its host stays small (about ten methods or fewer); one that240+needs more keeps a narrow interface inside `ui` instead, since moving it241+would mean exporting half of the model. New leaf packages are split off242+the same way when a part needs only the sheet or the theme.204243 205244 ## Files206245
M docs/extending.md +19 -3
@@ -34,10 +34,23 @@ be kept in sync by hand, turn it into a registry.
3434 UI features that take over input (menus, palette, find bar, pickers, chart3535 editor, sheet tabs, import flow) are components: a type with its own state,3636 key and mouse handling, layout and status line, owned by `Model` and driven37-through the `overlay` interface or its siblings. `Model` routes messages and37+through `overlay.Overlay` or its siblings. `Model` routes messages and3838 composes the screen; it doesn't hold a component's fields. Overlays are3939 composited over the finished grid, so a new one never shifts the layout.4040 41+A component is built with a host: the small interface of what it needs42+of the model (reading the sheet or the selection, running a command,43+opening or closing an overlay, a note, the theme), which the model44+implements, so it can be tested against a fake. When the host stays45+small, about ten methods or fewer, the component lives in a package of46+its own under `internal/ui` (`picker`, `cmdline`, `findbar`,47+`themepicker`), declares `Host`, and the model implements it through the48+`host` adapter in `hosts.go`; parts that need no host at all take plain49+values (`tabstrip`, `transfer`). A component that needs more keeps an50+unexported host inside package `ui` (`menuHost`, `pivotHost`,51+`macrosHost`) rather than exporting half of `Model`. Either way, running a52+command goes through `runCommand` (pattern 3).53+4154 ### 3. Actions go through commands4255 4356 Every user action is a registered command, whatever reaches it (key, menu,
@@ -156,8 +169,11 @@ a golden screen reviewed in the gallery for anything visible (see
156169 157170 Where the code doesn't follow the patterns yet:158171 159-- Components in `internal/ui` are handed the whole `*Model` rather than a160- narrower interface, so they stay in package `ui` (pattern 2).172+- Some overlays still keep the whole `*Model` as their host rather than a173+ narrower interface: the filter picker, the sort and choice bars, the174+ chart editor and selection, the shortcuts and the named ranges picker175+ (pattern 2). The cell entry, its suggestions and prompts are handed the176+ model too.161177 - Movement keys, typing, F4 in formulas, Alt+letter menus and direct mouse162178 manipulation act without a registered command (pattern 3). The macro163179 recorder covers what matters for replay without them (entries, the
M internal/ui/model.go +4 -4
@@ -88,18 +88,18 @@ type Model struct {
8888 8989 // Components. Each owns its state and the handling of the input it9090 // takes; Model routes messages to them and composes what they draw.91- line lineedit.Line // the edit line of entries, prompts and search fields: line.go91+ line lineedit.Line // the edit line of entries, prompts and search fields: package lineedit9292 entry entry // typing into a cell: entry.go9393 point pointer // the cell or range pointed at in POINT mode and range prompts9494 prompt *prompt // a question on the context line: prompt.go9595 overlay overlay.Overlay // the open menu, picker or bar, if any (modeMenu): overlay.go9696 mouse mouseState // drags, hover and double clicks: mouse.go97- tabs tabstrip.Strip // the sheet tabs and where each sheet was left: tabstrip.go98- find *findbar.Bar // the last search, reopened by Ctrl+F: find.go97+ tabs tabstrip.Strip // the sheet tabs and where each sheet was left: package tabstrip98+ find *findbar.Bar // the last search, reopened by Ctrl+F: package findbar9999 charts chartState // chart commands' target: charts.go100100 copied clipboard // what Ctrl+V pastes: clipboard.go101101 trace *trace // precedents or dependents being shown: trace.go102- xfer transfer.Transfer // imports and downloads: transfer.go102+ xfer transfer.Transfer // the import running and the file imported: package transfer103103 jev *jevRunner // answers JEV functions; nil without an API key: jev.go104104 term terminal // what the terminal supports: graphics.go105105 prefs prefs // the settings in effect and the theme chosen: prefs.go