@aiwayds/dsh-tui-pi 0.1.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +324 -0
  3. package/bin/dsh-tui-pi +5 -0
  4. package/cordis.patch.yml +9 -0
  5. package/lib/append-system.d.ts +66 -0
  6. package/lib/append-system.js +161 -0
  7. package/lib/append-system.js.map +1 -0
  8. package/lib/commands.d.ts +53 -0
  9. package/lib/commands.js +167 -0
  10. package/lib/commands.js.map +1 -0
  11. package/lib/dsh-events.d.ts +106 -0
  12. package/lib/dsh-events.js +30 -0
  13. package/lib/dsh-events.js.map +1 -0
  14. package/lib/editor.d.ts +28 -0
  15. package/lib/editor.js +70 -0
  16. package/lib/editor.js.map +1 -0
  17. package/lib/footer.d.ts +36 -0
  18. package/lib/footer.js +112 -0
  19. package/lib/footer.js.map +1 -0
  20. package/lib/frame.d.ts +35 -0
  21. package/lib/frame.js +75 -0
  22. package/lib/frame.js.map +1 -0
  23. package/lib/git.d.ts +17 -0
  24. package/lib/git.js +51 -0
  25. package/lib/git.js.map +1 -0
  26. package/lib/index.d.ts +15 -0
  27. package/lib/index.js +781 -0
  28. package/lib/index.js.map +1 -0
  29. package/lib/instructions.d.ts +29 -0
  30. package/lib/instructions.js +67 -0
  31. package/lib/instructions.js.map +1 -0
  32. package/lib/live-widgets.d.ts +85 -0
  33. package/lib/live-widgets.js +218 -0
  34. package/lib/live-widgets.js.map +1 -0
  35. package/lib/messages.d.ts +277 -0
  36. package/lib/messages.js +734 -0
  37. package/lib/messages.js.map +1 -0
  38. package/lib/permission.d.ts +27 -0
  39. package/lib/permission.js +48 -0
  40. package/lib/permission.js.map +1 -0
  41. package/lib/provider-catalog.d.ts +114 -0
  42. package/lib/provider-catalog.js +124 -0
  43. package/lib/provider-catalog.js.map +1 -0
  44. package/lib/quotes.d.ts +28 -0
  45. package/lib/quotes.js +144 -0
  46. package/lib/quotes.js.map +1 -0
  47. package/lib/reload.d.ts +23 -0
  48. package/lib/reload.js +171 -0
  49. package/lib/reload.js.map +1 -0
  50. package/lib/selectors.d.ts +48 -0
  51. package/lib/selectors.js +261 -0
  52. package/lib/selectors.js.map +1 -0
  53. package/lib/session.d.ts +157 -0
  54. package/lib/session.js +555 -0
  55. package/lib/session.js.map +1 -0
  56. package/lib/sessions.d.ts +73 -0
  57. package/lib/sessions.js +253 -0
  58. package/lib/sessions.js.map +1 -0
  59. package/lib/settings.d.ts +180 -0
  60. package/lib/settings.js +1328 -0
  61. package/lib/settings.js.map +1 -0
  62. package/lib/text.d.ts +22 -0
  63. package/lib/text.js +45 -0
  64. package/lib/text.js.map +1 -0
  65. package/lib/theme/index.d.ts +79 -0
  66. package/lib/theme/index.js +121 -0
  67. package/lib/theme/index.js.map +1 -0
  68. package/lib/theme/palette.d.ts +56 -0
  69. package/lib/theme/palette.js +154 -0
  70. package/lib/theme/palette.js.map +1 -0
  71. package/lib/theme-settings.d.ts +68 -0
  72. package/lib/theme-settings.js +223 -0
  73. package/lib/theme-settings.js.map +1 -0
  74. package/lib/tui.d.ts +70 -0
  75. package/lib/tui.js +206 -0
  76. package/lib/tui.js.map +1 -0
  77. package/lib/welcome.d.ts +91 -0
  78. package/lib/welcome.js +281 -0
  79. package/lib/welcome.js.map +1 -0
  80. package/package.json +50 -0
  81. package/patches/@earendil-works__pi-tui.patch +72 -0
  82. package/pnpm-workspace.yaml +2 -0
  83. package/templates/APPEND_SYSTEM.md +34 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Startup welcome banner: a pixel-art whale (generated pixel-by-pixel from
3
+ * the user's image) with the "DSH" wordmark to its right, drawn in the same
4
+ * pixel language as the whale. Each letter is a hand-authored 20×20 pixel
5
+ * bitmap (PIXEL_FONT: 20 columns × 20 half-cell rows, '#' a pixel, '.'
6
+ * empty) — an upright glyph sheared into an arcade italic: every 4 pixel
7
+ * rows the stroke shifts 1 pixel right, so the top leans 4 pixels —
8
+ * rendered to the terminal with the whale's own half-block mapping
9
+ * (top and bottom pixel → '█', top only → '▀', bottom only → '▄', neither
10
+ * → ' '), so the letters show the same visible pixel grid: 3-pixel strokes,
11
+ * and D's bowl and S's spine step diagonally in 2-pixel staircase corners
12
+ * instead of meeting at square rectangle corners.
13
+ *
14
+ * The banner is a 94-column × 10-row grid: 28 columns of whale art, a
15
+ * 2-column gap, then the wordmark — D (20) + 2 + S (20) + 2 + H (20) =
16
+ * 64 columns of letters (each letter spans 20 columns — slimmer than the
17
+ * whale's body mass). Below 96 terminal columns the wordmark is
18
+ * dropped — the banner degrades to the 10 whale rows (28 columns each, no
19
+ * gap, no letters), so a narrow terminal never wraps the letter rows; the
20
+ * full banner comes back as soon as the terminal is 96 columns or wider
21
+ * again (the transcript rebuilds on resize). Everything is painted in the
22
+ * whale brand blue. Transparent cells stay unpainted — they show the
23
+ * terminal default background. This is deliberate: the transcript never
24
+ * paints a canvas background (the TUI startup already sets the terminal
25
+ * background to the theme canvas), so a half-block's transparent half must
26
+ * fall through to the terminal default, not to an explicit theme
27
+ * background. Painting it would mismatch on any terminal whose default
28
+ * background differs from the theme canvas.
29
+ */
30
+ /** The whale color — the same brand blue as the powerline 'dsh' segment. */
31
+ export declare const WHALE_COLOR: "#4D6BFE";
32
+ /**
33
+ * Whale pixel art, 28 columns × 10 rows, generated pixel-by-pixel from the
34
+ * user's image (regenerate with `node assets/whale-gen.mjs`; see
35
+ * assets/whale-gen.mjs). Only ' ', '█', '▀' and '▄' appear (see the glyph
36
+ * semantics above). Every row carries at least one whale glyph, so the
37
+ * banner text as a whole is never blank — Text.render only skips an
38
+ * entirely blank text, and individual blank rows would render as empty
39
+ * lines either way.
40
+ */
41
+ export declare const WHALE_ART: readonly string[];
42
+ /** The wordmark: D S H, each letter a 28-column glyph, spaced 2 columns apart. */
43
+ export declare const WORDMARK = "DSH";
44
+ /**
45
+ * Pixel font for the wordmark letters: '#' is a pixel, '.' is empty. Each
46
+ * glyph is a hand-authored 20×20 pixel bitmap: an upright body (16 pixels
47
+ * wide, 17 for D's bowl) with 3-pixel strokes and 2-pixel staircase
48
+ * corners, sheared italic — every 4 pixel rows shift 1 pixel right (the
49
+ * top block leans 4 pixels, ~14°) — so every stroke's edge is a pixel
50
+ * staircase and each letter spans exactly 20 columns, slimmer than the
51
+ * whale's body (21–24). renderPixelGlyph folds every two pixel
52
+ * rows into one terminal row of '█'/'▀'/'▄'/' ' — the exact mapping the
53
+ * whale generator uses — so the letters and the whale share one pixel
54
+ * grid. Strokes render in the whale brand blue; empties fall through to
55
+ * the terminal default background like the whale's spaces.
56
+ */
57
+ export declare const PIXEL_FONT: Record<string, readonly string[]>;
58
+ /**
59
+ * Full banner width: whale + gap + wordmark block — derived from the
60
+ * constants so a layout tweak (gap, glyph widths) cannot silently desync
61
+ * the degradation threshold in `buildWelcomeBanner` from the assembled
62
+ * rows: 28 + 2 + 20 + 2 + 20 + 2 + 20 = 94 columns.
63
+ */
64
+ export declare const WELCOME_FULL_WIDTH: number;
65
+ /**
66
+ * Fold a 20×20 pixel bitmap into its 10 terminal rows with the whale's own
67
+ * half-block mapping: both pixel rows on → '█', top only → '▀', bottom
68
+ * only → '▄', neither → ' '. Two pixels vertically per cell is what makes
69
+ * the pixels square on screen — one column wide, one half-cell tall — the
70
+ * same grid the whale art lives on.
71
+ */
72
+ export declare function renderPixelGlyph(px: readonly string[]): readonly string[];
73
+ /**
74
+ * Assemble the welcome banner text. Every row is the styled whale row, the
75
+ * 2-column gap, then the 88-column pixel-letter row (letters 2 columns
76
+ * apart) — all painted in the whale brand blue. The banner is
77
+ * theme-independent: no theme colors, no bold, nothing to repaint on a
78
+ * theme switch.
79
+ *
80
+ * Width floor: the full banner is 94 columns wide plus the transcript
81
+ * Text's 1-column left/right padding (the renderer passes its Text paddingX
82
+ * — currently 1 — so the threshold is 96). Below it the banner degrades
83
+ * to the whale alone: the 10 art rows, 28 columns each, no gap and no
84
+ * letters, which needs only 30 columns with the padding. A terminal
85
+ * narrower than that wraps the whale rows onto extra lines (accepted
86
+ * degradation, documented rather than clipped, because a terminal that
87
+ * narrow can't show the whale either way). `undefined` (non-TTY contexts,
88
+ * e.g. tests) counts as wide — the full banner, conservative like
89
+ * PANEL_LINE_CAP_FALLBACK.
90
+ */
91
+ export declare function buildWelcomeBanner(columns: number | undefined): string;
package/lib/welcome.js ADDED
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Startup welcome banner: a pixel-art whale (generated pixel-by-pixel from
3
+ * the user's image) with the "DSH" wordmark to its right, drawn in the same
4
+ * pixel language as the whale. Each letter is a hand-authored 20×20 pixel
5
+ * bitmap (PIXEL_FONT: 20 columns × 20 half-cell rows, '#' a pixel, '.'
6
+ * empty) — an upright glyph sheared into an arcade italic: every 4 pixel
7
+ * rows the stroke shifts 1 pixel right, so the top leans 4 pixels —
8
+ * rendered to the terminal with the whale's own half-block mapping
9
+ * (top and bottom pixel → '█', top only → '▀', bottom only → '▄', neither
10
+ * → ' '), so the letters show the same visible pixel grid: 3-pixel strokes,
11
+ * and D's bowl and S's spine step diagonally in 2-pixel staircase corners
12
+ * instead of meeting at square rectangle corners.
13
+ *
14
+ * The banner is a 94-column × 10-row grid: 28 columns of whale art, a
15
+ * 2-column gap, then the wordmark — D (20) + 2 + S (20) + 2 + H (20) =
16
+ * 64 columns of letters (each letter spans 20 columns — slimmer than the
17
+ * whale's body mass). Below 96 terminal columns the wordmark is
18
+ * dropped — the banner degrades to the 10 whale rows (28 columns each, no
19
+ * gap, no letters), so a narrow terminal never wraps the letter rows; the
20
+ * full banner comes back as soon as the terminal is 96 columns or wider
21
+ * again (the transcript rebuilds on resize). Everything is painted in the
22
+ * whale brand blue. Transparent cells stay unpainted — they show the
23
+ * terminal default background. This is deliberate: the transcript never
24
+ * paints a canvas background (the TUI startup already sets the terminal
25
+ * background to the theme canvas), so a half-block's transparent half must
26
+ * fall through to the terminal default, not to an explicit theme
27
+ * background. Painting it would mismatch on any terminal whose default
28
+ * background differs from the theme canvas.
29
+ */
30
+ import { ansiFg, POWERLINE, RESET } from "./theme/index.js";
31
+ /** The whale color — the same brand blue as the powerline 'dsh' segment. */
32
+ export const WHALE_COLOR = POWERLINE.brand;
33
+ /**
34
+ * Whale pixel art, 28 columns × 10 rows, generated pixel-by-pixel from the
35
+ * user's image (regenerate with `node assets/whale-gen.mjs`; see
36
+ * assets/whale-gen.mjs). Only ' ', '█', '▀' and '▄' appear (see the glyph
37
+ * semantics above). Every row carries at least one whale glyph, so the
38
+ * banner text as a whole is never blank — Text.render only skips an
39
+ * entirely blank text, and individual blank rows would render as empty
40
+ * lines either way.
41
+ */
42
+ export const WHALE_ART = [
43
+ ' ▄▄▄▄ █ ',
44
+ ' ▄██████████▄ ████ ▄▄▄█▀',
45
+ ' ▄██████████████▄ ████████ ',
46
+ ' ██▀▀█████████████▄ ▄██▀▀ ',
47
+ '██ ▀▀█████▄▀██████ ',
48
+ '▀██ ▀████▄ ████▀ ',
49
+ ' ███ ▀████████▀ ',
50
+ ' ▀██▄ █▄▄ ▀█████▀ ',
51
+ ' ▀▀███▄▄███▄▄▄█████▄ ',
52
+ ' ▀▀▀████▀▀▀ ',
53
+ ];
54
+ /** Blank columns between the whale and the wordmark column. */
55
+ const WHALE_TITLE_GAP = 2;
56
+ /** Blank columns between the wordmark's letters. */
57
+ const LETTER_GAP = 2;
58
+ /** The wordmark: D S H, each letter a 28-column glyph, spaced 2 columns apart. */
59
+ export const WORDMARK = 'DSH';
60
+ /** Whale art width in columns (every row is this wide). */
61
+ const WHALE_ART_WIDTH = Math.max(...WHALE_ART.map(row => row.length));
62
+ /**
63
+ * Pixel font for the wordmark letters: '#' is a pixel, '.' is empty. Each
64
+ * glyph is a hand-authored 20×20 pixel bitmap: an upright body (16 pixels
65
+ * wide, 17 for D's bowl) with 3-pixel strokes and 2-pixel staircase
66
+ * corners, sheared italic — every 4 pixel rows shift 1 pixel right (the
67
+ * top block leans 4 pixels, ~14°) — so every stroke's edge is a pixel
68
+ * staircase and each letter spans exactly 20 columns, slimmer than the
69
+ * whale's body (21–24). renderPixelGlyph folds every two pixel
70
+ * rows into one terminal row of '█'/'▀'/'▄'/' ' — the exact mapping the
71
+ * whale generator uses — so the letters and the whale share one pixel
72
+ * grid. Strokes render in the whale brand blue; empties fall through to
73
+ * the terminal default background like the whale's spaces.
74
+ */
75
+ export const PIXEL_FONT = {
76
+ D: [
77
+ '....#############...',
78
+ '....#############...',
79
+ '....#############...',
80
+ '....###.........###.',
81
+ '...###.........###..',
82
+ '...###...........###',
83
+ '...###...........###',
84
+ '...###...........###',
85
+ '..###...........###.',
86
+ '..###...........###.',
87
+ '..###...........###.',
88
+ '..###...........###.',
89
+ '.###...........###..',
90
+ '.###...........###..',
91
+ '.###...........###..',
92
+ '.###.........###....',
93
+ '###.........###.....',
94
+ '#############.......',
95
+ '#############.......',
96
+ '#############.......',
97
+ ],
98
+ S: [
99
+ '.......#############',
100
+ '.......#############',
101
+ '.......#############',
102
+ '....###.............',
103
+ '...###..............',
104
+ '...###..............',
105
+ '...###..............',
106
+ '...#############....',
107
+ '..#############.....',
108
+ '..#############.....',
109
+ '..#############.....',
110
+ '...............###..',
111
+ '..............###...',
112
+ '..............###...',
113
+ '..............###...',
114
+ '..............###...',
115
+ '.............###....',
116
+ '############........',
117
+ '############........',
118
+ '############........',
119
+ ],
120
+ H: [
121
+ '....###..........###',
122
+ '....###..........###',
123
+ '....###..........###',
124
+ '....###..........###',
125
+ '...###..........###.',
126
+ '...###..........###.',
127
+ '...###..........###.',
128
+ '...###..........###.',
129
+ '..################..',
130
+ '..################..',
131
+ '..################..',
132
+ '..################..',
133
+ '.###..........###...',
134
+ '.###..........###...',
135
+ '.###..........###...',
136
+ '.###..........###...',
137
+ '###..........###....',
138
+ '###..........###....',
139
+ '###..........###....',
140
+ '###..........###....',
141
+ ],
142
+ };
143
+ // Load-time validation: a wordmark letter without a font glyph, or a glyph
144
+ // that cannot fold into the whale's 10 terminal rows, is a programming
145
+ // error — fail at module load, not at some later resize when the full
146
+ // banner is first assembled (a narrow terminal would defer the crash into
147
+ // the resize handler).
148
+ for (const ch of WORDMARK) {
149
+ if (ch !== ' ' && PIXEL_FONT[ch] === undefined) {
150
+ throw new Error(`PIXEL_FONT lacks glyph for '${ch}' in WORDMARK`);
151
+ }
152
+ }
153
+ for (const [ch, glyph] of Object.entries(PIXEL_FONT)) {
154
+ if (glyph.length !== 2 * WHALE_ART.length) {
155
+ throw new Error(`PIXEL_FONT['${ch}'] has ${glyph.length} pixel rows — need ${2 * WHALE_ART.length} (two per banner row)`);
156
+ }
157
+ const widths = [...new Set(glyph.map(row => row.length))];
158
+ if (widths.length !== 1) {
159
+ throw new Error(`PIXEL_FONT['${ch}'] has ragged pixel rows: widths ${widths.join(', ')}`);
160
+ }
161
+ }
162
+ /**
163
+ * The wordmark letters' total width in columns — the glyphs' widths
164
+ * summed: 20 (D) + 20 (S) + 20 (H) = 60. The 2-column gaps between
165
+ * letters are added separately in WELCOME_FULL_WIDTH (2 gaps × 2 columns
166
+ * → the wordmark block spans 64).
167
+ */
168
+ const WORDMARK_WIDTH = [...WORDMARK].reduce((width, ch) => width + (ch === ' ' ? 1 : PIXEL_FONT[ch][0].length), 0);
169
+ /**
170
+ * Full banner width: whale + gap + wordmark block — derived from the
171
+ * constants so a layout tweak (gap, glyph widths) cannot silently desync
172
+ * the degradation threshold in `buildWelcomeBanner` from the assembled
173
+ * rows: 28 + 2 + 20 + 2 + 20 + 2 + 20 = 94 columns.
174
+ */
175
+ export const WELCOME_FULL_WIDTH = WHALE_ART_WIDTH + WHALE_TITLE_GAP + WORDMARK_WIDTH + LETTER_GAP * (WORDMARK.length - 1);
176
+ /**
177
+ * Fold a 20×20 pixel bitmap into its 10 terminal rows with the whale's own
178
+ * half-block mapping: both pixel rows on → '█', top only → '▀', bottom
179
+ * only → '▄', neither → ' '. Two pixels vertically per cell is what makes
180
+ * the pixels square on screen — one column wide, one half-cell tall — the
181
+ * same grid the whale art lives on.
182
+ */
183
+ export function renderPixelGlyph(px) {
184
+ const rows = [];
185
+ for (let i = 0; i < px.length / 2; i++) {
186
+ let row = '';
187
+ for (let c = 0; c < px[0].length; c++) {
188
+ const top = px[2 * i][c] === '#';
189
+ const bottom = px[2 * i + 1][c] === '#';
190
+ row += top && bottom ? '█' : top ? '▀' : bottom ? '▄' : ' ';
191
+ }
192
+ rows.push(row);
193
+ }
194
+ return rows;
195
+ }
196
+ /**
197
+ * Lay out the wordmark's 10 letter rows, 64 columns each: the D, S and H
198
+ * glyphs with a 2-column gap between letters (20 + 2 + 20 + 2 + 20
199
+ * columns). The pixel bitmaps are folded by renderPixelGlyph into the same
200
+ * '█'/'▀'/'▄'/' ' glyphs the whale uses, so the rows paint exactly like
201
+ * whale rows (see the header note). Wordmark glyph coverage and shape are
202
+ * validated at module load.
203
+ */
204
+ function wordmarkRows() {
205
+ const glyphRows = [...WORDMARK].map(ch => renderPixelGlyph(PIXEL_FONT[ch]));
206
+ const rows = Array.from({ length: WHALE_ART.length }, () => '');
207
+ for (let i = 0; i < glyphRows.length; i++) {
208
+ for (let r = 0; r < rows.length; r++) {
209
+ rows[r] += glyphRows[i][r];
210
+ if (i < glyphRows.length - 1)
211
+ rows[r] += ' '.repeat(LETTER_GAP);
212
+ }
213
+ }
214
+ return rows;
215
+ }
216
+ /**
217
+ * Paint one consecutive run of brand-blue glyphs as a single SGR span,
218
+ * RESET at the end. Runs are painted in the whale blue only — half-block
219
+ * glyphs ('▀'/'▄') are never given a background, so their transparent half
220
+ * shows the terminal default background (see the header note).
221
+ */
222
+ function paintWhaleRun(run) {
223
+ return ansiFg(WHALE_COLOR) + run + RESET;
224
+ }
225
+ /**
226
+ * Paint one row's consecutive runs of non-space glyphs as whale-blue spans,
227
+ * RESET after each. The spaces between runs stay unstyled (transparent,
228
+ * terminal background). Used for whale rows and the wordmark's letter rows
229
+ * alike.
230
+ */
231
+ function paintRowRuns(row) {
232
+ let styled = '';
233
+ let run = '';
234
+ for (const ch of row) {
235
+ if (ch === ' ') {
236
+ if (run !== '') {
237
+ styled += paintWhaleRun(run);
238
+ run = '';
239
+ }
240
+ styled += ' ';
241
+ }
242
+ else {
243
+ run += ch;
244
+ }
245
+ }
246
+ if (run !== '')
247
+ styled += paintWhaleRun(run);
248
+ return styled;
249
+ }
250
+ /**
251
+ * Assemble the welcome banner text. Every row is the styled whale row, the
252
+ * 2-column gap, then the 88-column pixel-letter row (letters 2 columns
253
+ * apart) — all painted in the whale brand blue. The banner is
254
+ * theme-independent: no theme colors, no bold, nothing to repaint on a
255
+ * theme switch.
256
+ *
257
+ * Width floor: the full banner is 94 columns wide plus the transcript
258
+ * Text's 1-column left/right padding (the renderer passes its Text paddingX
259
+ * — currently 1 — so the threshold is 96). Below it the banner degrades
260
+ * to the whale alone: the 10 art rows, 28 columns each, no gap and no
261
+ * letters, which needs only 30 columns with the padding. A terminal
262
+ * narrower than that wraps the whale rows onto extra lines (accepted
263
+ * degradation, documented rather than clipped, because a terminal that
264
+ * narrow can't show the whale either way). `undefined` (non-TTY contexts,
265
+ * e.g. tests) counts as wide — the full banner, conservative like
266
+ * PANEL_LINE_CAP_FALLBACK.
267
+ */
268
+ export function buildWelcomeBanner(columns) {
269
+ // 2 = the transcript Text's paddingX on each side (messages.ts renders the
270
+ // banner with `new Text(..., 1, 0)`).
271
+ if (columns !== undefined && columns < WELCOME_FULL_WIDTH + 2) {
272
+ // Narrow terminal: whale-only, the 10 art rows (28 columns each) — no
273
+ // gap, no letters. Wraps only below the 30-column floor.
274
+ return WHALE_ART.map(row => paintRowRuns(row)).join('\n');
275
+ }
276
+ const letters = wordmarkRows();
277
+ return WHALE_ART.map((row, r) => {
278
+ return paintRowRuns(row) + ' '.repeat(WHALE_TITLE_GAP + WHALE_ART_WIDTH - row.length) + paintRowRuns(letters[r]);
279
+ }).join('\n');
280
+ }
281
+ //# sourceMappingURL=welcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../src/welcome.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@aiwayds/dsh-tui-pi",
3
+ "version": "0.1.0",
4
+ "description": "pi-style terminal UI for DeepSeek Harness (dsh) — pi-tui look & feel, dsh slash commands, GitHub light/dark themes, powerline footer",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "bin": {
9
+ "dsh-tui-pi": "bin/dsh-tui-pi"
10
+ },
11
+ "license": "MIT",
12
+ "engines": {
13
+ "node": ">=22.19.0"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "bin",
18
+ "cordis.patch.yml",
19
+ "patches",
20
+ "pnpm-workspace.yaml",
21
+ "templates",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "check": "tsc --noEmit -p tsconfig.json",
28
+ "pretest": "npm run build",
29
+ "test": "node --test test/*.test.mjs"
30
+ },
31
+ "dependencies": {
32
+ "@deepseek-ai/dsh-client-schema-form": "0.1.0-rc.6",
33
+ "@earendil-works/pi-tui": "0.84.2"
34
+ },
35
+ "devDependencies": {
36
+ "@deepseek-ai/dsh-settings": "0.1.0-rc.6",
37
+ "@deepseek-ai/schemastery": "3.18.1",
38
+ "@types/node": "^24.0.0",
39
+ "typescript": "~5.9.0"
40
+ },
41
+ "dsh": {
42
+ "bundle": {
43
+ "patch": "./cordis.patch.yml"
44
+ }
45
+ },
46
+ "author": "fan56",
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }
@@ -0,0 +1,72 @@
1
+ diff --git a/dist/components/editor.js b/dist/components/editor.js
2
+ index 4786b46932b65785cfbd2304610ca77ccead910a..e0e4e39c3be8e5d4d828621ad2d3d8768984add8 100644
3
+ --- a/dist/components/editor.js
4
+ +++ b/dist/components/editor.js
5
+ @@ -463,14 +463,20 @@ export class Editor {
6
+ else {
7
+ result.push(horizontal.repeat(width));
8
+ }
9
+ - // Add autocomplete list if active
10
+ + // Add autocomplete list if active — inside a full box (┌─┐ rows)
11
+ if (this.autocompleteState && this.autocompleteList) {
12
+ - const autocompleteResult = this.autocompleteList.render(contentWidth);
13
+ + const boxInnerWidth = Math.max(1, contentWidth - 2);
14
+ + const autocompleteResult = this.autocompleteList.render(boxInnerWidth);
15
+ + result.push(`${leftPadding}${this.borderColor(`┌${"─".repeat(boxInnerWidth)}┐`)}${rightPadding}`);
16
+ for (const line of autocompleteResult) {
17
+ const lineWidth = visibleWidth(line);
18
+ - const linePadding = " ".repeat(Math.max(0, contentWidth - lineWidth));
19
+ - result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);
20
+ + const linePadding = " ".repeat(Math.max(0, boxInnerWidth - lineWidth));
21
+ + // Strip trailing RESETs so the row's own backdrop SGR bleeds
22
+ + // into the padding and the right side border (solid interior);
23
+ + // the reset before rightPadding keeps the editor margin clear.
24
+ + result.push(`${leftPadding}│${line.replace(/(?:\x1b\[0m)+$/, "")}${linePadding}│\x1b[0m${rightPadding}`);
25
+ }
26
+ + result.push(`${leftPadding}${this.borderColor(`└${"─".repeat(boxInnerWidth)}┘`)}${rightPadding}`);
27
+ }
28
+ return result;
29
+ }
30
+ diff --git a/dist/components/select-list.d.ts b/dist/components/select-list.d.ts
31
+ index d507de4ebff8e7fbaacd089677099c63b4dfff6c..4b8a374cd1db0544a11082f8008f73f8eb8d7ede 100644
32
+ --- a/dist/components/select-list.d.ts
33
+ +++ b/dist/components/select-list.d.ts
34
+ @@ -10,6 +10,8 @@ export interface SelectListTheme {
35
+ description: (text: string) => string;
36
+ scrollInfo: (text: string) => string;
37
+ noMatch: (text: string) => string;
38
+ + /** Style hook for unselected rows (plain rows render without any theme call upstream). */
39
+ + unselectedText?: (text: string) => string;
40
+ }
41
+ export interface SelectListTruncatePrimaryContext {
42
+ text: string;
43
+ diff --git a/dist/components/select-list.js b/dist/components/select-list.js
44
+ index 2e9231150951c3d3f3e13ac8ae8d623f06d38d5d..d5c8859ecd08fe4eccdc3cfca8ac7a3226f8fcb5 100644
45
+ --- a/dist/components/select-list.js
46
+ +++ b/dist/components/select-list.js
47
+ @@ -101,18 +101,21 @@ export class SelectList {
48
+ if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
49
+ const truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, "");
50
+ if (isSelected) {
51
+ - return this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`);
52
+ + // selectedPrefix is otherwise dead in renderItem: style the
53
+ + // arrow accent, then the row text (concatenated, not nested —
54
+ + // nesting would let the prefix's RESETs clear the row style).
55
+ + return this.theme.selectedPrefix("") + this.theme.selectedText(`${truncatedValue}${spacing}${truncatedDesc}`);
56
+ }
57
+ const descText = this.theme.description(spacing + truncatedDesc);
58
+ - return prefix + truncatedValue + descText;
59
+ + return (this.theme.unselectedText ?? ((t) => t))(prefix + truncatedValue) + descText;
60
+ }
61
+ }
62
+ const maxWidth = width - prefixWidth - 2;
63
+ const truncatedValue = this.truncatePrimary(item, isSelected, maxWidth, maxWidth);
64
+ if (isSelected) {
65
+ - return this.theme.selectedText(`${prefix}${truncatedValue}`);
66
+ + return this.theme.selectedPrefix("") + this.theme.selectedText(`${truncatedValue}`);
67
+ }
68
+ - return prefix + truncatedValue;
69
+ + return (this.theme.unselectedText ?? ((t) => t))(prefix + truncatedValue);
70
+ }
71
+ getPrimaryColumnWidth() {
72
+ const { min, max } = this.getPrimaryColumnBounds();
@@ -0,0 +1,2 @@
1
+ patchedDependencies:
2
+ '@earendil-works/pi-tui': patches/@earendil-works__pi-tui.patch
@@ -0,0 +1,34 @@
1
+ # APPEND_SYSTEM.md — Orchestrator identity
2
+
3
+ ## Identity: I am an orchestrator, not an executor
4
+ The user defines my core identity as an **orchestrator**: I analyze tasks,
5
+ break them into plans, dispatch sub-agents and consolidate results — I do
6
+ not perform concrete operations myself.
7
+
8
+ ## Core rules
9
+
10
+ 1. All research work must be done through sub-agents.
11
+ 2. Forbidden:
12
+ - Running tests sequentially in the conversation, silently reading files,
13
+ or analyzing code on my own.
14
+ - Using execution tools directly (read/write/search/bash/edit), unless a
15
+ sub-agent cannot do the job.
16
+ - Suggesting the user do things manually.
17
+ 3. Tasks must pass through verify/review agents. Without review, a task must
18
+ not be marked "completed".
19
+ 4. Never assume "the problem is known".
20
+ 5. Never guess — every claim must be backed by evidence.
21
+
22
+ ## Execution workflow
23
+
24
+ 1. **Analyze**: understand the request, identify the task boundaries.
25
+ 2. **Create todos**: break the task into executable subtasks.
26
+ 3. **Dispatch sub-agents**: dispatch sub-agents in parallel.
27
+ 4. **Verify**: check the results returned by the sub-agents.
28
+ 5. **Consolidate**: integrate the results and report to the user.
29
+
30
+ ---
31
+
32
+ > **Remember**: my value lies in orchestration and decisions, not execution.
33
+ > Let sub-agents do the concrete work; I focus on analysis, planning and
34
+ > quality control.