@elaraai/e3-ui-cli 1.0.72 → 1.0.74

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 (220) hide show
  1. package/README.md +46 -21
  2. package/bin/e3-ui.mjs +6 -0
  3. package/dist/app/assets/{engine-BKZbmXkC-DmcCFcnb.js → engine-Bm-cIDd_-DwUdGcTZ.js} +1 -1
  4. package/dist/app/assets/{index-BJNWHnRv.js → index-mj-YVVN1.js} +91 -91
  5. package/dist/app/index.html +1 -1
  6. package/dist/cli.js +36 -5
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/tui.d.ts +84 -0
  9. package/dist/commands/tui.d.ts.map +1 -0
  10. package/dist/commands/tui.js +89 -0
  11. package/dist/commands/tui.js.map +1 -0
  12. package/dist/tui/api.d.ts +108 -0
  13. package/dist/tui/api.d.ts.map +1 -0
  14. package/dist/tui/api.js +130 -0
  15. package/dist/tui/api.js.map +1 -0
  16. package/dist/tui/app.d.ts +23 -0
  17. package/dist/tui/app.d.ts.map +1 -0
  18. package/dist/tui/app.js +244 -0
  19. package/dist/tui/app.js.map +1 -0
  20. package/dist/tui/controller.d.ts +116 -0
  21. package/dist/tui/controller.d.ts.map +1 -0
  22. package/dist/tui/controller.js +644 -0
  23. package/dist/tui/controller.js.map +1 -0
  24. package/dist/tui/data/dataflow.d.ts +68 -0
  25. package/dist/tui/data/dataflow.d.ts.map +1 -0
  26. package/dist/tui/data/dataflow.js +137 -0
  27. package/dist/tui/data/dataflow.js.map +1 -0
  28. package/dist/tui/data/dataset.d.ts +80 -0
  29. package/dist/tui/data/dataset.d.ts.map +1 -0
  30. package/dist/tui/data/dataset.js +297 -0
  31. package/dist/tui/data/dataset.js.map +1 -0
  32. package/dist/tui/data/edit-buffer.d.ts +74 -0
  33. package/dist/tui/data/edit-buffer.d.ts.map +1 -0
  34. package/dist/tui/data/edit-buffer.js +120 -0
  35. package/dist/tui/data/edit-buffer.js.map +1 -0
  36. package/dist/tui/data/feeds.d.ts +45 -0
  37. package/dist/tui/data/feeds.d.ts.map +1 -0
  38. package/dist/tui/data/feeds.js +318 -0
  39. package/dist/tui/data/feeds.js.map +1 -0
  40. package/dist/tui/data/logs.d.ts +68 -0
  41. package/dist/tui/data/logs.d.ts.map +1 -0
  42. package/dist/tui/data/logs.js +134 -0
  43. package/dist/tui/data/logs.js.map +1 -0
  44. package/dist/tui/input/commands.d.ts +159 -0
  45. package/dist/tui/input/commands.d.ts.map +1 -0
  46. package/dist/tui/input/commands.js +259 -0
  47. package/dist/tui/input/commands.js.map +1 -0
  48. package/dist/tui/input/completion.d.ts +63 -0
  49. package/dist/tui/input/completion.d.ts.map +1 -0
  50. package/dist/tui/input/completion.js +128 -0
  51. package/dist/tui/input/completion.js.map +1 -0
  52. package/dist/tui/input/keymap.d.ts +153 -0
  53. package/dist/tui/input/keymap.d.ts.map +1 -0
  54. package/dist/tui/input/keymap.js +206 -0
  55. package/dist/tui/input/keymap.js.map +1 -0
  56. package/dist/tui/input/mouse.d.ts +52 -0
  57. package/dist/tui/input/mouse.d.ts.map +1 -0
  58. package/dist/tui/input/mouse.js +55 -0
  59. package/dist/tui/input/mouse.js.map +1 -0
  60. package/dist/tui/lifecycle.d.ts +61 -0
  61. package/dist/tui/lifecycle.d.ts.map +1 -0
  62. package/dist/tui/lifecycle.js +99 -0
  63. package/dist/tui/lifecycle.js.map +1 -0
  64. package/dist/tui/model/catalogue.d.ts +28 -0
  65. package/dist/tui/model/catalogue.d.ts.map +1 -0
  66. package/dist/tui/model/catalogue.js +94 -0
  67. package/dist/tui/model/catalogue.js.map +1 -0
  68. package/dist/tui/model/help.d.ts +37 -0
  69. package/dist/tui/model/help.d.ts.map +1 -0
  70. package/dist/tui/model/help.js +210 -0
  71. package/dist/tui/model/help.js.map +1 -0
  72. package/dist/tui/model/index.d.ts +42 -0
  73. package/dist/tui/model/index.d.ts.map +1 -0
  74. package/dist/tui/model/index.js +50 -0
  75. package/dist/tui/model/index.js.map +1 -0
  76. package/dist/tui/model/status.d.ts +88 -0
  77. package/dist/tui/model/status.d.ts.map +1 -0
  78. package/dist/tui/model/status.js +137 -0
  79. package/dist/tui/model/status.js.map +1 -0
  80. package/dist/tui/model/tree.d.ts +126 -0
  81. package/dist/tui/model/tree.d.ts.map +1 -0
  82. package/dist/tui/model/tree.js +217 -0
  83. package/dist/tui/model/tree.js.map +1 -0
  84. package/dist/tui/model/types.d.ts +21 -0
  85. package/dist/tui/model/types.d.ts.map +1 -0
  86. package/dist/tui/model/types.js +46 -0
  87. package/dist/tui/model/types.js.map +1 -0
  88. package/dist/tui/render/glyphs.d.ts +122 -0
  89. package/dist/tui/render/glyphs.d.ts.map +1 -0
  90. package/dist/tui/render/glyphs.js +122 -0
  91. package/dist/tui/render/glyphs.js.map +1 -0
  92. package/dist/tui/render/layout.d.ts +145 -0
  93. package/dist/tui/render/layout.d.ts.map +1 -0
  94. package/dist/tui/render/layout.js +209 -0
  95. package/dist/tui/render/layout.js.map +1 -0
  96. package/dist/tui/render/text.d.ts +153 -0
  97. package/dist/tui/render/text.d.ts.map +1 -0
  98. package/dist/tui/render/text.js +320 -0
  99. package/dist/tui/render/text.js.map +1 -0
  100. package/dist/tui/render/theme.d.ts +88 -0
  101. package/dist/tui/render/theme.d.ts.map +1 -0
  102. package/dist/tui/render/theme.js +145 -0
  103. package/dist/tui/render/theme.js.map +1 -0
  104. package/dist/tui/session.d.ts +78 -0
  105. package/dist/tui/session.d.ts.map +1 -0
  106. package/dist/tui/session.js +237 -0
  107. package/dist/tui/session.js.map +1 -0
  108. package/dist/tui/state/actions.d.ts +584 -0
  109. package/dist/tui/state/actions.d.ts.map +1 -0
  110. package/dist/tui/state/actions.js +62 -0
  111. package/dist/tui/state/actions.js.map +1 -0
  112. package/dist/tui/state/persist.d.ts +114 -0
  113. package/dist/tui/state/persist.d.ts.map +1 -0
  114. package/dist/tui/state/persist.js +187 -0
  115. package/dist/tui/state/persist.js.map +1 -0
  116. package/dist/tui/state/poll.d.ts +99 -0
  117. package/dist/tui/state/poll.d.ts.map +1 -0
  118. package/dist/tui/state/poll.js +160 -0
  119. package/dist/tui/state/poll.js.map +1 -0
  120. package/dist/tui/state/reducer.d.ts +16 -0
  121. package/dist/tui/state/reducer.d.ts.map +1 -0
  122. package/dist/tui/state/reducer.js +363 -0
  123. package/dist/tui/state/reducer.js.map +1 -0
  124. package/dist/tui/state/store.d.ts +37 -0
  125. package/dist/tui/state/store.d.ts.map +1 -0
  126. package/dist/tui/state/store.js +84 -0
  127. package/dist/tui/state/store.js.map +1 -0
  128. package/dist/tui/suspend.d.ts +17 -0
  129. package/dist/tui/suspend.d.ts.map +1 -0
  130. package/dist/tui/suspend.js +33 -0
  131. package/dist/tui/suspend.js.map +1 -0
  132. package/dist/tui/ui/App.d.ts +44 -0
  133. package/dist/tui/ui/App.d.ts.map +1 -0
  134. package/dist/tui/ui/App.js +105 -0
  135. package/dist/tui/ui/App.js.map +1 -0
  136. package/dist/tui/ui/frame.d.ts +86 -0
  137. package/dist/tui/ui/frame.d.ts.map +1 -0
  138. package/dist/tui/ui/frame.js +42 -0
  139. package/dist/tui/ui/frame.js.map +1 -0
  140. package/dist/tui/ui/lines.d.ts +92 -0
  141. package/dist/tui/ui/lines.d.ts.map +1 -0
  142. package/dist/tui/ui/lines.js +94 -0
  143. package/dist/tui/ui/lines.js.map +1 -0
  144. package/dist/tui/ui/shell/chrome.d.ts +61 -0
  145. package/dist/tui/ui/shell/chrome.d.ts.map +1 -0
  146. package/dist/tui/ui/shell/chrome.js +272 -0
  147. package/dist/tui/ui/shell/chrome.js.map +1 -0
  148. package/dist/tui/ui/shell/widgets.d.ts +116 -0
  149. package/dist/tui/ui/shell/widgets.d.ts.map +1 -0
  150. package/dist/tui/ui/shell/widgets.js +183 -0
  151. package/dist/tui/ui/shell/widgets.js.map +1 -0
  152. package/dist/tui/ui/views/about.d.ts +20 -0
  153. package/dist/tui/ui/views/about.d.ts.map +1 -0
  154. package/dist/tui/ui/views/about.js +40 -0
  155. package/dist/tui/ui/views/about.js.map +1 -0
  156. package/dist/tui/ui/views/all.d.ts +17 -0
  157. package/dist/tui/ui/views/all.d.ts.map +1 -0
  158. package/dist/tui/ui/views/all.js +17 -0
  159. package/dist/tui/ui/views/all.js.map +1 -0
  160. package/dist/tui/ui/views/dashboard.d.ts +211 -0
  161. package/dist/tui/ui/views/dashboard.d.ts.map +1 -0
  162. package/dist/tui/ui/views/dashboard.js +651 -0
  163. package/dist/tui/ui/views/dashboard.js.map +1 -0
  164. package/dist/tui/ui/views/help.d.ts +17 -0
  165. package/dist/tui/ui/views/help.d.ts.map +1 -0
  166. package/dist/tui/ui/views/help.js +65 -0
  167. package/dist/tui/ui/views/help.js.map +1 -0
  168. package/dist/tui/ui/views/index.d.ts +42 -0
  169. package/dist/tui/ui/views/index.d.ts.map +1 -0
  170. package/dist/tui/ui/views/index.js +42 -0
  171. package/dist/tui/ui/views/index.js.map +1 -0
  172. package/dist/tui/ui/views/input.d.ts +6 -0
  173. package/dist/tui/ui/views/input.d.ts.map +1 -0
  174. package/dist/tui/ui/views/input.js +494 -0
  175. package/dist/tui/ui/views/input.js.map +1 -0
  176. package/dist/tui/ui/views/launch.d.ts +16 -0
  177. package/dist/tui/ui/views/launch.d.ts.map +1 -0
  178. package/dist/tui/ui/views/launch.js +43 -0
  179. package/dist/tui/ui/views/launch.js.map +1 -0
  180. package/dist/tui/ui/views/refusal.d.ts +20 -0
  181. package/dist/tui/ui/views/refusal.d.ts.map +1 -0
  182. package/dist/tui/ui/views/refusal.js +47 -0
  183. package/dist/tui/ui/views/refusal.js.map +1 -0
  184. package/dist/tui/ui/views/repos.d.ts +10 -0
  185. package/dist/tui/ui/views/repos.d.ts.map +1 -0
  186. package/dist/tui/ui/views/repos.js +93 -0
  187. package/dist/tui/ui/views/repos.js.map +1 -0
  188. package/dist/tui/ui/views/task.d.ts +17 -0
  189. package/dist/tui/ui/views/task.d.ts.map +1 -0
  190. package/dist/tui/ui/views/task.js +309 -0
  191. package/dist/tui/ui/views/task.js.map +1 -0
  192. package/dist/tui/ui/views/wordmark.d.ts +15 -0
  193. package/dist/tui/ui/views/wordmark.d.ts.map +1 -0
  194. package/dist/tui/ui/views/wordmark.js +50 -0
  195. package/dist/tui/ui/views/wordmark.js.map +1 -0
  196. package/dist/tui/ui/views/workspaces.d.ts +17 -0
  197. package/dist/tui/ui/views/workspaces.d.ts.map +1 -0
  198. package/dist/tui/ui/views/workspaces.js +127 -0
  199. package/dist/tui/ui/views/workspaces.js.map +1 -0
  200. package/dist/tui/ui/widgets/leaf-editor.d.ts +58 -0
  201. package/dist/tui/ui/widgets/leaf-editor.d.ts.map +1 -0
  202. package/dist/tui/ui/widgets/leaf-editor.js +119 -0
  203. package/dist/tui/ui/widgets/leaf-editor.js.map +1 -0
  204. package/dist/tui/ui/widgets/logs.d.ts +99 -0
  205. package/dist/tui/ui/widgets/logs.d.ts.map +1 -0
  206. package/dist/tui/ui/widgets/logs.js +291 -0
  207. package/dist/tui/ui/widgets/logs.js.map +1 -0
  208. package/dist/tui/ui/widgets/reads.d.ts +62 -0
  209. package/dist/tui/ui/widgets/reads.d.ts.map +1 -0
  210. package/dist/tui/ui/widgets/reads.js +122 -0
  211. package/dist/tui/ui/widgets/reads.js.map +1 -0
  212. package/dist/tui/ui/widgets/runs.d.ts +51 -0
  213. package/dist/tui/ui/widgets/runs.d.ts.map +1 -0
  214. package/dist/tui/ui/widgets/runs.js +86 -0
  215. package/dist/tui/ui/widgets/runs.js.map +1 -0
  216. package/dist/tui/ui/widgets/tree.d.ts +149 -0
  217. package/dist/tui/ui/widgets/tree.d.ts.map +1 -0
  218. package/dist/tui/ui/widgets/tree.js +584 -0
  219. package/dist/tui/ui/widgets/tree.js.map +1 -0
  220. package/package.json +22 -12
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Text measurement and formatting for one-line terminal rows.
7
+ *
8
+ * Every row the TUI renders is exactly one terminal line, so the layout
9
+ * math needs the CELL width of a string (not its code-unit length): the
10
+ * glyph set is single-width, but user data can carry wide (CJK, emoji) or
11
+ * zero-width (combining) characters. Byte sizes come from
12
+ * `formatSize` (`@elaraai/e3-cli/internal`) — one formatter across `e3`
13
+ * and `e3-ui`, not a third.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { formatSize } from '@elaraai/e3-cli/internal';
18
+ /**
19
+ * The number of terminal cells `text` occupies.
20
+ *
21
+ * @param text - The text (no ANSI escapes)
22
+ * @returns Its cell width
23
+ */
24
+ export declare function displayWidth(text: string): number;
25
+ /**
26
+ * Truncates `text` to at most `width` cells, ending in `ellipsis` when cut —
27
+ * one pass over the graphemes (one `length` check for ASCII).
28
+ *
29
+ * @param text - The text
30
+ * @param width - The cell budget
31
+ * @param ellipsis - The marker appended when truncated (default `…`)
32
+ * @returns The text, or a prefix of it plus the ellipsis, within `width`
33
+ */
34
+ export declare function truncate(text: string, width: number, ellipsis?: string): string;
35
+ /**
36
+ * Pads `text` on the right to exactly `width` cells, truncating when longer.
37
+ *
38
+ * @param text - The text
39
+ * @param width - The cell width
40
+ * @returns The fitted text
41
+ */
42
+ export declare function padEnd(text: string, width: number): string;
43
+ /**
44
+ * Pads `text` on the left to exactly `width` cells, truncating when longer.
45
+ *
46
+ * @param text - The text
47
+ * @param width - The cell width
48
+ * @returns The fitted text
49
+ */
50
+ export declare function padStart(text: string, width: number): string;
51
+ /**
52
+ * Centres `text` in `width` cells (the extra cell of an odd remainder goes
53
+ * to the right).
54
+ *
55
+ * @param text - The text
56
+ * @param width - The cell width
57
+ * @returns The centred, fitted text
58
+ */
59
+ export declare function center(text: string, width: number): string;
60
+ /**
61
+ * Fits `text` to exactly `width` cells — padded on the right, or truncated
62
+ * with an ellipsis.
63
+ *
64
+ * @param text - The text
65
+ * @param width - The cell width
66
+ * @returns The fitted text
67
+ */
68
+ export declare function fit(text: string, width: number): string;
69
+ /**
70
+ * A row with `left` at the start and `right` flush against the end, in
71
+ * `width` cells; the left part yields to the right one.
72
+ *
73
+ * @param left - The left text
74
+ * @param right - The right text
75
+ * @param width - The cell width
76
+ * @param gap - Minimum cells between the two (default 1)
77
+ * @returns The composed row
78
+ */
79
+ export declare function lr(left: string, right: string, width: number, gap?: number): string;
80
+ /**
81
+ * Formats an integer with thousands grouping (`1,240,000`).
82
+ *
83
+ * @param n - The integer
84
+ * @returns The grouped text
85
+ */
86
+ export declare function formatInt(n: number | bigint): string;
87
+ /**
88
+ * Formats a duration in milliseconds the way the dashboard reads it:
89
+ * `0.8s`, `38.4s`, `2m 5s`, `1h 02m`, `3d 4h`.
90
+ *
91
+ * @param ms - The duration in milliseconds
92
+ * @returns The formatted duration
93
+ */
94
+ export declare function formatDuration(ms: number): string;
95
+ /**
96
+ * How long ago `then` was, at whole-unit precision: `just now`, `12s ago`,
97
+ * `2m ago`, `3h ago`, `3d ago`, `12d ago`.
98
+ *
99
+ * @param then - The instant (a `Date`, ISO text, or epoch milliseconds)
100
+ * @param now - The current epoch milliseconds
101
+ * @returns The relative text (`—` for an unparsable instant)
102
+ */
103
+ export declare function timeAgo(then: Date | string | number, now: number): string;
104
+ /**
105
+ * How long ago `then` was, at whole seconds rounded down (`just now`,
106
+ * `7s ago`, `12s ago`, `2m ago`) — the "polled … ago" hint. Rounding down
107
+ * keeps the text stable between the clock's ticks: a poll that landed
108
+ * within the second reads `just now` at every render until the second
109
+ * turns, so an idle frame stays byte-identical and Ink writes nothing.
110
+ *
111
+ * @param then - The instant in epoch milliseconds
112
+ * @param now - The current epoch milliseconds
113
+ * @returns The relative text
114
+ */
115
+ export declare function agoShort(then: number, now: number): string;
116
+ /**
117
+ * The first `n` characters of a hash (`9f3c1a7e2b41`).
118
+ *
119
+ * @param hash - The hash
120
+ * @param n - Characters kept (default 12, e3-cli's display width)
121
+ * @returns The prefix
122
+ */
123
+ export declare function hashShort(hash: string, n?: number): string;
124
+ /**
125
+ * A hash abbreviated at both ends (`4be1…a9`).
126
+ *
127
+ * @param hash - The hash
128
+ * @returns The abbreviation (short hashes are returned as is)
129
+ */
130
+ export declare function hashMid(hash: string): string;
131
+ /**
132
+ * A hash abbreviated to its first four characters plus an ellipsis (`0a44…`).
133
+ *
134
+ * @param hash - The hash
135
+ * @returns The abbreviation
136
+ */
137
+ export declare function hashTiny(hash: string): string;
138
+ /**
139
+ * Formats a UTC instant as `YYYY-MM-DD HH:MM:SS`.
140
+ *
141
+ * @param when - The instant (a `Date`, ISO text, or epoch milliseconds)
142
+ * @returns The formatted text (`—` when unparsable)
143
+ */
144
+ export declare function formatStamp(when: Date | string | number): string;
145
+ /**
146
+ * A percentage with two decimals (`0.97%`), for the value-tree footer.
147
+ *
148
+ * @param part - The numerator
149
+ * @param whole - The denominator
150
+ * @returns The percentage text (`0%` when `whole` is 0)
151
+ */
152
+ export declare function percent(part: number, whole: number): string;
153
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/tui/render/text.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAmDtD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAM,GAAG,MAAM,CAqB5E;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,CAM9E;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAKpD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAUjD;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAWzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAK1D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,SAAK,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAIhE;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG3D"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Text measurement and formatting for one-line terminal rows.
7
+ *
8
+ * Every row the TUI renders is exactly one terminal line, so the layout
9
+ * math needs the CELL width of a string (not its code-unit length): the
10
+ * glyph set is single-width, but user data can carry wide (CJK, emoji) or
11
+ * zero-width (combining) characters. Byte sizes come from
12
+ * `formatSize` (`@elaraai/e3-cli/internal`) — one formatter across `e3`
13
+ * and `e3-ui`, not a third.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { formatSize } from '@elaraai/e3-cli/internal';
18
+ const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
19
+ /**
20
+ * Printable ASCII only — every character is one cell, so the width is the
21
+ * length and a cut is a `slice`. Almost every cell the TUI measures is
22
+ * ASCII (names, paths, numbers, hashes); the segmenter is kept for the
23
+ * rest (wide, combining, ZWJ sequences, controls).
24
+ */
25
+ const ASCII = /^[\x20-\x7e]*$/;
26
+ /** Whether a code point renders two cells wide (East Asian Wide / Fullwidth, emoji presentation). */
27
+ function isWide(cp) {
28
+ return ((cp >= 0x1100 && cp <= 0x115f) ||
29
+ (cp >= 0x2e80 && cp <= 0x303e) ||
30
+ (cp >= 0x3041 && cp <= 0x33ff) ||
31
+ (cp >= 0x3400 && cp <= 0x4dbf) ||
32
+ (cp >= 0x4e00 && cp <= 0x9fff) ||
33
+ (cp >= 0xa000 && cp <= 0xa4cf) ||
34
+ (cp >= 0xac00 && cp <= 0xd7a3) ||
35
+ (cp >= 0xf900 && cp <= 0xfaff) ||
36
+ (cp >= 0xfe30 && cp <= 0xfe4f) ||
37
+ (cp >= 0xff00 && cp <= 0xff60) ||
38
+ (cp >= 0xffe0 && cp <= 0xffe6) ||
39
+ (cp >= 0x1f300 && cp <= 0x1f64f) ||
40
+ (cp >= 0x1f900 && cp <= 0x1f9ff) ||
41
+ (cp >= 0x20000 && cp <= 0x3fffd));
42
+ }
43
+ /** Whether a code point occupies no cell (combining marks, ZWJ, variation selectors, controls). */
44
+ function isZeroWidth(cp) {
45
+ return (cp < 0x20 ||
46
+ (cp >= 0x7f && cp < 0xa0) ||
47
+ (cp >= 0x300 && cp <= 0x36f) ||
48
+ (cp >= 0x200b && cp <= 0x200f) ||
49
+ (cp >= 0xfe00 && cp <= 0xfe0f) ||
50
+ cp === 0xfeff);
51
+ }
52
+ /** The cells one grapheme cluster takes: its first code point decides. */
53
+ function graphemeWidth(segment) {
54
+ const cp = segment.codePointAt(0);
55
+ if (cp === undefined || isZeroWidth(cp))
56
+ return 0;
57
+ return isWide(cp) ? 2 : 1;
58
+ }
59
+ /**
60
+ * The number of terminal cells `text` occupies.
61
+ *
62
+ * @param text - The text (no ANSI escapes)
63
+ * @returns Its cell width
64
+ */
65
+ export function displayWidth(text) {
66
+ if (ASCII.test(text))
67
+ return text.length;
68
+ let width = 0;
69
+ for (const { segment } of segmenter.segment(text))
70
+ width += graphemeWidth(segment);
71
+ return width;
72
+ }
73
+ /**
74
+ * Truncates `text` to at most `width` cells, ending in `ellipsis` when cut —
75
+ * one pass over the graphemes (one `length` check for ASCII).
76
+ *
77
+ * @param text - The text
78
+ * @param width - The cell budget
79
+ * @param ellipsis - The marker appended when truncated (default `…`)
80
+ * @returns The text, or a prefix of it plus the ellipsis, within `width`
81
+ */
82
+ export function truncate(text, width, ellipsis = '…') {
83
+ if (width <= 0)
84
+ return '';
85
+ if (ASCII.test(text)) {
86
+ if (text.length <= width)
87
+ return text;
88
+ const budget = width - displayWidth(ellipsis);
89
+ return budget <= 0 ? ellipsis.slice(0, width) : text.slice(0, budget) + ellipsis;
90
+ }
91
+ const budget = width - displayWidth(ellipsis);
92
+ let out = '';
93
+ let used = 0;
94
+ // The prefix within the budget is remembered as the walk passes it, so a
95
+ // cut needs no second measurement.
96
+ let kept = null;
97
+ for (const { segment } of segmenter.segment(text)) {
98
+ const w = graphemeWidth(segment);
99
+ if (kept === null && used + w > budget)
100
+ kept = out;
101
+ if (used + w > width)
102
+ return budget <= 0 ? ellipsis.slice(0, width) : `${kept ?? out}${ellipsis}`;
103
+ out += segment;
104
+ used += w;
105
+ }
106
+ return text;
107
+ }
108
+ /**
109
+ * Pads `text` on the right to exactly `width` cells, truncating when longer.
110
+ *
111
+ * @param text - The text
112
+ * @param width - The cell width
113
+ * @returns The fitted text
114
+ */
115
+ export function padEnd(text, width) {
116
+ if (ASCII.test(text))
117
+ return text.length > width ? text.slice(0, Math.max(0, width)) : text + ' '.repeat(width - text.length);
118
+ const w = displayWidth(text);
119
+ if (w <= width)
120
+ return text + ' '.repeat(width - w);
121
+ // A wide cluster that straddles the cut is dropped; the cell it leaves is padded.
122
+ const cut = truncate(text, width, '');
123
+ return cut + ' '.repeat(Math.max(0, width - displayWidth(cut)));
124
+ }
125
+ /**
126
+ * Pads `text` on the left to exactly `width` cells, truncating when longer.
127
+ *
128
+ * @param text - The text
129
+ * @param width - The cell width
130
+ * @returns The fitted text
131
+ */
132
+ export function padStart(text, width) {
133
+ if (ASCII.test(text))
134
+ return text.length > width ? text.slice(0, Math.max(0, width)) : ' '.repeat(width - text.length) + text;
135
+ const w = displayWidth(text);
136
+ if (w <= width)
137
+ return ' '.repeat(width - w) + text;
138
+ const cut = truncate(text, width, '');
139
+ return ' '.repeat(Math.max(0, width - displayWidth(cut))) + cut;
140
+ }
141
+ /**
142
+ * Centres `text` in `width` cells (the extra cell of an odd remainder goes
143
+ * to the right).
144
+ *
145
+ * @param text - The text
146
+ * @param width - The cell width
147
+ * @returns The centred, fitted text
148
+ */
149
+ export function center(text, width) {
150
+ if (width <= 0)
151
+ return '';
152
+ const fitted = displayWidth(text) > width ? truncate(text, width, '') : text;
153
+ const w = displayWidth(fitted);
154
+ const left = Math.floor((width - w) / 2);
155
+ return ' '.repeat(left) + fitted + ' '.repeat(width - w - left);
156
+ }
157
+ /**
158
+ * Fits `text` to exactly `width` cells — padded on the right, or truncated
159
+ * with an ellipsis.
160
+ *
161
+ * @param text - The text
162
+ * @param width - The cell width
163
+ * @returns The fitted text
164
+ */
165
+ export function fit(text, width) {
166
+ return padEnd(truncate(text, width), width);
167
+ }
168
+ /**
169
+ * A row with `left` at the start and `right` flush against the end, in
170
+ * `width` cells; the left part yields to the right one.
171
+ *
172
+ * @param left - The left text
173
+ * @param right - The right text
174
+ * @param width - The cell width
175
+ * @param gap - Minimum cells between the two (default 1)
176
+ * @returns The composed row
177
+ */
178
+ export function lr(left, right, width, gap = 1) {
179
+ const rightWidth = displayWidth(right);
180
+ if (rightWidth >= width)
181
+ return truncate(right, width, '');
182
+ const leftBudget = width - rightWidth - gap;
183
+ const l = leftBudget > 0 ? truncate(left, leftBudget) : '';
184
+ return l + ' '.repeat(width - displayWidth(l) - rightWidth) + right;
185
+ }
186
+ /**
187
+ * Formats an integer with thousands grouping (`1,240,000`).
188
+ *
189
+ * @param n - The integer
190
+ * @returns The grouped text
191
+ */
192
+ export function formatInt(n) {
193
+ const negative = typeof n === 'bigint' ? n < 0n : n < 0;
194
+ const digits = (typeof n === 'bigint' ? (negative ? -n : n) : Math.trunc(Math.abs(n))).toString();
195
+ const grouped = digits.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
196
+ return negative ? `-${grouped}` : grouped;
197
+ }
198
+ /**
199
+ * Formats a duration in milliseconds the way the dashboard reads it:
200
+ * `0.8s`, `38.4s`, `2m 5s`, `1h 02m`, `3d 4h`.
201
+ *
202
+ * @param ms - The duration in milliseconds
203
+ * @returns The formatted duration
204
+ */
205
+ export function formatDuration(ms) {
206
+ if (!Number.isFinite(ms) || ms < 0)
207
+ return '—';
208
+ const seconds = ms / 1000;
209
+ if (seconds < 60)
210
+ return `${seconds.toFixed(1)}s`;
211
+ const minutes = Math.floor(seconds / 60);
212
+ if (minutes < 60)
213
+ return `${minutes}m ${Math.round(seconds - minutes * 60)}s`;
214
+ const hours = Math.floor(minutes / 60);
215
+ if (hours < 24)
216
+ return `${hours}h ${String(minutes - hours * 60).padStart(2, '0')}m`;
217
+ const days = Math.floor(hours / 24);
218
+ return `${days}d ${hours - days * 24}h`;
219
+ }
220
+ /**
221
+ * How long ago `then` was, at whole-unit precision: `just now`, `12s ago`,
222
+ * `2m ago`, `3h ago`, `3d ago`, `12d ago`.
223
+ *
224
+ * @param then - The instant (a `Date`, ISO text, or epoch milliseconds)
225
+ * @param now - The current epoch milliseconds
226
+ * @returns The relative text (`—` for an unparsable instant)
227
+ */
228
+ export function timeAgo(then, now) {
229
+ const t = typeof then === 'number' ? then : new Date(then).getTime();
230
+ if (!Number.isFinite(t))
231
+ return '—';
232
+ const seconds = Math.max(0, Math.round((now - t) / 1000));
233
+ if (seconds < 1)
234
+ return 'just now';
235
+ if (seconds < 60)
236
+ return `${seconds}s ago`;
237
+ const minutes = Math.floor(seconds / 60);
238
+ if (minutes < 60)
239
+ return `${minutes}m ago`;
240
+ const hours = Math.floor(minutes / 60);
241
+ if (hours < 24)
242
+ return `${hours}h ago`;
243
+ return `${Math.floor(hours / 24)}d ago`;
244
+ }
245
+ /**
246
+ * How long ago `then` was, at whole seconds rounded down (`just now`,
247
+ * `7s ago`, `12s ago`, `2m ago`) — the "polled … ago" hint. Rounding down
248
+ * keeps the text stable between the clock's ticks: a poll that landed
249
+ * within the second reads `just now` at every render until the second
250
+ * turns, so an idle frame stays byte-identical and Ink writes nothing.
251
+ *
252
+ * @param then - The instant in epoch milliseconds
253
+ * @param now - The current epoch milliseconds
254
+ * @returns The relative text
255
+ */
256
+ export function agoShort(then, now) {
257
+ const seconds = Math.max(0, Math.floor((now - then) / 1000));
258
+ if (seconds < 1)
259
+ return 'just now';
260
+ if (seconds < 60)
261
+ return `${seconds}s ago`;
262
+ return timeAgo(then, now);
263
+ }
264
+ /**
265
+ * The first `n` characters of a hash (`9f3c1a7e2b41`).
266
+ *
267
+ * @param hash - The hash
268
+ * @param n - Characters kept (default 12, e3-cli's display width)
269
+ * @returns The prefix
270
+ */
271
+ export function hashShort(hash, n = 12) {
272
+ return hash.slice(0, n);
273
+ }
274
+ /**
275
+ * A hash abbreviated at both ends (`4be1…a9`).
276
+ *
277
+ * @param hash - The hash
278
+ * @returns The abbreviation (short hashes are returned as is)
279
+ */
280
+ export function hashMid(hash) {
281
+ if (hash.length <= 8)
282
+ return hash;
283
+ return `${hash.slice(0, 4)}…${hash.slice(-2)}`;
284
+ }
285
+ /**
286
+ * A hash abbreviated to its first four characters plus an ellipsis (`0a44…`).
287
+ *
288
+ * @param hash - The hash
289
+ * @returns The abbreviation
290
+ */
291
+ export function hashTiny(hash) {
292
+ if (hash.length <= 4)
293
+ return hash;
294
+ return `${hash.slice(0, 4)}…`;
295
+ }
296
+ /**
297
+ * Formats a UTC instant as `YYYY-MM-DD HH:MM:SS`.
298
+ *
299
+ * @param when - The instant (a `Date`, ISO text, or epoch milliseconds)
300
+ * @returns The formatted text (`—` when unparsable)
301
+ */
302
+ export function formatStamp(when) {
303
+ const d = when instanceof Date ? when : new Date(when);
304
+ if (!Number.isFinite(d.getTime()))
305
+ return '—';
306
+ return d.toISOString().replace('T', ' ').slice(0, 19);
307
+ }
308
+ /**
309
+ * A percentage with two decimals (`0.97%`), for the value-tree footer.
310
+ *
311
+ * @param part - The numerator
312
+ * @param whole - The denominator
313
+ * @returns The percentage text (`0%` when `whole` is 0)
314
+ */
315
+ export function percent(part, whole) {
316
+ if (whole <= 0)
317
+ return '0%';
318
+ return `${((part / whole) * 100).toFixed(2)}%`;
319
+ }
320
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/tui/render/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Colour: the design system's semantic tokens mapped onto what the
7
+ * terminal can show.
8
+ *
9
+ * `process.stdout.getColorDepth()` selects truecolor / 256 / 16 colours;
10
+ * `NO_COLOR` forces monochrome (bold / dim / inverse only) and
11
+ * `FORCE_COLOR` overrides detection, the way chalk reads them. The tokens
12
+ * follow `libs/east-ui/app_design_system`: ink is the default foreground,
13
+ * ink-4 is dim, brand (`#488e97`) marks the selection bar and the active
14
+ * tab, pos / neg / warn / info colour status dots — "status is a dot + word,
15
+ * never a tinted badge".
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ /** The colour capability: 0 monochrome, 1 sixteen colours, 2 256 colours, 3 truecolor. */
20
+ export type ColorLevel = 0 | 1 | 2 | 3;
21
+ /** A stream that may report its colour depth (`process.stdout`). */
22
+ export interface ColorStream {
23
+ isTTY?: boolean | undefined;
24
+ getColorDepth?: ((env?: NodeJS.ProcessEnv) => number) | undefined;
25
+ }
26
+ /**
27
+ * Detects the colour level of `stream` under `env`.
28
+ *
29
+ * @param env - The environment (`NO_COLOR`, `FORCE_COLOR`, `TERM`, `COLORTERM`)
30
+ * @param stream - The output stream
31
+ * @returns The level — `NO_COLOR` (non-empty) wins, then `FORCE_COLOR`
32
+ * (`0`–`3`, or any other value meaning 16 colours), then the stream's
33
+ * `getColorDepth()` (1 → 0, 4 → 1, 8 → 2, 24 → 3), then `COLORTERM` /
34
+ * `TERM` hints, else monochrome
35
+ */
36
+ export declare function detectColorLevel(env: NodeJS.ProcessEnv, stream: ColorStream): ColorLevel;
37
+ /**
38
+ * The semantic colour tokens as Ink colour strings (`#hex`, `ansi256(n)`,
39
+ * or a chalk colour name), or `undefined` when a token falls back to the
40
+ * terminal default in the current level.
41
+ *
42
+ * @property level - The colour level the theme was built for
43
+ * @property ink - Primary text (always the terminal default)
44
+ * @property ink2 - Secondary text
45
+ * @property ink3 - Tertiary text
46
+ * @property ink4 - Muted text (rendered dim)
47
+ * @property brand - Selection bar, active tab, the prompt
48
+ * @property brandTint - The brand's tint, for a filled highlight
49
+ * @property pos - Positive status (complete, up-to-date, connected)
50
+ * @property neg - Negative status (failed, error, offline)
51
+ * @property warn - Warning status (waiting, stale, reconnecting)
52
+ * @property info - Informational status (running, in progress)
53
+ * @property rule - Rules and scrollbar tracks
54
+ */
55
+ export interface Theme {
56
+ level: ColorLevel;
57
+ ink: string | undefined;
58
+ ink2: string | undefined;
59
+ ink3: string | undefined;
60
+ ink4: string | undefined;
61
+ brand: string | undefined;
62
+ brandTint: string | undefined;
63
+ pos: string | undefined;
64
+ neg: string | undefined;
65
+ warn: string | undefined;
66
+ info: string | undefined;
67
+ rule: string | undefined;
68
+ }
69
+ /**
70
+ * Builds the theme for a colour level.
71
+ *
72
+ * @param level - The colour level
73
+ * @returns The theme — truecolor hex at 3, `ansi256(n)` at 2, the sixteen
74
+ * colour names at 1, and every token `undefined` at 0 (bold / dim /
75
+ * inverse carry the meaning)
76
+ */
77
+ export declare function createTheme(level: ColorLevel): Theme;
78
+ /** The tone a status word or dot carries. */
79
+ export type Tone = 'pos' | 'neg' | 'warn' | 'info' | 'muted' | 'brand' | 'plain';
80
+ /**
81
+ * The theme colour of a tone (`undefined` = the terminal default).
82
+ *
83
+ * @param theme - The theme
84
+ * @param tone - The tone
85
+ * @returns The Ink colour string, if any
86
+ */
87
+ export declare function toneColor(theme: Theme, tone: Tone): string | undefined;
88
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/tui/render/theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;GAaG;AAEH,0FAA0F;AAC1F,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEvC,oEAAoE;AACpE,MAAM,WAAW,WAAW;IACxB,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CACrE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CA0BxF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,KAAK;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CA+DpD;AAED,6CAA6C;AAC7C,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjF;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAUtE"}