Skip to content

v0.8.0

Choose a tag to compare

@unixsuperhero unixsuperhero released this 26 Oct 21:12
· 430 commits to main since this release

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.