Skip to content

Releases: ofamtools/ochazuke

v0.17.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 13:57
  • Framework namespace is ochazuke: framework nouns are ochazuke.settings, ochazuke.page, ochazuke.state and so on. In v0.16.0 they were app.<noun>, which collided with the app noun's own verbs (app.state). The short names stay as aliases, and app.<framework noun> is accepted silently for one release. Exported noun constants changed value.
  • cli.ExtensionNamespace(bin): strips a leading o- and refuses reserved names (o-ai → ai, o-qbt → qbt). Hosts use it with cli.Namespace.
  • Catalog fix: cli.Catalog lists each noun once, sorted by name. It used to split one noun into several entries.
  • Card timeline: AddTimeline draws boxed cards with wrapped headers (no …), lazily drawn bodies with foldable sections, Active (bold, ●) and Muted cards, group rules, and follow-tail scrolling.

v0.16.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 13:05
  • Noun namespaces:
    • Framework nouns are now app.<noun> (app.settings, app.config, …), and their short names stay as aliases.
    • Define extends a framework noun with the same short name; DefineOwn(name) lets an app claim the short name instead.
    • Dotted nouns split on the last dot (SplitKey); a verb can't contain a dot.
    • The CLI takes nested, dotted and alias spellings (bin app settings list, bin app.settings list, bin settings list).
    • Extension hosts namespace extension nouns with cli.Namespace(ext). The catalog publishes aliases.
  • Compatibility: Runtime.Nouns and Runtime.Operations are keyed by canonical names, so look framework nouns up through Noun()/Operation(). Several exported noun constants changed value.
  • State explorer: ochazuke/explore presses keys through any app built headless with fakes, and checks invariants in every state:
    • no panic, focus and modal rules, honest hints, no traps, render fits, jobs settle.
    • Findings come with shrunk key paths, as gap stories or Go tests.
    • Entry points: explore.Run, Survey, Reproduce; o-story explore; make explore.

v0.15.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 11:32
  • config.reload: reads themes.json, settings.json, actions.json and bindings.json again, in dependency order, then each app hook (Runtime.OnConfigReload(name, fn)). Problems are reported per file and stale notices withdrawn.
  • Noun places: each instance's noun, ancestor-noun chain, page and modal are recorded and persisted per app in noun-places.json. Read them with nouns places, nouns list and Runtime.NounPlaces.
  • ? help views: S cycles here → pick → table. Pick shows a noun and mode you choose (n, m); table shows keys by mode. Both resolve at a recorded place (p), defaulting to where ? was opened. Settings help.pick-view, help.table-view and help.pending-modes.
  • ctrl+\\ opens the help from any insert mode.
  • Bindings export: rows print as mode, noun, request (verb first), keys. bindings list --all and bindings.reference.json are sorted the same way.

v0.14.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 07:17
  • Modals: about 80% of the terminal width with no 78-column cap; a chooser fills the modal width and no longer resizes while typing (story chooser/wide-fills).
  • Terminal overlays: Terminal.SetOverlay(*TerminalOverlay{Dim, Marks}) draws over a live pane without touching its frame or stream. With an app passthrough mode entered from the leader, this gives tuios-style hints (stories terminal/hints-*).
  • Keys: the key classes upper and ctrl+alpha, and the argument source letter.
  • Palette: Parameter.Noun lists that noun's things as the parameter's choices.

v0.13.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 03:30

Table cell mode is opt-in: CellSelect(); every other table selects rows as before v0.12.0.

Table cell mode is opt-in: only a table added with CellSelect() is cell-based.

Tables

  • CellSelect() (an InstanceOption for AddList) makes a table cell-based: the current column, h/l, 0/^/home, $/end, N|, g c, y (copy the cell) and z >/z </z =, with the column and widths remembered in tables.json, as v0.12.0 gave every table. AddTree ignores it.
  • Every other table selects rows again, as on v0.11.0: h/l are free for row nouns, $, ^ and 0 (without a count) run list.scroll-columns (verb guard table-scrolls), z l/z h scroll a column, no current cell is drawn, and nothing is read from or written to tables.json for it (widths a v0.12.0 cell table saved no longer stick to it). Story tables/row-select.
  • RowSelect() is a deprecated no-op; the framework's trees, ? help modal and Settings page no longer pass it.
  • Y (copy the row's cells joined by tabs) answers on every table (verb guard list-table), y only on a cell-mode table.
  • V answers on every table with marks: a visual range over the marks, cell-mode or not. esc ends it through list.end-visual (verb and row guard list-visual), so the row no longer hides an outer esc hint, such as a modal's esc modal.pop, over a table.
  • The width keys z >/z </z = need a current column, so they answer on cell-mode tables only; List.SetColumnWidth still sets a width on any table.
  • list.column, list.choose-column and list.resize-column refuse a row-select table ("not a cell table"); list.yank {what: cell} too.
  • cli: catalog tables select rows, so the groups rail's h back row (guard list-first-column) answers at once on them. Story catalog/groups-table-h.

o-story

  • A story list takes cellSelect (ListSpec.CellSelect); the cell stories set it, and the goldens of tables that select rows read R| again.

v0.12.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 03:10

Tables are cell-based: a current column, per-column widths, and V for rows.

Tables are cell-based, like o-csv's grid: a current column, column widths, and a visual row selection.

Tables

  • A list with columns has a current column (List.Column, SetColumn; column in its state). On the list noun in normal mode, on a cell-mode table only: h/l/left/right move by column (counts), 0/^/home and $/end go to the first and last, N| to column N, g c chooses one from a chooser of the titles (list.column, list.choose-column, motions); j/k keep the column. Every frame scrolls the column offset as little as shows the current column; z l/z h carry it along. $, ^ and 0 no longer run list.scroll-columns; list.column scrolls a row-select table the same way. Stories tables/cell-motion, tables/column-count.
  • The current cell is drawn reverse and underlined inside the row's highlight, the header underlines the current column's title, and an unfocused table fades both. Story tables/unfocused-cell-dimmed.
  • y and Y copy the current cell, or the row's cells joined by tabs, into the registers (list.yank {what, register}).
  • z >/z < widen or narrow the current column by ColumnWidthStep (2) cells, never below 3; z = makes every column automatic (list.resize-column, list.reset-columns; List.SetColumnWidth, ColumnWidths, ResetColumnWidths). The widths and the current column are remembered per table id in the state directory's tables.json (Runtime.TableState, SetTableState). Story tables/column-resize.
  • V starts a visual row selection drawn into the marks: j/k extend it (they clamp), a row's application verb runs for every selected row through list.apply, esc or V ends it and the marks go back (list.visual, List.Visual, StartVisual, EndVisual). Story tables/visual-apply.
  • list.apply takes args for every row's request, and a verb with a confirm asks once for all the rows.
  • RowSelect() keeps a table on row selection: trees' lists (h/l expand and collapse), the ? help modal and the Settings page use it.
  • y, Y, V and the width keys are hidden from the footer; ? help lists them.
  • cli: the groups rail's h/left back rows take the new guard list-first-column, so in a table h moves left and goes back to the rail from the first column. Story catalog/groups-table-h.

grid

  • Per-column widths: z >/z </z = on the grid noun (grid.resize-column, grid.reset-columns; Grid.SetColumnWidth, ColumnWidth, ColumnWidths, ResetColumnWidths). A given width replaces MaxCellWidth for its column and is remembered under the grid's id.

o-story

  • Goldens read C| (c| dimmed) for a highlighted row that holds a table's current cell; the expect field cell names the current cell's text.

v0.11.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 02:28

The terminal leader can wait for the next key indefinitely.

  • terminal-leader-timeout (TerminalLeaderTimeoutSetting, duration): how long leader_pending waits for the next key before it sends the leader alone. 0 never expires, so the leader waits like tmux's prefix; a positive value is the leader's own timeout. EnsureTerminalLeader declares it beside terminal-leader unless the app declared it first (with its own default); the framework default is DefaultBindingTimeout, and until settings.json holds a value the leader follows BindingTimeout. Other key sequences keep BindingTimeout. Story terminal/leader-timeout-zero-waits.

v0.10.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 27 Oct 01:18

An unfocused list keeps its selected row, faded; the dims are settings. A list can clear its filter when enter picks a row.

Unfocused lists and dims

  • A page list without the focus keeps its selected row in reverse video and is drawn faded as a whole, so the row an unfocused list is on (a tree beside a live terminal) stays visible and the focused row is the one highlight at full strength. A list in a modal stays plain until focused; a tree editing a line is not faded. Replaces the old rule "an unfocused list draws every row plain" (UX rule R5.1). Stories lists/unfocused-dimmed, lists/beside-terminal-keeps-row.
  • Two framework settings, declared by EnsureConfigSettings: dim-unfocused (percent an unfocused list fades toward the background, default 20) and modal-dim (the page behind a modal, default 35, the backdrop's look until now). Both 0 to 90 (MaxDim); 0 is off.
  • Styles.Fade(percent) blends the scheme's text colour toward its surface (lipgloss.Blend1D, perceptual); in mono or NO_COLOR any fade is the faint attribute. Styles.WithDims(modal, unfocused) sets the new Styles.Backdrop and Styles.Unfocused; Model.Styles() applies the settings. NewStyles uses the defaults.
  • o-story: highlighted counts full-strength highlights only; the new dimmed counts highlights under faint; goldens mark a dimmed-only row r|.

Lists

  • ClearFilterOnEnter() (an InstanceOption for AddList and AddTree): enter in the list's filter line chooses the selected match (list.filter-choose, guard list-chooses): the filter clears, the selection stays on that row and on screen, the mode is normal. With no match enter keeps the filter, as on every list; other lists are unchanged. Story lists/filter-enter-clears.

Fixes

  • Custom components' explicit Block.Cursor now reaches Model.View().Cursor without a focused input, using the normal block or visual underline shape. Blocks without a cursor remain hidden; input and terminal cursor shapes are unchanged.

v0.9.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 26 Oct 21:31

List motions

  • Every list answers Vim's paging motions in normal mode: g g and G go to the first and last row, and with a count to that row (5G, 5gg, list.goto {position, row}); ctrl+d/ctrl+u move half a page (a count is rows) and ctrl+f/ctrl+b a page (a count is pages) (list.scroll {delta, half, steps}). A page is the list's own window, else the page body's height; the window moves with the selection. They clamp at the list's edges, never spill, and are motions (off the footer, under movement in ?). A row noun that binds the same key keeps it. Story lists/page-motions.
  • A table with more columns than fit scrolls them (list.scroll-columns {to, delta, steps}, List.ColumnOffset): $ brings the last column into view, ^ (and 0 without a count) goes back, z l/z h move one column. The first column stays; the unscrolled view still drops columns by Priority. The keys answer only on a table that hides a column or is scrolled. Story tables/column-motions.
  • A key-sequence prefix whose only remaining row is its generated <prefix> esc cancel (every sequence on it guarded off) no longer waits for a second key.

v0.8.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 26 Oct 21:12

The Settings page is redesigned after tuios's: a list of fields, not a table.

A state verb answers what changed since the host's last poll instead of every row, and the catalog tells a tool's own verbs from the framework's. Every list filters with /, and an Update no longer visits list rows.

Settings page

  • Sections are tabs across the top instead of a rail, so the rows get the whole width. tab and ] show the next section, shift+tab and [ the previous, wrapping (settings.section {step}, or {name}); each section keeps its selected row. With one section there is no tab row.
  • One row per setting: its name on the left, its value on the right. There is no header and no Value/Default/Source columns. A • after the name marks a value that is not the default. ‹ value › marks a value h/l steps.
  • The description under the rows says what the default is and where the value comes from: not in settings.json, set in settings.json, or settings.json holds an invalid value (with the reason) so the default applies.
  • List settings: a list setting's row shows its item count ((none) when empty), and each item is a row directly beneath it with the name column blank. On a list's row enter or n adds an item at the end. On an item row enter or i edits it, n adds one after it, d d deletes it and r resets the whole list. An empty item is refused.
  • u (or ctrl+z) takes back the last change made on the page and shows its row. A value that was invalid in settings.json comes back as it was. u is offered only when there is a change to take back.
  • backspace and delete reset a setting, as r does.
  • The page reopens on the section and row it was left on.

State cache and deltas

  • A state verb run through cli.Shell takes --since LIST=HASH,... and --since-cache ID (cli.StateFlags, FormatSince/ParseSince; --since= asks for every list in full, with hashes) and answers JSON Lines: a header {"state":1,"cache":ID}, then per list unchanged, a delta (the changed and added rows whole at their indexes, the removed ids) or the list in full, each with its content hash (RecordHash, ListHash). An unknown hash, another cache id or a pruned version answers full, never wrong. The catalog declares stateDelta: 1.
  • The extension side caches each list's last versions (Shell.StateCache): (id, record hash) files under $XDG_CACHE_HOME/<bin>/state/<scope>/lists/<list>/, written temp + rename and content-addressed, Keep (2) versions per list and Scopes (8) scopes, the rest deleted. StateCache.Scope(args) adds to the scope (a TUIOS server); StateCache.Fingerprint(args) (StatFingerprint(paths...)) answers an unchanged poll without running the verb, except under --fresh; StateCache.Off turns it all off.
  • MountPage uses it: with PageConfig.FetchFlags a page's Reload sends its list hashes and applies the answer (PageView.ApplyState): unchanged touches nothing and skips OnRows, a delta goes through List.Patch, full through SetRows; a list whose hash does not match after applying is fetched again in full, once. PageView.Refresh forgets the hashes. An older extension (no stateDelta) keeps full answers through Fetch.
  • List.Patch(ListPatch{Upsert, At, Remove}) (and PatchItems) is SetItems over the patched rows: kept rows keep their instances, selection and marks, and only removed rows close. SetItems keeps a table's measured widths when every row draws the same cells.
  • cli.DecodeState and RowReader.Answer() read a delta answer. Benchmarks BenchmarkStatePoll and BenchmarkStateApply cover an 8,000-row poll, unchanged and with one row changed.

Builtin verbs

  • Every ochazuke tool's catalog lists the framework's verbs beside the tool's, and some are application verbs (the outline's 14, the chooser's 3), so hosts mounted them as the tool's (ai-outline, qbt-outline). Operation.Builtin now marks every operation the framework registers: everything NewRuntime and NewModel register, and what a framework constructor registers later (NewPicker, NewPalette, NewAppearance, grid.Register, markdown.Register, cli.AddCommands). An application's verb is never builtin, even on a framework noun (history.capture-hook).
  • The catalog's verbs carry builtin (cli.CatalogVerb.Builtin). CatalogDoc.AppNouns() is what a host mounting a tool's verbs mounts: each noun with only its non-builtin verbs (CatalogNoun.AppVerbs()), nouns left with none dropped. A MountPage list of a noun's verbs lists only the tool's.

Every list filters

  • / on a focused list (page lists, tables, catalog lists, the Settings page's section, a list in a modal) opens a query line above the rows; typing keeps the rows whose label or cells hold every word, ignoring case, in list order, N of M on the right. In the list_filter mode up/down and ctrl+n/ctrl+p move among the matches (no spill), enter keeps the filter (/ edits it, esc clears it), esc clears it and selects the row / found, backspace on an empty query leaves, ctrl+u empties it.
  • Rows are hidden, never removed: SetItems and Patch filter the new rows and keep the selection by id, marks survive, list.apply acts on the marked rows shown, focus landing on a hidden row clears the filter.
  • An application's own / wins at any level: the framework's is an app row (guard list-filterable), its / filter hint last on the footer.

Faster updates

  • Runtime.components() (every Deliver and PendingCommands) walks an index of the instances that are not list rows instead of sorting every instance, and the list verbs no longer copy every row into their result. BenchmarkUpdateKey at 16,000 rows: 818 µs to 11 µs a key; nabe's ai page 295 µs to 32 µs.

API changes

  • SettingList ("list"): a setting whose value is []string. settings.json keeps it as a JSON array, settings set NAME '["a","b"]' sets it, and Settings.Strings(name) reads a copy. A value of another shape is refused, as are an Enum and a default that is not a list of strings.

  • settings.section {step, name} and settings.undo. The nouns are setting-item (edit, add, delete, reset) and setting.add. The guards are setting-list and settings-undo.

  • A table whose columns have no titles draws no header (List.View).

  • The page's lists are __settings/list[/SECTION] (were __settings/table[/SECTION]); the __settings/sections rail and the settings-section noun are gone. Item rows are __settings/item/NAME/INDEX.

  • Operation.Builtin, Runtime.Builtin(register func()) (marks the operations register added; for framework packages), cli.CatalogVerb.Builtin, CatalogDoc.AppNouns(), CatalogNoun.AppVerbs().

  • list filter --target LIST --query Q (empty clears), list.filter-open|type|erase|keep|clear, list.move {clamp}; List.SetFilter, Filter, Filtering, Shown, Height (Len plus the query line); ListFilterMode. Items/Len stay every row; a filtered list's state has filter and total and its items are the rows shown.

  • cli.StateDeltaVersion, StateFlags, FormatSince, ParseSince, StateList, RecordHash, ListHash, StateCache, StatFingerprint, DecodeState, RowReader.Answer(), PageConfig.FetchFlags, PageView.ApplyState, PageView.Refresh, PageView.SinceFlags(); ochazuke.ListPatch, List.Patch, PatchItems.

Migrating

  • Stories or tests that read the old columns (Setting, Value, Default, Source) or the rail (Sections, __settings/section…) read the tabs, the rows and the description instead.
  • A host that mounts another tool's catalog verbs (nabe, mtm) mounts doc.AppNouns() instead of comparing keys against a fresh NewRuntime()+NewModel(). A catalog from a tool built before this release has no builtin, so every verb reads as the tool's; keep the old check as a fallback until the tools are rebuilt.