@agi-fans/dsh-tui 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/lib/CHANGELOG.md +249 -3
  2. package/lib/chrome/box.d.ts +47 -16
  3. package/lib/chrome/box.js +172 -130
  4. package/lib/chrome/box.js.map +1 -1
  5. package/lib/chrome/code-highlight.d.ts +15 -0
  6. package/lib/chrome/code-highlight.js +60 -4
  7. package/lib/chrome/code-highlight.js.map +1 -1
  8. package/lib/chrome/command-output.d.ts +29 -2
  9. package/lib/chrome/command-output.js +100 -4
  10. package/lib/chrome/command-output.js.map +1 -1
  11. package/lib/chrome/main-screen-renderer.d.ts +18 -0
  12. package/lib/chrome/main-screen-renderer.js +58 -5
  13. package/lib/chrome/main-screen-renderer.js.map +1 -1
  14. package/lib/chrome/markdown.js +30 -10
  15. package/lib/chrome/markdown.js.map +1 -1
  16. package/lib/chrome/relative-time.d.ts +17 -0
  17. package/lib/chrome/relative-time.js +36 -0
  18. package/lib/chrome/relative-time.js.map +1 -1
  19. package/lib/chrome/renderer.d.ts +9 -0
  20. package/lib/chrome/renderer.js +1 -1
  21. package/lib/chrome/renderer.js.map +1 -1
  22. package/lib/chrome/secret-mask.d.ts +35 -0
  23. package/lib/chrome/secret-mask.js +180 -0
  24. package/lib/chrome/secret-mask.js.map +1 -0
  25. package/lib/chrome/status-config.d.ts +161 -51
  26. package/lib/chrome/status-config.js +237 -126
  27. package/lib/chrome/status-config.js.map +1 -1
  28. package/lib/chrome/status-line.d.ts +3 -2
  29. package/lib/chrome/status-line.js +105 -63
  30. package/lib/chrome/status-line.js.map +1 -1
  31. package/lib/chrome/terminal-colors.d.ts +85 -0
  32. package/lib/chrome/terminal-colors.js +230 -0
  33. package/lib/chrome/terminal-colors.js.map +1 -0
  34. package/lib/chrome/terminal-images.d.ts +9 -1
  35. package/lib/chrome/terminal-images.js +27 -4
  36. package/lib/chrome/terminal-images.js.map +1 -1
  37. package/lib/chrome/theme.d.ts +32 -9
  38. package/lib/chrome/theme.js +616 -520
  39. package/lib/chrome/theme.js.map +1 -1
  40. package/lib/chrome/tool-args.d.ts +1 -1
  41. package/lib/chrome/tool-args.js +3 -1
  42. package/lib/chrome/tool-args.js.map +1 -1
  43. package/lib/chrome/tool-renderers.d.ts +2 -0
  44. package/lib/chrome/tool-renderers.js +69 -3
  45. package/lib/chrome/tool-renderers.js.map +1 -1
  46. package/lib/chrome/tools-list.d.ts +1 -4
  47. package/lib/chrome/tools-list.js +1 -41
  48. package/lib/chrome/tools-list.js.map +1 -1
  49. package/lib/chrome/welcome-tips.d.ts +20 -7
  50. package/lib/chrome/welcome-tips.js +22 -41
  51. package/lib/chrome/welcome-tips.js.map +1 -1
  52. package/lib/commands/auth-ids.d.ts +25 -0
  53. package/lib/commands/auth-ids.js +27 -0
  54. package/lib/commands/auth-ids.js.map +1 -0
  55. package/lib/commands/auth-input.d.ts +34 -0
  56. package/lib/commands/auth-input.js +96 -0
  57. package/lib/commands/auth-input.js.map +1 -0
  58. package/lib/commands/auth-providers.d.ts +39 -0
  59. package/lib/commands/auth-providers.js +125 -0
  60. package/lib/commands/auth-providers.js.map +1 -0
  61. package/lib/commands/auth.d.ts +16 -22
  62. package/lib/commands/auth.js +346 -299
  63. package/lib/commands/auth.js.map +1 -1
  64. package/lib/commands/btw.d.ts +27 -0
  65. package/lib/commands/btw.js +59 -0
  66. package/lib/commands/btw.js.map +1 -0
  67. package/lib/commands/diff.js +30 -5
  68. package/lib/commands/diff.js.map +1 -1
  69. package/lib/commands/export.d.ts +1 -1
  70. package/lib/commands/export.js +5 -3
  71. package/lib/commands/export.js.map +1 -1
  72. package/lib/commands/files.d.ts +17 -1
  73. package/lib/commands/files.js +69 -61
  74. package/lib/commands/files.js.map +1 -1
  75. package/lib/commands/integrations.js +18 -6
  76. package/lib/commands/integrations.js.map +1 -1
  77. package/lib/commands/jobs.d.ts +19 -4
  78. package/lib/commands/jobs.js +50 -7
  79. package/lib/commands/jobs.js.map +1 -1
  80. package/lib/commands/model.js +109 -76
  81. package/lib/commands/model.js.map +1 -1
  82. package/lib/commands/permission.js +46 -13
  83. package/lib/commands/permission.js.map +1 -1
  84. package/lib/commands/plugins.js +196 -42
  85. package/lib/commands/plugins.js.map +1 -1
  86. package/lib/commands/schedule.d.ts +29 -0
  87. package/lib/commands/schedule.js +228 -0
  88. package/lib/commands/schedule.js.map +1 -0
  89. package/lib/commands/session-configuration.d.ts +1 -1
  90. package/lib/commands/session-configuration.js +29 -28
  91. package/lib/commands/session-configuration.js.map +1 -1
  92. package/lib/commands/session.js +75 -80
  93. package/lib/commands/session.js.map +1 -1
  94. package/lib/commands/terminal.d.ts +24 -4
  95. package/lib/commands/terminal.js +38 -94
  96. package/lib/commands/terminal.js.map +1 -1
  97. package/lib/commands/trajectory.js +4 -1
  98. package/lib/commands/trajectory.js.map +1 -1
  99. package/lib/definition.d.ts +150 -11
  100. package/lib/index.d.ts +1 -0
  101. package/lib/index.js +1 -0
  102. package/lib/index.js.map +1 -1
  103. package/lib/input/actions.d.ts +51 -0
  104. package/lib/input/actions.js +120 -0
  105. package/lib/input/actions.js.map +1 -0
  106. package/lib/input/editor-discovery.js +2 -2
  107. package/lib/input/editor-discovery.js.map +1 -1
  108. package/lib/input/editor.d.ts +0 -10
  109. package/lib/input/editor.js +11 -9
  110. package/lib/input/editor.js.map +1 -1
  111. package/lib/input/image-paste.d.ts +19 -1
  112. package/lib/input/image-paste.js +82 -1
  113. package/lib/input/image-paste.js.map +1 -1
  114. package/lib/input/keybindings-config.d.ts +15 -4
  115. package/lib/input/keybindings-config.js +57 -35
  116. package/lib/input/keybindings-config.js.map +1 -1
  117. package/lib/input/keys.d.ts +18 -0
  118. package/lib/input/keys.js +48 -3
  119. package/lib/input/keys.js.map +1 -1
  120. package/lib/input/wheel-scroll.d.ts +46 -0
  121. package/lib/input/wheel-scroll.js +69 -0
  122. package/lib/input/wheel-scroll.js.map +1 -0
  123. package/lib/prompt-intents.d.ts +182 -0
  124. package/lib/prompt-intents.js +127 -0
  125. package/lib/prompt-intents.js.map +1 -0
  126. package/lib/runtime/agent-profile.d.ts +5 -0
  127. package/lib/runtime/agent-profile.js +12 -0
  128. package/lib/runtime/agent-profile.js.map +1 -1
  129. package/lib/runtime/approval-rules.d.ts +59 -0
  130. package/lib/runtime/approval-rules.js +179 -0
  131. package/lib/runtime/approval-rules.js.map +1 -0
  132. package/lib/runtime/btw-policy.d.ts +7 -0
  133. package/lib/runtime/btw-policy.js +18 -0
  134. package/lib/runtime/btw-policy.js.map +1 -0
  135. package/lib/runtime/btw.d.ts +47 -0
  136. package/lib/runtime/btw.js +200 -0
  137. package/lib/runtime/btw.js.map +1 -0
  138. package/lib/runtime/cancellable.d.ts +75 -0
  139. package/lib/runtime/cancellable.js +76 -0
  140. package/lib/runtime/cancellable.js.map +1 -0
  141. package/lib/runtime/composer-images.js +5 -4
  142. package/lib/runtime/composer-images.js.map +1 -1
  143. package/lib/runtime/composer-shell.d.ts +30 -0
  144. package/lib/runtime/composer-shell.js +150 -0
  145. package/lib/runtime/composer-shell.js.map +1 -0
  146. package/lib/runtime/file-recovery-local.d.ts +73 -0
  147. package/lib/runtime/file-recovery-local.js +351 -0
  148. package/lib/runtime/file-recovery-local.js.map +1 -0
  149. package/lib/runtime/file-recovery-recording.d.ts +23 -0
  150. package/lib/runtime/file-recovery-recording.js +131 -0
  151. package/lib/runtime/file-recovery-recording.js.map +1 -0
  152. package/lib/runtime/file-recovery-review.d.ts +3 -0
  153. package/lib/runtime/file-recovery-review.js +130 -0
  154. package/lib/runtime/file-recovery-review.js.map +1 -0
  155. package/lib/runtime/file-recovery-store.d.ts +83 -0
  156. package/lib/runtime/file-recovery-store.js +329 -0
  157. package/lib/runtime/file-recovery-store.js.map +1 -0
  158. package/lib/runtime/file-recovery-worker.d.ts +22 -0
  159. package/lib/runtime/file-recovery-worker.js +153 -0
  160. package/lib/runtime/file-recovery-worker.js.map +1 -0
  161. package/lib/runtime/file-review.d.ts +6 -3
  162. package/lib/runtime/file-review.js +44 -7
  163. package/lib/runtime/file-review.js.map +1 -1
  164. package/lib/runtime/herdr-agent.d.ts +4 -13
  165. package/lib/runtime/herdr-agent.js +19 -63
  166. package/lib/runtime/herdr-agent.js.map +1 -1
  167. package/lib/runtime/plain-tui.d.ts +2 -0
  168. package/lib/runtime/plain-tui.js +7 -1
  169. package/lib/runtime/plain-tui.js.map +1 -1
  170. package/lib/runtime/plugin-settings.d.ts +25 -2
  171. package/lib/runtime/plugin-settings.js +329 -31
  172. package/lib/runtime/plugin-settings.js.map +1 -1
  173. package/lib/runtime/provider-local.d.ts +81 -24
  174. package/lib/runtime/provider-local.js +1807 -608
  175. package/lib/runtime/provider-local.js.map +1 -1
  176. package/lib/runtime/runner.js +52 -12
  177. package/lib/runtime/runner.js.map +1 -1
  178. package/lib/runtime/session-archive.d.ts +6 -0
  179. package/lib/runtime/session-archive.js +129 -3
  180. package/lib/runtime/session-archive.js.map +1 -1
  181. package/lib/runtime/session-runtime.js +1 -1
  182. package/lib/runtime/session-runtime.js.map +1 -1
  183. package/lib/runtime/settings-preferences.d.ts +5 -0
  184. package/lib/runtime/settings-preferences.js +34 -0
  185. package/lib/runtime/settings-preferences.js.map +1 -0
  186. package/lib/runtime/startup-notices.js +2 -0
  187. package/lib/runtime/startup-notices.js.map +1 -1
  188. package/lib/runtime/terminal-modes.d.ts +47 -0
  189. package/lib/runtime/terminal-modes.js +86 -0
  190. package/lib/runtime/terminal-modes.js.map +1 -0
  191. package/lib/runtime/terminal-notifications.d.ts +3 -5
  192. package/lib/runtime/terminal-notifications.js +4 -8
  193. package/lib/runtime/terminal-notifications.js.map +1 -1
  194. package/lib/runtime/terminal-probe.d.ts +36 -0
  195. package/lib/runtime/terminal-probe.js +37 -0
  196. package/lib/runtime/terminal-probe.js.map +1 -0
  197. package/lib/runtime/terminal-session.d.ts +57 -0
  198. package/lib/runtime/terminal-session.js +164 -0
  199. package/lib/runtime/terminal-session.js.map +1 -0
  200. package/lib/runtime/terminal-state.d.ts +29 -0
  201. package/lib/runtime/terminal-state.js +62 -0
  202. package/lib/runtime/terminal-state.js.map +1 -0
  203. package/lib/runtime/tool-presentation.js +50 -14
  204. package/lib/runtime/tool-presentation.js.map +1 -1
  205. package/lib/session/external-agents.d.ts +5 -0
  206. package/lib/session/external-agents.js +27 -0
  207. package/lib/session/external-agents.js.map +1 -0
  208. package/lib/session/feature-toggles.d.ts +1 -1
  209. package/lib/session/feature-toggles.js +1 -1
  210. package/lib/session/interaction-adapter.d.ts +1 -6
  211. package/lib/session/interaction-adapter.js +95 -18
  212. package/lib/session/interaction-adapter.js.map +1 -1
  213. package/lib/session/message-queue.js +1 -1
  214. package/lib/session/message-queue.js.map +1 -1
  215. package/lib/session/model-readiness.d.ts +16 -0
  216. package/lib/session/model-readiness.js +43 -0
  217. package/lib/session/model-readiness.js.map +1 -0
  218. package/lib/session/package-metadata.d.ts +8 -0
  219. package/lib/session/package-metadata.js +8 -0
  220. package/lib/session/package-metadata.js.map +1 -1
  221. package/lib/session/project-context.d.ts +5 -0
  222. package/lib/session/project-context.js +36 -0
  223. package/lib/session/project-context.js.map +1 -1
  224. package/lib/session/session-controller.d.ts +10 -13
  225. package/lib/session/session-controller.js +183 -98
  226. package/lib/session/session-controller.js.map +1 -1
  227. package/lib/session/session-library.js +2 -0
  228. package/lib/session/session-library.js.map +1 -1
  229. package/lib/session/session-tree.d.ts +3 -0
  230. package/lib/session/session-tree.js +3 -1
  231. package/lib/session/session-tree.js.map +1 -1
  232. package/lib/session/subagent-roster.d.ts +2 -1
  233. package/lib/session/subagent-roster.js +24 -3
  234. package/lib/session/subagent-roster.js.map +1 -1
  235. package/lib/session/subagent-tracker.d.ts +6 -2
  236. package/lib/session/subagent-tracker.js +49 -2
  237. package/lib/session/subagent-tracker.js.map +1 -1
  238. package/lib/session/tui-settings.d.ts +47 -13
  239. package/lib/session/tui-settings.js +76 -34
  240. package/lib/session/tui-settings.js.map +1 -1
  241. package/lib/session/working-directory.d.ts +4 -0
  242. package/lib/session/working-directory.js +4 -0
  243. package/lib/session/working-directory.js.map +1 -0
  244. package/lib/session/worktrees.d.ts +13 -0
  245. package/lib/session/worktrees.js +49 -0
  246. package/lib/session/worktrees.js.map +1 -0
  247. package/lib/views/approval-rule.d.ts +3 -0
  248. package/lib/views/approval-rule.js +4 -0
  249. package/lib/views/approval-rule.js.map +1 -0
  250. package/lib/views/at-complete.d.ts +8 -0
  251. package/lib/views/at-complete.js +64 -2
  252. package/lib/views/at-complete.js.map +1 -1
  253. package/lib/views/autocomplete.d.ts +22 -2
  254. package/lib/views/autocomplete.js +70 -40
  255. package/lib/views/autocomplete.js.map +1 -1
  256. package/lib/views/copy-targets.d.ts +13 -14
  257. package/lib/views/copy-targets.js +42 -54
  258. package/lib/views/copy-targets.js.map +1 -1
  259. package/lib/views/document-reader.js +18 -5
  260. package/lib/views/document-reader.js.map +1 -1
  261. package/lib/views/event-views.d.ts +1 -1
  262. package/lib/views/event-views.js +60 -6
  263. package/lib/views/event-views.js.map +1 -1
  264. package/lib/views/file-actions.d.ts +12 -0
  265. package/lib/views/file-actions.js +72 -0
  266. package/lib/views/file-actions.js.map +1 -0
  267. package/lib/views/history-search.js +15 -21
  268. package/lib/views/history-search.js.map +1 -1
  269. package/lib/views/hotkeys.d.ts +8 -1
  270. package/lib/views/hotkeys.js +53 -26
  271. package/lib/views/hotkeys.js.map +1 -1
  272. package/lib/views/list-navigation.d.ts +62 -0
  273. package/lib/views/list-navigation.js +73 -0
  274. package/lib/views/list-navigation.js.map +1 -0
  275. package/lib/views/list-view.d.ts +99 -0
  276. package/lib/views/list-view.js +165 -0
  277. package/lib/views/list-view.js.map +1 -0
  278. package/lib/views/prompt-selector.d.ts +34 -4
  279. package/lib/views/prompt-selector.js +270 -134
  280. package/lib/views/prompt-selector.js.map +1 -1
  281. package/lib/views/session-files.d.ts +1 -2
  282. package/lib/views/session-files.js +1 -11
  283. package/lib/views/session-files.js.map +1 -1
  284. package/lib/views/settings-list.d.ts +51 -10
  285. package/lib/views/settings-list.js +267 -315
  286. package/lib/views/settings-list.js.map +1 -1
  287. package/lib/views/trajectory-timeline.d.ts +72 -0
  288. package/lib/views/trajectory-timeline.js +133 -0
  289. package/lib/views/trajectory-timeline.js.map +1 -0
  290. package/lib/views/trajectory.d.ts +15 -4
  291. package/lib/views/trajectory.js +557 -123
  292. package/lib/views/trajectory.js.map +1 -1
  293. package/lib/views/transcript-export.js +9 -0
  294. package/lib/views/transcript-export.js.map +1 -1
  295. package/lib/views/transcript-render.d.ts +97 -25
  296. package/lib/views/transcript-render.js +542 -170
  297. package/lib/views/transcript-render.js.map +1 -1
  298. package/lib/views/transcript-search.d.ts +39 -6
  299. package/lib/views/transcript-search.js +46 -11
  300. package/lib/views/transcript-search.js.map +1 -1
  301. package/lib/views/transcript-types.d.ts +63 -5
  302. package/lib/views/transcript-types.js +23 -11
  303. package/lib/views/transcript-types.js.map +1 -1
  304. package/package.json +95 -65
  305. package/lib/commands/loop.d.ts +0 -52
  306. package/lib/commands/loop.js +0 -307
  307. package/lib/commands/loop.js.map +0 -1
  308. package/lib/commands/steer.d.ts +0 -5
  309. package/lib/commands/steer.js +0 -24
  310. package/lib/commands/steer.js.map +0 -1
  311. package/lib/runtime/terminal-reader.d.ts +0 -11
  312. package/lib/runtime/terminal-reader.js +0 -87
  313. package/lib/runtime/terminal-reader.js.map +0 -1
  314. package/lib/views/agent-hub.d.ts +0 -32
  315. package/lib/views/agent-hub.js +0 -206
  316. package/lib/views/agent-hub.js.map +0 -1
  317. package/lib/views/copy-selector.d.ts +0 -51
  318. package/lib/views/copy-selector.js +0 -139
  319. package/lib/views/copy-selector.js.map +0 -1
@@ -1,83 +1,193 @@
1
1
  /**
2
2
  * Status-line customization shared by settings, persistence, and rendering.
3
+ *
4
+ * The footer is two rows of fixed copy. This module owns *which* parts of those
5
+ * rows appear and how much of each telemetry group is shown — nothing more.
6
+ *
7
+ * There used to be a layout engine here: twelve independently placed items, each
8
+ * with its own colour, its own column, and two levels of ordering, plus a legacy
9
+ * `metrics` colour slot for groups that had no colour of their own. That is a
10
+ * page of settings for a two-line footer, and it asked the reader to decide
11
+ * things the footer could decide better. The reader now picks a density, and can
12
+ * turn individual telemetry groups off when a terminal is narrow or a metric is
13
+ * not interesting to them.
14
+ *
3
15
  * @module @agi-fans/dsh-tui/status-config
4
16
  */
5
17
  import type { VolatileSnapshot } from '@deepseek-ai/cordis';
6
- /** Stable telemetry groups users can show, hide, and reorder. */
18
+ /**
19
+ * Telemetry groups on the second line, in the order they are shown.
20
+ *
21
+ * The order is fixed and follows the priority stated in `AGENTS.md`: cache,
22
+ * tokens, and latency first because they say how the turn is going; durations
23
+ * second; turn and step counts last, because they are the least actionable.
24
+ */
7
25
  export declare const STATUS_GROUP_IDS: readonly ["context", "cache", "tokens", "speed", "durations", "counts"];
8
26
  export type StatusGroupId = (typeof STATUS_GROUP_IDS)[number];
9
27
  /** First-line pieces: model, reasoning effort, workspace path, Git branch, and session title. */
10
28
  export declare const STATUS_META_IDS: readonly ["model", "effort", "path", "git", "session"];
11
29
  export type StatusMetaId = (typeof STATUS_META_IDS)[number];
12
- /** Every independently placed footer item. */
30
+ /** Every part of the footer a reader can show or hide. */
13
31
  export declare const STATUS_ITEM_IDS: readonly ["model", "effort", "path", "git", "session", "context", "cache", "tokens", "speed", "durations", "counts"];
14
32
  export type StatusItemId = (typeof STATUS_ITEM_IDS)[number];
15
33
  export declare const STATUS_LABEL_STYLES: readonly ["compact", "full"];
16
34
  export type StatusLabelStyle = (typeof STATUS_LABEL_STYLES)[number];
35
+ /** How context occupancy is written, which is a rendering choice, not a layout one. */
17
36
  export declare const STATUS_CONTEXT_STYLES: readonly ["percent", "bar", "tokens", "detailed"];
18
37
  export type StatusContextStyle = (typeof STATUS_CONTEXT_STYLES)[number];
19
38
  /**
20
- * Color slots: one per preview item, plus legacy `metrics` as a fallback for
21
- * telemetry groups that do not have their own color yet.
39
+ * How much the footer says.
40
+ *
41
+ * A density is the whole vocabulary now: three whole-footer shapes a reader can
42
+ * recognize, instead of twelve items they have to assemble. Individual groups
43
+ * can still be hidden on top of one, which is the one per-item decision that
44
+ * survives — "I do not care about this number" is real, "put this number on the
45
+ * left in accent green" is chrome nobody asked for.
46
+ */
47
+ export declare const STATUS_DENSITIES: readonly ["minimal", "standard", "verbose"];
48
+ export type StatusDensity = (typeof STATUS_DENSITIES)[number];
49
+ /**
50
+ * Which telemetry row each group is drawn on.
51
+ *
52
+ * Two rows exist so the widest density can state everything without dropping
53
+ * anything: the first row is the window and the turn in flight, the second is
54
+ * what the session has accumulated. `minimal` and `standard` only fill the first
55
+ * row, so their footer stays two lines; the row is a property of the group, not
56
+ * of the density, so nothing moves between rows when the density changes.
57
+ */
58
+ export declare const STATUS_ROWS: readonly (readonly StatusGroupId[])[];
59
+ /**
60
+ * Groups drawn against the left edge of their row.
61
+ *
62
+ * A row answers two questions from opposite edges, and each side keeps its
63
+ * meaning down the footer: occupancy and the time already spent read on the
64
+ * left, throughput and the count of work done on the right.
65
+ */
66
+ export declare const STATUS_LEFT_GROUPS: readonly StatusGroupId[];
67
+ /** Which telemetry row a group is drawn on; 0 is the row under the composer. */
68
+ export declare function statusGroupRow(id: StatusGroupId): number;
69
+ /** Which side of its row a group is drawn on. */
70
+ export declare function statusGroupSide(id: StatusGroupId): 'left' | 'right';
71
+ /** Which groups each density shows, in footer order. */
72
+ export declare const DENSITY_GROUPS: Readonly<Record<StatusDensity, readonly StatusGroupId[]>>;
73
+ /** Which first-line pieces each density shows. */
74
+ export declare const DENSITY_META: Readonly<Record<StatusDensity, readonly StatusMetaId[]>>;
75
+ /** The label style each density defaults to. */
76
+ export declare const DENSITY_LABELS: Readonly<Record<StatusDensity, StatusLabelStyle>>;
77
+ /** The context representation each density defaults to. */
78
+ export declare const DENSITY_CONTEXT: Readonly<Record<StatusDensity, StatusContextStyle>>;
79
+ export declare const DEFAULT_STATUS_DENSITY: StatusDensity;
80
+ /**
81
+ * Stored status-line input.
82
+ *
83
+ * `density` is the whole user-facing decision. `meta` and `groups` are narrowings
84
+ * on top of it: a reader who turned a number off keeps it off across densities,
85
+ * because hiding something is a statement about the number, not about the layout.
22
86
  */
23
- export declare const STATUS_COLOR_SLOTS: readonly ["model", "effort", "path", "git", "session", "context", "cache", "tokens", "speed", "durations", "counts", "metrics"];
24
- export type StatusColorSlot = (typeof STATUS_COLOR_SLOTS)[number];
25
- /** Theme tokens offered for a status item. `default` keeps the built-in hierarchy. */
26
- export declare const STATUS_COLOR_TOKENS: readonly ["default", "text", "muted", "accent", "success", "warning", "border", "label"];
27
- export type StatusColorToken = (typeof STATUS_COLOR_TOKENS)[number];
28
- /** Footer column an item can occupy. */
29
- export declare const STATUS_SIDES: readonly ["left", "right"];
30
- export type StatusSide = (typeof STATUS_SIDES)[number];
31
- export declare const DEFAULT_STATUS_SIDES: Record<StatusItemId, StatusSide>;
32
- /** Per-item color choices. Omitted items keep their built-in tone. */
33
- export type StatusBarColors = Partial<Record<StatusColorSlot, StatusColorToken>>;
34
- /** User-owned status-line layout. Missing `groups`/`meta` are hidden; `order` retains their slots. */
35
87
  export interface StatusBarConfig {
88
+ density: StatusDensity;
89
+ /**
90
+ * Whether the second line is drawn at all.
91
+ *
92
+ * Kept as its own field rather than expressed as "every group hidden": hiding
93
+ * all of them and calling that off would forget what to bring back when the
94
+ * reader turns telemetry on again.
95
+ */
96
+ enabled?: boolean;
97
+ /** Pieces the reader hid, on top of what the density shows. */
98
+ hidden: StatusItemId[];
99
+ /**
100
+ * Pieces the reader added beyond what the density shows.
101
+ *
102
+ * Without this, a part the density omits could not be turned on at all: `Space`
103
+ * on it was a no-op, while the row still said `hidden` — a control that looked
104
+ * broken. Positive and negative narrowing are both deltas from the density, so
105
+ * changing the density still changes everything the reader has not overruled.
106
+ */
107
+ shown: StatusItemId[];
108
+ }
109
+ /** A status-line document, including every field earlier releases wrote. */
110
+ export type StatusBarInput = Partial<VolatileSnapshot<StatusBarConfig>> & {
111
+ /** Earlier releases spelled the whole layout out; read only while migrating. */
112
+ enabled?: boolean;
113
+ labels?: StatusLabelStyle;
114
+ contextStyle?: StatusContextStyle;
115
+ groups?: readonly StatusGroupId[];
116
+ order?: readonly StatusGroupId[];
117
+ meta?: readonly StatusMetaId[];
118
+ metaOrder?: readonly StatusMetaId[];
119
+ colors?: Readonly<Record<string, string>>;
120
+ sides?: Readonly<Record<string, string>>;
121
+ };
122
+ /** What the renderer consumes: the density expanded, with narrowings applied. */
123
+ export interface ResolvedStatusBarConfig {
124
+ density: StatusDensity;
125
+ /** False when the reader hid the whole second line. */
36
126
  enabled: boolean;
37
127
  labels: StatusLabelStyle;
38
- /** Context occupancy representation; omitted preferences default to percent. */
39
- contextStyle?: StatusContextStyle;
40
- groups: StatusGroupId[];
41
- /** Complete visual order, including hidden groups. Absent in legacy settings. */
42
- order?: StatusGroupId[];
43
- /** Visible first-line items. Absent means every meta item is shown. */
44
- meta?: StatusMetaId[];
45
- /** Complete first-line order, including hidden meta items. */
46
- metaOrder?: StatusMetaId[];
47
- /** Optional per-item colors for the two-line footer. */
48
- colors?: StatusBarColors;
49
- /** Optional left/right column for each item. */
50
- sides?: Partial<Record<StatusItemId, StatusSide>>;
51
- }
52
- /** Stored status-line input: a settings document value or a frozen config snapshot. */
53
- export type StatusBarInput = Partial<VolatileSnapshot<StatusBarConfig>>;
54
- /** Normalized layout always carries complete orders, colors, and columns. */
55
- export interface ResolvedStatusBarConfig extends StatusBarConfig {
56
128
  contextStyle: StatusContextStyle;
57
- order: StatusGroupId[];
129
+ /** Visible telemetry groups, in fixed footer order. */
130
+ groups: StatusGroupId[];
131
+ /** Visible first-line pieces, in fixed footer order. */
58
132
  meta: StatusMetaId[];
59
- metaOrder: StatusMetaId[];
60
- colors: Required<StatusBarColors>;
61
- sides: Record<StatusItemId, StatusSide>;
62
133
  }
63
- /** Legacy value accepted while existing settings documents migrate. */
134
+ /** Earlier releases stored a three-value `statusPreset` beside the layout. */
64
135
  export declare const STATUS_PRESETS: readonly ["minimal", "compact", "full"];
65
136
  export type StatusPreset = (typeof STATUS_PRESETS)[number];
137
+ /** Everything shown at the shipped density. */
66
138
  export declare const DEFAULT_STATUS_GROUPS: readonly StatusGroupId[];
67
- /**
68
- * Session title is offered but off by default: adding it to the first line
69
- * would push narrow terminals into degradation sooner. The terminal window
70
- * title carries it unconditionally.
71
- */
72
139
  export declare const DEFAULT_STATUS_META: readonly StatusMetaId[];
73
- export declare const DEFAULT_STATUS_COLORS: Required<StatusBarColors>;
74
- /** Return a fresh default so callers can safely replace its group list. */
75
- export declare function defaultStatusBarConfig(): ResolvedStatusBarConfig;
76
- export declare function isStatusMetaId(value: unknown): value is StatusMetaId;
77
140
  export declare function isStatusItemId(value: unknown): value is StatusItemId;
78
- export declare function itemSide(config: StatusBarConfig, id: StatusItemId): StatusSide;
141
+ export declare function isStatusMetaId(value: unknown): value is StatusMetaId;
142
+ export declare function isStatusGroupId(value: unknown): value is StatusGroupId;
79
143
  /**
80
- * Normalize persisted input at the single configuration seam. Invalid and
81
- * duplicate groups disappear; legacy presets retain their previous meaning.
144
+ * Normalize persisted input at the single configuration seam.
145
+ *
146
+ * A document written by an earlier release described its layout item by item.
147
+ * Rather than fail it or resurrect the engine, the shape it asked for is reduced
148
+ * to the nearest density: what was visible decides the narrowing, and the retired
149
+ * per-item colour and column choices are dropped, because both shipped palettes
150
+ * already paint them the right way.
82
151
  */
83
152
  export declare function resolveStatusBarConfig(config?: StatusBarInput, legacyPreset?: StatusPreset): ResolvedStatusBarConfig;
153
+ /**
154
+ * What a stored document asks to hide, relative to its density.
155
+ *
156
+ * Exported because writing the document back needs the same reading: a caller
157
+ * that reconstructed the narrowing from the *rendered* footer instead would turn
158
+ * every part its density happens to omit into a permanent hiding, and changing
159
+ * the density afterwards would no longer restore them.
160
+ */
161
+ export declare function statusNarrowing(config: StatusBarInput | undefined, density: StatusDensity): StatusItemId[];
162
+ /** What a stored document adds beyond its density. */
163
+ export declare function statusAdditions(config: StatusBarInput | undefined): StatusItemId[];
164
+ /**
165
+ * True when a document has already been expanded.
166
+ *
167
+ * `resolveStatusBarConfig` is idempotent only if it can tell the two shapes
168
+ * apart: an expanded config carries `groups`/`meta`, which a stored document
169
+ * never does. Without this the renderer re-resolved its own input and silently
170
+ * dropped every narrowing, so a hidden group came back.
171
+ */
172
+ export declare function isResolvedStatusBarConfig(config: ResolvedStatusBarConfig | StatusBarInput): config is ResolvedStatusBarConfig;
173
+ /**
174
+ * The document a resolved config came from.
175
+ *
176
+ * Visibility cannot say which of two reasons a part is missing: the reader hid
177
+ * it, or the density never included it. Rebuilding the document from what the
178
+ * footer shows therefore has to keep them apart — a part the density omits must
179
+ * stay out of `hidden`, or raising the density would silently do nothing — and it
180
+ * is the only way a caller that holds the resolved form (the provider holds the
181
+ * footer it is drawing) can hand the document back without losing a choice.
182
+ */
183
+ export declare function statusBarDocument(config: ResolvedStatusBarConfig): StatusBarConfig;
184
+ /** True when the footer shows this part at the current narrowing. */
185
+ export declare function statusItemVisible(config: ResolvedStatusBarConfig, id: StatusItemId): boolean;
186
+ /**
187
+ * The same document with one part turned on or off.
188
+ *
189
+ * Both directions are deltas from the density, so a part the density omits can
190
+ * be added and a part it includes can be removed, without either one rewriting
191
+ * the other's state.
192
+ */
193
+ export declare function toggleStatusItem(config: StatusBarConfig, id: StatusItemId): StatusBarConfig;
@@ -1,159 +1,270 @@
1
1
  /**
2
2
  * Status-line customization shared by settings, persistence, and rendering.
3
+ *
4
+ * The footer is two rows of fixed copy. This module owns *which* parts of those
5
+ * rows appear and how much of each telemetry group is shown — nothing more.
6
+ *
7
+ * There used to be a layout engine here: twelve independently placed items, each
8
+ * with its own colour, its own column, and two levels of ordering, plus a legacy
9
+ * `metrics` colour slot for groups that had no colour of their own. That is a
10
+ * page of settings for a two-line footer, and it asked the reader to decide
11
+ * things the footer could decide better. The reader now picks a density, and can
12
+ * turn individual telemetry groups off when a terminal is narrow or a metric is
13
+ * not interesting to them.
14
+ *
3
15
  * @module @agi-fans/dsh-tui/status-config
4
16
  */
5
- /** Stable telemetry groups users can show, hide, and reorder. */
17
+ /**
18
+ * Telemetry groups on the second line, in the order they are shown.
19
+ *
20
+ * The order is fixed and follows the priority stated in `AGENTS.md`: cache,
21
+ * tokens, and latency first because they say how the turn is going; durations
22
+ * second; turn and step counts last, because they are the least actionable.
23
+ */
6
24
  export const STATUS_GROUP_IDS = ['context', 'cache', 'tokens', 'speed', 'durations', 'counts'];
7
25
  /** First-line pieces: model, reasoning effort, workspace path, Git branch, and session title. */
8
26
  export const STATUS_META_IDS = ['model', 'effort', 'path', 'git', 'session'];
9
- /** Every independently placed footer item. */
27
+ /** Every part of the footer a reader can show or hide. */
10
28
  export const STATUS_ITEM_IDS = [...STATUS_META_IDS, ...STATUS_GROUP_IDS];
11
29
  export const STATUS_LABEL_STYLES = ['compact', 'full'];
30
+ /** How context occupancy is written, which is a rendering choice, not a layout one. */
12
31
  export const STATUS_CONTEXT_STYLES = ['percent', 'bar', 'tokens', 'detailed'];
13
32
  /**
14
- * Color slots: one per preview item, plus legacy `metrics` as a fallback for
15
- * telemetry groups that do not have their own color yet.
33
+ * How much the footer says.
34
+ *
35
+ * A density is the whole vocabulary now: three whole-footer shapes a reader can
36
+ * recognize, instead of twelve items they have to assemble. Individual groups
37
+ * can still be hidden on top of one, which is the one per-item decision that
38
+ * survives — "I do not care about this number" is real, "put this number on the
39
+ * left in accent green" is chrome nobody asked for.
16
40
  */
17
- export const STATUS_COLOR_SLOTS = [...STATUS_ITEM_IDS, 'metrics'];
18
- /** Theme tokens offered for a status item. `default` keeps the built-in hierarchy. */
19
- export const STATUS_COLOR_TOKENS = ['default', 'text', 'muted', 'accent', 'success', 'warning', 'border', 'label'];
20
- /** Footer column an item can occupy. */
21
- export const STATUS_SIDES = ['left', 'right'];
22
- export const DEFAULT_STATUS_SIDES = {
23
- model: 'left',
24
- effort: 'left',
25
- path: 'right',
26
- git: 'right',
27
- session: 'left',
28
- context: 'left',
29
- cache: 'left',
30
- tokens: 'left',
31
- speed: 'left',
32
- durations: 'right',
33
- counts: 'right',
34
- };
35
- /** Legacy value accepted while existing settings documents migrate. */
36
- export const STATUS_PRESETS = ['minimal', 'compact', 'full'];
37
- export const DEFAULT_STATUS_GROUPS = STATUS_GROUP_IDS;
41
+ export const STATUS_DENSITIES = ['minimal', 'standard', 'verbose'];
42
+ /**
43
+ * Which telemetry row each group is drawn on.
44
+ *
45
+ * Two rows exist so the widest density can state everything without dropping
46
+ * anything: the first row is the window and the turn in flight, the second is
47
+ * what the session has accumulated. `minimal` and `standard` only fill the first
48
+ * row, so their footer stays two lines; the row is a property of the group, not
49
+ * of the density, so nothing moves between rows when the density changes.
50
+ */
51
+ export const STATUS_ROWS = [
52
+ ['context', 'cache', 'tokens', 'speed'],
53
+ ['durations', 'counts'],
54
+ ];
38
55
  /**
39
- * Session title is offered but off by default: adding it to the first line
40
- * would push narrow terminals into degradation sooner. The terminal window
41
- * title carries it unconditionally.
56
+ * Groups drawn against the left edge of their row.
57
+ *
58
+ * A row answers two questions from opposite edges, and each side keeps its
59
+ * meaning down the footer: occupancy and the time already spent read on the
60
+ * left, throughput and the count of work done on the right.
42
61
  */
43
- export const DEFAULT_STATUS_META = ['model', 'effort', 'path', 'git'];
44
- export const DEFAULT_STATUS_COLORS = {
45
- model: 'default',
46
- effort: 'default',
47
- path: 'default',
48
- git: 'default',
49
- session: 'default',
50
- metrics: 'default',
51
- context: 'default',
52
- cache: 'default',
53
- tokens: 'default',
54
- speed: 'default',
55
- durations: 'default',
56
- counts: 'default',
62
+ export const STATUS_LEFT_GROUPS = ['context', 'cache', 'durations'];
63
+ /** Which telemetry row a group is drawn on; 0 is the row under the composer. */
64
+ export function statusGroupRow(id) {
65
+ const row = STATUS_ROWS.findIndex(groups => groups.includes(id));
66
+ return row < 0 ? 0 : row;
67
+ }
68
+ /** Which side of its row a group is drawn on. */
69
+ export function statusGroupSide(id) {
70
+ return STATUS_LEFT_GROUPS.includes(id) ? 'left' : 'right';
71
+ }
72
+ /** Which groups each density shows, in footer order. */
73
+ export const DENSITY_GROUPS = {
74
+ /** Occupancy only: how full the window is and how much of it came from cache. */
75
+ minimal: ['context', 'cache'],
76
+ /** Adds latency — the first number that says a turn feels slow rather than long. */
77
+ standard: ['context', 'cache', 'speed'],
78
+ /** Every group, with full labels and the detailed context form. */
79
+ verbose: [...STATUS_GROUP_IDS],
57
80
  };
58
- /** Return a fresh default so callers can safely replace its group list. */
59
- export function defaultStatusBarConfig() {
60
- return {
61
- enabled: true,
62
- labels: 'compact',
63
- contextStyle: 'percent',
64
- groups: [...DEFAULT_STATUS_GROUPS],
65
- order: [...DEFAULT_STATUS_GROUPS],
66
- meta: [...DEFAULT_STATUS_META],
67
- // Every meta id keeps its slot so the settings list stays stable when an
68
- // item is toggled; only `meta` decides what is actually painted.
69
- metaOrder: [...STATUS_META_IDS],
70
- colors: { ...DEFAULT_STATUS_COLORS },
71
- sides: { ...DEFAULT_STATUS_SIDES },
72
- };
81
+ /** Which first-line pieces each density shows. */
82
+ export const DENSITY_META = {
83
+ /** Where the model is and where the work is; the footer's original two facts. */
84
+ minimal: ['model', 'path'],
85
+ standard: ['model', 'effort', 'path', 'git'],
86
+ /** `session` joins at the widest setting; the terminal title shows it anyway. */
87
+ verbose: ['model', 'effort', 'path', 'git', 'session'],
88
+ };
89
+ /** The label style each density defaults to. */
90
+ export const DENSITY_LABELS = {
91
+ minimal: 'compact',
92
+ standard: 'compact',
93
+ verbose: 'full',
94
+ };
95
+ /** The context representation each density defaults to. */
96
+ export const DENSITY_CONTEXT = {
97
+ minimal: 'percent',
98
+ standard: 'percent',
99
+ verbose: 'detailed',
100
+ };
101
+ export const DEFAULT_STATUS_DENSITY = 'standard';
102
+ /** Earlier releases stored a three-value `statusPreset` beside the layout. */
103
+ export const STATUS_PRESETS = ['minimal', 'compact', 'full'];
104
+ /** Everything shown at the shipped density. */
105
+ export const DEFAULT_STATUS_GROUPS = DENSITY_GROUPS[DEFAULT_STATUS_DENSITY];
106
+ export const DEFAULT_STATUS_META = DENSITY_META[DEFAULT_STATUS_DENSITY];
107
+ function isStatusDensity(value) {
108
+ return typeof value === 'string' && STATUS_DENSITIES.includes(value);
73
109
  }
74
- function isStatusGroupId(value) {
75
- return typeof value === 'string' && STATUS_GROUP_IDS.includes(value);
110
+ export function isStatusItemId(value) {
111
+ return typeof value === 'string' && STATUS_ITEM_IDS.includes(value);
76
112
  }
77
113
  export function isStatusMetaId(value) {
78
114
  return typeof value === 'string' && STATUS_META_IDS.includes(value);
79
115
  }
80
- export function isStatusItemId(value) {
81
- return typeof value === 'string' && STATUS_ITEM_IDS.includes(value);
116
+ export function isStatusGroupId(value) {
117
+ return typeof value === 'string' && STATUS_GROUP_IDS.includes(value);
82
118
  }
83
- function isStatusLabelStyle(value) {
84
- return typeof value === 'string' && STATUS_LABEL_STYLES.includes(value);
119
+ /**
120
+ * Map a retired `statusPreset` onto a density.
121
+ *
122
+ * `minimal` meant "no second line"; that is now `minimal` density with telemetry
123
+ * hidden, which is the same footer the reader had. `full` meant long labels,
124
+ * which is what `verbose` says.
125
+ */
126
+ function densityFromPreset(preset) {
127
+ if (preset === 'minimal')
128
+ return { density: 'minimal', enabled: false };
129
+ if (preset === 'full')
130
+ return { density: 'verbose', enabled: true };
131
+ return { density: DEFAULT_STATUS_DENSITY, enabled: true };
85
132
  }
86
- function isStatusColorToken(value) {
87
- return typeof value === 'string' && STATUS_COLOR_TOKENS.includes(value);
133
+ /**
134
+ * Normalize persisted input at the single configuration seam.
135
+ *
136
+ * A document written by an earlier release described its layout item by item.
137
+ * Rather than fail it or resurrect the engine, the shape it asked for is reduced
138
+ * to the nearest density: what was visible decides the narrowing, and the retired
139
+ * per-item colour and column choices are dropped, because both shipped palettes
140
+ * already paint them the right way.
141
+ */
142
+ export function resolveStatusBarConfig(config, legacyPreset) {
143
+ const fallback = densityFromPreset(legacyPreset);
144
+ if (config === undefined)
145
+ return expand(fallback.density, { enabled: fallback.enabled, hidden: [] });
146
+ const density = isStatusDensity(config.density) ? config.density : fallback.density;
147
+ /**
148
+ * A legacy document listed what it wanted shown; a current one lists what it
149
+ * wants hidden. Both cannot be read from `density`: the schema defaults that
150
+ * field, so an old document arrives here already looking current, while the
151
+ * layout arrays only exist on an old one.
152
+ */
153
+ const enabled = typeof config.enabled === 'boolean' ? config.enabled : fallback.enabled;
154
+ return expand(density, { enabled, hidden: statusNarrowing(config, density), shown: statusAdditions(config) });
155
+ }
156
+ /**
157
+ * What a stored document asks to hide, relative to its density.
158
+ *
159
+ * Exported because writing the document back needs the same reading: a caller
160
+ * that reconstructed the narrowing from the *rendered* footer instead would turn
161
+ * every part its density happens to omit into a permanent hiding, and changing
162
+ * the density afterwards would no longer restore them.
163
+ */
164
+ export function statusNarrowing(config, density) {
165
+ if (config === undefined)
166
+ return [];
167
+ const legacyLayout = config.groups !== undefined || config.meta !== undefined
168
+ || config.order !== undefined || config.metaOrder !== undefined;
169
+ return legacyLayout
170
+ ? legacyHidden(config, density)
171
+ : [...new Set((config.hidden ?? []).filter(isStatusItemId))];
88
172
  }
89
- function isStatusSide(value) {
90
- return value === 'left' || value === 'right';
173
+ /** What a stored document adds beyond its density. */
174
+ export function statusAdditions(config) {
175
+ return [...new Set((config?.shown ?? []).filter(isStatusItemId))];
91
176
  }
92
- export function itemSide(config, id) {
93
- return config.sides?.[id] ?? DEFAULT_STATUS_SIDES[id];
177
+ /** What an earlier release's explicit lists hid, expressed as a narrowing. */
178
+ function legacyHidden(config, density) {
179
+ const shownGroups = config.groups === undefined ? DENSITY_GROUPS[density] : config.groups;
180
+ const shownMeta = config.meta === undefined ? DENSITY_META[density] : config.meta;
181
+ const hidden = [];
182
+ for (const id of STATUS_GROUP_IDS)
183
+ if (!shownGroups.includes(id))
184
+ hidden.push(id);
185
+ for (const id of STATUS_META_IDS)
186
+ if (!shownMeta.includes(id))
187
+ hidden.push(id);
188
+ return hidden;
94
189
  }
95
- function resolveStatusSides(sides) {
96
- const resolved = { ...DEFAULT_STATUS_SIDES };
97
- if (sides === undefined)
98
- return resolved;
190
+ /**
191
+ * True when a document has already been expanded.
192
+ *
193
+ * `resolveStatusBarConfig` is idempotent only if it can tell the two shapes
194
+ * apart: an expanded config carries `groups`/`meta`, which a stored document
195
+ * never does. Without this the renderer re-resolved its own input and silently
196
+ * dropped every narrowing, so a hidden group came back.
197
+ */
198
+ export function isResolvedStatusBarConfig(config) {
199
+ return Array.isArray(config.groups)
200
+ && Array.isArray(config.meta)
201
+ && typeof config.enabled === 'boolean';
202
+ }
203
+ /** Expand one density plus its deltas into the renderer's view. */
204
+ function expand(density, options) {
205
+ const hidden = new Set(options.hidden);
206
+ const shown = new Set(options.shown ?? []);
207
+ // Catalog order, so a part the reader added sits where its family sits rather
208
+ // than at the end of the row where it would move every time it is toggled.
209
+ const visible = (catalog, base) => catalog.filter(id => (base.includes(id) || shown.has(id)) && !hidden.has(id));
210
+ return {
211
+ density,
212
+ enabled: options.enabled,
213
+ labels: DENSITY_LABELS[density],
214
+ contextStyle: DENSITY_CONTEXT[density],
215
+ groups: visible(STATUS_GROUP_IDS, DENSITY_GROUPS[density]),
216
+ meta: visible(STATUS_META_IDS, DENSITY_META[density]),
217
+ };
218
+ }
219
+ /**
220
+ * The document a resolved config came from.
221
+ *
222
+ * Visibility cannot say which of two reasons a part is missing: the reader hid
223
+ * it, or the density never included it. Rebuilding the document from what the
224
+ * footer shows therefore has to keep them apart — a part the density omits must
225
+ * stay out of `hidden`, or raising the density would silently do nothing — and it
226
+ * is the only way a caller that holds the resolved form (the provider holds the
227
+ * footer it is drawing) can hand the document back without losing a choice.
228
+ */
229
+ export function statusBarDocument(config) {
230
+ const densityHas = (id) => isStatusMetaId(id) ? DENSITY_META[config.density].includes(id) : DENSITY_GROUPS[config.density].includes(id);
231
+ const hidden = [];
232
+ const shown = [];
99
233
  for (const id of STATUS_ITEM_IDS) {
100
- const side = sides[id];
101
- if (isStatusSide(side))
102
- resolved[id] = side;
103
- }
104
- return resolved;
105
- }
106
- function completeOrder(ids, preferred) {
107
- const ordered = [...new Set(preferred.filter((id) => ids.includes(id)))];
108
- return [...ordered, ...ids.filter(id => !ordered.includes(id))];
109
- }
110
- function resolveStatusColors(colors) {
111
- const resolved = { ...DEFAULT_STATUS_COLORS };
112
- if (colors === undefined)
113
- return resolved;
114
- for (const slot of STATUS_COLOR_SLOTS) {
115
- const token = colors[slot];
116
- if (isStatusColorToken(token))
117
- resolved[slot] = token;
118
- }
119
- const legacyMetrics = resolved.metrics;
120
- if (legacyMetrics !== 'default') {
121
- for (const group of STATUS_GROUP_IDS) {
122
- if (colors[group] === undefined)
123
- resolved[group] = legacyMetrics;
124
- }
234
+ const visible = statusItemVisible(config, id);
235
+ if (visible === densityHas(id))
236
+ continue;
237
+ if (visible)
238
+ shown.push(id);
239
+ else
240
+ hidden.push(id);
125
241
  }
126
- return resolved;
242
+ return { density: config.density, hidden, shown, enabled: config.enabled };
243
+ }
244
+ /** True when the footer shows this part at the current narrowing. */
245
+ export function statusItemVisible(config, id) {
246
+ return isStatusMetaId(id) ? config.meta.includes(id) : config.groups.includes(id);
127
247
  }
128
248
  /**
129
- * Normalize persisted input at the single configuration seam. Invalid and
130
- * duplicate groups disappear; legacy presets retain their previous meaning.
249
+ * The same document with one part turned on or off.
250
+ *
251
+ * Both directions are deltas from the density, so a part the density omits can
252
+ * be added and a part it includes can be removed, without either one rewriting
253
+ * the other's state.
131
254
  */
132
- export function resolveStatusBarConfig(config, legacyPreset) {
133
- if (config === undefined) {
134
- const fallback = defaultStatusBarConfig();
135
- if (legacyPreset === 'minimal')
136
- fallback.enabled = false;
137
- if (legacyPreset === 'full')
138
- fallback.labels = 'full';
139
- return fallback;
255
+ export function toggleStatusItem(config, id) {
256
+ const density = isStatusDensity(config.density) ? config.density : DEFAULT_STATUS_DENSITY;
257
+ const hidden = new Set((config.hidden ?? []).filter(isStatusItemId));
258
+ const shown = new Set((config.shown ?? []).filter(isStatusItemId));
259
+ const visible = statusItemVisible(expand(density, { enabled: true, hidden: [...hidden], shown: [...shown] }), id);
260
+ if (visible) {
261
+ hidden.add(id);
262
+ shown.delete(id);
140
263
  }
141
- const groups = config.groups === undefined
142
- ? [...DEFAULT_STATUS_GROUPS]
143
- : [...new Set(config.groups.filter(isStatusGroupId))];
144
- const meta = config.meta === undefined
145
- ? [...DEFAULT_STATUS_META]
146
- : [...new Set(config.meta.filter(isStatusMetaId))];
147
- return {
148
- enabled: config.enabled ?? true,
149
- labels: isStatusLabelStyle(config.labels) ? config.labels : 'compact',
150
- contextStyle: STATUS_CONTEXT_STYLES.includes(config.contextStyle) ? config.contextStyle : 'percent',
151
- groups,
152
- order: completeOrder(STATUS_GROUP_IDS, config.order === undefined ? groups : config.order),
153
- meta,
154
- metaOrder: completeOrder(STATUS_META_IDS, config.metaOrder === undefined ? meta : config.metaOrder),
155
- colors: resolveStatusColors(config.colors),
156
- sides: resolveStatusSides(config.sides),
157
- };
264
+ else {
265
+ shown.add(id);
266
+ hidden.delete(id);
267
+ }
268
+ return { ...config, hidden: [...hidden], shown: [...shown] };
158
269
  }
159
270
  //# sourceMappingURL=status-config.js.map