|
| 1 | +# Logseq CLI List Title Display Width Implementation Plan |
| 2 | + |
| 3 | +Goal: Make every `TITLE` column in `logseq list *` human output use a fixed maximum visual width with correct CJK-aware width handling via `string-width`. |
| 4 | + |
| 5 | +Architecture: Keep list data retrieval unchanged in db-worker-node thread APIs and implement presentation-only changes in the CLI formatter layer. |
| 6 | + |
| 7 | +Architecture: Introduce a display-width-aware truncation and padding path that is applied to list title cells before table rendering. |
| 8 | + |
| 9 | +Tech Stack: ClojureScript, `logseq.cli.format`, `logseq.cli.command.list`, db-worker thread APIs in `frontend.worker.db-core`, JavaScript `string-width`. |
| 10 | + |
| 11 | +Related: Builds on `docs/agent-guide/062-logseq-cli-list-default-sort-updated-at.md`, `docs/agent-guide/066-logseq-cli-list-property-cardinality-column.md`, `docs/agent-guide/078-logseq-cli-task-subcommands.md`, and `docs/cli/logseq-cli.md`. |
| 12 | + |
| 13 | +## Problem statement |
| 14 | + |
| 15 | +Current human table rendering in `src/main/logseq/cli/format.cljs` uses `(count text)` for width calculation and right padding. |
| 16 | + |
| 17 | +`count` is code-point length, not terminal display width. |
| 18 | + |
| 19 | +This breaks alignment when titles contain CJK or mixed-width text. |
| 20 | + |
| 21 | +Current `list` human output also allows unbounded title length, which can push important columns out of view for `list page`, `list tag`, `list property`, and `list task`. |
| 22 | + |
| 23 | +We need a stable, readable table contract where `TITLE` is capped to a reasonable visual width and aligned correctly for mixed-language content. |
| 24 | + |
| 25 | +## Current baseline from implementation |
| 26 | + |
| 27 | +The active CLI stack is the `src/main/logseq/cli/*` implementation, not the legacy `deps/cli` command stack. |
| 28 | + |
| 29 | +List command data is produced by db-worker helpers and returned through thread APIs without formatter concerns. |
| 30 | + |
| 31 | +The relevant path is: |
| 32 | + |
| 33 | +```text |
| 34 | +logseq list <page|tag|property|task> |
| 35 | + -> /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/list.cljs |
| 36 | + -> transport/invoke :thread-api/cli-list-* |
| 37 | + -> /Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs |
| 38 | + -> /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/db_worker.cljs |
| 39 | + -> /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs |
| 40 | +``` |
| 41 | + |
| 42 | +`TITLE` columns are defined in formatter column configs in `src/main/logseq/cli/format.cljs`. |
| 43 | + |
| 44 | +All table rendering goes through `render-table` and `format-counted-table` in the same file. |
| 45 | + |
| 46 | +## Scope |
| 47 | + |
| 48 | +In scope commands are: |
| 49 | + |
| 50 | +| Command | Human output table has `TITLE` column | Source formatter function | |
| 51 | +| --- | --- | --- | |
| 52 | +| `logseq list page` | Yes | `format-list-page` | |
| 53 | +| `logseq list tag` | Yes | `format-list-tag` | |
| 54 | +| `logseq list property` | Yes | `format-list-property` | |
| 55 | +| `logseq list task` | Yes | `format-list-task` | |
| 56 | + |
| 57 | +The default max visual width target for title is `40` columns. |
| 58 | + |
| 59 | +The value is configurable in `cli.edn` via a new CLI config key. |
| 60 | + |
| 61 | +The cap is measured by display width, not character count. |
| 62 | + |
| 63 | +Out of scope commands include `search *`, `query list`, `graph list`, and `server list`. |
| 64 | + |
| 65 | +Out of scope outputs include JSON and EDN payload shape changes. |
| 66 | + |
| 67 | +## Design decisions |
| 68 | + |
| 69 | +### 1) Keep db-worker-node payloads unchanged |
| 70 | + |
| 71 | +No changes are required in `src/main/logseq/cli/common/db_worker.cljs` or thread API wiring in `src/main/frontend/worker/db_core.cljs`. |
| 72 | + |
| 73 | +db-worker should continue returning full `:block/title` values. |
| 74 | + |
| 75 | +Human formatting is the only layer that applies visual truncation. |
| 76 | + |
| 77 | +### 2) Add explicit title max-width policy in formatter |
| 78 | + |
| 79 | +Introduce a formatter default constant such as `list-human-title-max-display-width-default` with value `40`. |
| 80 | + |
| 81 | +Read an optional override from resolved CLI config using a new `cli.edn` key `:list-title-max-display-width`. |
| 82 | + |
| 83 | +Apply this policy only to `TITLE` extractors in list column definitions. |
| 84 | + |
| 85 | +Truncated values append a suffix `…` and remain within the max display width. |
| 86 | + |
| 87 | +### 3) Use `string-width` for display width operations |
| 88 | + |
| 89 | +Add npm interop in `src/main/logseq/cli/format.cljs` for `string-width`. |
| 90 | + |
| 91 | +Use `string-width` for: |
| 92 | + |
| 93 | +- Width measurement during truncation. |
| 94 | +- Width measurement during table column width calculation. |
| 95 | +- Padding logic in `pad-right` or equivalent. |
| 96 | + |
| 97 | +This ensures CJK and mixed-width titles align with other columns. |
| 98 | + |
| 99 | +### 4) Preserve existing list data semantics |
| 100 | + |
| 101 | +Sorting, filtering, `--fields`, `--limit`, and `--offset` behavior in `src/main/logseq/cli/command/list.cljs` remain unchanged. |
| 102 | + |
| 103 | +Only rendered human strings are affected. |
| 104 | + |
| 105 | +### 5) Keep non-list command behavior stable |
| 106 | + |
| 107 | +Prefer implementing title truncation in list-specific extractors. |
| 108 | + |
| 109 | +If `render-table` is upgraded to display-width-aware padding globally, verify non-list tables are unaffected except improved alignment. |
| 110 | + |
| 111 | +## Proposed implementation steps |
| 112 | + |
| 113 | +1. Invoke `@test-driven-development` and add failing formatter tests for long ASCII title truncation in each list command. |
| 114 | + |
| 115 | +2. Add failing formatter tests for long CJK and mixed CJK/ASCII titles in each list command. |
| 116 | + |
| 117 | +3. Add failing config resolution tests in `src/test/logseq/cli/config_test.cljs` for `:list-title-max-display-width`, including default fallback to `40` and valid `cli.edn` override. |
| 118 | + |
| 119 | +4. Add a focused formatter unit test for truncation helper behavior ensuring output display width is `<= 40` and suffix handling is correct. |
| 120 | + |
| 121 | +5. Add a focused formatter unit test for width-aware padding behavior with CJK text so column alignment is deterministic. |
| 122 | + |
| 123 | +6. Add config normalization helpers in `src/main/logseq/cli/config.cljs` to parse and validate `:list-title-max-display-width` as a positive integer. |
| 124 | + |
| 125 | +7. Add `string-width` import and helper functions in `src/main/logseq/cli/format.cljs`. |
| 126 | + |
| 127 | +8. Add title truncation helper and wire it into the `TITLE` extractor entries in `list-page-columns`, `list-tag-columns`, `list-property-columns`, and `list-task-columns`. |
| 128 | + |
| 129 | +9. Replace `(count ...)`-based width and padding internals in table rendering with display-width-aware logic. |
| 130 | + |
| 131 | +10. Run formatter and config tests and update exact spacing snapshots where needed. |
| 132 | + |
| 133 | +11. Update user-facing docs in `docs/cli/logseq-cli.md` to mention that list human `TITLE` uses default width `40`, supports `cli.edn` override, and always truncates with `…`. |
| 134 | + |
| 135 | +12. Optionally add one CLI e2e non-sync human-output case for long mixed-width title rendering if we want package-level regression protection. |
| 136 | + |
| 137 | +## Files to modify |
| 138 | + |
| 139 | +- `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/config.cljs`. |
| 140 | +- `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/config_test.cljs`. |
| 141 | +- `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs`. |
| 142 | +- `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs`. |
| 143 | +- `/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.md`. |
| 144 | +- `/Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_cases.edn` (optional, only if we decide to cover human output end-to-end). |
| 145 | +- `/Users/rcmerci/gh-repos/logseq/package.json` (only if `string-width` is added as an explicit direct dependency). |
| 146 | + |
| 147 | +## Edge cases to cover |
| 148 | + |
| 149 | +A title with only CJK characters that exceeds max width. |
| 150 | + |
| 151 | +A title with alternating CJK and ASCII characters. |
| 152 | + |
| 153 | +A title exactly at width boundary. |
| 154 | + |
| 155 | +A title one display cell over boundary. |
| 156 | + |
| 157 | +A title containing emoji or variation selectors. |
| 158 | + |
| 159 | +A nil title where current logic falls back to `-`. |
| 160 | + |
| 161 | +A title containing newlines where current multiline row rendering is preserved. |
| 162 | + |
| 163 | +A very short title where no extra truncation marker appears. |
| 164 | + |
| 165 | +An invalid `cli.edn` width value such as `0`, negative numbers, or non-numeric values that must fall back to default `40`. |
| 166 | + |
| 167 | +## Risks and mitigation |
| 168 | + |
| 169 | +Risk: Moving width calculations to display width can shift spacing in non-list tables. |
| 170 | + |
| 171 | +Mitigation: Add or adjust tests for affected command outputs and constrain behavioral changes to alignment only. |
| 172 | + |
| 173 | +Risk: `string-width` import style may vary by bundling mode. |
| 174 | + |
| 175 | +Mitigation: Verify in unit tests and CLI runtime, and choose the interop form that matches current CJS usage conventions in the repo. |
| 176 | + |
| 177 | +Risk: New truncation can reduce discoverability of long titles. |
| 178 | + |
| 179 | +Mitigation: Keep JSON and EDN outputs untruncated and document this behavior clearly. |
| 180 | + |
| 181 | +Risk: Invalid `cli.edn` width values can produce inconsistent rendering if not normalized. |
| 182 | + |
| 183 | +Mitigation: Parse as positive integer with fallback to default `40` and add config tests for invalid values. |
| 184 | + |
| 185 | +## Open questions |
| 186 | + |
| 187 | +No open question. |
| 188 | + |
| 189 | +Decisions locked for this plan are default width `40`, `cli.edn` configurability, truncation suffix `…`, and list-only scope. |
| 190 | + |
| 191 | +## Testing Plan |
| 192 | + |
| 193 | +I will add formatter behavior tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs` that verify title truncation and alignment for `list page`, `list tag`, `list property`, and `list task` human output. |
| 194 | + |
| 195 | +I will add helper-level tests in the same test namespace to validate width-aware truncation and padding with CJK and mixed-width samples. |
| 196 | + |
| 197 | +I will add config tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/config_test.cljs` to validate default value `40`, `cli.edn` override, and invalid-value fallback behavior. |
| 198 | + |
| 199 | +I will run focused tests first with `bb dev:test -v logseq.cli.format-test/test-human-output-list-page`, `bb dev:test -v logseq.cli.format-test/test-human-output-list-tag-property`, and `bb dev:test -v logseq.cli.format-test/test-human-output-list-task`. |
| 200 | + |
| 201 | +I will run broader CLI tests with `bb dev:test -v logseq.cli.format-test` and config tests with `bb dev:test -v logseq.cli.config-test`. |
| 202 | + |
| 203 | +If e2e coverage is added, I will run `bb -f cli-e2e/bb.edn test --skip-build`. |
| 204 | + |
| 205 | +I will finish with `bb dev:lint-and-test`. |
| 206 | + |
| 207 | +NOTE: I will write *all* tests before I add any implementation behavior. |
| 208 | + |
| 209 | +## Step-by-step execution checklist |
| 210 | + |
| 211 | +| Step | Action | Verification | |
| 212 | +| --- | --- | --- | |
| 213 | +| 1 | Add failing list title truncation tests. | Tests fail with current unbounded title output. | |
| 214 | +| 2 | Add failing CJK width alignment tests. | Tests fail due current `count`-based width logic. | |
| 215 | +| 3 | Add failing config tests for `:list-title-max-display-width`. | Tests fail before config parsing is implemented. | |
| 216 | +| 4 | Implement config parsing with default `40` and invalid fallback. | Config tests pass for default and override behavior. | |
| 217 | +| 5 | Implement `string-width` helpers. | Helper tests pass. | |
| 218 | +| 6 | Implement title truncation in list column extractors. | List tests show bounded title width and `…` suffix. | |
| 219 | +| 7 | Switch table padding/width to display-width-aware logic. | CJK alignment tests pass. | |
| 220 | +| 8 | Update docs and optional e2e. | Docs mention truncation behavior and tests stay green. | |
| 221 | +| 9 | Run full regression. | `bb dev:lint-and-test` passes. | |
| 222 | + |
| 223 | +## Testing Details |
| 224 | + |
| 225 | +The tests validate observable CLI behavior by asserting final human output strings for list commands, including truncation marker presence and table alignment under mixed-width input. |
| 226 | + |
| 227 | +The tests do not assert internal helper data structures beyond what is needed to validate final output semantics. |
| 228 | + |
| 229 | +## Implementation Details |
| 230 | + |
| 231 | +- Keep default max title width as formatter constant `40`. |
| 232 | +- Allow override from `cli.edn` key `:list-title-max-display-width`. |
| 233 | +- Parse config width as positive integer and fall back to default on invalid values. |
| 234 | +- Truncate by display width instead of character count. |
| 235 | +- Keep truncation policy limited to list `TITLE` columns. |
| 236 | +- Always use `…` as truncation suffix in human list output. |
| 237 | +- Preserve db-worker payload contracts and thread API names. |
| 238 | +- Preserve JSON and EDN full title output. |
| 239 | +- Ensure multiline table behavior is not regressed. |
| 240 | +- Document human-output-only truncation and config override in CLI docs. |
| 241 | + |
| 242 | +## Question |
| 243 | + |
| 244 | +No blocking question for implementation. |
| 245 | + |
| 246 | +--- |
0 commit comments