From b4cfcde0250d75060596b4c61feaf4610e176751 Mon Sep 17 00:00:00 2001 From: Chris Birch Date: Sun, 8 Nov 2026 13:53:54 -0500 Subject: [PATCH 1/4] feat(tui): take the board's universal keys from gotermboard, and open an item's detail in place The board retyped gotermboard's key table: the row shape, the filter on applies, and j k g G ctrl+d ctrl+u J K [ ] ? q with near-identical labels. gotermboard now exports them, so the board's table is its own rows layered ahead of gotermboard's UniversalActions, acting on App as a Host. The modal lists, the picker, the move and the dependency links, keep their own movement, because each moves a cursor of its own. Enter and space are the shared rows too. Space opens an item's detail under its row however tall, and Enter opens it there where it fits the pane and full screen where it does not, because both show one document. The detail is the item's projects, created date, what blocks it and what it blocks, its tasks and its note, where it was the note alone, so every item now carries a chevron. A detail open in place is read again when the board reads its items. The footer's hints take gotermboard's words, such as "a add" for "[a]dd", two spaces apart, and the status bar pins "? keys" at its right. Agent-Name: claude-code-unattended Agent-Session: 86a41f0d-4642-4e49-bf73-b5688a1bb146 Agent-Run: 01a1bc5d-2bb7-7e54-bc32-13efbb7743af Worklog: 2605.1 --- CLAUDE.md | 11 +- go.mod | 4 +- go.sum | 8 +- real_binary_flows_test.go | 2 +- .../add-project-and-item/item-added.txt | 4 +- .../item-in-two-projects.txt | 6 +- .../add-project-and-item/item-prompt.style | 2 +- .../add-project-and-item/item-prompt.txt | 2 +- .../add-project-and-item/project-added.style | 4 +- .../add-project-and-item/project-added.txt | 8 +- .../project-picker-toggled.style | 2 +- .../project-picker-toggled.txt | 2 +- .../add-project-and-item/project-picker.style | 2 +- .../add-project-and-item/project-picker.txt | 2 +- .../project-prompt-typed.style | 2 +- .../project-prompt-typed.txt | 2 +- .../add-project-and-item/project-prompt.style | 2 +- .../add-project-and-item/project-prompt.txt | 2 +- .../add-task/added-from-the-list.style | 4 +- .../add-task/added-from-the-list.txt | 8 +- .../TestRealBinaryFlows/add-task/prompt.style | 2 +- .../TestRealBinaryFlows/add-task/prompt.txt | 2 +- .../add-task/task-deleted.style | 4 +- .../add-task/task-deleted.txt | 8 +- .../add-task/task-toggled.style | 4 +- .../add-task/task-toggled.txt | 8 +- .../api-mode/item-added.style | 4 +- .../api-mode/item-added.txt | 10 +- .../TestRealBinaryFlows/api-mode/opened.style | 4 +- .../TestRealBinaryFlows/api-mode/opened.txt | 8 +- .../browse/archived-filter.style | 4 +- .../browse/archived-filter.txt | 10 +- .../browse/blocked-filter.txt | 6 +- .../browse/cursor-on-last-item.style | 4 +- .../browse/cursor-on-last-item.txt | 8 +- .../browse/cursor-on-task.style | 4 +- .../browse/cursor-on-task.txt | 8 +- .../browse/first-screen.style | 4 +- .../browse/first-screen.txt | 8 +- .../browse/items-focused.style | 4 +- .../browse/items-focused.txt | 8 +- .../browse/last-project.txt | 26 +- .../browse/long-list-bottom.txt | 26 +- .../browse/long-list-half-page.txt | 26 +- .../browse/two-projects-selected.style | 4 +- .../browse/two-projects-selected.txt | 12 +- .../dependency-tree/back-in-the-tree.txt | 2 +- .../dependency-tree/flipped.txt | 2 +- .../dependency-tree/focus-pane.txt | 2 +- .../dependency-tree/forest.txt | 2 +- .../link-overlay-filtered.style | 2 +- .../dependency-tree/link-overlay-filtered.txt | 2 +- .../dependency-tree/link-overlay.style | 2 +- .../dependency-tree/link-overlay.txt | 2 +- .../dependency-tree/linked.style | 4 +- .../dependency-tree/linked.txt | 8 +- .../dependency-tree/unlink-overlay.style | 2 +- .../dependency-tree/unlink-overlay.txt | 2 +- .../detail-closed.style} | 4 +- .../detail-closed.txt} | 8 +- .../every-item-opens.style} | 4 +- .../every-item-opens.txt} | 8 +- .../item-stays-on-top.style} | 34 +- .../item-stays-on-top.txt | 32 +- .../scrolled-by-lines.style | 44 +-- .../scrolled-by-lines.txt} | 8 +- .../short-detail-in-place.style | 110 ++++++ .../detail-in-place/short-detail-in-place.txt | 30 ++ .../tall-detail-full-screen.style | 14 + .../tall-detail-full-screen.txt | 30 ++ .../tall-detail-in-place.style} | 58 ++- .../detail-in-place/tall-detail-in-place.txt | 30 ++ .../edit-note/note-in-place.style | 37 +- .../edit-note/note-in-place.txt | 18 +- .../edit-note/note-in-the-reader.style | 4 - .../edit-note/note-in-the-reader.txt | 30 -- .../edit-note/note-saved.txt | 6 +- .../filter/all-items-filtered.txt | 2 +- .../TestRealBinaryFlows/filter/cleared.style | 4 +- .../TestRealBinaryFlows/filter/cleared.txt | 8 +- .../filter/note-match-kept.txt | 2 +- .../filter/project-filter.txt | 26 +- .../filter/title-match.txt | 4 +- .../from-the-project-pane.style | 2 +- .../from-the-project-pane.txt | 2 +- .../move/carried-down.style | 6 +- .../TestRealBinaryFlows/move/carried-down.txt | 8 +- .../TestRealBinaryFlows/move/dropped.style | 4 +- testdata/TestRealBinaryFlows/move/dropped.txt | 8 +- .../TestRealBinaryFlows/move/grabbed.style | 6 +- testdata/TestRealBinaryFlows/move/grabbed.txt | 8 +- .../move/project-carried.style | 4 +- .../move/project-carried.txt | 8 +- .../TestRealBinaryFlows/move/put-back.style | 4 +- .../TestRealBinaryFlows/move/put-back.txt | 8 +- .../narrow-window/filter-prompt.style | 4 +- .../narrow-window/filter-prompt.txt | 4 +- .../narrow-window/first-screen.style | 4 +- .../narrow-window/first-screen.txt | 6 +- .../narrow-window/items-focused.style | 4 +- .../narrow-window/items-focused.txt | 6 +- .../narrow-window/task-prompt.style | 2 +- .../narrow-window/task-prompt.txt | 2 +- .../note-in-place/scrolled-by-lines.txt | 30 -- .../reader/closed-onto-the-list.style | 4 +- .../reader/closed-onto-the-list.txt | 8 +- .../reader/item-bottom.style | 11 +- .../reader/item-half-page-down.style | 14 +- .../TestRealBinaryFlows/reader/item-top.style | 8 +- .../TestRealBinaryFlows/reader/project.style | 6 +- .../undo/archive-undone.style | 4 +- .../undo/archive-undone.txt | 8 +- .../TestRealBinaryFlows/undo/archived.txt | 6 +- .../undo/refused-while-blocked.style | 4 +- .../undo/refused-while-blocked.txt | 8 +- testdata/TestRealBinaryFlows/undo/toggled.txt | 8 +- testdata/TestRealBinaryFlows/undo/undone.txt | 8 +- .../TestRealBinaryFlows/views/all-items.style | 4 +- .../TestRealBinaryFlows/views/all-items.txt | 12 +- .../views/back-to-by-project.txt | 8 +- .../views/by-project.style | 4 +- .../TestRealBinaryFlows/views/by-project.txt | 8 +- .../views/dependencies.txt | 2 +- .../views/group-folded.txt | 18 +- .../views/jumped-past-the-tasks.txt | 12 +- testdata/flows/add-task.keys | 2 +- testdata/flows/browse.keys | 14 +- testdata/flows/dependency-tree.keys | 4 +- testdata/flows/detail-in-place.keys | 46 +++ testdata/flows/edit-note.keys | 12 +- testdata/flows/filter.keys | 6 +- testdata/flows/help.keys | 14 +- testdata/flows/move.keys | 4 +- testdata/flows/narrow-window.keys | 4 +- testdata/flows/note-in-place.keys | 29 -- testdata/flows/reader.keys | 8 +- testdata/flows/undo.keys | 10 +- testdata/flows/views.keys | 2 +- tui/action_table.go | 338 ++++++++---------- tui/app.go | 93 ++--- tui/deptree.go | 20 +- tui/deptree_test.go | 11 +- tui/item_rows.go | 36 +- tui/key_host.go | 159 ++++++++ 144 files changed, 1110 insertions(+), 879 deletions(-) rename testdata/TestRealBinaryFlows/{note-in-place/note-closed.style => detail-in-place/detail-closed.style} (96%) rename testdata/TestRealBinaryFlows/{note-in-place/note-closed.txt => detail-in-place/detail-closed.txt} (87%) rename testdata/TestRealBinaryFlows/{note-in-place/chevron-only-where-it-opens.style => detail-in-place/every-item-opens.style} (96%) rename testdata/TestRealBinaryFlows/{note-in-place/chevron-only-where-it-opens.txt => detail-in-place/every-item-opens.txt} (87%) rename testdata/TestRealBinaryFlows/{note-in-place/note-open.style => detail-in-place/item-stays-on-top.style} (79%) rename testdata/TestRealBinaryFlows/{note-in-place => detail-in-place}/item-stays-on-top.txt (61%) rename testdata/TestRealBinaryFlows/{note-in-place => detail-in-place}/scrolled-by-lines.style (75%) rename testdata/TestRealBinaryFlows/{note-in-place/note-open.txt => detail-in-place/scrolled-by-lines.txt} (92%) create mode 100644 testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.style create mode 100644 testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.txt create mode 100644 testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.style create mode 100644 testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.txt rename testdata/TestRealBinaryFlows/{note-in-place/item-stays-on-top.style => detail-in-place/tall-detail-in-place.style} (70%) create mode 100644 testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.txt delete mode 100644 testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.style delete mode 100644 testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.txt delete mode 100644 testdata/TestRealBinaryFlows/note-in-place/scrolled-by-lines.txt create mode 100644 testdata/flows/detail-in-place.keys delete mode 100644 testdata/flows/note-in-place.keys create mode 100644 tui/key_host.go diff --git a/CLAUDE.md b/CLAUDE.md index 97aa82c..b6df388 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -171,9 +171,14 @@ builds the tree. Help renders through `gotermstyle/cobrahelp`. - **Every key the board takes is a row in `tui/action_table.go`, in every mode.** The key handler, the hints in the status bar and in each prompt, picker and overlay, and the `?` overlay all read it. So a - key added there shows in each, and a row's `applies` keeps it out of each where it does not act. A - mode with a text box binds only keys that type nothing, and `typesText` hands the rest to the box. - The hosted reader takes its own keys while it shows. + key added there shows in each, and a row's `Applies` keeps it out of each where it does not act. A + row is gotermboard's `KeyAction`. On the board, `j` `k` `g` `G`, `ctrl+d` `ctrl+u`, `J` `K`, `[` + `]`, Enter, space, `?` and `q` are gotermboard's `UniversalActions`, acting on `App` as a + `gotermboard.Host` in `tui/key_host.go`, and the board's own rows take a key ahead of them where + they apply. A mode with a text box binds only keys that type nothing, and `typesText` hands the + rest to the box. The hosted reader takes its own keys while it shows. +- **Enter and space open an item's detail, the page its reader shows.** Space opens it under the + row however tall. Enter opens it there where it fits the pane, and full screen where it does not. - **The board hosts gotermboard's `Reader` and not its `Model`.** Its own panes, glyphs and footer are drawn in `tui`, so `App` forwards keys, pastes and window sizes to the reader while one shows, and `Reader.ClosesWith` hands the screen back. diff --git a/go.mod b/go.mod index af66b1c..e2c46f7 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/datapointchris/goclikit v0.5.3 github.com/datapointchris/goclilogin v0.9.0 github.com/datapointchris/goselfupdate v0.11.0 - github.com/datapointchris/gotermboard v0.31.0 - github.com/datapointchris/gotermstyle v0.4.0 + github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a + github.com/datapointchris/gotermstyle v0.5.0 github.com/datapointchris/tuidrive v0.1.0 github.com/go-viper/mapstructure/v2 v2.4.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index f1c2d87..149b492 100644 --- a/go.sum +++ b/go.sum @@ -91,10 +91,10 @@ github.com/datapointchris/goclilogin v0.9.0 h1:xajsKfzuBvuREUZqw7B9Gv5PHxylSc8ss github.com/datapointchris/goclilogin v0.9.0/go.mod h1:oiMIbY0jwfqH5pX8G1T1iBPa+cG1m3JQhqpaP9JxJ+w= github.com/datapointchris/goselfupdate v0.11.0 h1:aUJ27Cm9ZNGMfC7+kKChBsnm8offBE99wwUa10lFo2w= github.com/datapointchris/goselfupdate v0.11.0/go.mod h1:YcgyvM+wH6nh9zo9xioUqTHeH5HTYeCHevv4U+G7INc= -github.com/datapointchris/gotermboard v0.31.0 h1:Ri8KUGjPXoS5xM+/PA4IPicwzzNKKxMVwa2jH0ZtFbc= -github.com/datapointchris/gotermboard v0.31.0/go.mod h1:gzSG1LtOC2McInuKV5mSFJOLZ3EnFIY/1chPyivuxe0= -github.com/datapointchris/gotermstyle v0.4.0 h1:kOovTrAVF4oI/P6ptNg5VTuU9woNpPyjrhcKd6ndJsI= -github.com/datapointchris/gotermstyle v0.4.0/go.mod h1:pcTXWC/xls5Riw0rFtTueCQGlnszB3sqlepRItgdgeg= +github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a h1:ECJfA1i2DjXoENu8jhr4hlaVWLuXq7WtHnD6EnML440= +github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a/go.mod h1:A/9phVumidQPEVvHBLHTIWGgMfwbyDT+1/0iOHtjmao= +github.com/datapointchris/gotermstyle v0.5.0 h1:xdpuNASa4vS0jSMpBDQOO0rRFGY7jaft5H3hTHOrREE= +github.com/datapointchris/gotermstyle v0.5.0/go.mod h1:pcTXWC/xls5Riw0rFtTueCQGlnszB3sqlepRItgdgeg= github.com/datapointchris/tuidrive v0.1.0 h1:s6esDWL9P8EkQYzdvrPwvBxYjPF98u0ukpnZbfBIGOc= github.com/datapointchris/tuidrive v0.1.0/go.mod h1:lbRNaZkLTfPvfVqkJlcCzfuItgbePNp1flvS70mf/gI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/real_binary_flows_test.go b/real_binary_flows_test.go index 1900075..6818e8e 100644 --- a/real_binary_flows_test.go +++ b/real_binary_flows_test.go @@ -41,7 +41,7 @@ func TestRealBinaryFlows(t *testing.T) { {name: "views", script: "views.keys"}, {name: "filter", script: "filter.keys"}, {name: "move", script: "move.keys"}, - {name: "note-in-place", script: "note-in-place.keys"}, + {name: "detail-in-place", script: "detail-in-place.keys"}, {name: "reader", script: "reader.keys"}, {name: "add-project-and-item", script: "add-project-and-item.keys"}, {name: "add-task", script: "add-task.keys"}, diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/item-added.txt b/testdata/TestRealBinaryFlows/add-project-and-item/item-added.txt index 2733c76..f904edc 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/item-added.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/item-added.txt @@ -1,7 +1,7 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Repaint the fence ┃ -│ Move the blog to plain files ○3 ✓1 │┃> ○ Scrape and prime the boards ┃ +│ Move the blog to plain files ○3 ✓1 │┃> ▸ ○ Scrape and prime the boards ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 39┃ │ Plan the Lisbon trip ○31 ✓0 │┃ ┃ │> Repaint the fence ○1 ✓0 │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item created [?]help LOCAL projects.db + Item created ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/item-in-two-projects.txt b/testdata/TestRealBinaryFlows/add-project-and-item/item-in-two-projects.txt index 941e430..c678023 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/item-in-two-projects.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/item-in-two-projects.txt @@ -1,9 +1,9 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Repaint the fence ┃ -│ Move the blog to plain files ○3 ✓1 │┃> ○ Scrape and prime the boards ┃ +│ Move the blog to plain files ○3 ✓1 │┃> ▸ ○ Scrape and prime the boards ┃ │ Replace the kitchen faucet ○4 ✓0 │┃ 39┃ -│ Plan the Lisbon trip ○31 ✓0 │┃ ○ Buy exterior paint ◈ ┃ +│ Plan the Lisbon trip ○31 ✓0 │┃ ▸ ○ Buy exterior paint ◈ ┃ │> Repaint the fence ○2 ✓0 │┃ also in Replace the kitchen faucet 40┃ │ │┃ ┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item created [?]help LOCAL projects.db + Item created ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.style b/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.style index 899521f..6d85653 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.style @@ -10,7 +10,7 @@ 15: 81-81 fg=4 16: 20-81 fg=4 17: 20-20 fg=4 -17: 25-50 fg=8 +17: 25-49 fg=8 17: 81-81 fg=4 18: 20-81 fg=4 19: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.txt b/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.txt index c897c13..fea2d4e 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/item-prompt.txt @@ -14,6 +14,6 @@ │ │ │ > Enter item title... │ │ │ - │ [Enter]confirm [Esc]cancel │ + │ enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-added.style b/testdata/TestRealBinaryFlows/add-project-and-item/project-added.style index 3ff413f..7d6f87a 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-added.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-added.style @@ -43,7 +43,9 @@ 8: 100-100 fg=8 9: 1-40 fg=4 9: 41-41 fg=8 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-added.txt b/testdata/TestRealBinaryFlows/add-project-and-item/project-added.txt index be470d5..d26b609 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-added.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-added.txt @@ -6,12 +6,12 @@ ┃ Plan the Lisbon trip ○31 ✓0 ┃│ ✓ Dump the database │ ┃ Repaint the fence ○0 ✓0 ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - Project created [?]help LOCAL projects.db + Project created ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.style b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.style index fd62690..2c64010 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.style @@ -23,7 +23,7 @@ 19: 81-81 fg=4 20: 20-81 fg=4 21: 20-20 fg=4 -21: 25-64 fg=8 +21: 25-63 fg=8 21: 81-81 fg=4 22: 20-81 fg=4 23: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.txt b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.txt index 757db62..91d7d0c 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker-toggled.txt @@ -18,6 +18,6 @@ │ │ │ Currently in 2 project(s). Must belong to at least 1. │ │ │ - │ [space]toggle [Enter]confirm [Esc]cancel │ + │ space toggle enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.style b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.style index 2b4e2e3..1f406f4 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.style @@ -23,7 +23,7 @@ 19: 81-81 fg=4 20: 20-81 fg=4 21: 20-20 fg=4 -21: 25-64 fg=8 +21: 25-63 fg=8 21: 81-81 fg=4 22: 20-81 fg=4 23: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.txt b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.txt index 551d6a1..ca1ac6f 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-picker.txt @@ -18,6 +18,6 @@ │ │ │ Currently in 1 project(s). Must belong to at least 1. │ │ │ - │ [space]toggle [Enter]confirm [Esc]cancel │ + │ space toggle enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.style b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.style index d439f37..d94571c 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.style @@ -9,7 +9,7 @@ 15: 81-81 fg=4 16: 20-81 fg=4 17: 20-20 fg=4 -17: 25-50 fg=8 +17: 25-49 fg=8 17: 81-81 fg=4 18: 20-81 fg=4 19: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.txt b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.txt index f0d786d..9bd1f40 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt-typed.txt @@ -14,6 +14,6 @@ │ │ │ > Repaint the fence │ │ │ - │ [Enter]confirm [Esc]cancel │ + │ enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.style b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.style index 340d264..40098bd 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.style +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.style @@ -10,7 +10,7 @@ 15: 81-81 fg=4 16: 20-81 fg=4 17: 20-20 fg=4 -17: 25-50 fg=8 +17: 25-49 fg=8 17: 81-81 fg=4 18: 20-81 fg=4 19: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.txt b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.txt index 2f05fd3..c43e395 100644 --- a/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.txt +++ b/testdata/TestRealBinaryFlows/add-project-and-item/project-prompt.txt @@ -14,6 +14,6 @@ │ │ │ > Enter project name... │ │ │ - │ [Enter]confirm [Esc]cancel │ + │ enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-task/added-from-the-list.style b/testdata/TestRealBinaryFlows/add-task/added-from-the-list.style index 661002b..fce6e97 100644 --- a/testdata/TestRealBinaryFlows/add-task/added-from-the-list.style +++ b/testdata/TestRealBinaryFlows/add-task/added-from-the-list.style @@ -45,7 +45,9 @@ 9: 100-100 fg=4 10: 1-40 fg=8 10: 41-41 fg=4 -10: 42-46 strike +10: 42-44 strike +10: 45-45 fg=8 strike +10: 46-46 strike 10: 47-47 fg=8 strike 10: 48-48 strike 10: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/add-task/added-from-the-list.txt b/testdata/TestRealBinaryFlows/add-task/added-from-the-list.txt index c92cf4b..8dccc18 100644 --- a/testdata/TestRealBinaryFlows/add-task/added-from-the-list.txt +++ b/testdata/TestRealBinaryFlows/add-task/added-from-the-list.txt @@ -7,12 +7,12 @@ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ │ │┃ ○ Check the image links ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove [Tab]view … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/add-task/prompt.style b/testdata/TestRealBinaryFlows/add-task/prompt.style index 9cc294b..4d5ccd8 100644 --- a/testdata/TestRealBinaryFlows/add-task/prompt.style +++ b/testdata/TestRealBinaryFlows/add-task/prompt.style @@ -10,7 +10,7 @@ 15: 81-81 fg=4 16: 20-81 fg=4 17: 20-20 fg=4 -17: 25-50 fg=8 +17: 25-49 fg=8 17: 81-81 fg=4 18: 20-81 fg=4 19: 20-81 fg=4 diff --git a/testdata/TestRealBinaryFlows/add-task/prompt.txt b/testdata/TestRealBinaryFlows/add-task/prompt.txt index 6e2bcee..94999e9 100644 --- a/testdata/TestRealBinaryFlows/add-task/prompt.txt +++ b/testdata/TestRealBinaryFlows/add-task/prompt.txt @@ -14,6 +14,6 @@ │ │ │ > New task... │ │ │ - │ [Enter]confirm [Esc]cancel │ + │ enter confirm esc cancel │ │ │ ╰────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/add-task/task-deleted.style b/testdata/TestRealBinaryFlows/add-task/task-deleted.style index 64b1808..dc4735a 100644 --- a/testdata/TestRealBinaryFlows/add-task/task-deleted.style +++ b/testdata/TestRealBinaryFlows/add-task/task-deleted.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/add-task/task-deleted.txt b/testdata/TestRealBinaryFlows/add-task/task-deleted.txt index 707abd1..6c6c52d 100644 --- a/testdata/TestRealBinaryFlows/add-task/task-deleted.txt +++ b/testdata/TestRealBinaryFlows/add-task/task-deleted.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ > ○ Download the images ┃ │ │┃ ○ Check the image links ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [a]dd [space]done [D]elete [Tab]view [/]filter [?]help LOCAL projects.db + a add space done D delete tab view / filter j/k move [/] jump … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/add-task/task-toggled.style b/testdata/TestRealBinaryFlows/add-task/task-toggled.style index 1838d09..406a24b 100644 --- a/testdata/TestRealBinaryFlows/add-task/task-toggled.style +++ b/testdata/TestRealBinaryFlows/add-task/task-toggled.style @@ -52,7 +52,9 @@ 9: 100-100 fg=4 10: 1-40 fg=8 10: 41-41 fg=4 -10: 42-46 strike +10: 42-44 strike +10: 45-45 fg=8 strike +10: 46-46 strike 10: 47-47 fg=8 strike 10: 48-48 strike 10: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/add-task/task-toggled.txt b/testdata/TestRealBinaryFlows/add-task/task-toggled.txt index daa04e6..07f1094 100644 --- a/testdata/TestRealBinaryFlows/add-task/task-toggled.txt +++ b/testdata/TestRealBinaryFlows/add-task/task-toggled.txt @@ -7,12 +7,12 @@ │ │┃ > ✓ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ │ │┃ ○ Check the image links ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [a]dd [space]done [D]elete [Tab]view [/]filter [?]help LOCAL projects.db + a add space done D delete tab view / filter j/k move [/] jump … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/api-mode/item-added.style b/testdata/TestRealBinaryFlows/api-mode/item-added.style index a54150e..30552eb 100644 --- a/testdata/TestRealBinaryFlows/api-mode/item-added.style +++ b/testdata/TestRealBinaryFlows/api-mode/item-added.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/api-mode/item-added.txt b/testdata/TestRealBinaryFlows/api-mode/item-added.txt index b51c119..d57354b 100644 --- a/testdata/TestRealBinaryFlows/api-mode/item-added.txt +++ b/testdata/TestRealBinaryFlows/api-mode/item-added.txt @@ -6,15 +6,15 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ -│ │┃ ○ Rotate the API keys ┃ +│ │┃ ▸ ○ Rotate the API keys ┃ │ │┃ 39┃ │ │┃ ┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item created [?]help API 127.0.0.1: + Item created ? keys API 127.0.0.1: diff --git a/testdata/TestRealBinaryFlows/api-mode/opened.style b/testdata/TestRealBinaryFlows/api-mode/opened.style index be69960..c61a32d 100644 --- a/testdata/TestRealBinaryFlows/api-mode/opened.style +++ b/testdata/TestRealBinaryFlows/api-mode/opened.style @@ -41,7 +41,9 @@ 8: 100-100 fg=8 9: 1-40 fg=4 9: 41-41 fg=8 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/api-mode/opened.txt b/testdata/TestRealBinaryFlows/api-mode/opened.txt index 0c35ffe..7612def 100644 --- a/testdata/TestRealBinaryFlows/api-mode/opened.txt +++ b/testdata/TestRealBinaryFlows/api-mode/opened.txt @@ -6,12 +6,12 @@ ┃ Plan the Lisbon trip ○31 ✓0 ┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]read [space]mark [a]dd [e]dit [M]ove [Tab]view [/]filter [?]help API 127.0.0.1: + enter read space mark a add e edit M move tab view / filter … ? keys API 127.0.0.1: diff --git a/testdata/TestRealBinaryFlows/browse/archived-filter.style b/testdata/TestRealBinaryFlows/browse/archived-filter.style index 5ed016d..bedc30c 100644 --- a/testdata/TestRealBinaryFlows/browse/archived-filter.style +++ b/testdata/TestRealBinaryFlows/browse/archived-filter.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/archived-filter.txt b/testdata/TestRealBinaryFlows/browse/archived-filter.txt index aa01995..f758e5a 100644 --- a/testdata/TestRealBinaryFlows/browse/archived-filter.txt +++ b/testdata/TestRealBinaryFlows/browse/archived-filter.txt @@ -6,15 +6,15 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃> ○ Set up redirects for the old URLs ┃ +│ │┃> ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ -│ │┃ ○ Compare hosting quotes ┃ +│ │┃ ▸ ○ Compare hosting quotes ┃ │ │┃ 5┃ │ │┃ ┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter 0 reset … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/blocked-filter.txt b/testdata/TestRealBinaryFlows/browse/blocked-filter.txt index 64c3541..a731b1f 100644 --- a/testdata/TestRealBinaryFlows/browse/blocked-filter.txt +++ b/testdata/TestRealBinaryFlows/browse/blocked-filter.txt @@ -1,10 +1,10 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Move the blog to plain files [BLOCKED] ┃ -│> Move the blog to plain files ○3 ✓1 │┃> ○ Set up redirects for the old URLs ┃ +│> Move the blog to plain files ○3 ✓1 │┃> ▸ ○ Set up redirects for the old URLs ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 3┃ │ Plan the Lisbon trip ○31 ✓0 │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter 0 reset … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.style b/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.style index 9b784d6..d89e4df 100644 --- a/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.style +++ b/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.style @@ -41,7 +41,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.txt b/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.txt index 8c67a69..4e2f625 100644 --- a/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.txt +++ b/testdata/TestRealBinaryFlows/browse/cursor-on-last-item.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃> ○ Point DNS at the new host ┃ +│ │┃> ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/cursor-on-task.style b/testdata/TestRealBinaryFlows/browse/cursor-on-task.style index 3c9b79a..b0461b9 100644 --- a/testdata/TestRealBinaryFlows/browse/cursor-on-task.style +++ b/testdata/TestRealBinaryFlows/browse/cursor-on-task.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/cursor-on-task.txt b/testdata/TestRealBinaryFlows/browse/cursor-on-task.txt index bb436f0..9b4de0a 100644 --- a/testdata/TestRealBinaryFlows/browse/cursor-on-task.txt +++ b/testdata/TestRealBinaryFlows/browse/cursor-on-task.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ > ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [a]dd [space]done [D]elete [Tab]view [/]filter [?]help LOCAL projects.db + a add space done D delete tab view / filter j/k move [/] jump … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/first-screen.style b/testdata/TestRealBinaryFlows/browse/first-screen.style index a174474..9aa3b0f 100644 --- a/testdata/TestRealBinaryFlows/browse/first-screen.style +++ b/testdata/TestRealBinaryFlows/browse/first-screen.style @@ -41,7 +41,9 @@ 8: 100-100 fg=8 9: 1-40 fg=4 9: 41-41 fg=8 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/first-screen.txt b/testdata/TestRealBinaryFlows/browse/first-screen.txt index 353d508..51243aa 100644 --- a/testdata/TestRealBinaryFlows/browse/first-screen.txt +++ b/testdata/TestRealBinaryFlows/browse/first-screen.txt @@ -6,12 +6,12 @@ ┃ Plan the Lisbon trip ○31 ✓0 ┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]read [space]mark [a]dd [e]dit [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + enter read space mark a add e edit M move tab view / filter … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/items-focused.style b/testdata/TestRealBinaryFlows/browse/items-focused.style index 097e7f9..e99a40d 100644 --- a/testdata/TestRealBinaryFlows/browse/items-focused.style +++ b/testdata/TestRealBinaryFlows/browse/items-focused.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/items-focused.txt b/testdata/TestRealBinaryFlows/browse/items-focused.txt index 4084d16..a8dbe3b 100644 --- a/testdata/TestRealBinaryFlows/browse/items-focused.txt +++ b/testdata/TestRealBinaryFlows/browse/items-focused.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove [Tab]view … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/last-project.txt b/testdata/TestRealBinaryFlows/browse/last-project.txt index 9958dde..00753ff 100644 --- a/testdata/TestRealBinaryFlows/browse/last-project.txt +++ b/testdata/TestRealBinaryFlows/browse/last-project.txt @@ -1,30 +1,30 @@ By project All items Dependencies ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓╭──────────────────────────────────────────────────────────╮ ┃ Projects ┃│ Plan the Lisbon trip │ -┃ Move the blog to plain files ○3 ✓1 ┃│> ○ Ask Sam for a plumber and a place to stay ◈ │ +┃ Move the blog to plain files ○3 ✓1 ┃│> ▸ ○ Ask Sam for a plumber and a place to stay ◈ │ ┃ Replace the kitchen faucet ○3 ✓0 ┃│ also in Replace the kitchen faucet 8│ -┃> Plan the Lisbon trip ○31 ✓0 ┃│ ○ Write up the trip on the new blog │ +┃> Plan the Lisbon trip ○31 ✓0 ┃│ ▸ ○ Write up the trip on the new blog │ ┃ ┃│ 9│ ┃ ┃│ └─ blocked by: Move the blog…: Point DNS at th… (4)│ -┃ ┃│ ○ Read every neighborhood guide and write down which…│ +┃ ┃│ ▸ ○ Read every neighborhood guide and write down which…│ ┃ ┃│ 10│ -┃ ┃│ ○ Day 1: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 1: walk the next neighborhood │ ┃ ┃│ 11│ -┃ ┃│ ○ Day 2: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 2: walk the next neighborhood │ ┃ ┃│ 12│ -┃ ┃│ ○ Day 3: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 3: walk the next neighborhood │ ┃ ┃│ 13│ -┃ ┃│ ○ Day 4: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 4: walk the next neighborhood │ ┃ ┃│ 14│ -┃ ┃│ ○ Day 5: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 5: walk the next neighborhood │ ┃ ┃│ 15│ -┃ ┃│ ○ Day 6: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 6: walk the next neighborhood │ ┃ ┃│ 16│ -┃ ┃│ ○ Day 7: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 7: walk the next neighborhood │ ┃ ┃│ 17│ -┃ ┃│ ○ Day 8: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 8: walk the next neighborhood │ ┃ ┃│ 18│ -┃ ┃│ ○ Day 9: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 9: walk the next neighborhood │ ┃ ┃│ 1/31 │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]read [space]mark [a]dd [e]dit [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + enter read space mark a add e edit M move tab view / filter … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/long-list-bottom.txt b/testdata/TestRealBinaryFlows/browse/long-list-bottom.txt index 5b37d55..5009c52 100644 --- a/testdata/TestRealBinaryFlows/browse/long-list-bottom.txt +++ b/testdata/TestRealBinaryFlows/browse/long-list-bottom.txt @@ -1,30 +1,30 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Plan the Lisbon trip ┃ -│ Move the blog to plain files ○3 ✓1 │┃ ○ Day 17: walk the next neighborhood ┃ +│ Move the blog to plain files ○3 ✓1 │┃ ▸ ○ Day 17: walk the next neighborhood ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 27┃ -│> Plan the Lisbon trip ○31 ✓0 │┃ ○ Day 18: walk the next neighborhood ┃ +│> Plan the Lisbon trip ○31 ✓0 │┃ ▸ ○ Day 18: walk the next neighborhood ┃ │ │┃ 28┃ -│ │┃ ○ Day 19: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 19: walk the next neighborhood ┃ │ │┃ 29┃ -│ │┃ ○ Day 20: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 20: walk the next neighborhood ┃ │ │┃ 30┃ -│ │┃ ○ Day 21: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 21: walk the next neighborhood ┃ │ │┃ 31┃ -│ │┃ ○ Day 22: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 22: walk the next neighborhood ┃ │ │┃ 32┃ -│ │┃ ○ Day 23: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 23: walk the next neighborhood ┃ │ │┃ 33┃ -│ │┃ ○ Day 24: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 24: walk the next neighborhood ┃ │ │┃ 34┃ -│ │┃ ○ Day 25: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 25: walk the next neighborhood ┃ │ │┃ 35┃ -│ │┃ ○ Day 26: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 26: walk the next neighborhood ┃ │ │┃ 36┃ -│ │┃ ○ Day 27: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 27: walk the next neighborhood ┃ │ │┃ 37┃ -│ │┃> ○ Day 28: walk the next neighborhood ┃ +│ │┃> ▸ ○ Day 28: walk the next neighborhood ┃ │ │┃ 38┃ │ │┃ 31/31 ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/long-list-half-page.txt b/testdata/TestRealBinaryFlows/browse/long-list-half-page.txt index 421d227..c48f15f 100644 --- a/testdata/TestRealBinaryFlows/browse/long-list-half-page.txt +++ b/testdata/TestRealBinaryFlows/browse/long-list-half-page.txt @@ -1,30 +1,30 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Plan the Lisbon trip ┃ -│ Move the blog to plain files ○3 ✓1 │┃ ○ Write up the trip on the new blog ┃ +│ Move the blog to plain files ○3 ✓1 │┃ ▸ ○ Write up the trip on the new blog ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ └─ blocked by: Move the blog…: Point DNS at th… (4)┃ -│> Plan the Lisbon trip ○31 ✓0 │┃ ○ Read every neighborhood guide and write down which…┃ +│> Plan the Lisbon trip ○31 ✓0 │┃ ▸ ○ Read every neighborhood guide and write down which…┃ │ │┃ 10┃ -│ │┃ ○ Day 1: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 1: walk the next neighborhood ┃ │ │┃ 11┃ -│ │┃ ○ Day 2: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 2: walk the next neighborhood ┃ │ │┃ 12┃ -│ │┃ ○ Day 3: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 3: walk the next neighborhood ┃ │ │┃ 13┃ -│ │┃ ○ Day 4: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 4: walk the next neighborhood ┃ │ │┃ 14┃ -│ │┃ ○ Day 5: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 5: walk the next neighborhood ┃ │ │┃ 15┃ -│ │┃ ○ Day 6: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 6: walk the next neighborhood ┃ │ │┃ 16┃ -│ │┃ ○ Day 7: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 7: walk the next neighborhood ┃ │ │┃ 17┃ -│ │┃ ○ Day 8: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 8: walk the next neighborhood ┃ │ │┃ 18┃ -│ │┃ ○ Day 9: walk the next neighborhood ┃ +│ │┃ ▸ ○ Day 9: walk the next neighborhood ┃ │ │┃ 19┃ -│ │┃> ○ Day 10: walk the next neighborhood ┃ +│ │┃> ▸ ○ Day 10: walk the next neighborhood ┃ │ │┃ 20┃ │ │┃ 13/31 ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/browse/two-projects-selected.style b/testdata/TestRealBinaryFlows/browse/two-projects-selected.style index de2ed17..8a6db89 100644 --- a/testdata/TestRealBinaryFlows/browse/two-projects-selected.style +++ b/testdata/TestRealBinaryFlows/browse/two-projects-selected.style @@ -47,7 +47,9 @@ 9: 100-100 fg=8 10: 1-40 fg=4 10: 41-41 fg=8 -10: 42-46 strike +10: 42-44 strike +10: 45-45 fg=8 strike +10: 46-46 strike 10: 47-47 fg=8 strike 10: 48-48 strike 10: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/browse/two-projects-selected.txt b/testdata/TestRealBinaryFlows/browse/two-projects-selected.txt index e4d8a73..f06cc20 100644 --- a/testdata/TestRealBinaryFlows/browse/two-projects-selected.txt +++ b/testdata/TestRealBinaryFlows/browse/two-projects-selected.txt @@ -7,24 +7,24 @@ ┃ ┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ ┃ ┃│ ▾ ── Replace the kitchen faucet ── ○3 ✓0 │ ┃ ┃│ ▸ ○ Buy the faucet ≡ │ ┃ ┃│ 6│ -┃ ┃│ ○ Shut off the water and swap it │ +┃ ┃│ ▸ ○ Shut off the water and swap it │ ┃ ┃│ 7│ ┃ ┃│ └─ blocked by: Buy the faucet (6) │ -┃ ┃│ ○ Ask Sam for a plumber and a place to stay ◈ │ +┃ ┃│ ▸ ○ Ask Sam for a plumber and a place to stay ◈ │ ┃ ┃│ also in Plan the Lisbon trip 8│ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]read [space]mark [a]dd [e]dit [M]ove [Esc]back [Tab]view … [?]help LOCAL projects.db + enter read space mark a add e edit M move esc back tab view … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/back-in-the-tree.txt b/testdata/TestRealBinaryFlows/dependency-tree/back-in-the-tree.txt index cb236af..a73ab6b 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/back-in-the-tree.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/back-in-the-tree.txt @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰────────────────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [l]drill [h]back [Enter]read [i]flip [.]finished [Esc]back … [?]help LOCAL projects.db + l drill h back enter read i flip . finished esc back tab view … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/flipped.txt b/testdata/TestRealBinaryFlows/dependency-tree/flipped.txt index 2e37ace..7ec039c 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/flipped.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/flipped.txt @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰────────────────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [l]drill [h]back [Enter]read [i]flip [.]finished [Esc]back … [?]help LOCAL projects.db + l drill h back enter read i flip . finished esc back tab view … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/focus-pane.txt b/testdata/TestRealBinaryFlows/dependency-tree/focus-pane.txt index cb236af..a73ab6b 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/focus-pane.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/focus-pane.txt @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰────────────────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [l]drill [h]back [Enter]read [i]flip [.]finished [Esc]back … [?]help LOCAL projects.db + l drill h back enter read i flip . finished esc back tab view … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/forest.txt b/testdata/TestRealBinaryFlows/dependency-tree/forest.txt index 4d987eb..874c596 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/forest.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/forest.txt @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰────────────────────────────────────────────────╯ - [l]drill [Enter]read [.]finished [Tab]view [/]filter [?]help LOCAL projects.db + l drill enter read . finished tab view / filter j/k move q quit ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.style b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.style index 2618826..5de1ba7 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.style +++ b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.style @@ -17,7 +17,7 @@ 17: 91-91 fg=4 18: 10-91 fg=4 19: 10-10 fg=4 -19: 15-36 fg=8 +19: 15-35 fg=8 19: 91-91 fg=4 20: 10-91 fg=4 21: 10-91 fg=4 diff --git a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.txt b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.txt index 6ce7ecd..a8bb3c4 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay-filtered.txt @@ -16,6 +16,6 @@ │ ── Replace the kitchen faucet ── │ │ > ○ Buy the faucet 6 │ │ │ - │ [Enter]keep [Esc]clear │ + │ enter keep esc clear │ │ │ ╰────────────────────────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.style b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.style index 9904ede..9e709fb 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.style +++ b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.style @@ -74,7 +74,7 @@ 26: 91-91 fg=4 27: 10-91 fg=4 28: 10-10 fg=4 -28: 15-47 fg=8 +28: 15-46 fg=8 28: 91-91 fg=4 29: 10-91 fg=4 30: 10-91 fg=4 diff --git a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.txt b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.txt index 314bc9d..276467d 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/link-overlay.txt @@ -25,6 +25,6 @@ │ ○ Day 8: walk the next neighborhood 18 │ │ ○ Day 9: walk the next neighborhood 19 │ │ │ - │ [Enter]link [/]filter [Esc]cancel │ + │ enter link / filter esc cancel │ │ │ ╰────────────────────────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/dependency-tree/linked.style b/testdata/TestRealBinaryFlows/dependency-tree/linked.style index 01e6234..ec1b513 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/linked.style +++ b/testdata/TestRealBinaryFlows/dependency-tree/linked.style @@ -47,7 +47,9 @@ 9: 100-100 fg=4 10: 1-40 fg=8 10: 41-41 fg=4 -10: 42-46 strike +10: 42-44 strike +10: 45-45 fg=8 strike +10: 46-46 strike 10: 47-47 fg=8 strike 10: 48-48 strike 10: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/dependency-tree/linked.txt b/testdata/TestRealBinaryFlows/dependency-tree/linked.txt index 6d1dfff..365fe02 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/linked.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/linked.txt @@ -7,12 +7,12 @@ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ │ │┃ └─ blocked by: Replace the k…: Buy the faucet (6) ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Dependency linked [?]help LOCAL projects.db + Dependency linked ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.style b/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.style index 80735ad..0856a59 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.style +++ b/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.style @@ -10,7 +10,7 @@ 15: 91-91 fg=4 16: 10-91 fg=4 17: 10-10 fg=4 -17: 15-39 fg=8 +17: 15-38 fg=8 17: 91-91 fg=4 18: 10-91 fg=4 19: 10-91 fg=4 diff --git a/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.txt b/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.txt index 129b2f0..abaed89 100644 --- a/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.txt +++ b/testdata/TestRealBinaryFlows/dependency-tree/unlink-overlay.txt @@ -14,6 +14,6 @@ │ │ │ > ○ Buy the faucet 6 │ │ │ - │ [Enter]unlink [Esc]cancel │ + │ enter unlink esc cancel │ │ │ ╰────────────────────────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/note-in-place/note-closed.style b/testdata/TestRealBinaryFlows/detail-in-place/detail-closed.style similarity index 96% rename from testdata/TestRealBinaryFlows/note-in-place/note-closed.style rename to testdata/TestRealBinaryFlows/detail-in-place/detail-closed.style index 097e7f9..e99a40d 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/note-closed.style +++ b/testdata/TestRealBinaryFlows/detail-in-place/detail-closed.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/note-in-place/note-closed.txt b/testdata/TestRealBinaryFlows/detail-in-place/detail-closed.txt similarity index 87% rename from testdata/TestRealBinaryFlows/note-in-place/note-closed.txt rename to testdata/TestRealBinaryFlows/detail-in-place/detail-closed.txt index 4084d16..a8dbe3b 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/note-closed.txt +++ b/testdata/TestRealBinaryFlows/detail-in-place/detail-closed.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove [Tab]view … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.style b/testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.style similarity index 96% rename from testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.style rename to testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.style index 097e7f9..e99a40d 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.style +++ b/testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.txt b/testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.txt similarity index 87% rename from testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.txt rename to testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.txt index 4084d16..a8dbe3b 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/chevron-only-where-it-opens.txt +++ b/testdata/TestRealBinaryFlows/detail-in-place/every-item-opens.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove [Tab]view … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/note-in-place/note-open.style b/testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.style similarity index 79% rename from testdata/TestRealBinaryFlows/note-in-place/note-open.style rename to testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.style index 8aa3f12..69be5af 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/note-open.style +++ b/testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.style @@ -21,83 +21,75 @@ 5: 1-1 fg=8 5: 32-33 fg=3 5: 35-40 fg=8 -5: 41-41 fg=4 -5: 99-99 fg=8 -5: 100-100 fg=4 +5: 41-100 fg=4 6: 1-1 fg=8 6: 26-28 fg=3 6: 30-40 fg=8 6: 41-41 fg=4 -6: 49-54 fg=12 bold reverse -6: 55-55 fg=12 reverse -6: 56-59 fg=12 bold reverse +6: 49-53 fg=12 bold 6: 100-100 fg=4 7: 1-40 fg=8 7: 41-100 fg=4 8: 1-40 fg=8 8: 41-41 fg=4 -8: 77-90 bold +8: 49-54 fg=12 bold reverse +8: 55-55 fg=12 reverse +8: 56-59 fg=12 bold reverse 8: 100-100 fg=4 9: 1-40 fg=8 -9: 41-41 fg=4 -9: 100-100 fg=4 +9: 41-100 fg=4 10: 1-40 fg=8 10: 41-41 fg=4 +10: 77-90 bold 10: 100-100 fg=4 11: 1-40 fg=8 11: 41-41 fg=4 11: 100-100 fg=4 12: 1-40 fg=8 12: 41-41 fg=4 -12: 94-96 italic 12: 100-100 fg=4 13: 1-40 fg=8 13: 41-41 fg=4 13: 100-100 fg=4 14: 1-40 fg=8 14: 41-41 fg=4 +14: 94-96 italic 14: 100-100 fg=4 15: 1-40 fg=8 -15: 41-100 fg=4 +15: 41-41 fg=4 +15: 100-100 fg=4 16: 1-40 fg=8 16: 41-41 fg=4 -16: 49-49 fg=12 -16: 51-55 fg=12 bold 16: 100-100 fg=4 17: 1-40 fg=8 17: 41-100 fg=4 18: 1-40 fg=8 18: 41-41 fg=4 -18: 49-50 fg=12 +18: 49-55 fg=12 bold 18: 100-100 fg=4 19: 1-40 fg=8 -19: 41-41 fg=4 -19: 100-100 fg=4 +19: 41-100 fg=4 20: 1-40 fg=8 20: 41-41 fg=4 -20: 49-50 fg=12 20: 100-100 fg=4 21: 1-40 fg=8 21: 41-41 fg=4 21: 100-100 fg=4 22: 1-40 fg=8 22: 41-41 fg=4 -22: 49-50 fg=12 22: 100-100 fg=4 23: 1-40 fg=8 23: 41-41 fg=4 -23: 52-79 fg=11 23: 100-100 fg=4 24: 1-40 fg=8 24: 41-41 fg=4 -24: 49-50 fg=12 24: 100-100 fg=4 25: 1-40 fg=8 25: 41-41 fg=4 +25: 52-79 fg=11 25: 100-100 fg=4 26: 1-40 fg=8 26: 41-41 fg=4 -26: 49-50 fg=12 26: 100-100 fg=4 27: 1-40 fg=8 27: 41-41 fg=4 diff --git a/testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.txt b/testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.txt similarity index 61% rename from testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.txt rename to testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.txt index 76a0d31..c3e17f8 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.txt +++ b/testdata/TestRealBinaryFlows/detail-in-place/item-stays-on-top.txt @@ -3,7 +3,21 @@ │ Projects │┃ Move the blog to plain files ┃ │> Move the blog to plain files ○3 ✓1 │┃> ▾ ○ Export posts from the old CMS ≡ [1/3] ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ ┃ -│ Plan the Lisbon trip ○31 ✓0 │┃ 1. Dump the database from the read replica, never ┃ +│ Plan the Lisbon trip ○31 ✓0 │┃ Notes ┃ +│ │┃ ┃ +│ │┃ Export plan ┃ +│ │┃ ┃ +│ │┃ The old CMS stores posts as HTML fragments in a ┃ +│ │┃ single table, with images referenced by absolute ┃ +│ │┃ URLs. The export has to keep every published URL ┃ +│ │┃ working, because search engines and a few old ┃ +│ │┃ forum threads still link to them. Drafts are not ┃ +│ │┃ exported. They stay in the database dump in case ┃ +│ │┃ one is worth finishing. ┃ +│ │┃ ┃ +│ │┃ ▌ Steps ┃ +│ │┃ ┃ +│ │┃ 1. Dump the database from the read replica, never ┃ │ │┃ the primary. ┃ │ │┃ 2. Convert each fragment to markdown with the ┃ │ │┃ script below. ┃ @@ -11,20 +25,6 @@ │ │┃ static/images///. ┃ │ │┃ 4. Rewrite the image links to the new relative ┃ │ │┃ paths. ┃ -│ │┃ 5. Diff a sample of twenty rendered posts against ┃ -│ │┃ the live site. ┃ -│ │┃ ┃ -│ │┃ ▌ Conversion script ┃ -│ │┃ ┃ -│ │┃ ╭─ bash ┃ -│ │┃ │ mysqldump --single-transaction blog posts > ┃ -│ │┃ │ ↪ posts.sql ┃ -│ │┃ │ python3 convert.py --input posts.sql --output ┃ -│ │┃ │ ↪ content/posts ┃ -│ │┃ │ rg --count ' Move the blog to plain files ○3 ✓1 │┃> ▾ ○ Export posts from the old CMS ≡ [1/3] ┃ -│ Replace the kitchen faucet ○3 ✓0 │┃ 1┃ -│ Plan the Lisbon trip ○31 ✓0 │┃ Export plan ┃ -│ │┃ ┃ +│ Replace the kitchen faucet ○3 ✓0 │┃ Export plan ┃ +│ Plan the Lisbon trip ○31 ✓0 │┃ ┃ │ │┃ The old CMS stores posts as HTML fragments in a ┃ │ │┃ single table, with images referenced by absolute ┃ │ │┃ URLs. The export has to keep every published URL ┃ @@ -25,6 +24,7 @@ │ │┃ paths. ┃ │ │┃ 5. Diff a sample of twenty rendered posts against ┃ │ │┃ the live site. ┃ +│ │┃ ┃ │ │┃ 1/7 ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [J/K]scroll [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.style b/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.style new file mode 100644 index 0000000..e3eb5ed --- /dev/null +++ b/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.style @@ -0,0 +1,110 @@ +1: 2-11 fg=4 bold +1: 15-38 fg=8 +2: 1-40 fg=8 +2: 41-100 fg=4 +3: 1-1 fg=8 +3: 3-10 bold +3: 40-40 fg=8 +3: 41-41 fg=4 +3: 43-70 bold +3: 100-100 fg=4 +4: 1-1 fg=8 +4: 2-31 fg=2 bold +4: 33-34 fg=3 +4: 36-37 fg=2 +4: 40-40 fg=8 +4: 41-41 fg=4 +4: 79-79 fg=6 +4: 81-85 fg=8 +4: 100-100 fg=4 +5: 1-1 fg=8 +5: 32-33 fg=3 +5: 35-40 fg=8 +5: 41-41 fg=4 +5: 99-99 fg=8 +5: 100-100 fg=4 +6: 1-1 fg=8 +6: 26-28 fg=3 +6: 30-40 fg=8 +6: 41-41 fg=4 +6: 51-54 fg=8 strike +6: 55-55 strike +6: 56-58 fg=8 strike +6: 59-59 strike +6: 60-67 fg=8 strike +6: 100-100 fg=4 +7: 1-40 fg=8 +7: 41-41 fg=4 +7: 100-100 fg=4 +8: 1-40 fg=8 +8: 41-41 fg=4 +8: 100-100 fg=4 +9: 1-40 fg=8 +9: 41-41 fg=4 +9: 42-46 fg=2 bold +9: 48-51 fg=8 strike +9: 52-52 strike +9: 53-53 fg=8 strike +9: 54-54 strike +9: 55-59 fg=8 strike +9: 100-100 fg=4 +10: 1-40 fg=8 +10: 41-41 fg=4 +10: 99-99 fg=8 +10: 100-100 fg=4 +11: 1-40 fg=8 +11: 41-41 fg=4 +11: 49-63 fg=8 +11: 100-100 fg=4 +12: 1-40 fg=8 +12: 41-41 fg=4 +12: 49-86 fg=8 +12: 100-100 fg=4 +13: 1-40 fg=8 +13: 41-41 fg=4 +13: 95-95 fg=4 +14: 1-40 fg=8 +14: 41-41 fg=4 +14: 100-100 fg=4 +15: 1-40 fg=8 +15: 41-41 fg=4 +15: 99-99 fg=8 +15: 100-100 fg=4 +16: 1-40 fg=8 +16: 41-41 fg=4 +16: 49-96 fg=1 +16: 100-100 fg=4 +17: 1-40 fg=8 +17: 41-41 fg=4 +17: 100-100 fg=4 +18: 1-40 fg=8 +18: 41-41 fg=4 +18: 99-99 fg=8 +18: 100-100 fg=4 +19: 1-40 fg=8 +19: 41-41 fg=4 +19: 49-99 fg=1 +19: 100-100 fg=4 +20: 1-40 fg=8 +20: 41-100 fg=4 +21: 1-40 fg=8 +21: 41-100 fg=4 +22: 1-40 fg=8 +22: 41-100 fg=4 +23: 1-40 fg=8 +23: 41-100 fg=4 +24: 1-40 fg=8 +24: 41-100 fg=4 +25: 1-40 fg=8 +25: 41-100 fg=4 +26: 1-40 fg=8 +26: 41-100 fg=4 +27: 1-40 fg=8 +27: 41-100 fg=4 +28: 1-40 fg=8 +28: 41-100 fg=4 +29: 1-40 fg=8 +29: 41-100 fg=4 +30: 3-79 fg=8 +30: 82-86 fg=2 bold +30: 88-98 fg=8 diff --git a/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.txt b/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.txt new file mode 100644 index 0000000..0677584 --- /dev/null +++ b/testdata/TestRealBinaryFlows/detail-in-place/short-detail-in-place.txt @@ -0,0 +1,30 @@ + By project All items Dependencies +╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +│ Projects │┃ Move the blog to plain files ┃ +│> Move the blog to plain files ○3 ✓1 │┃ ▸ ○ Export posts from the old CMS ≡ [1/3] ┃ +│ Replace the kitchen faucet ○3 ✓0 │┃ 1┃ +│ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ +│ │┃ ○ Convert HTML to markdown ┃ +│ │┃ ○ Download the images ┃ +│ │┃> ▾ ✓ Pick a theme ┃ +│ │┃ 2┃ +│ │┃ Item 2 · ✓ done ┃ +│ │┃ Projects: Move the blog to plain files ┃ +│ │┃ Created: ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ +│ │┃ 3┃ +│ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ +│ │┃ 4┃ +│ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +│ │┃ ┃ +╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.style b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.style new file mode 100644 index 0000000..789edb4 --- /dev/null +++ b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.style @@ -0,0 +1,14 @@ +1: 2-30 bold +2: 2-16 fg=8 +3: 2-39 fg=8 +6: 2-7 fg=12 bold +9: 2-10 fg=12 bold +10: 2-20 fg=8 +14: 2-6 fg=12 bold +16: 2-7 fg=12 bold reverse +16: 8-8 fg=12 reverse +16: 9-12 fg=12 bold reverse +18: 30-43 bold +20: 47-49 italic +23: 2-8 fg=12 bold +27: 51-78 fg=11 diff --git a/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.txt b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.txt new file mode 100644 index 0000000..ffb53c9 --- /dev/null +++ b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-full-screen.txt @@ -0,0 +1,30 @@ + Export posts from the old CMS + Item 1 · ○ open + Projects: Move the blog to plain files + Created: + + Blocks + ○ Set up redirects for the old URLs (3) + + Tasks 1/3 + ✓ Dump the database + ○ Convert HTML to markdown + ○ Download the images + + Notes + + Export plan + + The old CMS stores posts as HTML fragments in a single table, with images referenced by absolute + URLs. The export has to keep every published URL working, because search engines and a few old + forum threads still link to them. Drafts are not exported. They stay in the database dump in case + one is worth finishing. + + ▌ Steps + + 1. Dump the database from the read replica, never the primary. + 2. Convert each fragment to markdown with the script below. + 3. Download every image the posts reference into static/images///. + 4. Rewrite the image links to the new relative paths. + 5. Diff a sample of twenty rendered posts against the live site. +28/79 j/k scroll [/] section / search y copy ? keys esc/space back q quit diff --git a/testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.style b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.style similarity index 70% rename from testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.style rename to testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.style index efeb7f1..f783806 100644 --- a/testdata/TestRealBinaryFlows/note-in-place/item-stays-on-top.style +++ b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.style @@ -21,89 +21,83 @@ 5: 1-1 fg=8 5: 32-33 fg=3 5: 35-40 fg=8 -5: 41-100 fg=4 +5: 41-41 fg=4 +5: 99-99 fg=8 +5: 100-100 fg=4 6: 1-1 fg=8 6: 26-28 fg=3 6: 30-40 fg=8 6: 41-41 fg=4 -6: 49-50 fg=12 +6: 49-63 fg=8 6: 100-100 fg=4 7: 1-40 fg=8 7: 41-41 fg=4 +7: 49-86 fg=8 7: 100-100 fg=4 8: 1-40 fg=8 8: 41-41 fg=4 -8: 49-50 fg=12 -8: 100-100 fg=4 +8: 95-95 fg=4 9: 1-40 fg=8 -9: 41-41 fg=4 -9: 100-100 fg=4 +9: 41-100 fg=4 10: 1-40 fg=8 10: 41-41 fg=4 -10: 49-50 fg=12 +10: 49-54 fg=12 bold 10: 100-100 fg=4 11: 1-40 fg=8 11: 41-41 fg=4 -11: 52-79 fg=11 11: 100-100 fg=4 12: 1-40 fg=8 -12: 41-41 fg=4 -12: 49-50 fg=12 -12: 100-100 fg=4 +12: 41-100 fg=4 13: 1-40 fg=8 13: 41-41 fg=4 +13: 49-57 fg=12 bold 13: 100-100 fg=4 14: 1-40 fg=8 14: 41-41 fg=4 -14: 49-50 fg=12 +14: 49-67 fg=8 14: 100-100 fg=4 15: 1-40 fg=8 15: 41-41 fg=4 15: 100-100 fg=4 16: 1-40 fg=8 -16: 41-100 fg=4 +16: 41-41 fg=4 +16: 100-100 fg=4 17: 1-40 fg=8 -17: 41-41 fg=4 -17: 49-49 fg=12 -17: 51-67 fg=12 bold -17: 100-100 fg=4 +17: 41-100 fg=4 18: 1-40 fg=8 -18: 41-100 fg=4 +18: 41-41 fg=4 +18: 49-53 fg=12 bold +18: 100-100 fg=4 19: 1-40 fg=8 -19: 41-41 fg=4 -19: 49-55 fg=8 -19: 100-100 fg=4 +19: 41-100 fg=4 20: 1-40 fg=8 20: 41-41 fg=4 -20: 49-49 fg=8 +20: 49-54 fg=12 bold reverse +20: 55-55 fg=12 reverse +20: 56-59 fg=12 bold reverse 20: 100-100 fg=4 21: 1-40 fg=8 -21: 41-41 fg=4 -21: 49-51 fg=8 -21: 100-100 fg=4 +21: 41-100 fg=4 22: 1-40 fg=8 22: 41-41 fg=4 -22: 49-49 fg=8 +22: 77-90 bold 22: 100-100 fg=4 23: 1-40 fg=8 23: 41-41 fg=4 -23: 49-51 fg=8 23: 100-100 fg=4 24: 1-40 fg=8 24: 41-41 fg=4 -24: 49-49 fg=8 -24: 62-67 fg=10 24: 100-100 fg=4 25: 1-40 fg=8 25: 41-41 fg=4 -25: 49-51 fg=8 25: 100-100 fg=4 26: 1-40 fg=8 26: 41-41 fg=4 -26: 49-50 fg=8 +26: 94-96 italic 26: 100-100 fg=4 27: 1-40 fg=8 -27: 41-100 fg=4 +27: 41-41 fg=4 +27: 100-100 fg=4 28: 1-40 fg=8 28: 41-41 fg=4 28: 43-45 fg=8 diff --git a/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.txt b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.txt new file mode 100644 index 0000000..1abf153 --- /dev/null +++ b/testdata/TestRealBinaryFlows/detail-in-place/tall-detail-in-place.txt @@ -0,0 +1,30 @@ + By project All items Dependencies +╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +│ Projects │┃ Move the blog to plain files ┃ +│> Move the blog to plain files ○3 ✓1 │┃> ▾ ○ Export posts from the old CMS ≡ [1/3] ┃ +│ Replace the kitchen faucet ○3 ✓0 │┃ 1┃ +│ Plan the Lisbon trip ○31 ✓0 │┃ Item 1 · ○ open ┃ +│ │┃ Projects: Move the blog to plain files ┃ +│ │┃ Created: ┃ +│ │┃ ┃ +│ │┃ Blocks ┃ +│ │┃ ○ Set up redirects for the old URLs (3) ┃ +│ │┃ ┃ +│ │┃ Tasks 1/3 ┃ +│ │┃ ✓ Dump the database ┃ +│ │┃ ○ Convert HTML to markdown ┃ +│ │┃ ○ Download the images ┃ +│ │┃ ┃ +│ │┃ Notes ┃ +│ │┃ ┃ +│ │┃ Export plan ┃ +│ │┃ ┃ +│ │┃ The old CMS stores posts as HTML fragments in a ┃ +│ │┃ single table, with images referenced by absolute ┃ +│ │┃ URLs. The export has to keep every published URL ┃ +│ │┃ working, because search engines and a few old ┃ +│ │┃ forum threads still link to them. Drafts are not ┃ +│ │┃ exported. They stay in the database dump in case ┃ +│ │┃ 1/7 ┃ +╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/edit-note/note-in-place.style b/testdata/TestRealBinaryFlows/edit-note/note-in-place.style index 3721a49..7a91a3f 100644 --- a/testdata/TestRealBinaryFlows/edit-note/note-in-place.style +++ b/testdata/TestRealBinaryFlows/edit-note/note-in-place.style @@ -56,41 +56,48 @@ 10: 100-100 fg=4 11: 1-40 fg=8 11: 41-41 fg=4 +11: 49-63 fg=8 11: 100-100 fg=4 12: 1-40 fg=8 12: 41-41 fg=4 +12: 49-86 fg=8 12: 100-100 fg=4 13: 1-40 fg=8 13: 41-41 fg=4 -13: 99-99 fg=8 -13: 100-100 fg=4 +13: 95-95 fg=4 14: 1-40 fg=8 -14: 41-41 fg=4 -14: 49-96 fg=1 -14: 100-100 fg=4 +14: 41-100 fg=4 15: 1-40 fg=8 15: 41-41 fg=4 +15: 49-53 fg=12 bold 15: 100-100 fg=4 16: 1-40 fg=8 -16: 41-41 fg=4 -16: 99-99 fg=8 -16: 100-100 fg=4 +16: 41-100 fg=4 17: 1-40 fg=8 17: 41-41 fg=4 -17: 49-99 fg=1 17: 100-100 fg=4 18: 1-40 fg=8 -18: 41-100 fg=4 +18: 41-41 fg=4 +18: 100-100 fg=4 19: 1-40 fg=8 -19: 41-100 fg=4 +19: 41-41 fg=4 +19: 99-99 fg=8 +19: 100-100 fg=4 20: 1-40 fg=8 -20: 41-100 fg=4 +20: 41-41 fg=4 +20: 49-96 fg=1 +20: 100-100 fg=4 21: 1-40 fg=8 -21: 41-100 fg=4 +21: 41-41 fg=4 +21: 100-100 fg=4 22: 1-40 fg=8 -22: 41-100 fg=4 +22: 41-41 fg=4 +22: 99-99 fg=8 +22: 100-100 fg=4 23: 1-40 fg=8 -23: 41-100 fg=4 +23: 41-41 fg=4 +23: 49-99 fg=1 +23: 100-100 fg=4 24: 1-40 fg=8 24: 41-100 fg=4 25: 1-40 fg=8 diff --git a/testdata/TestRealBinaryFlows/edit-note/note-in-place.txt b/testdata/TestRealBinaryFlows/edit-note/note-in-place.txt index 963b7da..770d552 100644 --- a/testdata/TestRealBinaryFlows/edit-note/note-in-place.txt +++ b/testdata/TestRealBinaryFlows/edit-note/note-in-place.txt @@ -8,11 +8,17 @@ │ │┃ ○ Download the images ┃ │ │┃> ▾ ✓ Pick a theme ≡ ┃ │ │┃ 2┃ +│ │┃ Item 2 · ✓ done ┃ +│ │┃ Projects: Move the blog to plain files ┃ +│ │┃ Created: ┃ +│ │┃ ┃ +│ │┃ Notes ┃ +│ │┃ ┃ │ │┃ Written from the editor. ┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -20,11 +26,5 @@ │ │┃ ┃ │ │┃ ┃ │ │┃ ┃ -│ │┃ ┃ -│ │┃ ┃ -│ │┃ ┃ -│ │┃ ┃ -│ │┃ ┃ -│ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item updated [?]help LOCAL projects.db + Item updated ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.style b/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.style deleted file mode 100644 index ce15d8e..0000000 --- a/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.style +++ /dev/null @@ -1,4 +0,0 @@ -1: 2-13 bold -2: 2-16 fg=8 -3: 2-39 fg=8 -6: 2-6 fg=12 bold diff --git a/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.txt b/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.txt deleted file mode 100644 index 7501fe0..0000000 --- a/testdata/TestRealBinaryFlows/edit-note/note-in-the-reader.txt +++ /dev/null @@ -1,30 +0,0 @@ - Pick a theme - Item 2 · ✓ done - Projects: Move the blog to plain files - Created: - - Notes - - Written from the editor. - - - - - - - - - - - - - - - - - - - - - -7/7 j/k scroll [/] section / search y copy ? keys esc/space back q quit diff --git a/testdata/TestRealBinaryFlows/edit-note/note-saved.txt b/testdata/TestRealBinaryFlows/edit-note/note-saved.txt index 48d5282..cd33e9c 100644 --- a/testdata/TestRealBinaryFlows/edit-note/note-saved.txt +++ b/testdata/TestRealBinaryFlows/edit-note/note-saved.txt @@ -8,10 +8,10 @@ │ │┃ ○ Download the images ┃ │ │┃> ▸ ✓ Pick a theme ≡ ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item updated [?]help LOCAL projects.db + Item updated ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/filter/all-items-filtered.txt b/testdata/TestRealBinaryFlows/filter/all-items-filtered.txt index c21772e..ee951eb 100644 --- a/testdata/TestRealBinaryFlows/filter/all-items-filtered.txt +++ b/testdata/TestRealBinaryFlows/filter/all-items-filtered.txt @@ -27,4 +27,4 @@ ┃ ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]keep [Esc]clear [?]help LOCAL projects.db + enter keep esc clear ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/filter/cleared.style b/testdata/TestRealBinaryFlows/filter/cleared.style index 097e7f9..e99a40d 100644 --- a/testdata/TestRealBinaryFlows/filter/cleared.style +++ b/testdata/TestRealBinaryFlows/filter/cleared.style @@ -42,7 +42,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/filter/cleared.txt b/testdata/TestRealBinaryFlows/filter/cleared.txt index 4084d16..a8dbe3b 100644 --- a/testdata/TestRealBinaryFlows/filter/cleared.txt +++ b/testdata/TestRealBinaryFlows/filter/cleared.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [M]ove [Tab]view … [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/filter/note-match-kept.txt b/testdata/TestRealBinaryFlows/filter/note-match-kept.txt index 0317db2..05f5c1d 100644 --- a/testdata/TestRealBinaryFlows/filter/note-match-kept.txt +++ b/testdata/TestRealBinaryFlows/filter/note-match-kept.txt @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [a]dd [n]ote [Esc]back [Tab]view … [?]help LOCAL projects.db + c complete a add n note esc back tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/filter/project-filter.txt b/testdata/TestRealBinaryFlows/filter/project-filter.txt index d1571fe..93cf78e 100644 --- a/testdata/TestRealBinaryFlows/filter/project-filter.txt +++ b/testdata/TestRealBinaryFlows/filter/project-filter.txt @@ -1,30 +1,30 @@ By project All items Dependencies ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓╭──────────────────────────────────────────────────────────╮ ┃ Projects ┃│ Plan the Lisbon trip │ -┃ /lisbon ┃│> ○ Ask Sam for a plumber and a place to stay ◈ │ +┃ /lisbon ┃│> ▸ ○ Ask Sam for a plumber and a place to stay ◈ │ ┃> Plan the Lisbon trip ○31 ✓0 ┃│ also in Replace the kitchen faucet 8│ -┃ ┃│ ○ Write up the trip on the new blog │ +┃ ┃│ ▸ ○ Write up the trip on the new blog │ ┃ ┃│ 9│ ┃ ┃│ └─ blocked by: Move the blog…: Point DNS at th… (4)│ -┃ ┃│ ○ Read every neighborhood guide and write down which…│ +┃ ┃│ ▸ ○ Read every neighborhood guide and write down which…│ ┃ ┃│ 10│ -┃ ┃│ ○ Day 1: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 1: walk the next neighborhood │ ┃ ┃│ 11│ -┃ ┃│ ○ Day 2: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 2: walk the next neighborhood │ ┃ ┃│ 12│ -┃ ┃│ ○ Day 3: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 3: walk the next neighborhood │ ┃ ┃│ 13│ -┃ ┃│ ○ Day 4: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 4: walk the next neighborhood │ ┃ ┃│ 14│ -┃ ┃│ ○ Day 5: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 5: walk the next neighborhood │ ┃ ┃│ 15│ -┃ ┃│ ○ Day 6: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 6: walk the next neighborhood │ ┃ ┃│ 16│ -┃ ┃│ ○ Day 7: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 7: walk the next neighborhood │ ┃ ┃│ 17│ -┃ ┃│ ○ Day 8: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 8: walk the next neighborhood │ ┃ ┃│ 18│ -┃ ┃│ ○ Day 9: walk the next neighborhood │ +┃ ┃│ ▸ ○ Day 9: walk the next neighborhood │ ┃ ┃│ 1/31 │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]keep [Esc]clear [?]help LOCAL projects.db + enter keep esc clear ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/filter/title-match.txt b/testdata/TestRealBinaryFlows/filter/title-match.txt index 26c5123..dbd039e 100644 --- a/testdata/TestRealBinaryFlows/filter/title-match.txt +++ b/testdata/TestRealBinaryFlows/filter/title-match.txt @@ -7,7 +7,7 @@ │ │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]keep [Esc]clear [?]help LOCAL projects.db + enter keep esc clear ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.style b/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.style index 64c701a..436febf 100644 --- a/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.style +++ b/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.style @@ -52,7 +52,7 @@ 26: 86-86 fg=4 27: 15-86 fg=4 28: 15-15 fg=4 -28: 20-37 fg=8 +28: 20-36 fg=8 28: 86-86 fg=4 29: 15-86 fg=4 30: 15-86 fg=4 diff --git a/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.txt b/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.txt index 2244199..ed16a7f 100644 --- a/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.txt +++ b/testdata/TestRealBinaryFlows/help-taller-than-the-window/from-the-project-pane.txt @@ -25,6 +25,6 @@ │ ? show these keys │ │ q Ctrl+c quit │ │ │ - │ [Esc]close [q]quit │ + │ esc close q quit │ │ │ ╰──────────────────────────────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/move/carried-down.style b/testdata/TestRealBinaryFlows/move/carried-down.style index b7842d1..b72476b 100644 --- a/testdata/TestRealBinaryFlows/move/carried-down.style +++ b/testdata/TestRealBinaryFlows/move/carried-down.style @@ -14,7 +14,9 @@ 4: 36-37 fg=2 4: 40-40 fg=8 4: 41-41 fg=4 -4: 42-46 strike +4: 42-44 strike +4: 45-45 fg=8 strike +4: 46-46 strike 4: 47-47 fg=8 strike 4: 48-48 strike 4: 49-52 fg=8 strike @@ -101,6 +103,6 @@ 28: 41-100 fg=4 29: 1-40 fg=8 29: 41-100 fg=4 -30: 3-51 fg=3 bold +30: 3-50 fg=3 bold 30: 82-86 fg=2 bold 30: 88-98 fg=8 diff --git a/testdata/TestRealBinaryFlows/move/carried-down.txt b/testdata/TestRealBinaryFlows/move/carried-down.txt index 44ef564..ff13183 100644 --- a/testdata/TestRealBinaryFlows/move/carried-down.txt +++ b/testdata/TestRealBinaryFlows/move/carried-down.txt @@ -1,17 +1,17 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Move the blog to plain files ┃ -│> Move the blog to plain files ○3 ✓1 │┃ ✓ Pick a theme ┃ +│> Move the blog to plain files ○3 ✓1 │┃ ▸ ✓ Pick a theme ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 2┃ │ Plan the Lisbon trip ○31 ✓0 │┃▶ ▸ ○ Export posts from the old CMS ≡ [1/3] ◀ MOVING ┃ │ │┃ 1┃ │ │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [j/k]move [g/G]top/bottom [Enter]drop [Esc]cancel [?]help LOCAL projects.db + j/k move g/G top/bottom enter drop esc cancel ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/move/dropped.style b/testdata/TestRealBinaryFlows/move/dropped.style index 467e030..84954d1 100644 --- a/testdata/TestRealBinaryFlows/move/dropped.style +++ b/testdata/TestRealBinaryFlows/move/dropped.style @@ -14,7 +14,9 @@ 4: 36-37 fg=2 4: 40-40 fg=8 4: 41-41 fg=4 -4: 42-46 strike +4: 42-44 strike +4: 45-45 fg=8 strike +4: 46-46 strike 4: 47-47 fg=8 strike 4: 48-48 strike 4: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/move/dropped.txt b/testdata/TestRealBinaryFlows/move/dropped.txt index 43266e8..314e759 100644 --- a/testdata/TestRealBinaryFlows/move/dropped.txt +++ b/testdata/TestRealBinaryFlows/move/dropped.txt @@ -1,17 +1,17 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Move the blog to plain files ┃ -│> Move the blog to plain files ○3 ✓1 │┃ ✓ Pick a theme ┃ +│> Move the blog to plain files ○3 ✓1 │┃ ▸ ✓ Pick a theme ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 2┃ │ Plan the Lisbon trip ○31 ✓0 │┃> ▸ ○ Export posts from the old CMS ≡ [1/3] ┃ │ │┃ 1┃ │ │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item reordered [?]help LOCAL projects.db + Item reordered ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/move/grabbed.style b/testdata/TestRealBinaryFlows/move/grabbed.style index bc15c2a..c09ca7c 100644 --- a/testdata/TestRealBinaryFlows/move/grabbed.style +++ b/testdata/TestRealBinaryFlows/move/grabbed.style @@ -40,7 +40,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike @@ -101,6 +103,6 @@ 28: 41-100 fg=4 29: 1-40 fg=8 29: 41-100 fg=4 -30: 3-51 fg=3 bold +30: 3-50 fg=3 bold 30: 82-86 fg=2 bold 30: 88-98 fg=8 diff --git a/testdata/TestRealBinaryFlows/move/grabbed.txt b/testdata/TestRealBinaryFlows/move/grabbed.txt index 4fd2599..b8e57b7 100644 --- a/testdata/TestRealBinaryFlows/move/grabbed.txt +++ b/testdata/TestRealBinaryFlows/move/grabbed.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [j/k]move [g/G]top/bottom [Enter]drop [Esc]cancel [?]help LOCAL projects.db + j/k move g/G top/bottom enter drop esc cancel ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/move/project-carried.style b/testdata/TestRealBinaryFlows/move/project-carried.style index 1b33f9a..3f613ff 100644 --- a/testdata/TestRealBinaryFlows/move/project-carried.style +++ b/testdata/TestRealBinaryFlows/move/project-carried.style @@ -13,7 +13,9 @@ 4: 35-36 fg=8 4: 40-40 fg=4 4: 41-41 fg=8 -4: 42-46 strike +4: 42-44 strike +4: 45-45 fg=8 strike +4: 46-46 strike 4: 47-47 fg=8 strike 4: 48-48 strike 4: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/move/project-carried.txt b/testdata/TestRealBinaryFlows/move/project-carried.txt index 1f26dcd..68cafa3 100644 --- a/testdata/TestRealBinaryFlows/move/project-carried.txt +++ b/testdata/TestRealBinaryFlows/move/project-carried.txt @@ -1,17 +1,17 @@ By project All items Dependencies ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓╭──────────────────────────────────────────────────────────╮ ┃ Projects ┃│ Move the blog to plain files │ -┃ Replace the kitchen faucet ○3 ✓0 ┃│ ✓ Pick a theme │ +┃ Replace the kitchen faucet ○3 ✓0 ┃│ ▸ ✓ Pick a theme │ ┃▶ Move the blog to plain fil… ◀ MOVING┃│ 2│ ┃ Plan the Lisbon trip ○31 ✓0 ┃│> ▸ ○ Export posts from the old CMS ≡ [1/3] │ ┃ ┃│ 1│ ┃ ┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - Item reordered [?]help LOCAL projects.db + Item reordered ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/move/put-back.style b/testdata/TestRealBinaryFlows/move/put-back.style index 467e030..84954d1 100644 --- a/testdata/TestRealBinaryFlows/move/put-back.style +++ b/testdata/TestRealBinaryFlows/move/put-back.style @@ -14,7 +14,9 @@ 4: 36-37 fg=2 4: 40-40 fg=8 4: 41-41 fg=4 -4: 42-46 strike +4: 42-44 strike +4: 45-45 fg=8 strike +4: 46-46 strike 4: 47-47 fg=8 strike 4: 48-48 strike 4: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/move/put-back.txt b/testdata/TestRealBinaryFlows/move/put-back.txt index 43266e8..314e759 100644 --- a/testdata/TestRealBinaryFlows/move/put-back.txt +++ b/testdata/TestRealBinaryFlows/move/put-back.txt @@ -1,17 +1,17 @@ By project All items Dependencies ╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ Projects │┃ Move the blog to plain files ┃ -│> Move the blog to plain files ○3 ✓1 │┃ ✓ Pick a theme ┃ +│> Move the blog to plain files ○3 ✓1 │┃ ▸ ✓ Pick a theme ┃ │ Replace the kitchen faucet ○3 ✓0 │┃ 2┃ │ Plan the Lisbon trip ○31 ✓0 │┃> ▸ ○ Export posts from the old CMS ≡ [1/3] ┃ │ │┃ 1┃ │ │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Item reordered [?]help LOCAL projects.db + Item reordered ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.style b/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.style index e504290..13f55f1 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.style +++ b/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.style @@ -46,7 +46,9 @@ 9: 50-50 fg=4 10: 1-20 fg=8 10: 21-21 fg=4 -10: 22-26 strike +10: 22-24 strike +10: 25-25 fg=8 strike +10: 26-26 strike 10: 27-27 fg=8 strike 10: 28-28 strike 10: 29-32 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.txt b/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.txt index 0d466ca..7a60ab0 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.txt +++ b/testdata/TestRealBinaryFlows/narrow-window/filter-prompt.txt @@ -7,8 +7,8 @@ │ │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to ma…┃ │ │┃ ○ Download the images┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ │ │┃ 1/7 ┃ ╰──────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]keep … [?]help LOCAL projects.db + enter keep … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/narrow-window/first-screen.style b/testdata/TestRealBinaryFlows/narrow-window/first-screen.style index 852213e..ce723c7 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/first-screen.style +++ b/testdata/TestRealBinaryFlows/narrow-window/first-screen.style @@ -41,7 +41,9 @@ 8: 50-50 fg=8 9: 1-20 fg=4 9: 21-21 fg=8 -9: 22-26 strike +9: 22-24 strike +9: 25-25 fg=8 strike +9: 26-26 strike 9: 27-27 fg=8 strike 9: 28-28 strike 9: 29-32 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/narrow-window/first-screen.txt b/testdata/TestRealBinaryFlows/narrow-window/first-screen.txt index 47311a0..d3b38ae 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/first-screen.txt +++ b/testdata/TestRealBinaryFlows/narrow-window/first-screen.txt @@ -6,9 +6,9 @@ ┃ Plan th… ○31 ✓0┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to ma…│ ┃ ┃│ ○ Download the images│ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for…│ +┃ ┃│ ▸ ○ Set up redirects for…│ ┃ ┃│ 1/7 │ ┗━━━━━━━━━━━━━━━━━━┛╰────────────────────────────╯ - [Enter]read … [?]help LOCAL projects.db + enter read … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/narrow-window/items-focused.style b/testdata/TestRealBinaryFlows/narrow-window/items-focused.style index 348045a..e3a3794 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/items-focused.style +++ b/testdata/TestRealBinaryFlows/narrow-window/items-focused.style @@ -42,7 +42,9 @@ 8: 50-50 fg=4 9: 1-20 fg=8 9: 21-21 fg=4 -9: 22-26 strike +9: 22-24 strike +9: 25-25 fg=8 strike +9: 26-26 strike 9: 27-27 fg=8 strike 9: 28-28 strike 9: 29-32 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/narrow-window/items-focused.txt b/testdata/TestRealBinaryFlows/narrow-window/items-focused.txt index c101f13..0bbc136 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/items-focused.txt +++ b/testdata/TestRealBinaryFlows/narrow-window/items-focused.txt @@ -6,9 +6,9 @@ │ Plan th… ○31 ✓0│┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to ma…┃ │ │┃ ○ Download the images┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for…┃ +│ │┃ ▸ ○ Set up redirects for…┃ │ │┃ 1/7 ┃ ╰──────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read … [?]help LOCAL projects.db + c complete a add … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/narrow-window/task-prompt.style b/testdata/TestRealBinaryFlows/narrow-window/task-prompt.style index 2a24735..381e641 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/task-prompt.style +++ b/testdata/TestRealBinaryFlows/narrow-window/task-prompt.style @@ -13,7 +13,7 @@ 8: 49-49 fg=4 9: 2-49 fg=4 10: 2-2 fg=4 -10: 7-32 fg=8 +10: 7-31 fg=8 10: 49-49 fg=4 11: 2-49 fg=4 12: 2-49 fg=4 diff --git a/testdata/TestRealBinaryFlows/narrow-window/task-prompt.txt b/testdata/TestRealBinaryFlows/narrow-window/task-prompt.txt index 621afd9..22eb82b 100644 --- a/testdata/TestRealBinaryFlows/narrow-window/task-prompt.txt +++ b/testdata/TestRealBinaryFlows/narrow-window/task-prompt.txt @@ -7,6 +7,6 @@ │ │ │ > New task... │ │ │ - │ [Enter]confirm [Esc]cancel │ + │ enter confirm esc cancel │ │ │ ╰──────────────────────────────────────────────╯ diff --git a/testdata/TestRealBinaryFlows/note-in-place/scrolled-by-lines.txt b/testdata/TestRealBinaryFlows/note-in-place/scrolled-by-lines.txt deleted file mode 100644 index 6f87bdb..0000000 --- a/testdata/TestRealBinaryFlows/note-in-place/scrolled-by-lines.txt +++ /dev/null @@ -1,30 +0,0 @@ - By project All items Dependencies -╭──────────────────────────────────────╮┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ -│ Projects │┃ Move the blog to plain files ┃ -│> Move the blog to plain files ○3 ✓1 │┃> ▾ ○ Export posts from the old CMS ≡ [1/3] ┃ -│ Replace the kitchen faucet ○3 ✓0 │┃ 2. Convert each fragment to markdown with the ┃ -│ Plan the Lisbon trip ○31 ✓0 │┃ script below. ┃ -│ │┃ 3. Download every image the posts reference into ┃ -│ │┃ static/images///. ┃ -│ │┃ 4. Rewrite the image links to the new relative ┃ -│ │┃ paths. ┃ -│ │┃ 5. Diff a sample of twenty rendered posts against ┃ -│ │┃ the live site. ┃ -│ │┃ ┃ -│ │┃ ▌ Conversion script ┃ -│ │┃ ┃ -│ │┃ ╭─ bash ┃ -│ │┃ │ mysqldump --single-transaction blog posts > ┃ -│ │┃ │ ↪ posts.sql ┃ -│ │┃ │ python3 convert.py --input posts.sql --output ┃ -│ │┃ │ ↪ content/posts ┃ -│ │┃ │ rg --count ' ○ Set up redirects for the old URLs ┃ +│ │┃> ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Undo: undid update on item [?]help LOCAL projects.db + Undo: undid update on item ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/undo/archived.txt b/testdata/TestRealBinaryFlows/undo/archived.txt index 73e47a2..86f41db 100644 --- a/testdata/TestRealBinaryFlows/undo/archived.txt +++ b/testdata/TestRealBinaryFlows/undo/archived.txt @@ -6,10 +6,10 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃> ○ Set up redirects for the old URLs ┃ +│ │┃> ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Archived [?]help LOCAL projects.db + Archived ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/undo/refused-while-blocked.style b/testdata/TestRealBinaryFlows/undo/refused-while-blocked.style index ba5e4a0..0ecbb1a 100644 --- a/testdata/TestRealBinaryFlows/undo/refused-while-blocked.style +++ b/testdata/TestRealBinaryFlows/undo/refused-while-blocked.style @@ -41,7 +41,9 @@ 8: 100-100 fg=4 9: 1-40 fg=8 9: 41-41 fg=4 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/undo/refused-while-blocked.txt b/testdata/TestRealBinaryFlows/undo/refused-while-blocked.txt index abb69e6..495b42d 100644 --- a/testdata/TestRealBinaryFlows/undo/refused-while-blocked.txt +++ b/testdata/TestRealBinaryFlows/undo/refused-while-blocked.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃ ✓ Pick a theme ┃ +│ │┃ ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃> ○ Point DNS at the new host ┃ +│ │┃> ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Cannot complete: item has unresolved blockers [?]help LOCAL projects.db + Cannot complete: item has unresolved blockers ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/undo/toggled.txt b/testdata/TestRealBinaryFlows/undo/toggled.txt index 6491e31..7a0c340 100644 --- a/testdata/TestRealBinaryFlows/undo/toggled.txt +++ b/testdata/TestRealBinaryFlows/undo/toggled.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃> ○ Pick a theme ┃ +│ │┃> ▸ ○ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Marked incomplete [?]help LOCAL projects.db + Marked incomplete ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/undo/undone.txt b/testdata/TestRealBinaryFlows/undo/undone.txt index 2b6f737..ebd9cf1 100644 --- a/testdata/TestRealBinaryFlows/undo/undone.txt +++ b/testdata/TestRealBinaryFlows/undo/undone.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃> ✓ Pick a theme ┃ +│ │┃> ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - Undo: undid update on item [?]help LOCAL projects.db + Undo: undid update on item ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/all-items.style b/testdata/TestRealBinaryFlows/views/all-items.style index 6a3c490..04fbbe9 100644 --- a/testdata/TestRealBinaryFlows/views/all-items.style +++ b/testdata/TestRealBinaryFlows/views/all-items.style @@ -30,7 +30,9 @@ 9: 1-1 fg=4 9: 100-100 fg=4 10: 1-1 fg=4 -10: 2-6 strike +10: 2-4 strike +10: 5-5 fg=8 strike +10: 6-6 strike 10: 7-7 fg=8 strike 10: 8-8 strike 10: 9-12 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/views/all-items.txt b/testdata/TestRealBinaryFlows/views/all-items.txt index bac47a6..211cc07 100644 --- a/testdata/TestRealBinaryFlows/views/all-items.txt +++ b/testdata/TestRealBinaryFlows/views/all-items.txt @@ -7,24 +7,24 @@ ┃ ✓ Dump the database ┃ ┃ ○ Convert HTML to markdown ┃ ┃ ○ Download the images ┃ -┃ ✓ Pick a theme ┃ +┃ ▸ ✓ Pick a theme ┃ ┃ 2┃ -┃ ○ Set up redirects for the old URLs ┃ +┃ ▸ ○ Set up redirects for the old URLs ┃ ┃ 3┃ ┃ └─ blocked by: Export posts from the old CMS (1) ┃ -┃ ○ Point DNS at the new host ┃ +┃ ▸ ○ Point DNS at the new host ┃ ┃ 4┃ ┃ └─ blocked by: Set up redirects for the old URLs (3) ┃ ┃ ┃ ┃ ▾ ── Replace the kitchen faucet ── ○3 ✓0 ┃ ┃ ▸ ○ Buy the faucet ≡ ┃ ┃ 6┃ -┃ ○ Shut off the water and swap it ┃ +┃ ▸ ○ Shut off the water and swap it ┃ ┃ 7┃ ┃ └─ blocked by: Buy the faucet (6) ┃ -┃ ○ Ask Sam for a plumber and a place to stay ◈ ┃ +┃ ▸ ○ Ask Sam for a plumber and a place to stay ◈ ┃ ┃ also in Plan the Lisbon trip 8┃ ┃ ┃ ┃ 2/44 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [space]note [c]omplete [n]ote [Tab]view [/]filter [?]help LOCAL projects.db + c complete n note tab view / filter j/k move [/] jump … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/back-to-by-project.txt b/testdata/TestRealBinaryFlows/views/back-to-by-project.txt index 6815ead..38c5cf2 100644 --- a/testdata/TestRealBinaryFlows/views/back-to-by-project.txt +++ b/testdata/TestRealBinaryFlows/views/back-to-by-project.txt @@ -6,12 +6,12 @@ │ Plan the Lisbon trip ○31 ✓0 │┃ ✓ Dump the database ┃ │ │┃ ○ Convert HTML to markdown ┃ │ │┃ ○ Download the images ┃ -│ │┃> ✓ Pick a theme ┃ +│ │┃> ▸ ✓ Pick a theme ┃ │ │┃ 2┃ -│ │┃ ○ Set up redirects for the old URLs ┃ +│ │┃ ▸ ○ Set up redirects for the old URLs ┃ │ │┃ 3┃ │ │┃ └─ blocked by: Export posts from the old CMS (1) ┃ -│ │┃ ○ Point DNS at the new host ┃ +│ │┃ ▸ ○ Point DNS at the new host ┃ │ │┃ 4┃ │ │┃ └─ blocked by: Set up redirects for the old UR… (3)┃ │ │┃ ┃ @@ -27,4 +27,4 @@ │ │┃ ┃ │ │┃ ┃ ╰──────────────────────────────────────╯┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [a]dd [n]ote [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + c complete a add n note M move tab view / filter j/k move … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/by-project.style b/testdata/TestRealBinaryFlows/views/by-project.style index a174474..9aa3b0f 100644 --- a/testdata/TestRealBinaryFlows/views/by-project.style +++ b/testdata/TestRealBinaryFlows/views/by-project.style @@ -41,7 +41,9 @@ 8: 100-100 fg=8 9: 1-40 fg=4 9: 41-41 fg=8 -9: 42-46 strike +9: 42-44 strike +9: 45-45 fg=8 strike +9: 46-46 strike 9: 47-47 fg=8 strike 9: 48-48 strike 9: 49-52 fg=8 strike diff --git a/testdata/TestRealBinaryFlows/views/by-project.txt b/testdata/TestRealBinaryFlows/views/by-project.txt index 353d508..51243aa 100644 --- a/testdata/TestRealBinaryFlows/views/by-project.txt +++ b/testdata/TestRealBinaryFlows/views/by-project.txt @@ -6,12 +6,12 @@ ┃ Plan the Lisbon trip ○31 ✓0 ┃│ ✓ Dump the database │ ┃ ┃│ ○ Convert HTML to markdown │ ┃ ┃│ ○ Download the images │ -┃ ┃│ ✓ Pick a theme │ +┃ ┃│ ▸ ✓ Pick a theme │ ┃ ┃│ 2│ -┃ ┃│ ○ Set up redirects for the old URLs │ +┃ ┃│ ▸ ○ Set up redirects for the old URLs │ ┃ ┃│ 3│ ┃ ┃│ └─ blocked by: Export posts from the old CMS (1) │ -┃ ┃│ ○ Point DNS at the new host │ +┃ ┃│ ▸ ○ Point DNS at the new host │ ┃ ┃│ 4│ ┃ ┃│ └─ blocked by: Set up redirects for the old UR… (3)│ ┃ ┃│ │ @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰──────────────────────────────────────────────────────────╯ - [Enter]read [space]mark [a]dd [e]dit [M]ove [Tab]view [/]filter [?]help LOCAL projects.db + enter read space mark a add e edit M move tab view / filter … ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/dependencies.txt b/testdata/TestRealBinaryFlows/views/dependencies.txt index 4d987eb..874c596 100644 --- a/testdata/TestRealBinaryFlows/views/dependencies.txt +++ b/testdata/TestRealBinaryFlows/views/dependencies.txt @@ -27,4 +27,4 @@ ┃ ┃│ │ ┃ ┃│ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛╰────────────────────────────────────────────────╯ - [l]drill [Enter]read [.]finished [Tab]view [/]filter [?]help LOCAL projects.db + l drill enter read . finished tab view / filter j/k move q quit ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/group-folded.txt b/testdata/TestRealBinaryFlows/views/group-folded.txt index e3cc618..0206a60 100644 --- a/testdata/TestRealBinaryFlows/views/group-folded.txt +++ b/testdata/TestRealBinaryFlows/views/group-folded.txt @@ -6,25 +6,25 @@ ┃ ▾ ── Replace the kitchen faucet ── ○3 ✓0 ┃ ┃ ▸ ○ Buy the faucet ≡ ┃ ┃ 6┃ -┃ ○ Shut off the water and swap it ┃ +┃ ▸ ○ Shut off the water and swap it ┃ ┃ 7┃ ┃ └─ blocked by: Buy the faucet (6) ┃ -┃ ○ Ask Sam for a plumber and a place to stay ◈ ┃ +┃ ▸ ○ Ask Sam for a plumber and a place to stay ◈ ┃ ┃ also in Plan the Lisbon trip 8┃ ┃ ┃ ┃ ▾ ── Plan the Lisbon trip ── ○31 ✓0 ┃ -┃ ○ Ask Sam for a plumber and a place to stay ◈ ┃ +┃ ▸ ○ Ask Sam for a plumber and a place to stay ◈ ┃ ┃ also in Replace the kitchen faucet 8┃ -┃ ○ Write up the trip on the new blog ┃ +┃ ▸ ○ Write up the trip on the new blog ┃ ┃ 9┃ ┃ └─ blocked by: Move the blog to plain …: Point DNS at the new host (4) ┃ -┃ ○ Read every neighborhood guide and write down which three are worth the walk, the tram ride…┃ +┃ ▸ ○ Read every neighborhood guide and write down which three are worth the walk, the tram ride…┃ ┃ 10┃ -┃ ○ Day 1: walk the next neighborhood ┃ +┃ ▸ ○ Day 1: walk the next neighborhood ┃ ┃ 11┃ -┃ ○ Day 2: walk the next neighborhood ┃ +┃ ▸ ○ Day 2: walk the next neighborhood ┃ ┃ 12┃ -┃ ○ Day 3: walk the next neighborhood ┃ +┃ ▸ ○ Day 3: walk the next neighborhood ┃ ┃ 1/37 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]fold [Tab]view [/]filter [?]help LOCAL projects.db + tab view / filter j/k move [/] jump enter open space fold q quit ? keys LOCAL projects.db diff --git a/testdata/TestRealBinaryFlows/views/jumped-past-the-tasks.txt b/testdata/TestRealBinaryFlows/views/jumped-past-the-tasks.txt index 42a82ca..12e1b22 100644 --- a/testdata/TestRealBinaryFlows/views/jumped-past-the-tasks.txt +++ b/testdata/TestRealBinaryFlows/views/jumped-past-the-tasks.txt @@ -7,24 +7,24 @@ ┃ ✓ Dump the database ┃ ┃ ○ Convert HTML to markdown ┃ ┃ ○ Download the images ┃ -┃> ✓ Pick a theme ┃ +┃> ▸ ✓ Pick a theme ┃ ┃ 2┃ -┃ ○ Set up redirects for the old URLs ┃ +┃ ▸ ○ Set up redirects for the old URLs ┃ ┃ 3┃ ┃ └─ blocked by: Export posts from the old CMS (1) ┃ -┃ ○ Point DNS at the new host ┃ +┃ ▸ ○ Point DNS at the new host ┃ ┃ 4┃ ┃ └─ blocked by: Set up redirects for the old URLs (3) ┃ ┃ ┃ ┃ ▾ ── Replace the kitchen faucet ── ○3 ✓0 ┃ ┃ ▸ ○ Buy the faucet ≡ ┃ ┃ 6┃ -┃ ○ Shut off the water and swap it ┃ +┃ ▸ ○ Shut off the water and swap it ┃ ┃ 7┃ ┃ └─ blocked by: Buy the faucet (6) ┃ -┃ ○ Ask Sam for a plumber and a place to stay ◈ ┃ +┃ ▸ ○ Ask Sam for a plumber and a place to stay ◈ ┃ ┃ also in Plan the Lisbon trip 8┃ ┃ ┃ ┃ 6/44 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - [Enter]read [c]omplete [n]ote [Tab]view [/]filter [?]help LOCAL projects.db + c complete n note tab view / filter j/k move [/] jump … ? keys LOCAL projects.db diff --git a/testdata/flows/add-task.keys b/testdata/flows/add-task.keys index b9312df..c466aad 100644 --- a/testdata/flows/add-task.keys +++ b/testdata/flows/add-task.keys @@ -1,7 +1,7 @@ # Add a task from the list, then toggle one and delete one. wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press t wait New task for "Export posts from the old CMS" diff --git a/testdata/flows/browse.keys b/testdata/flows/browse.keys index a8f74b7..c6d05a2 100644 --- a/testdata/flows/browse.keys +++ b/testdata/flows/browse.keys @@ -4,7 +4,7 @@ wait Export posts from the old CMS frame first-screen press l -wait [Enter]read +wait c complete frame items-focused press j @@ -12,9 +12,9 @@ wait /> +✓ Dump the database/ frame cursor-on-task press ] -wait /> +✓ Pick a theme/ +wait /> [▸▾] ✓ Pick a theme/ press G -wait /> +○ Point DNS at the new host/ +wait /> [▸▾] ○ Point DNS at the new host/ frame cursor-on-last-item press g wait /> +▸ ○ Export posts from the old CMS/ @@ -31,7 +31,7 @@ press 0 gone [ALL] press h -wait [space]mark +wait space mark press space wait Selected (1) press j @@ -46,12 +46,12 @@ press G wait > Plan the Lisbon trip frame last-project press l -wait [Enter]read +wait c complete press ctrl+d -wait /> +○ Day 10: walk the next neighborhood/ +wait /> [▸▾] ○ Day 10: walk the next neighborhood/ frame long-list-half-page press G -wait /> +○ Day 28: walk the next neighborhood/ +wait /> [▸▾] ○ Day 28: walk the next neighborhood/ frame long-list-bottom press ctrl+c diff --git a/testdata/flows/dependency-tree.keys b/testdata/flows/dependency-tree.keys index 7d4314a..661c00d 100644 --- a/testdata/flows/dependency-tree.keys +++ b/testdata/flows/dependency-tree.keys @@ -8,7 +8,7 @@ wait All trees frame forest press l -wait [i]flip +wait i flip frame focus-pane press i wait Tree — unblocks @@ -29,7 +29,7 @@ wait All trees press tab wait /(?m)^.*Projects/ press l -wait [Enter]read +wait c complete press b wait Link dependency for: Export posts from the old CMS (1) frame link-overlay diff --git a/testdata/flows/detail-in-place.keys b/testdata/flows/detail-in-place.keys new file mode 100644 index 0000000..c55eb61 --- /dev/null +++ b/testdata/flows/detail-in-place.keys @@ -0,0 +1,46 @@ +# Enter opens an item's detail in place where it is short, and full screen where it runs past +# the pane. Space opens it in place however tall, and closes it again. Every item has a detail, +# so every item's row carries a chevron. Scrolling through the detail keeps the item's line on top. +mask /Created: [A-Z][a-z]+ \d+, \d{4}/ "Created: " +wait Export posts from the old CMS +press l +wait c complete +frame every-item-opens + +press ] +wait > ▸ ✓ Pick a theme +press enter +wait ▾ ✓ Pick a theme +wait Item 2 · +frame short-detail-in-place +press enter +wait ▸ ✓ Pick a theme +gone Item 2 · + +press [ +wait > ▸ ○ Export posts from the old CMS +press enter +wait esc/space back +frame tall-detail-full-screen +press esc +wait c complete + +press space +wait ▾ ○ Export posts from the old CMS +wait Item 1 · +frame tall-detail-in-place + +press ctrl+d +wait /(?m)^.*┃> ▾ ○ Export posts from the old CMS/ +frame item-stays-on-top +press J J J +wait Diff a sample of twenty +frame scrolled-by-lines + +press space +wait ▸ ○ Export posts from the old CMS +gone Item 1 · +frame detail-closed + +press q +exits diff --git a/testdata/flows/edit-note.keys b/testdata/flows/edit-note.keys index 86ba871..6c3b2fd 100644 --- a/testdata/flows/edit-note.keys +++ b/testdata/flows/edit-note.keys @@ -4,9 +4,9 @@ mask /Created: [A-Z][a-z]+ \d+, \d{4}/ "Created: " wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press ] -wait /> +✓ Pick a theme/ +wait /> [▸▾] ✓ Pick a theme/ press e wait /Pick a theme ≡/ @@ -14,11 +14,7 @@ frame note-saved press space wait Written from the editor. frame note-in-place -press enter -wait Notes -frame note-in-the-reader - -press esc -wait [Enter]read +press space +gone Written from the editor. press q exits diff --git a/testdata/flows/filter.keys b/testdata/flows/filter.keys index e13547c..4002218 100644 --- a/testdata/flows/filter.keys +++ b/testdata/flows/filter.keys @@ -2,7 +2,7 @@ # clears it. "replica" is only in the export item's note. wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press / type redirects @@ -18,7 +18,7 @@ type replica wait Export posts from the old CMS gone Pick a theme press enter -gone [Enter]keep +gone enter keep frame note-match-kept press esc wait Pick a theme @@ -44,7 +44,7 @@ wait Buy the faucet gone ── Move the blog to plain files ── frame all-items-filtered press enter -gone [Enter]keep +gone enter keep press q exits diff --git a/testdata/flows/help.keys b/testdata/flows/help.keys index 3ded7ae..932c5d3 100644 --- a/testdata/flows/help.keys +++ b/testdata/flows/help.keys @@ -3,20 +3,20 @@ wait Export posts from the old CMS press ? -wait [Esc]close [q]quit +wait esc close q quit frame from-the-project-pane press ? -gone [Esc]close [q]quit +gone esc close q quit press l -wait [Enter]read +wait c complete press ? -wait [Esc]close [q]quit +wait esc close q quit press esc -wait [Enter]read +wait c complete press j -wait [D]elete +wait D delete press ? wait On a task press esc @@ -24,7 +24,7 @@ press esc press k wait /> +▸ ○ Export posts from the old CMS/ press M -wait [Enter]drop +wait enter drop press ? wait Moving a row press esc diff --git a/testdata/flows/move.keys b/testdata/flows/move.keys index aa439d1..9b332a5 100644 --- a/testdata/flows/move.keys +++ b/testdata/flows/move.keys @@ -1,11 +1,11 @@ # M grabs a row, j and k carry it, Enter drops it and esc puts it back. wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press M wait ◀ MOVING -wait [Enter]drop +wait enter drop frame grabbed press j wait /✓ Pick a theme[\s\S]*▶ .*Export posts from the old CMS/ diff --git a/testdata/flows/narrow-window.keys b/testdata/flows/narrow-window.keys index a7b359d..7a53a10 100644 --- a/testdata/flows/narrow-window.keys +++ b/testdata/flows/narrow-window.keys @@ -15,10 +15,10 @@ press esc gone New task for press / -wait [Enter]keep +wait enter keep frame filter-prompt press esc -gone [Enter]keep +gone enter keep press enter wait Tasks 1/3 diff --git a/testdata/flows/note-in-place.keys b/testdata/flows/note-in-place.keys deleted file mode 100644 index c6866a8..0000000 --- a/testdata/flows/note-in-place.keys +++ /dev/null @@ -1,29 +0,0 @@ -# Space opens a note in place under its row, however tall, and closes it again. A chevron -# sits only on a row that opens. Scrolling through the note keeps the item's line on top. -wait Export posts from the old CMS -press l -wait [Enter]read -frame chevron-only-where-it-opens - -press space -wait ▾ ○ Export posts from the old CMS -wait Export plan -frame note-open - -press ctrl+d -wait Conversion script -gone Export plan -wait /(?m)^.*┃> ▾ ○ Export posts from the old CMS/ -frame item-stays-on-top -press J J J -wait [J/K]scroll -frame scrolled-by-lines - -press space -gone Conversion script -wait ▸ ○ Export posts from the old CMS -wait Pick a theme -frame note-closed - -press q -exits diff --git a/testdata/flows/reader.keys b/testdata/flows/reader.keys index c9264c1..c6497b3 100644 --- a/testdata/flows/reader.keys +++ b/testdata/flows/reader.keys @@ -5,7 +5,7 @@ mask /Project [0-9a-f]{8}/ "Project " wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press enter wait Item 1 · @@ -20,17 +20,17 @@ press G wait Last line of the note. frame item-bottom press esc -wait [Enter]read +wait c complete frame closed-onto-the-list press h -wait [space]mark +wait space mark press enter wait /Project [0-9a-f]{8} · active/ wait Why move frame project press space -wait [space]mark +wait space mark press q exits diff --git a/testdata/flows/undo.keys b/testdata/flows/undo.keys index 95cb8d8..0f9a2e3 100644 --- a/testdata/flows/undo.keys +++ b/testdata/flows/undo.keys @@ -4,23 +4,23 @@ mask /on item [0-9a-f]{8}/ "on item " wait Export posts from the old CMS press l -wait [Enter]read +wait c complete press G -wait /> +○ Point DNS at the new host/ +wait /> [▸▾] ○ Point DNS at the new host/ press c wait Cannot complete: item has unresolved blockers frame refused-while-blocked press [ [ -wait /> +✓ Pick a theme/ +wait /> [▸▾] ✓ Pick a theme/ press c wait Marked incomplete -wait /> +○ Pick a theme/ +wait /> [▸▾] ○ Pick a theme/ frame toggled press u wait Undo: -wait /> +✓ Pick a theme/ +wait /> [▸▾] ✓ Pick a theme/ frame undone press x diff --git a/testdata/flows/views.keys b/testdata/flows/views.keys index 9fc3d5d..61e705a 100644 --- a/testdata/flows/views.keys +++ b/testdata/flows/views.keys @@ -21,7 +21,7 @@ wait > ▾ ── Move the blog to plain files ── press j wait /> +▸ ○ Export posts from the old CMS/ press ] -wait /> +✓ Pick a theme/ +wait /> [▸▾] ✓ Pick a theme/ frame jumped-past-the-tasks press tab diff --git a/tui/action_table.go b/tui/action_table.go index 6c48da5..e9f0446 100644 --- a/tui/action_table.go +++ b/tui/action_table.go @@ -7,42 +7,34 @@ import ( tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" "github.com/charmbracelet/x/ansi" + "github.com/datapointchris/gotermboard" "github.com/datapointchris/projects/model" ) -// action is one row of the key table. The key handler, the footer and the ? overlay all read -// the same rows, so a key bound in one shows in the other two. -type action struct { - keys []string - // section heads the action's group in the ? overlay. - section string - label string - // hint is the footer's word for it, such as "[a]dd", or empty to leave it to the overlay. - hint string - run func(*App) tea.Cmd - applies func(*App) bool -} +// action is one row of the key table, in gotermboard's shape. The key handler, the footer and the +// ? overlay all read the same rows, so a key bound in one shows in the other two. Footer is the +// footer's words for the key, or empty to leave it to the overlay, and Section heads its group +// in the overlay. +type action = gotermboard.KeyAction[*App] const ( - sectionMove = "Moving" - sectionProject = "On a project" - sectionItem = "On an item" - sectionTask = "On a task" - sectionGroup = "On a project's group" - sectionViews = "Views and filters" - sectionMap = "In the dependency map" - sectionMoving = "Moving a row" - sectionEveryone = "Everywhere" - sectionPrompt = "Typing" - sectionPicker = "Choosing projects" - sectionLinking = "Choosing a dependency" - sectionKeys = "This list" + sectionProject = "On a project" + sectionItem = "On an item" + sectionTask = "On a task" + sectionViews = "Views and filters" + sectionMap = "In the dependency map" + sectionMoving = "Moving a row" + sectionPrompt = "Typing" + sectionPicker = "Choosing projects" + sectionLinking = "Choosing a dependency" + sectionKeys = "This list" ) // actions is every key the screen can use now, so a key it cannot use is in none of the -// handler, the footer and the overlay. A screen with a text box binds only keys that type -// nothing, and the box takes the rest. +// handler, the footer and the overlay. On the board, its own rows come ahead of gotermboard's +// universal ones, so a key of its own wins where it applies. A screen with a text box binds only +// keys that type nothing, and the box takes the rest. func (m *App) actions() []action { var table []action switch { @@ -61,35 +53,40 @@ func (m *App) actions() []action { case m.appMode != modeNormal: table = promptActions() case m.view == viewDependencies: - table = slices.Concat(mapActions(), viewActions(), everywhereActions()) + return gotermboard.ActionsNow(m, slices.Concat(mapActions(), viewActions(), everywhereActions()), gotermboard.UniversalActions[*App]()) case m.view == viewProjects && m.activePane == projectPane: - table = slices.Concat(listMoveActions(), projectActions(), viewActions(), everywhereActions()) + return gotermboard.ActionsNow(m, slices.Concat(paneActions(), projectActions(), viewActions(), everywhereActions()), gotermboard.UniversalActions[*App]()) default: - table = slices.Concat(listMoveActions(), itemActions(), taskActions(), groupActions(), viewActions(), everywhereActions()) + return gotermboard.ActionsNow(m, slices.Concat(paneActions(), itemActions(), taskActions(), viewActions(), everywhereActions()), gotermboard.UniversalActions[*App]()) } - return slices.DeleteFunc(table, func(a action) bool { return a.applies != nil && !a.applies(m) }) + return gotermboard.ActionsNow(m, table) } // runKey runs the action bound to key, and reports whether one was. func (m *App) runKey(key string) (tea.Cmd, bool) { for _, a := range m.actions() { - if slices.Contains(a.keys, key) { - return a.run(m), true + if slices.Contains(a.Keys, key) { + return a.Run(m), true } } return nil, false } +// footerHints is the footer's words for each key the screen can use now, two spaces apart. The +// status bar pins ? at its right, so the list leaves it out. func (m *App) footerHints() string { var hints []string for _, a := range m.actions() { - if a.hint != "" { - hints = append(hints, a.hint) + if a.Footer != "" && a.Keys[0] != "?" { + hints = append(hints, a.Footer) } } - return strings.Join(hints, " ") + return strings.Join(hints, hintGap) } +// hintGap parts one hint from the next, which a hint's own words never hold. +const hintGap = " " + // keyNames is how the overlay writes a key Bubble Tea names otherwise. var keyNames = map[string]string{ "down": "↓", "up": "↑", "left": "←", "right": "→", @@ -109,25 +106,32 @@ func keysText(keys []string) string { return strings.Join(named, " ") } -// helpLines is the ? overlay: every action the screen can use now, under its section, each -// label wrapped at width under its own column so no word of it reads as a key. +// helpLines is the ? overlay: every action the screen can use now, under its section in the +// order each section first appears, each label wrapped at width under its own column so no word +// of it reads as a key. func (m *App) helpLines(width int) []string { - var lines []string - section := "" + var sections []string + bySection := map[string][]action{} for _, a := range m.actions() { - if a.section != section { - if section != "" { - lines = append(lines, "") - } - section = a.section - lines = append(lines, " "+overlayTitleStyle.Render(section)) + if _, seen := bySection[a.Section]; !seen { + sections = append(sections, a.Section) + } + bySection[a.Section] = append(bySection[a.Section], a) + } + var lines []string + for i, section := range sections { + if i > 0 { + lines = append(lines, "") } - keys := " " + padRight(keysText(a.keys), 18) - indent := strings.Repeat(" ", lipgloss.Width(keys)) - label := strings.Split(ansi.Wordwrap(a.label, max(width-len(indent), 10), ""), "\n") - lines = append(lines, keys+label[0]) - for _, rest := range label[1:] { - lines = append(lines, indent+rest) + lines = append(lines, " "+overlayTitleStyle.Render(section)) + for _, a := range bySection[section] { + keys := " " + padRight(keysText(a.Keys), 18) + indent := strings.Repeat(" ", lipgloss.Width(keys)) + label := strings.Split(ansi.Wordwrap(a.Label, max(width-len(indent), 10), ""), "\n") + lines = append(lines, keys+label[0]) + for _, rest := range label[1:] { + lines = append(lines, indent+rest) + } } } return lines @@ -142,45 +146,19 @@ func padRight(s string, width int) string { // --- The tables --- -func listMoveActions() []action { +// paneActions is moving between the panes of the projects view. gotermboard's universal rows move +// within a pane. +func paneActions() []action { return []action{ - {keys: []string{"j", "down"}, section: sectionMove, label: "move down a row", run: func(m *App) tea.Cmd { return m.cursorDown() }}, - {keys: []string{"k", "up"}, section: sectionMove, label: "move up a row", run: func(m *App) tea.Cmd { return m.cursorUp() }}, - {keys: []string{"g", "home"}, section: sectionMove, label: "go to the top", run: func(m *App) tea.Cmd { return m.cursorTop() }}, - {keys: []string{"G", "end"}, section: sectionMove, label: "go to the bottom", run: func(m *App) tea.Cmd { return m.cursorBottom() }}, - { - keys: []string{"ctrl+d", "pgdown"}, section: sectionMove, label: "half a screen down the open note, or down the list", - run: func(m *App) tea.Cmd { return m.halfScreen(1) }, - }, - { - keys: []string{"ctrl+u", "pgup"}, section: sectionMove, label: "half a screen up the open note, or up the list", - run: func(m *App) tea.Cmd { return m.halfScreen(-1) }, - }, - { - keys: []string{"]"}, section: sectionMove, label: "jump to the next item or group, skipping tasks", - run: func(m *App) tea.Cmd { m.jumpRow(1); return nil }, applies: (*App).onItemList, - }, { - keys: []string{"["}, section: sectionMove, label: "jump to the previous item or group, skipping tasks", - run: func(m *App) tea.Cmd { m.jumpRow(-1); return nil }, applies: (*App).onItemList, + Keys: []string{"h", "left"}, Section: gotermboard.SectionMoving, Label: "go to the projects", + Run: func(m *App) tea.Cmd { m.activePane = projectPane; return nil }, + Applies: func(m *App) bool { return m.view == viewProjects && m.activePane == itemPane }, }, { - keys: []string{"J"}, section: sectionMove, label: "scroll the open note down a line", hint: "[J/K]scroll", - run: func(m *App) tea.Cmd { m.scrollOpenText(1); return nil }, applies: (*App).onOpenText, - }, - { - keys: []string{"K"}, section: sectionMove, label: "scroll the open note up a line", - run: func(m *App) tea.Cmd { m.scrollOpenText(-1); return nil }, applies: (*App).onOpenText, - }, - { - keys: []string{"h", "left"}, section: sectionMove, label: "go to the projects", - run: func(m *App) tea.Cmd { m.activePane = projectPane; return nil }, - applies: func(m *App) bool { return m.view == viewProjects && m.activePane == itemPane }, - }, - { - keys: []string{"l", "right"}, section: sectionMove, label: "go to the items", - run: func(m *App) tea.Cmd { m.activePane = itemPane; return nil }, - applies: func(m *App) bool { return m.view == viewProjects && m.activePane == projectPane }, + Keys: []string{"l", "right"}, Section: gotermboard.SectionMoving, Label: "go to the items", + Run: func(m *App) tea.Cmd { m.activePane = itemPane; return nil }, + Applies: func(m *App) bool { return m.view == viewProjects && m.activePane == projectPane }, }, } } @@ -190,84 +168,71 @@ func projectActions() []action { active := func(m *App) bool { return m.onProject() && m.projects[m.projectCursor].Status == model.StatusActive } closed := func(m *App) bool { return m.onProject() && m.projects[m.projectCursor].Status != model.StatusActive } return []action{ - {keys: []string{"enter"}, section: sectionProject, label: "read the project", hint: "[Enter]read", run: (*App).readProject, applies: onProject}, - {keys: []string{"space"}, section: sectionProject, label: "mark the project, to list several projects' items", hint: "[space]mark", run: (*App).toggleProjectMark, applies: onProject}, - {keys: []string{"a"}, section: sectionProject, label: "add a project", hint: "[a]dd", run: (*App).startAddProject}, - {keys: []string{"A"}, section: sectionProject, label: "add an item to several projects", run: (*App).startAddItemToSeveral, applies: onProject}, - {keys: []string{"r"}, section: sectionProject, label: "rename the project", run: (*App).startRenameProject, applies: onProject}, - {keys: []string{"e", "n"}, section: sectionProject, label: "write the description in $EDITOR", hint: "[e]dit", run: (*App).editProjectDescription, applies: onProject}, - {keys: []string{"c"}, section: sectionProject, label: "complete the project", run: (*App).completeProject, applies: active}, - {keys: []string{"c"}, section: sectionProject, label: "reopen the shelved or closed project", hint: "[c]reopen", run: (*App).reopenProject, applies: closed}, - {keys: []string{"x"}, section: sectionProject, label: "drop the project, saying why", run: (*App).startDropProject, applies: active}, - {keys: []string{"S"}, section: sectionProject, label: "shelve the project for someday", run: (*App).shelveProject, applies: active}, + {Keys: []string{"enter"}, Section: sectionProject, Label: "read the project", Footer: "enter read", Run: (*App).readProject, Applies: onProject}, + {Keys: []string{"space"}, Section: sectionProject, Label: "mark the project, to list several projects' items", Footer: "space mark", Run: (*App).toggleProjectMark, Applies: onProject}, + {Keys: []string{"a"}, Section: sectionProject, Label: "add a project", Footer: "a add", Run: (*App).startAddProject}, + {Keys: []string{"A"}, Section: sectionProject, Label: "add an item to several projects", Run: (*App).startAddItemToSeveral, Applies: onProject}, + {Keys: []string{"r"}, Section: sectionProject, Label: "rename the project", Run: (*App).startRenameProject, Applies: onProject}, + {Keys: []string{"e", "n"}, Section: sectionProject, Label: "write the description in $EDITOR", Footer: "e edit", Run: (*App).editProjectDescription, Applies: onProject}, + {Keys: []string{"c"}, Section: sectionProject, Label: "complete the project", Run: (*App).completeProject, Applies: active}, + {Keys: []string{"c"}, Section: sectionProject, Label: "reopen the shelved or closed project", Footer: "c reopen", Run: (*App).reopenProject, Applies: closed}, + {Keys: []string{"x"}, Section: sectionProject, Label: "drop the project, saying why", Run: (*App).startDropProject, Applies: active}, + {Keys: []string{"S"}, Section: sectionProject, Label: "shelve the project for someday", Run: (*App).shelveProject, Applies: active}, { - keys: []string{"M"}, section: sectionProject, label: "move the project, then j and k, Enter to drop", hint: "[M]ove", - run: (*App).grabProject, applies: func(m *App) bool { return m.onProject() && len(m.projects) > 1 && m.projectQuery == "" }, + Keys: []string{"M"}, Section: sectionProject, Label: "move the project, then j and k, Enter to drop", Footer: "M move", + Run: (*App).grabProject, Applies: func(m *App) bool { return m.onProject() && len(m.projects) > 1 && m.projectQuery == "" }, }, - {keys: []string{"T"}, section: sectionProject, label: "open the dependency map", run: func(m *App) tea.Cmd { return m.switchView(viewDependencies, "") }}, - {keys: []string{"C"}, section: sectionProject, label: "open a Claude session on the project", run: (*App).claudeOnProject, applies: onProject}, + {Keys: []string{"T"}, Section: sectionProject, Label: "open the dependency map", Run: func(m *App) tea.Cmd { return m.switchView(viewDependencies, "") }}, + {Keys: []string{"C"}, Section: sectionProject, Label: "open a Claude session on the project", Run: (*App).claudeOnProject, Applies: onProject}, } } +// itemActions is an item's own keys. gotermboard's enter and space open its detail. func itemActions() []action { onItem := func(m *App) bool { return m.onKind(rowItem) } onItemOrTask := func(m *App) bool { return m.onKind(rowItem) || m.onKind(rowTask) } oneProject := func(m *App) bool { return !m.isGroupedView() && m.projectCursor < len(m.projects) } return []action{ - {keys: []string{"enter"}, section: sectionItem, label: "read the item", hint: "[Enter]read", run: (*App).readItem, applies: onItem}, - { - keys: []string{"space"}, section: sectionItem, label: "open or close the note in place", hint: "[space]note", - run: (*App).toggleNote, applies: func(m *App) bool { return onItem(m) && hasNote(m.currentItem().ProjectItem) }, - }, - {keys: []string{"c"}, section: sectionItem, label: "mark the item done or not done", hint: "[c]omplete", run: (*App).toggleItemDone, applies: onItem}, - {keys: []string{"a"}, section: sectionItem, label: "add an item to this project", hint: "[a]dd", run: (*App).startAddItem, applies: oneProject}, - {keys: []string{"A"}, section: sectionItem, label: "add an item to several projects", run: (*App).startAddItemToSeveral, applies: func(m *App) bool { return len(m.allProjects) > 0 }}, - {keys: []string{"n", "e"}, section: sectionItem, label: "write the note in $EDITOR", hint: "[n]ote", run: (*App).editItemNote, applies: onItem}, - {keys: []string{"r"}, section: sectionItem, label: "rename the item", run: (*App).startRenameItem, applies: onItem}, - {keys: []string{"x"}, section: sectionItem, label: "archive the item", run: (*App).archiveItem, applies: onItem}, - {keys: []string{"P"}, section: sectionItem, label: "choose the projects the item is filed in", run: (*App).manageItemProjects, applies: onItem}, - {keys: []string{"b"}, section: sectionItem, label: "link an item this one waits on", run: (*App).startLinkDependency, applies: onItem}, - {keys: []string{"B"}, section: sectionItem, label: "unlink an item this one waits on", run: (*App).startUnlinkDependency, applies: onItem}, + {Keys: []string{"c"}, Section: sectionItem, Label: "mark the item done or not done", Footer: "c complete", Run: (*App).toggleItemDone, Applies: onItem}, + {Keys: []string{"a"}, Section: sectionItem, Label: "add an item to this project", Footer: "a add", Run: (*App).startAddItem, Applies: oneProject}, + {Keys: []string{"A"}, Section: sectionItem, Label: "add an item to several projects", Run: (*App).startAddItemToSeveral, Applies: func(m *App) bool { return len(m.allProjects) > 0 }}, + {Keys: []string{"n", "e"}, Section: sectionItem, Label: "write the note in $EDITOR", Footer: "n note", Run: (*App).editItemNote, Applies: onItem}, + {Keys: []string{"r"}, Section: sectionItem, Label: "rename the item", Run: (*App).startRenameItem, Applies: onItem}, + {Keys: []string{"x"}, Section: sectionItem, Label: "archive the item", Run: (*App).archiveItem, Applies: onItem}, + {Keys: []string{"P"}, Section: sectionItem, Label: "choose the projects the item is filed in", Run: (*App).manageItemProjects, Applies: onItem}, + {Keys: []string{"b"}, Section: sectionItem, Label: "link an item this one waits on", Run: (*App).startLinkDependency, Applies: onItem}, + {Keys: []string{"B"}, Section: sectionItem, Label: "unlink an item this one waits on", Run: (*App).startUnlinkDependency, Applies: onItem}, { - keys: []string{"M"}, section: sectionItem, label: "move the item, then j and k, Enter to drop", hint: "[M]ove", - run: (*App).grabItem, applies: func(m *App) bool { return onItem(m) && oneProject(m) && len(m.items) > 1 && m.itemQuery == "" }, + Keys: []string{"M"}, Section: sectionItem, Label: "move the item, then j and k, Enter to drop", Footer: "M move", + Run: (*App).grabItem, Applies: func(m *App) bool { return onItem(m) && oneProject(m) && len(m.items) > 1 && m.itemQuery == "" }, }, - {keys: []string{"C"}, section: sectionItem, label: "open a Claude session on the item", run: (*App).claudeOnItem, applies: onItem}, - {keys: []string{"t"}, section: sectionItem, label: "add a task to the item", run: (*App).startAddTask, applies: onItemOrTask}, - {keys: []string{"T"}, section: sectionItem, label: "open the dependency map on the item", run: (*App).mapOnItem, applies: onItemOrTask}, + {Keys: []string{"C"}, Section: sectionItem, Label: "open a Claude session on the item", Run: (*App).claudeOnItem, Applies: onItem}, + {Keys: []string{"t"}, Section: sectionItem, Label: "add a task to the item", Run: (*App).startAddTask, Applies: onItemOrTask}, + {Keys: []string{"T"}, Section: sectionItem, Label: "open the dependency map on the item", Run: (*App).mapOnItem, Applies: onItemOrTask}, } } func taskActions() []action { onTask := func(m *App) bool { return m.onKind(rowTask) } return []action{ - {keys: []string{"space", "c"}, section: sectionTask, label: "mark the task done or not done", hint: "[space]done", run: (*App).toggleTaskDone, applies: onTask}, - {keys: []string{"D"}, section: sectionTask, label: "delete the task", hint: "[D]elete", run: (*App).deleteTask, applies: onTask}, - } -} - -func groupActions() []action { - return []action{ - { - keys: []string{"enter", "space"}, section: sectionGroup, label: "fold or open the project's items", hint: "[Enter]fold", - run: (*App).toggleGroup, applies: func(m *App) bool { return m.onKind(rowGroup) }, - }, + {Keys: []string{"space", "c"}, Section: sectionTask, Label: "mark the task done or not done", Footer: "space done", Run: (*App).toggleTaskDone, Applies: onTask}, + {Keys: []string{"D"}, Section: sectionTask, Label: "delete the task", Footer: "D delete", Run: (*App).deleteTask, Applies: onTask}, } } func viewActions() []action { return []action{ - {keys: []string{"esc"}, section: sectionViews, label: "clear the filter, then the marks, or back out of a drill", hint: "[Esc]back", run: (*App).backOut, applies: (*App).canBackOut}, - {keys: []string{"tab"}, section: sectionViews, label: "the next view", hint: "[Tab]view", run: func(m *App) tea.Cmd { return m.cycleView(1) }}, - {keys: []string{"shift+tab"}, section: sectionViews, label: "the previous view", run: func(m *App) tea.Cmd { return m.cycleView(-1) }}, - {keys: []string{"/"}, section: sectionViews, label: "filter the list, matching notes too", hint: "[/]filter", run: (*App).startFilter}, - {keys: []string{"1"}, section: sectionViews, label: "show only blocked items, or every one again", run: func(m *App) tea.Cmd { return m.setFilter(filterBlocked) }, applies: notMap}, - {keys: []string{"2"}, section: sectionViews, label: "show archived items too, or hide them again", run: func(m *App) tea.Cmd { return m.setFilter(filterAll) }, applies: notMap}, + {Keys: []string{"esc"}, Section: sectionViews, Label: "clear the filter, then the marks, or back out of a drill", Footer: "esc back", Run: (*App).backOut, Applies: (*App).canBackOut}, + {Keys: []string{"tab"}, Section: sectionViews, Label: "the next view", Footer: "tab view", Run: func(m *App) tea.Cmd { return m.cycleView(1) }}, + {Keys: []string{"shift+tab"}, Section: sectionViews, Label: "the previous view", Run: func(m *App) tea.Cmd { return m.cycleView(-1) }}, + {Keys: []string{"/"}, Section: sectionViews, Label: "filter the list, matching notes too", Footer: "/ filter", Run: (*App).startFilter}, + {Keys: []string{"1"}, Section: sectionViews, Label: "show only blocked items, or every one again", Run: func(m *App) tea.Cmd { return m.setFilter(filterBlocked) }, Applies: notMap}, + {Keys: []string{"2"}, Section: sectionViews, Label: "show archived items too, or hide them again", Run: func(m *App) tea.Cmd { return m.setFilter(filterAll) }, Applies: notMap}, { - keys: []string{"0"}, section: sectionViews, label: "show every item but the archived", hint: "[0]reset", - run: func(m *App) tea.Cmd { return m.setFilter(filterNone) }, applies: func(m *App) bool { return notMap(m) && m.filter != filterNone }, + Keys: []string{"0"}, Section: sectionViews, Label: "show every item but the archived", Footer: "0 reset", + Run: func(m *App) tea.Cmd { return m.setFilter(filterNone) }, Applies: func(m *App) bool { return notMap(m) && m.filter != filterNone }, }, - {keys: []string{"."}, section: sectionViews, label: "show or hide shelved and closed projects", run: (*App).toggleClosedProjects, applies: notMap}, + {Keys: []string{"."}, Section: sectionViews, Label: "show or hide shelved and closed projects", Run: (*App).toggleClosedProjects, Applies: notMap}, } } @@ -275,80 +240,73 @@ func notMap(m *App) bool { return m.view != viewDependencies } func everywhereActions() []action { return []action{ - {keys: []string{"u"}, section: sectionEveryone, label: "undo the last change", run: func(m *App) tea.Cmd { return undoCmd(m.backend) }}, - {keys: []string{"R"}, section: sectionEveryone, label: "read the data again now", run: (*App).readAgain}, - {keys: []string{"?"}, section: sectionEveryone, label: "show these keys", run: func(m *App) tea.Cmd { m.returnMode = m.appMode; m.appMode = modeHelp; return nil }}, - {keys: []string{"q", "ctrl+c"}, section: sectionEveryone, label: "quit", run: func(*App) tea.Cmd { return tea.Quit }}, + {Keys: []string{"u"}, Section: gotermboard.SectionEverywhere, Label: "undo the last change", Run: func(m *App) tea.Cmd { return undoCmd(m.backend) }}, + {Keys: []string{"R"}, Section: gotermboard.SectionEverywhere, Label: "read the data again now", Run: (*App).readAgain}, } } +// mapActions is the dependency map's own keys. gotermboard's universal rows move through it. func mapActions() []action { focused := func(m *App) bool { return m.depTree.active == focusPane } return []action{ - {keys: []string{"j", "down"}, section: sectionMove, label: "move down a row", run: func(m *App) tea.Cmd { m.depTreeMove(1); return nil }}, - {keys: []string{"k", "up"}, section: sectionMove, label: "move up a row", run: func(m *App) tea.Cmd { m.depTreeMove(-1); return nil }}, - {keys: []string{"g", "home"}, section: sectionMove, label: "go to the top", run: func(m *App) tea.Cmd { m.depTreeJump(0); return nil }}, - {keys: []string{"G", "end"}, section: sectionMove, label: "go to the bottom", run: func(m *App) tea.Cmd { m.depTreeJump(-1); return nil }}, - {keys: []string{"ctrl+d", "pgdown"}, section: sectionMove, label: "half a screen down", run: func(m *App) tea.Cmd { m.depTreeMove(m.depTreePaneHeight() / 2); return nil }}, - {keys: []string{"ctrl+u", "pgup"}, section: sectionMove, label: "half a screen up", run: func(m *App) tea.Cmd { m.depTreeMove(-m.depTreePaneHeight() / 2); return nil }}, - {keys: []string{"l", "right"}, section: sectionMap, label: "go into the tree, then drill into the row", hint: "[l]drill", run: (*App).depTreeRight}, + {Keys: []string{"l", "right"}, Section: sectionMap, Label: "go into the tree, then drill into the row", Footer: "l drill", Run: (*App).depTreeRight}, { - keys: []string{"h", "left"}, section: sectionMap, label: "back out a drill, then back to the map", hint: "[h]back", - run: (*App).depTreeLeft, applies: focused, + Keys: []string{"h", "left"}, Section: sectionMap, Label: "back out a drill, then back to the map", Footer: "h back", + Run: (*App).depTreeLeft, Applies: focused, }, - {keys: []string{"enter"}, section: sectionMap, label: "read the item", hint: "[Enter]read", run: (*App).readMapItem, applies: func(m *App) bool { return m.depTree.activeID() != "" }}, - {keys: []string{"i"}, section: sectionMap, label: "read the tree's edges the other way", hint: "[i]flip", run: (*App).depTreeFlip, applies: focused}, - {keys: []string{"."}, section: sectionMap, label: "show or hide the finished trees", hint: "[.]finished", run: (*App).depTreeToggleFinished}, + {Keys: []string{"enter"}, Section: sectionMap, Label: "read the item", Footer: "enter read", Run: (*App).readMapItem, Applies: func(m *App) bool { return m.depTree.activeID() != "" }}, + {Keys: []string{"i"}, Section: sectionMap, Label: "read the tree's edges the other way", Footer: "i flip", Run: (*App).depTreeFlip, Applies: focused}, + {Keys: []string{"."}, Section: sectionMap, Label: "show or hide the finished trees", Footer: ". finished", Run: (*App).depTreeToggleFinished}, } } func moveActions() []action { return []action{ - {keys: []string{"j", "down"}, section: sectionMoving, label: "move it down", hint: "[j/k]move", run: func(m *App) tea.Cmd { m.moveGrabbed(1); return nil }}, - {keys: []string{"k", "up"}, section: sectionMoving, label: "move it up", run: func(m *App) tea.Cmd { m.moveGrabbed(-1); return nil }}, - {keys: []string{"g", "home"}, section: sectionMoving, label: "move it to the top", hint: "[g/G]top/bottom", run: func(m *App) tea.Cmd { m.moveGrabbedToEnd(-1); return nil }}, - {keys: []string{"G", "end"}, section: sectionMoving, label: "move it to the bottom", run: func(m *App) tea.Cmd { m.moveGrabbedToEnd(1); return nil }}, - {keys: []string{"enter"}, section: sectionMoving, label: "drop it here", hint: "[Enter]drop", run: (*App).dropGrabbed}, - {keys: []string{"esc"}, section: sectionMoving, label: "put it back", hint: "[Esc]cancel", run: (*App).cancelGrab}, - {keys: []string{"?"}, section: sectionEveryone, label: "show these keys", run: func(m *App) tea.Cmd { m.returnMode = m.appMode; m.appMode = modeHelp; return nil }}, - {keys: []string{"ctrl+c"}, section: sectionEveryone, label: "quit", run: func(*App) tea.Cmd { return tea.Quit }}, + {Keys: []string{"j", "down"}, Section: sectionMoving, Label: "move it down", Footer: "j/k move", Run: func(m *App) tea.Cmd { m.moveGrabbed(1); return nil }}, + {Keys: []string{"k", "up"}, Section: sectionMoving, Label: "move it up", Run: func(m *App) tea.Cmd { m.moveGrabbed(-1); return nil }}, + {Keys: []string{"g", "home"}, Section: sectionMoving, Label: "move it to the top", Footer: "g/G top/bottom", Run: func(m *App) tea.Cmd { m.moveGrabbedToEnd(-1); return nil }}, + {Keys: []string{"G", "end"}, Section: sectionMoving, Label: "move it to the bottom", Run: func(m *App) tea.Cmd { m.moveGrabbedToEnd(1); return nil }}, + {Keys: []string{"enter"}, Section: sectionMoving, Label: "drop it here", Footer: "enter drop", Run: (*App).dropGrabbed}, + {Keys: []string{"esc"}, Section: sectionMoving, Label: "put it back", Footer: "esc cancel", Run: (*App).cancelGrab}, + {Keys: []string{"?"}, Section: gotermboard.SectionEverywhere, Label: "show these keys", Run: func(m *App) tea.Cmd { m.ShowKeys(); return nil }}, + {Keys: []string{"ctrl+c"}, Section: gotermboard.SectionEverywhere, Label: "quit", Run: func(*App) tea.Cmd { return tea.Quit }}, } } func helpActions() []action { return []action{ - {keys: []string{"esc", "?"}, section: sectionKeys, label: "close this list", hint: "[Esc]close", run: (*App).closeHelp}, - {keys: []string{"q", "ctrl+c"}, section: sectionKeys, label: "quit", hint: "[q]quit", run: func(*App) tea.Cmd { return tea.Quit }}, + {Keys: []string{"esc", "?"}, Section: sectionKeys, Label: "close this list", Footer: "esc close", Run: (*App).closeHelp}, + {Keys: []string{"q", "ctrl+c"}, Section: sectionKeys, Label: "quit", Footer: "q quit", Run: func(*App) tea.Cmd { return tea.Quit }}, } } func filterActions() []action { return []action{ - {keys: []string{"enter"}, section: sectionViews, label: "keep the filter and go back to the list", hint: "[Enter]keep", run: (*App).keepFilter}, - {keys: []string{"esc"}, section: sectionViews, label: "clear the filter", hint: "[Esc]clear", run: (*App).clearFilter}, + {Keys: []string{"enter"}, Section: sectionViews, Label: "keep the filter and go back to the list", Footer: "enter keep", Run: (*App).keepFilter}, + {Keys: []string{"esc"}, Section: sectionViews, Label: "clear the filter", Footer: "esc clear", Run: (*App).clearFilter}, } } // promptActions is a prompt for a name, a title or a reason. func promptActions() []action { return []action{ - {keys: []string{"enter"}, section: sectionPrompt, label: "do it with what is typed", hint: "[Enter]confirm", run: (*App).confirmPrompt}, - {keys: []string{"esc"}, section: sectionPrompt, label: "close the prompt, keeping nothing", hint: "[Esc]cancel", run: (*App).cancelPrompt}, + {Keys: []string{"enter"}, Section: sectionPrompt, Label: "do it with what is typed", Footer: "enter confirm", Run: (*App).confirmPrompt}, + {Keys: []string{"esc"}, Section: sectionPrompt, Label: "close the prompt, keeping nothing", Footer: "esc cancel", Run: (*App).cancelPrompt}, } } func pickerActions() []action { return []action{ - {keys: []string{"j", "down"}, section: sectionPicker, label: "move down a project", run: func(m *App) tea.Cmd { m.picker.down(); return nil }}, - {keys: []string{"k", "up"}, section: sectionPicker, label: "move up a project", run: func(m *App) tea.Cmd { m.picker.up(); return nil }}, - {keys: []string{"g", "home"}, section: sectionPicker, label: "go to the top", run: func(m *App) tea.Cmd { m.picker.cursor = 0; return nil }}, + {Keys: []string{"j", "down"}, Section: sectionPicker, Label: "move down a project", Run: func(m *App) tea.Cmd { m.picker.down(); return nil }}, + {Keys: []string{"k", "up"}, Section: sectionPicker, Label: "move up a project", Run: func(m *App) tea.Cmd { m.picker.up(); return nil }}, + {Keys: []string{"g", "home"}, Section: sectionPicker, Label: "go to the top", Run: func(m *App) tea.Cmd { m.picker.cursor = 0; return nil }}, { - keys: []string{"G", "end"}, section: sectionPicker, label: "go to the bottom", - run: func(m *App) tea.Cmd { m.picker.cursor = max(len(m.picker.projects)-1, 0); return nil }, + Keys: []string{"G", "end"}, Section: sectionPicker, Label: "go to the bottom", + Run: func(m *App) tea.Cmd { m.picker.cursor = max(len(m.picker.projects)-1, 0); return nil }, }, - {keys: []string{"space"}, section: sectionPicker, label: "tick or untick the project", hint: "[space]toggle", run: func(m *App) tea.Cmd { m.picker.toggle(); return nil }}, - {keys: []string{"enter"}, section: sectionPicker, label: "file the item in the ticked projects", hint: "[Enter]confirm", run: (*App).confirmPicker}, - {keys: []string{"esc"}, section: sectionPicker, label: "close, changing nothing", hint: "[Esc]cancel", run: (*App).cancelPicker}, + {Keys: []string{"space"}, Section: sectionPicker, Label: "tick or untick the project", Footer: "space toggle", Run: func(m *App) tea.Cmd { m.picker.toggle(); return nil }}, + {Keys: []string{"enter"}, Section: sectionPicker, Label: "file the item in the ticked projects", Footer: "enter confirm", Run: (*App).confirmPicker}, + {Keys: []string{"esc"}, Section: sectionPicker, Label: "close, changing nothing", Footer: "esc cancel", Run: (*App).cancelPicker}, } } @@ -362,20 +320,20 @@ func depLinkActions() []action { } return []action{ { - keys: []string{"enter"}, section: sectionLinking, label: "keep the filter and choose from what it kept", hint: "[Enter]keep", - run: func(m *App) tea.Cmd { return m.stopDepFilter(false) }, applies: typing, + Keys: []string{"enter"}, Section: sectionLinking, Label: "keep the filter and choose from what it kept", Footer: "enter keep", + Run: func(m *App) tea.Cmd { return m.stopDepFilter(false) }, Applies: typing, }, - {keys: []string{"esc"}, section: sectionLinking, label: "clear the filter", hint: "[Esc]clear", run: func(m *App) tea.Cmd { return m.stopDepFilter(true) }, applies: typing}, - {keys: []string{"j", "down"}, section: sectionLinking, label: "move down an item", run: func(m *App) tea.Cmd { m.moveDepCursor(m.depCursor + 1); return nil }, applies: listing}, - {keys: []string{"k", "up"}, section: sectionLinking, label: "move up an item", run: func(m *App) tea.Cmd { m.moveDepCursor(m.depCursor - 1); return nil }, applies: listing}, - {keys: []string{"g", "home"}, section: sectionLinking, label: "go to the top", run: func(m *App) tea.Cmd { m.moveDepCursor(0); return nil }, applies: listing}, - {keys: []string{"G", "end"}, section: sectionLinking, label: "go to the bottom", run: func(m *App) tea.Cmd { m.moveDepCursor(len(m.depItems)); return nil }, applies: listing}, - {keys: []string{"enter"}, section: sectionLinking, label: "link the item this one waits on", hint: "[Enter]link", run: (*App).chooseDependency, applies: choosing(modeDepLink)}, - {keys: []string{"enter"}, section: sectionLinking, label: "unlink the item this one waits on", hint: "[Enter]unlink", run: (*App).chooseDependency, applies: choosing(modeDepUnlink)}, + {Keys: []string{"esc"}, Section: sectionLinking, Label: "clear the filter", Footer: "esc clear", Run: func(m *App) tea.Cmd { return m.stopDepFilter(true) }, Applies: typing}, + {Keys: []string{"j", "down"}, Section: sectionLinking, Label: "move down an item", Run: func(m *App) tea.Cmd { m.moveDepCursor(m.depCursor + 1); return nil }, Applies: listing}, + {Keys: []string{"k", "up"}, Section: sectionLinking, Label: "move up an item", Run: func(m *App) tea.Cmd { m.moveDepCursor(m.depCursor - 1); return nil }, Applies: listing}, + {Keys: []string{"g", "home"}, Section: sectionLinking, Label: "go to the top", Run: func(m *App) tea.Cmd { m.moveDepCursor(0); return nil }, Applies: listing}, + {Keys: []string{"G", "end"}, Section: sectionLinking, Label: "go to the bottom", Run: func(m *App) tea.Cmd { m.moveDepCursor(len(m.depItems)); return nil }, Applies: listing}, + {Keys: []string{"enter"}, Section: sectionLinking, Label: "link the item this one waits on", Footer: "enter link", Run: (*App).chooseDependency, Applies: choosing(modeDepLink)}, + {Keys: []string{"enter"}, Section: sectionLinking, Label: "unlink the item this one waits on", Footer: "enter unlink", Run: (*App).chooseDependency, Applies: choosing(modeDepUnlink)}, { - keys: []string{"/"}, section: sectionLinking, label: "filter the items", hint: "[/]filter", run: (*App).startDepFilter, - applies: func(m *App) bool { return listing(m) && m.appMode == modeDepLink }, + Keys: []string{"/"}, Section: sectionLinking, Label: "filter the items", Footer: "/ filter", Run: (*App).startDepFilter, + Applies: func(m *App) bool { return listing(m) && m.appMode == modeDepLink }, }, - {keys: []string{"esc"}, section: sectionLinking, label: "close, changing nothing", hint: "[Esc]cancel", run: (*App).cancelDepLink, applies: listing}, + {Keys: []string{"esc"}, Section: sectionLinking, Label: "close, changing nothing", Footer: "esc cancel", Run: (*App).cancelDepLink, Applies: listing}, } } diff --git a/tui/app.go b/tui/app.go index 9472e1a..ac9bc2e 100644 --- a/tui/app.go +++ b/tui/app.go @@ -40,9 +40,10 @@ type App struct { // rows is the item pane's cursor stops: group headers, items and their tasks. rows []paneRow // folded holds each group folded under its header, by project ID, and opened each item - // whose note shows in place, by item ID. - folded map[string]bool - opened map[string]bool + // whose detail shows in place, by item ID. details is each such item's detail as last read. + folded map[string]bool + opened map[string]bool + details map[string]itemDetailMsg // scrolled says ctrl+d, ctrl+u, J or K moved the item pane through an open note, and the // pane holds that offset until the cursor moves. scrolled bool @@ -146,6 +147,7 @@ func NewApp(b backend.Backend, where Where) *App { itemBlockers: make(map[string][]model.ProjectItem), folded: make(map[string]bool), opened: make(map[string]bool), + details: make(map[string]itemDetailMsg), titleInput: ti, depFilter: df, filterInput: fi, @@ -241,6 +243,7 @@ type itemProjectsMsg struct { } type itemDetailMsg struct { + opening detailOpening detail *model.ProjectItemDetail blockers []model.ProjectItem projectNames map[string][]string @@ -622,7 +625,7 @@ func updateMembershipCmd(b backend.Backend, itemID string, toAdd, toRemove []str } } -func fetchItemDetailCmd(b backend.Backend, itemID string, isBlocked bool) tea.Cmd { +func fetchItemDetailCmd(b backend.Backend, itemID string, isBlocked bool, opening detailOpening) tea.Cmd { return func() tea.Msg { detail, err := b.GetItem(itemID) if err != nil { @@ -668,7 +671,7 @@ func fetchItemDetailCmd(b backend.Backend, itemID string, isBlocked bool) tea.Cm if err != nil { return errMsg{err} } - return itemDetailMsg{detail: detail, blockers: blockers, projectNames: pnames, dependents: dependents, tasks: tasks} + return itemDetailMsg{opening: opening, detail: detail, blockers: blockers, projectNames: pnames, dependents: dependents, tasks: tasks} } } @@ -891,11 +894,13 @@ func (m *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case allItemsMsg: m.takeItems(msg.itemsMsg, msg.groups) - return m, nil + cmd := m.readOpenDetailsAgain() + return m, cmd case itemsMsg: m.takeItems(msg, nil) - return m, nil + cmd := m.readOpenDetailsAgain() + return m, cmd case taskCreatedMsg, taskUpdatedMsg, taskDeletedMsg: cmd := m.fetchItems() @@ -942,7 +947,7 @@ func (m *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case itemDetailMsg: - m.openReader(msg.detail.Title, m.itemDocument(msg)) + m.tookDetail(msg) return m, nil case undoResultMsg: @@ -1479,43 +1484,6 @@ func (m *App) moveBy(rows int) tea.Cmd { return nil } -func (m *App) cursorDown() tea.Cmd { return m.moveBy(1) } -func (m *App) cursorUp() tea.Cmd { return m.moveBy(-1) } - -func (m *App) cursorTop() tea.Cmd { - if m.view == viewProjects && m.activePane == projectPane { - return m.pointAtProject(0) - } - m.moveCursorTo(0) - return nil -} - -func (m *App) cursorBottom() tea.Cmd { - if m.view == viewProjects && m.activePane == projectPane { - return m.pointAtProject(len(m.projects) - 1) - } - m.moveCursorTo(len(m.rows) - 1) - return nil -} - -// halfScreen is ctrl+d and ctrl+u: half a screen through the note open on the cursor's row, -// otherwise of the list. -func (m *App) halfScreen(direction int) tea.Cmd { - half := max(m.paneContentHeight()/2, 1) - if m.onOpenText() { - m.scrollOpenText(direction * half) - return nil - } - return m.moveBy(direction * half) -} - -// scrollOpenText moves the item pane through the open note by lines, and leaves the cursor -// on its row. -func (m *App) scrollOpenText(lines int) { - m.scrolled = true - m.rowScroll = max(m.rowScroll+lines, 0) -} - // paneContentHeight is how many lines a pane's list gets, under the header, its border, its // title and the status bar. func (m *App) paneContentHeight() int { @@ -1530,11 +1498,6 @@ func (m *App) readProject() tea.Cmd { return fetchProjectDetailCmd(m.backend, m.currentProject().ID) } -func (m *App) readItem() tea.Cmd { - item := m.currentItem() - return fetchItemDetailCmd(m.backend, item.ID, m.blockedSet[item.ID]) -} - func (m *App) toggleProjectMark() tea.Cmd { if m.selectedProjects == nil { m.selectedProjects = make(map[string]bool) @@ -1605,13 +1568,6 @@ func (m *App) reopenProject() tea.Cmd { return setProjectStatusCmd(m.backend, m.currentProject().ID, model.StatusActive, nil) } -func (m *App) toggleNote() tea.Cmd { - item := m.currentItem() - m.opened[item.ID] = !m.opened[item.ID] - m.scrolled = false - return nil -} - func (m *App) toggleGroup() tea.Cmd { row := m.currentRow() id := m.groups[row.group].projectID @@ -2038,9 +1994,9 @@ func (m *App) renderDepLinkOverlay() string { } // clipHints narrows a hint line to fit, dropping whole hints rather than -// cutting one in half: a trailing "[T]re…" reads as a rendering fault, where a -// dropped hint reads as a list that continues. Hints are split on the bracket -// that opens each one, since several carry a space of their own. +// cutting one in half: a trailing "T re…" reads as a rendering fault, where a +// dropped hint reads as a list that continues. Hints are split on the gap +// between them, since each carries a space of its own. func clipHints(hints string, maxWidth int) string { if lipgloss.Width(hints) <= maxWidth { return hints @@ -2048,15 +2004,12 @@ func clipHints(hints string, maxWidth int) string { if maxWidth < 4 { return "" } - parts := strings.Split(hints, " [") + parts := strings.Split(hints, hintGap) kept, width := []string{}, 0 for i, part := range parts { - if i > 0 { - part = "[" + part - } next := width + lipgloss.Width(part) if i > 0 { - next++ + next += len(hintGap) } if next+2 > maxWidth { break @@ -2066,7 +2019,7 @@ func clipHints(hints string, maxWidth int) string { if len(kept) == 0 { return "" } - return strings.Join(kept, " ") + " …" + return strings.Join(kept, hintGap) + " …" } func (m *App) statusBarHints() string { @@ -2088,10 +2041,10 @@ func (m *App) renderStatusBar() string { modeStr = modeLocalStyle.Render("LOCAL") + dimStyle.Render(" "+m.where.DatabasePath) } - // [?]help sits on the right, with the things that are always true, rather than at the end - // of the contextual list where clipping reaches it first. It is the one key that leads to - // every other key, so it is the last one that should disappear on a narrow window. - modeStr = "[?]help " + modeStr + // ? sits on the right, with the things that are always true, rather than at the end of the + // contextual list where clipping reaches it first. It is the one key that leads to every + // other key, so it is the last one that should disappear on a narrow window. + modeStr = "? keys " + modeStr modeWidth := lipgloss.Width(modeStr) var left string diff --git a/tui/deptree.go b/tui/deptree.go index 674db09..3ce36ba 100644 --- a/tui/deptree.go +++ b/tui/deptree.go @@ -311,7 +311,7 @@ func (m *App) depTreeToggleFinished() tea.Cmd { // readMapItem reads the item under the cursor. The item lists' blocked set may not hold an item // the map reaches, so its blockers are always read. func (m *App) readMapItem() tea.Cmd { - return fetchItemDetailCmd(m.backend, m.depTree.activeID(), true) + return fetchItemDetailCmd(m.backend, m.depTree.activeID(), true, openFullScreen) } // activeID is the item under whichever pane has focus. @@ -340,24 +340,6 @@ func (m *App) depTreeMove(delta int) { } } -func (m *App) depTreeJump(to int) { - s := &m.depTree - if s.active == focusPane { - if to < 0 { - to = len(s.focusRows) - 1 - } - s.focusCursor = clampCursor(to, len(s.focusRows)) - s.focusScroll = syncScroll(s.focusCursor, s.focusScroll, m.depTreePaneHeight()) - return - } - if to < 0 { - to = len(s.forestRows) - 1 - } - s.forestCursor = clampCursor(to, len(s.forestRows)) - s.forestScroll = syncScroll(s.forestCursor, s.forestScroll, m.depTreePaneHeight()) - s.focusOn(s.forestID()) -} - func clampCursor(value, length int) int { return max(min(value, length-1), 0) } diff --git a/tui/deptree_test.go b/tui/deptree_test.go index 47646fa..ed7a969 100644 --- a/tui/deptree_test.go +++ b/tui/deptree_test.go @@ -1,6 +1,7 @@ package tui import ( + "slices" "strconv" "strings" "testing" @@ -435,12 +436,16 @@ func TestStatusBar_NeverWiderThanTheWindow(t *testing.T) { t.Errorf("main bar is %d columns in a %d-column window", got, width) } // Clipping must not reach the one key that leads to every other key. - if !strings.Contains(bar, "[?]help") { + if !strings.Contains(bar, "? keys") { t.Errorf("help key clipped off at %d columns: %s", width, bar) } // A hint is shown whole or dropped, never cut in half. - if plain := ansi.Strip(bar); strings.Count(plain, "[") != strings.Count(plain, "]") { - t.Errorf("hint cut mid-token at %d columns: %s", width, bar) + whole := strings.Split(app.footerHints(), hintGap) + shown, _, _ := strings.Cut(strings.TrimSpace(ansi.Strip(bar)), " ") + for _, hint := range strings.Split(strings.TrimSuffix(shown, " …"), hintGap) { + if !slices.Contains(whole, hint) { + t.Errorf("hint %q cut mid-token at %d columns: %s", hint, width, bar) + } } openTree(t, app) diff --git a/tui/item_rows.go b/tui/item_rows.go index 1d6d02c..329d3d2 100644 --- a/tui/item_rows.go +++ b/tui/item_rows.go @@ -250,17 +250,6 @@ func (m *App) moveCursorTo(at int) { m.scrolled = false } -// onOpenText says the cursor's row shows its note open under it, which ctrl+d, ctrl+u, J and K -// then read through. -func (m *App) onOpenText() bool { - row := m.currentRow() - return row != nil && row.kind == rowItem && m.noteOpen(m.items[row.itemIdx].ProjectItem) -} - -func (m *App) noteOpen(item model.ProjectItem) bool { - return m.opened[item.ID] && hasNote(item) -} - // --- Layout --- // paneLayout is where every row of the item pane lands in line space. Scrolling counts lines @@ -305,8 +294,8 @@ func (m *App) itemPaneLayout(width int) paneLayout { body := 1 if row.kind == rowItem { body = 2 - if item := m.items[row.itemIdx].ProjectItem; m.noteOpen(item) { - body += len(m.noteLines(item, width)) + if item := m.items[row.itemIdx].ProjectItem; m.detailOpen(item.ID) { + body += len(m.detailLines(item.ID, width)) } } if row.kind != rowGroup && m.rowIsLastOfItem(i) { @@ -509,8 +498,8 @@ func (m *App) renderRowBlock(i, width int) []string { case rowItem: item := m.items[row.itemIdx] lines = append(lines, m.itemLines(item, row.group, i == m.rowCursor, width)...) - if m.noteOpen(item.ProjectItem) { - lines = append(lines, m.noteLines(item.ProjectItem, width)...) + if m.detailOpen(item.ID) { + lines = append(lines, m.detailLines(item.ID, width)...) } case rowTask: lines = append(lines, m.taskLine(row, i == m.rowCursor, width)) @@ -574,8 +563,7 @@ func (m *App) itemLines(item model.ProjectItemInProject, group int, cursor bool, if item.Completed { status = "✓" } - opens := hasNote(item.ProjectItem) - head := lead + chevron(opens, m.opened[item.ID]) + " " + status + " " + head := lead + chevron(true, m.detailOpen(item.ID)) + " " + status + " " var glyphs []string var plain string @@ -583,7 +571,7 @@ func (m *App) itemLines(item model.ProjectItemInProject, group int, cursor bool, plain += " ◈" glyphs = append(glyphs, multiProjectStyle.Render(" ◈")) } - if opens { + if hasNote(item.ProjectItem) { plain += " ≡" glyphs = append(glyphs, notesIndicatorStyle.Render(" ≡")) } @@ -676,17 +664,21 @@ func (m *App) taskLine(row paneRow, cursor bool, width int) string { return lead + check + " " + title } -// noteLines is an item's note laid out by gotermboard's markdown renderer, under its title. -func (m *App) noteLines(item model.ProjectItem, width int) []string { - room := max(min(width-titleColumn-1, gotermboard.ReadingWidth), 10) +// detailLines is the item's detail, as its reader shows it, laid out under its title. +func (m *App) detailLines(itemID string, width int) []string { indent := strings.Repeat(" ", titleColumn) var lines []string - for _, line := range m.markdown.Lines(noteOf(item), room) { + for _, line := range m.itemDocument(m.details[itemID])(detailWidth(width)) { lines = append(lines, ansi.Truncate(indent+line.Paint(m.palette), width, "…")) } return lines } +// detailWidth is the width an item's detail lays out at in place, under its title. +func detailWidth(paneWidth int) int { + return max(min(paneWidth-titleColumn-1, gotermboard.ReadingWidth), 10) +} + // blockerLines is what hangs below an item: each item that blocks it. func (m *App) blockerLines(item model.ProjectItemInProject, group int, width int) []string { here := "" diff --git a/tui/key_host.go b/tui/key_host.go new file mode 100644 index 0000000..b4de34a --- /dev/null +++ b/tui/key_host.go @@ -0,0 +1,159 @@ +package tui + +import ( + tea "charm.land/bubbletea/v2" +) + +// The board is the gotermboard.Host its universal rows act on. A key moves the cursor of the pane +// it works in: the dependency map's, the project list's, or the item list's. + +// MoveBy is gotermboard.Host's. +func (m *App) MoveBy(rows int) tea.Cmd { + if m.view == viewDependencies { + m.depTreeMove(rows) + return nil + } + return m.moveBy(rows) +} + +// HalfScreen is gotermboard.Host's. +func (m *App) HalfScreen() int { + if m.view == viewDependencies { + return max(m.depTreePaneHeight()/2, 1) + } + return max(m.paneContentHeight()/2, 1) +} + +// ScrollBy is gotermboard.Host's: it moves the item pane through the open detail by lines, and +// leaves the cursor on its row. +func (m *App) ScrollBy(lines int) { + m.scrolled = true + m.rowScroll = max(m.rowScroll+lines, 0) +} + +// OnOpenText is gotermboard.Host's. +func (m *App) OnOpenText() bool { + row := m.currentRow() + return m.view != viewDependencies && row != nil && row.kind == rowItem && m.detailOpen(m.items[row.itemIdx].ID) +} + +// JumpGroup is gotermboard.Host's. +func (m *App) JumpGroup(direction int) { m.jumpRow(direction) } + +// HasGroups is gotermboard.Host's. An item is the group its tasks sit in. +func (m *App) HasGroups() bool { return m.view != viewDependencies && m.onItemList() } + +// Toggles is gotermboard.Host's: enter and space open an item's detail or fold a project's +// group. The project list and the map bind their own. +func (m *App) Toggles() bool { + if m.view == viewDependencies || m.view == viewProjects && m.activePane == projectPane { + return false + } + return m.onKind(rowItem) || m.onKind(rowGroup) +} + +// Open is gotermboard.Host's: enter reads an item's detail full screen where it runs past the +// pane, and otherwise opens or closes it in place. The detail is read first, so the choice is +// made on what it holds now. +func (m *App) Open() tea.Cmd { + if m.onKind(rowGroup) { + return m.toggleGroup() + } + item := m.currentItem() + return fetchItemDetailCmd(m.backend, item.ID, m.blockedSet[item.ID], openByEnter) +} + +// ToggleInPlace is gotermboard.Host's: space opens an item's detail in place however tall, or +// closes it. +func (m *App) ToggleInPlace() tea.Cmd { + if m.onKind(rowGroup) { + return m.toggleGroup() + } + item := m.currentItem() + if m.detailOpen(item.ID) { + m.closeDetail(item.ID) + return nil + } + return fetchItemDetailCmd(m.backend, item.ID, m.blockedSet[item.ID], openInPlace) +} + +// ShowKeys is gotermboard.Host's. +func (m *App) ShowKeys() { + m.returnMode = m.appMode + m.appMode = modeHelp +} + +// Quit is gotermboard.Host's. +func (m *App) Quit() tea.Cmd { return tea.Quit } + +// detailOpening is what an item's detail is read for. +type detailOpening int + +const ( + // openFullScreen reads it on the full-screen reader. + openFullScreen detailOpening = iota + // openByEnter reads it full screen where it runs past the pane, and otherwise opens or + // closes it in place. + openByEnter + // openInPlace opens it under the item's row however tall. + openInPlace + // openAgain lays out again a detail open in place, from what the board read since. + openAgain +) + +// detailOpen says the item's detail shows in place under its row. +func (m *App) detailOpen(itemID string) bool { + _, read := m.details[itemID] + return m.opened[itemID] && read +} + +func (m *App) closeDetail(itemID string) { + m.opened[itemID] = false + delete(m.details, itemID) + m.scrolled = false +} + +// tallDetail says the detail runs past the item pane, so enter reads it full screen. +func (m *App) tallDetail(msg itemDetailMsg) bool { + return len(m.itemDocument(msg)(detailWidth(m.itemPaneWidth()-2))) >= m.paneContentHeight() +} + +// tookDetail is an item's detail read for opening. +func (m *App) tookDetail(msg itemDetailMsg) { + id := msg.detail.ID + switch msg.opening { + case openFullScreen: + m.openReader(msg.detail.Title, m.itemDocument(msg)) + return + case openByEnter: + if m.tallDetail(msg) { + m.openReader(msg.detail.Title, m.itemDocument(msg)) + return + } + if m.detailOpen(id) { + m.closeDetail(id) + return + } + case openAgain: + if !m.opened[id] { + return + } + } + m.details[id] = msg + m.opened[id] = true + m.scrolled = false +} + +// readOpenDetailsAgain reads again each detail open in place, after the board read its items, +// so the text under a row says what the item holds now. +func (m *App) readOpenDetailsAgain() tea.Cmd { + var cmds []tea.Cmd + read := map[string]bool{} + for _, item := range m.items { + if m.detailOpen(item.ID) && !read[item.ID] { + read[item.ID] = true + cmds = append(cmds, fetchItemDetailCmd(m.backend, item.ID, m.blockedSet[item.ID], openAgain)) + } + } + return tea.Batch(cmds...) +} From 5cc96bd536b4f37e1ac628ed9f38212d05b78da7 Mon Sep 17 00:00:00 2001 From: Chris Birch Date: Sun, 8 Nov 2026 13:54:33 -0500 Subject: [PATCH 2/4] feat(tui): open C's Claude session through gotermboard's opener The board copied slate's opener step for step, and outside tmux it ran tea.ExecProcess, so a failed session said only how it exited, such as "exit status 1". gotermboard now opens the session for a board and for a program that draws its own screen alike, so the copy goes, and a failed session's footer says the last line it wrote to stderr. A session that held the terminal reads the board again when it ends, in case it changed the items. Agent-Name: claude-code-unattended Agent-Session: 86a41f0d-4642-4e49-bf73-b5688a1bb146 Agent-Run: 01a1bc5d-2bb7-7e54-bc32-13efbb7743af Worklog: 2605.1 --- tui/app.go | 8 ++----- tui/claude_session.go | 55 +++++++++++++++---------------------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/tui/app.go b/tui/app.go index ac9bc2e..eef6648 100644 --- a/tui/app.go +++ b/tui/app.go @@ -926,12 +926,8 @@ func (m *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, saveCmd - case claudeSessionEndedMsg: - if msg.err != nil { - cmd := m.flash(fmt.Sprintf("The Claude session on %s, in %s, ended: %v", msg.handle, msg.ranIn, msg.err)) - return m, cmd - } - cmd := m.flash(fmt.Sprintf("Back from the Claude session on %s, which ran in %s", msg.handle, msg.ranIn)) + case gotermboard.ClaudeSessionOutcome: + cmd := m.tookClaudeOutcome(msg) return m, cmd case projectCreatedMsg: diff --git a/tui/claude_session.go b/tui/claude_session.go index 71d9118..bb111bd 100644 --- a/tui/claude_session.go +++ b/tui/claude_session.go @@ -3,31 +3,19 @@ package tui import ( "fmt" "os" - "os/exec" + "unicode" tea "charm.land/bubbletea/v2" "github.com/datapointchris/gotermboard" ) -// The first message of the Claude session C opens. It names the record by its handle alone, so -// the session reads the record's words as data with the command it names. +// The first message of the Claude session C opens begins with these. They name the record by its +// handle alone, so the session reads the record's words as data with the command they name. const ( - itemSessionBrief = "This is projects item %[1]s, opened from `projects tui`. " + - "`projects items show %[1]s` prints it whole. Read it, say what it asks for and what you would do, " + - "then wait before changing anything." - projectSessionBrief = "This is the projects project %[1]s, opened from `projects tui`. " + - "`projects show %[1]s` prints it with its open items. Read it, say what it asks for and what you would do, " + - "then wait before changing anything." + itemSessionBrief = "This is projects item %[1]s, opened from `projects tui`. `projects items show %[1]s` prints it whole." + projectSessionBrief = "This is the projects project %[1]s, opened from `projects tui`. `projects show %[1]s` prints it with its open items." ) -// claudeSessionEndedMsg is a Claude session that held the terminal giving it back. ranIn is the -// directory it ran in, with why where it was not the record's checkout. -type claudeSessionEndedMsg struct { - handle string - ranIn string - err error -} - func (m *App) claudeOnItem() tea.Cmd { item := m.currentItem() repo := "" @@ -48,32 +36,27 @@ func (m *App) claudeOnProject() tea.Cmd { return m.openClaudeSession(p.Name, repo, fmt.Sprintf(projectSessionBrief, p.ID)) } -// openClaudeSession is C: a Claude session in the repo's checkout. Inside tmux it opens in a -// window of its own, so the board stays usable while the session works, and outside tmux it -// takes the board's terminal until it ends. A session in the board's own directory is told so -// in its brief, because that directory may hold another repo's code. +// openClaudeSession is C: a Claude session in the repo's checkout, which gotermboard opens. func (m *App) openClaudeSession(handle, repo, brief string) tea.Cmd { - if _, err := exec.LookPath("claude"); err != nil { - return m.flash(fmt.Sprintf("claude is not on PATH, so no session opened on %s", handle)) - } directory, fallback, err := m.sessionDirectory(handle, repo) if err != nil { return m.flash(err.Error()) } - ranIn := directory - if fallback != "" { - ranIn += ", because " + fallback - brief += fmt.Sprintf(" This session runs in %s, because %s.", directory, fallback) + return gotermboard.OpenClaudeSession(gotermboard.ClaudeSession{On: handle, Dir: directory, Fallback: fallback, Brief: brief}) +} + +// tookClaudeOutcome flashes what came of C. A session that held the terminal may have changed +// what the board shows, so the board reads again. +func (m *App) tookClaudeOutcome(outcome gotermboard.ClaudeSessionOutcome) tea.Cmd { + said := []rune(outcome.Said) + if len(said) > 0 { + said[0] = unicode.ToUpper(said[0]) } - if os.Getenv("TMUX") != "" { - if err := gotermboard.OpenWindow("claude "+handle, directory, []string{"claude", brief}); err != nil { - return m.flash(err.Error()) - } - return m.flash(fmt.Sprintf("Opened a Claude session on %s in a tmux window, in %s", handle, ranIn)) + flash := m.flash(string(said)) + if outcome.HeldTerminal { + return tea.Batch(flash, m.rereadCmd()) } - session := exec.Command("claude", brief) - session.Dir = directory - return tea.ExecProcess(session, func(err error) tea.Msg { return claudeSessionEndedMsg{handle: handle, ranIn: ranIn, err: err} }) + return flash } // sessionDirectory is where C opens Claude: the checkout the registry names for the repo, else From c571ea436d2cf33b40e87e79776f255447bfdfae Mon Sep 17 00:00:00 2001 From: Chris Birch Date: Sun, 8 Nov 2026 14:23:13 -0500 Subject: [PATCH 3/4] refactor(tui): read the key table through gotermboard's readers The key handler, the footer's hints and the ? overlay's sections come from gotermboard's RunKey, FooterHints and BySection, so the board keeps no copy of how a table's rows are run, named on the footer or grouped under their sections. The pin moves to the gotermboard head that exports them. Agent-Name: claude-code-unattended Agent-Session: 86a41f0d-4642-4e49-bf73-b5688a1bb146 Agent-Run: 01a1bc88-08a6-79cb-9f61-c8b272c4f88b Worklog: 2605.1 --- go.mod | 2 +- go.sum | 4 ++-- tui/action_table.go | 30 ++++++------------------------ 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index e2c46f7..9ce7033 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/datapointchris/goclikit v0.5.3 github.com/datapointchris/goclilogin v0.9.0 github.com/datapointchris/goselfupdate v0.11.0 - github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a + github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a github.com/datapointchris/gotermstyle v0.5.0 github.com/datapointchris/tuidrive v0.1.0 github.com/go-viper/mapstructure/v2 v2.4.0 diff --git a/go.sum b/go.sum index 149b492..6877c6b 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/datapointchris/goclilogin v0.9.0 h1:xajsKfzuBvuREUZqw7B9Gv5PHxylSc8ss github.com/datapointchris/goclilogin v0.9.0/go.mod h1:oiMIbY0jwfqH5pX8G1T1iBPa+cG1m3JQhqpaP9JxJ+w= github.com/datapointchris/goselfupdate v0.11.0 h1:aUJ27Cm9ZNGMfC7+kKChBsnm8offBE99wwUa10lFo2w= github.com/datapointchris/goselfupdate v0.11.0/go.mod h1:YcgyvM+wH6nh9zo9xioUqTHeH5HTYeCHevv4U+G7INc= -github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a h1:ECJfA1i2DjXoENu8jhr4hlaVWLuXq7WtHnD6EnML440= -github.com/datapointchris/gotermboard v0.41.1-0.20261108183843-3992c2f54d3a/go.mod h1:A/9phVumidQPEVvHBLHTIWGgMfwbyDT+1/0iOHtjmao= +github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a h1:25sJQFAj9NfqV980J69Px9/2KSDd5wCdLQnV5QLBPC4= +github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a/go.mod h1:A/9phVumidQPEVvHBLHTIWGgMfwbyDT+1/0iOHtjmao= github.com/datapointchris/gotermstyle v0.5.0 h1:xdpuNASa4vS0jSMpBDQOO0rRFGY7jaft5H3hTHOrREE= github.com/datapointchris/gotermstyle v0.5.0/go.mod h1:pcTXWC/xls5Riw0rFtTueCQGlnszB3sqlepRItgdgeg= github.com/datapointchris/tuidrive v0.1.0 h1:s6esDWL9P8EkQYzdvrPwvBxYjPF98u0ukpnZbfBIGOc= diff --git a/tui/action_table.go b/tui/action_table.go index e9f0446..ebb4ed4 100644 --- a/tui/action_table.go +++ b/tui/action_table.go @@ -64,24 +64,14 @@ func (m *App) actions() []action { // runKey runs the action bound to key, and reports whether one was. func (m *App) runKey(key string) (tea.Cmd, bool) { - for _, a := range m.actions() { - if slices.Contains(a.Keys, key) { - return a.Run(m), true - } - } - return nil, false + return gotermboard.RunKey(m, m.actions(), key) } // footerHints is the footer's words for each key the screen can use now, two spaces apart. The // status bar pins ? at its right, so the list leaves it out. func (m *App) footerHints() string { - var hints []string - for _, a := range m.actions() { - if a.Footer != "" && a.Keys[0] != "?" { - hints = append(hints, a.Footer) - } - } - return strings.Join(hints, hintGap) + shown := slices.DeleteFunc(m.actions(), func(a action) bool { return a.Keys[0] == "?" }) + return strings.Join(gotermboard.FooterHints(shown), hintGap) } // hintGap parts one hint from the next, which a hint's own words never hold. @@ -110,21 +100,13 @@ func keysText(keys []string) string { // order each section first appears, each label wrapped at width under its own column so no word // of it reads as a key. func (m *App) helpLines(width int) []string { - var sections []string - bySection := map[string][]action{} - for _, a := range m.actions() { - if _, seen := bySection[a.Section]; !seen { - sections = append(sections, a.Section) - } - bySection[a.Section] = append(bySection[a.Section], a) - } var lines []string - for i, section := range sections { + for i, section := range gotermboard.BySection(m.actions()) { if i > 0 { lines = append(lines, "") } - lines = append(lines, " "+overlayTitleStyle.Render(section)) - for _, a := range bySection[section] { + lines = append(lines, " "+overlayTitleStyle.Render(section.Section)) + for _, a := range section.Actions { keys := " " + padRight(keysText(a.Keys), 18) indent := strings.Repeat(" ", lipgloss.Width(keys)) label := strings.Split(ansi.Wordwrap(a.Label, max(width-len(indent), 10), ""), "\n") From a89b9789fe3d3c45a40fff14a8a811c94429e9d7 Mon Sep 17 00:00:00 2001 From: Chris Birch Date: Sun, 8 Nov 2026 14:39:09 -0500 Subject: [PATCH 4/4] build: pin gotermboard v0.43.0, the release carrying the shared action rows The branch was pinned to the library pull request's head commit. v0.43.0 is the first tag holding it. Agent-Name: claude-code Agent-Session: 60bee577-11ae-4c13-9517-75a25f6211a6 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9ce7033..134036e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/datapointchris/goclikit v0.5.3 github.com/datapointchris/goclilogin v0.9.0 github.com/datapointchris/goselfupdate v0.11.0 - github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a + github.com/datapointchris/gotermboard v0.43.0 github.com/datapointchris/gotermstyle v0.5.0 github.com/datapointchris/tuidrive v0.1.0 github.com/go-viper/mapstructure/v2 v2.4.0 diff --git a/go.sum b/go.sum index 6877c6b..1c5c90d 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/datapointchris/goclilogin v0.9.0 h1:xajsKfzuBvuREUZqw7B9Gv5PHxylSc8ss github.com/datapointchris/goclilogin v0.9.0/go.mod h1:oiMIbY0jwfqH5pX8G1T1iBPa+cG1m3JQhqpaP9JxJ+w= github.com/datapointchris/goselfupdate v0.11.0 h1:aUJ27Cm9ZNGMfC7+kKChBsnm8offBE99wwUa10lFo2w= github.com/datapointchris/goselfupdate v0.11.0/go.mod h1:YcgyvM+wH6nh9zo9xioUqTHeH5HTYeCHevv4U+G7INc= -github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a h1:25sJQFAj9NfqV980J69Px9/2KSDd5wCdLQnV5QLBPC4= -github.com/datapointchris/gotermboard v0.42.1-0.20261108192106-e4a0f7982d7a/go.mod h1:A/9phVumidQPEVvHBLHTIWGgMfwbyDT+1/0iOHtjmao= +github.com/datapointchris/gotermboard v0.43.0 h1:WQ5xgmogGwu7V+0BaoJYrr+qATVTc46XAqkX/6rDpZ8= +github.com/datapointchris/gotermboard v0.43.0/go.mod h1:A/9phVumidQPEVvHBLHTIWGgMfwbyDT+1/0iOHtjmao= github.com/datapointchris/gotermstyle v0.5.0 h1:xdpuNASa4vS0jSMpBDQOO0rRFGY7jaft5H3hTHOrREE= github.com/datapointchris/gotermstyle v0.5.0/go.mod h1:pcTXWC/xls5Riw0rFtTueCQGlnszB3sqlepRItgdgeg= github.com/datapointchris/tuidrive v0.1.0 h1:s6esDWL9P8EkQYzdvrPwvBxYjPF98u0ukpnZbfBIGOc=