feat: item tags, tag view, and an editable detail view - #45
Merged
Conversation
Items carry a free-form lowercase tag set alongside their single category. Tags round-trip as one `tags: api, docs` meta line, parse from `#tag` (adds) and `tags:a,b` (replaces, bare key clears) in add/sub/edit, appear in `list --json`, and are matched by `--filter` and the board's `/` filter. Board changes: - `v` gains a tag view, grouping by an item's first tag (untagged last) and showing the rest on the row. - Rows carry a second flush-right label: whichever grouping axis the headers don't already name — priority in the category and tag views, category in the priority view. - `T` sets tags from the list or the detail view. - The detail view is now an editor: u/h/m/l/g/T/t/s/L/tab delegate to the list handlers and return to the detail view. Docs and help synced with the above; also fixes the note placeholder and detail footer key hints (`n`, not `e`), the `--board` usage line, and the stale "one flush-right value" help text. Includes a repo-wide simplification pass (-196 lines, no behaviour change): - store.ScanKV/ScanKVFile/ParseList replace four hand-rolled `key = value` scanners (config.toml, boards.toml, the embedded manifest). - cli.locked wraps the six mutating verbs' shared load→mutate→save lock dance; extractFlag replaces three near-identical flag strippers. - tui.prompt opens the shared text input; tui.spread lays out flush-right labels; one liveOnlyKeys guard replaces six in the board picker. - charts use ansi.Truncate, lipgloss width padding and sort.Slice instead of hand-rolled clip/pad/selection sort. - Drops the unused store.TodoPath and todo.SetSubStatus.
The detail view's three crammed hint lines become four labelled columns — fields / dates / item / go — matching the board footer. Pulls the layout out of helpGrid as keyGrid(cols, dim) plus a keyCol type, so both footers share the width spreading, key-column padding and faint-when-inert rendering; helpGrid and the new detailGrid are just their column lists and dim rules. Inert keys dim as they do in the list: all but o/esc/q in the read-only global view, and g/T on a subtask. Note mode keeps its single-line hint.
Adding with `a` while the cursor sits on a categorised row gave the new item no category, dropping it into the uncategorised tail — you then had to press g and retype the category you were looking at. addCategory sits beside filterCategory and falls back to it, so precedence is: an inline @category, then the selected row's category (a subtask row contributes its parent's), then a category filter's, then uncategorised. No selected row — an empty board, or a filter hiding every row — falls straight through to the filter rule, which the existing non-category-filter case relies on.
Pressing u/g/T/t/s/L from the detail view opened the right editor but drew it on the board: View dispatches on mode, and modeEdit/modeCategory/modeTags/modeDue/ modeDefer/modeLink all fell through to listView, so the item you were editing left the screen. m.fieldEdit — already set when an editor opens from the detail view — now selects detailView too, and the detail footer swaps its key grid for the prompt. The prompt line is one inputPrompt() shared with listFooter, so an editor reads the same wherever it was opened from. Also clears fieldEdit off the immediate mutations (h/m/l, tab). They delegate to updateList, which leaves the mode alone — it is already modeDetail — so the old `nm.mode == modeList` check never fired and the flag stayed set. Harmless while nothing read it; now it would pin a stale prompt in the footer. The check is `!nm.input.Focused()`: whether an editor actually opened, not a mode guess.
The footer grid shows both as live in the read-only aggregate, but updateGlobal had no case for either: A only worked one way (README says it toggles in and out, and esc was the sole way back), and F, a pure display preference, did nothing. Everything else in that grid already matches the handler — mutations are absent by structure, not per-case guards — so this makes the footer honest rather than dimming two keys that ought to work.
jwarykowski
force-pushed
the
feat/item-tags
branch
from
July 29, 2026 12:00
3828cee to
02db261
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Items gain a free-form tag set, the board gains a tag view and a second flush-right label, and the detail view becomes a place you can actually edit from. Includes a repo-wide simplification pass and the 0.19.0 bump.
Rebased onto
masternow that #44 has merged.tags
tags: api, docsmeta line;#tagadds,tags:a,breplaces, a bare key clears — the same grammaradd/sub/editalready use.list --json, matched by--filterand the board's/, described inshepherd schema.Tsets them from the list or the detail view.todo.Cloneshared theTagsbacking array with undo snapshots.board
vcycles a tag view: grouped by an item's first tag (untagged last), the rest shown on the row. Repeating an item under each of its tags would break the 1:1 row↔item index that the cursor, mutations and undo all rely on.ainherits the selected item's category, so a new item joins the group you were looking at. Precedence: inline@category, then the selected row, then a category filter, then uncategorised.detail view
u/h/m/l/g/T/t/s/L/tabdelegate to the list handlers (which already guard parent-only fields and re-place the cursor) and return here, not to the list.fields·dates·item·go.Viewdispatched on mode, so every editor mode fell through tolistViewand the item you were editing left the screen.simplification pass (-196 lines, no behaviour change)
store.ScanKV/ScanKVFile/ParseListreplace four hand-rolledkey = valuescanners (config.toml, boards.toml, the embedded manifest).cli.lockedwraps the six mutating verbs' shared load→mutate→save lock dance;extractFlagreplaces three near-identical flag strippers.tui.promptopens the shared text input,tui.spreadlays out flush-right labels,keyGridbacks both footers, oneliveOnlyKeysguard replaces six in the board picker.ansi.Truncate, lipgloss width padding andsort.Sliceinstead of hand-rolled clip/pad/selection sort.store.TodoPathandtodo.SetSubStatus.docs
README, AGENTS.md and the bundled skill cover tags, the tag view, the two right-hand labels and the detail editor. Also corrected the note-placeholder key (
n, note), the--boardusage line, and the stale "one flush-right value" help text.