@cortexkit/aft-opencode 0.64.0 → 0.65.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 (37) hide show
  1. package/dist/bg-notifications.d.ts.map +1 -1
  2. package/dist/config-live-reload.d.ts +2 -14
  3. package/dist/config-live-reload.d.ts.map +1 -1
  4. package/dist/config.d.ts +4 -16
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/entry/server.js +1271 -583
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1271 -583
  9. package/dist/shared/session-directory.d.ts +7 -2
  10. package/dist/shared/session-directory.d.ts.map +1 -1
  11. package/dist/shared/status.d.ts +2 -2
  12. package/dist/shared/status.d.ts.map +1 -1
  13. package/dist/tool-registration.d.ts +1 -1
  14. package/dist/tool-registration.d.ts.map +1 -1
  15. package/dist/tools/bash.d.ts.map +1 -1
  16. package/dist/tools/bash_watch.d.ts.map +1 -1
  17. package/dist/tools/definitions/v2.d.ts +1 -1
  18. package/dist/tools/definitions/v2.d.ts.map +1 -1
  19. package/dist/tui/shared-sidebar.d.ts +4 -0
  20. package/dist/tui/shared-sidebar.d.ts.map +1 -0
  21. package/dist/v2-session-lifecycle.d.ts.map +1 -1
  22. package/dist/workflow-hints.d.ts +1 -5
  23. package/dist/workflow-hints.d.ts.map +1 -1
  24. package/package.json +14 -12
  25. package/src/shared/session-directory.ts +80 -12
  26. package/src/shared/status.ts +4 -4
  27. package/src/tui/index.tsx +7 -3
  28. package/src/tui/shared-sidebar.ts +14 -0
  29. package/src/tui/sidebar-view.tsx +256 -245
  30. package/src/tui-compiled/index.tsx +4 -3
  31. package/src/tui-compiled/shared-sidebar-host.tsx +816 -0
  32. package/src/tui-compiled/shared-sidebar-local.tsx +816 -0
  33. package/src/tui-compiled/shared-sidebar.LICENSE +21 -0
  34. package/src/tui-compiled/shared-sidebar.ts +14 -0
  35. package/src/tui-compiled/sidebar-view.tsx +183 -221
  36. package/src/tui-compiled/sidebar.tsx +1 -0
  37. package/src/tui-compiled/v2.tsx +1 -0
@@ -0,0 +1,816 @@
1
+ // @ts-nocheck
2
+ import { createComponent as _$createComponent } from "opentui:runtime-module:%40opentui%2Fsolid";
3
+ import { createTextNode as _$createTextNode } from "opentui:runtime-module:%40opentui%2Fsolid";
4
+ import { use as _$use } from "opentui:runtime-module:%40opentui%2Fsolid";
5
+ import { memo as _$memo } from "opentui:runtime-module:%40opentui%2Fsolid";
6
+ import { effect as _$effect } from "opentui:runtime-module:%40opentui%2Fsolid";
7
+ import { insertNode as _$insertNode } from "opentui:runtime-module:%40opentui%2Fsolid";
8
+ import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid";
9
+ import { setProp as _$setProp } from "opentui:runtime-module:%40opentui%2Fsolid";
10
+ import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid";
11
+ import { createContext, createMemo, createSignal, For, Index, mergeProps, Show, useContext } from "opentui:runtime-module:solid-js";
12
+
13
+ /** `background` is the badge's text colour when drawn on `accent`; nothing else uses it. */
14
+
15
+ const FrameContext = createContext();
16
+ function frameContext() {
17
+ const context = useContext(FrameContext);
18
+ if (!context) throw new Error('Sidebar primitives must be inside SidebarFrame');
19
+ return context;
20
+ }
21
+ function displayWidth(text) {
22
+ const bun = globalThis.Bun;
23
+ return bun?.stringWidth ? bun.stringWidth(text) : Array.from(text).length;
24
+ }
25
+ function fitText(text, columns) {
26
+ if (displayWidth(text) <= columns) return text;
27
+ if (columns < 1) return '';
28
+ let kept = '';
29
+ let used = 0;
30
+ for (const char of text) {
31
+ const width = displayWidth(char);
32
+ if (used + width > columns - 1) break;
33
+ kept += char;
34
+ used += width;
35
+ }
36
+ return `${kept}\u2026`;
37
+ }
38
+
39
+ /** A heading and its plugin-owned rows, with the frame's section spacing. */
40
+ export function SidebarSection(props) {
41
+ const context = frameContext();
42
+ return [(() => {
43
+ var _el$ = _$createElement("box"),
44
+ _el$2 = _$createElement("text"),
45
+ _el$3 = _$createElement("b");
46
+ _$insertNode(_el$, _el$2);
47
+ _$setProp(_el$, "width", '100%');
48
+ _$setProp(_el$, "marginTop", 1);
49
+ _$insertNode(_el$2, _el$3);
50
+ _$insert(_el$3, () => props.title);
51
+ _$effect(_$p => _$setProp(_el$2, "fg", context.theme.text, _$p));
52
+ return _el$;
53
+ })(), _$memo(() => props.children)];
54
+ }
55
+
56
+ /** Keep labels whole and reserve one blank cell before an ellipsized value. */
57
+ export function SidebarStatRow(props) {
58
+ const context = frameContext();
59
+ const [width, setWidth] = createSignal(0);
60
+ let row;
61
+ const value = () => width() <= 0 ? props.value : fitText(props.value, width() - displayWidth(props.label) - 1);
62
+ return (() => {
63
+ var _el$4 = _$createElement("box"),
64
+ _el$5 = _$createElement("text"),
65
+ _el$6 = _$createElement("text"),
66
+ _el$7 = _$createElement("b");
67
+ _$insertNode(_el$4, _el$5);
68
+ _$insertNode(_el$4, _el$6);
69
+ _$use(element => {
70
+ row = element;
71
+ }, _el$4);
72
+ _$setProp(_el$4, "onSizeChange", () => setWidth(row?.width ?? 0));
73
+ _$setProp(_el$4, "width", '100%');
74
+ _$setProp(_el$4, "flexDirection", 'row');
75
+ _$setProp(_el$4, "justifyContent", 'space-between');
76
+ _$setProp(_el$4, "columnGap", 1);
77
+ _$setProp(_el$5, "wrapMode", 'none');
78
+ _$setProp(_el$5, "flexShrink", 0);
79
+ _$insert(_el$5, () => props.label);
80
+ _$insertNode(_el$6, _el$7);
81
+ _$setProp(_el$6, "wrapMode", 'none');
82
+ _$setProp(_el$6, "flexShrink", 1);
83
+ _$insert(_el$7, value);
84
+ _$effect(_p$ => {
85
+ var _v$ = context.theme.textMuted,
86
+ _v$2 = context.theme[props.tone ?? 'text'];
87
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$5, "fg", _v$, _p$.e));
88
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$6, "fg", _v$2, _p$.t));
89
+ return _p$;
90
+ }, {
91
+ e: undefined,
92
+ t: undefined
93
+ });
94
+ return _el$4;
95
+ })();
96
+ }
97
+ export function SidebarTextRow(props) {
98
+ const context = frameContext();
99
+ return (() => {
100
+ var _el$8 = _$createElement("text");
101
+ _$insert(_el$8, () => props.text);
102
+ _$effect(_p$ => {
103
+ var _v$3 = context.theme[props.tone ?? 'textMuted'],
104
+ _v$4 = props.wrapMode;
105
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$8, "fg", _v$3, _p$.e));
106
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$8, "wrapMode", _v$4, _p$.t));
107
+ return _p$;
108
+ }, {
109
+ e: undefined,
110
+ t: undefined
111
+ });
112
+ return _el$8;
113
+ })();
114
+ }
115
+ function usageTone(used, appearance) {
116
+ if (used >= appearance.errorThreshold) return 'error';
117
+ if (used >= appearance.warnThreshold) return 'warning';
118
+ return 'success';
119
+ }
120
+ function barSegments(used, appearance, pace) {
121
+ const width = appearance.barWidth;
122
+ const cells = pct => Math.max(0, Math.min(Math.round(pct / 100 * width), width));
123
+ const usedCells = cells(used);
124
+ const tone = usageTone(used, appearance);
125
+ // Keep the overlay's slot even when empty so live pacing changes reuse nodes.
126
+ const plain = [{
127
+ text: appearance.barFilledChar.repeat(usedCells),
128
+ tone
129
+ }, {
130
+ text: '',
131
+ tone
132
+ }, {
133
+ text: appearance.barEmptyChar.repeat(width - usedCells),
134
+ tone
135
+ }];
136
+ const overlay = pace?.overlay;
137
+ if (!overlay) return plain;
138
+ const paceCells = cells(overlay.percent);
139
+ const lo = Math.min(usedCells, paceCells);
140
+ const hi = Math.max(usedCells, paceCells);
141
+ if (lo === hi) return plain;
142
+ return [{
143
+ text: appearance.barFilledChar.repeat(lo),
144
+ tone
145
+ }, {
146
+ text: (overlay.kind === 'deficit' ? '\u2593' : '\u2592').repeat(hi - lo),
147
+ tone: overlay.kind === 'deficit' ? 'error' : 'success'
148
+ }, {
149
+ text: appearance.barEmptyChar.repeat(width - hi),
150
+ tone
151
+ }];
152
+ }
153
+ export function SidebarGaugeRow(props) {
154
+ const context = frameContext();
155
+ const [columns, setColumns] = createSignal(0);
156
+ let row;
157
+ const known = () => props.used !== undefined && Number.isFinite(props.used);
158
+ const used = () => Math.max(0, Math.min(100, props.used ?? 0));
159
+ const figure = () => known() ? `${Math.round(100 - used())}% left` : 'no reading';
160
+ const appearance = () => ({
161
+ ...context.appearance,
162
+ warnThreshold: props.warnAt ?? context.appearance.warnThreshold,
163
+ errorThreshold: props.errorAt ?? context.appearance.errorThreshold
164
+ });
165
+ const label = () => columns() <= 0 ? props.label : fitText(props.label, columns() - displayWidth(figure()) - 1);
166
+ const barCells = () => {
167
+ if (!known()) return 0;
168
+ const desired = context.appearance.barWidth;
169
+ if (columns() <= 0) return desired;
170
+ const room = columns() - displayWidth(label()) - 1 - displayWidth(figure()) - 1;
171
+ return room >= Math.min(4, desired) ? Math.min(desired, room) : 0;
172
+ };
173
+ const segments = createMemo(() => barSegments(used(), {
174
+ ...appearance(),
175
+ barWidth: barCells()
176
+ }), undefined, {
177
+ equals: (previous, next) => previous.every((segment, index) => segment.text === next[index]?.text && segment.tone === next[index]?.tone)
178
+ });
179
+ const value = () => {
180
+ if (!props.value) return '';
181
+ if (columns() <= 0) return props.value;
182
+ const room = columns() - displayWidth(label()) - 1 - displayWidth(figure()) - (barCells() > 0 ? barCells() + 1 : 0) - 1;
183
+ return room >= 2 ? fitText(props.value, room) : '';
184
+ };
185
+ const tone = () => known() ? usageTone(used(), appearance()) : 'textMuted';
186
+ return (() => {
187
+ var _el$9 = _$createElement("box"),
188
+ _el$0 = _$createElement("box"),
189
+ _el$1 = _$createElement("text"),
190
+ _el$10 = _$createTextNode(` `),
191
+ _el$13 = _$createElement("text");
192
+ _$insertNode(_el$9, _el$0);
193
+ _$use(element => {
194
+ row = element;
195
+ }, _el$9);
196
+ _$setProp(_el$9, "onSizeChange", () => setColumns(row?.width ?? 0));
197
+ _$setProp(_el$9, "width", '100%');
198
+ _$setProp(_el$9, "flexDirection", 'row');
199
+ _$setProp(_el$9, "justifyContent", 'space-between');
200
+ _$insertNode(_el$0, _el$1);
201
+ _$insertNode(_el$0, _el$13);
202
+ _$setProp(_el$0, "flexDirection", 'row');
203
+ _$setProp(_el$0, "flexShrink", 0);
204
+ _$insertNode(_el$1, _el$10);
205
+ _$setProp(_el$1, "wrapMode", 'none');
206
+ _$insert(_el$1, label, _el$10);
207
+ _$insert(_el$0, _$createComponent(Show, {
208
+ get when() {
209
+ return barCells() > 0;
210
+ },
211
+ get children() {
212
+ return [_$createComponent(Index, {
213
+ get each() {
214
+ return segments();
215
+ },
216
+ children: segment => (() => {
217
+ var _el$16 = _$createElement("text");
218
+ _$setProp(_el$16, "wrapMode", 'none');
219
+ _$insert(_el$16, () => segment().text);
220
+ _$effect(_p$ => {
221
+ var _v$7 = context.theme[segment().tone],
222
+ _v$8 = displayWidth(segment().text);
223
+ _v$7 !== _p$.e && (_p$.e = _$setProp(_el$16, "fg", _v$7, _p$.e));
224
+ _v$8 !== _p$.t && (_p$.t = _$setProp(_el$16, "width", _v$8, _p$.t));
225
+ return _p$;
226
+ }, {
227
+ e: undefined,
228
+ t: undefined
229
+ });
230
+ return _el$16;
231
+ })()
232
+ }), (() => {
233
+ var _el$11 = _$createElement("text");
234
+ _$insertNode(_el$11, _$createTextNode(` `));
235
+ _$setProp(_el$11, "wrapMode", 'none');
236
+ return _el$11;
237
+ })()];
238
+ }
239
+ }), _el$13);
240
+ _$setProp(_el$13, "wrapMode", 'none');
241
+ _$insert(_el$13, figure);
242
+ _$insert(_el$9, _$createComponent(Show, {
243
+ get when() {
244
+ return value();
245
+ },
246
+ get children() {
247
+ var _el$14 = _$createElement("text"),
248
+ _el$15 = _$createElement("b");
249
+ _$insertNode(_el$14, _el$15);
250
+ _$setProp(_el$14, "wrapMode", 'none');
251
+ _$insert(_el$15, value);
252
+ _$effect(_$p => _$setProp(_el$14, "fg", context.theme.text, _$p));
253
+ return _el$14;
254
+ }
255
+ }), null);
256
+ _$effect(_p$ => {
257
+ var _v$5 = context.theme.textMuted,
258
+ _v$6 = context.theme[tone()];
259
+ _v$5 !== _p$.e && (_p$.e = _$setProp(_el$1, "fg", _v$5, _p$.e));
260
+ _v$6 !== _p$.t && (_p$.t = _$setProp(_el$13, "fg", _v$6, _p$.t));
261
+ return _p$;
262
+ }, {
263
+ e: undefined,
264
+ t: undefined
265
+ });
266
+ return _el$9;
267
+ })();
268
+ }
269
+ function QuotaRow(props) {
270
+ const context = frameContext();
271
+ const [columns, setColumns] = createSignal(0);
272
+ let row;
273
+ const label = () => props.part.name + ' '.repeat(Math.max(0, props.labelWidth - displayWidth(props.part.name)));
274
+ const figure = () => ` ${props.part.kind === 'budget' && props.part.figure.startsWith(`${props.part.name} `) ? props.part.figure.slice(props.part.name.length + 1) : props.part.figure}`;
275
+ const hasReading = () => props.part.reading === 'known';
276
+ const used = () => 100 - (props.part.remainingPercent ?? 100);
277
+ // Size notifications can recompute an unchanged fit after text has been flushed
278
+ // for the frame. Keep equal segments, and update fixed slots in place when the
279
+ // bar changes, rather than replacing text nodes during layout.
280
+ const segments = createMemo(() => hasReading() ? barSegments(used(), {
281
+ ...context.appearance,
282
+ barWidth: props.barCells
283
+ }, props.pace) : [], undefined, {
284
+ equals: (previous, next) => previous.length === next.length && previous.every((segment, index) => segment.text === next[index]?.text && segment.tone === next[index]?.tone)
285
+ });
286
+ const paceLine = () => {
287
+ const text = props.pace?.text;
288
+ if (!text || columns() <= 0) return text;
289
+ return fitText(text, columns() - props.labelWidth);
290
+ };
291
+ return [(() => {
292
+ var _el$17 = _$createElement("box"),
293
+ _el$18 = _$createElement("box");
294
+ _$insertNode(_el$17, _el$18);
295
+ _$use(element => {
296
+ row = element;
297
+ }, _el$17);
298
+ _$setProp(_el$17, "onSizeChange", () => setColumns(row?.width ?? 0));
299
+ _$setProp(_el$17, "width", '100%');
300
+ _$setProp(_el$17, "flexDirection", 'row');
301
+ _$setProp(_el$17, "justifyContent", 'space-between');
302
+ _$setProp(_el$18, "flexDirection", 'row');
303
+ _$insert(_el$18, _$createComponent(Show, {
304
+ get when() {
305
+ return props.labelWidth > 0;
306
+ },
307
+ get children() {
308
+ var _el$19 = _$createElement("text");
309
+ _$setProp(_el$19, "wrapMode", 'none');
310
+ _$setProp(_el$19, "flexShrink", 0);
311
+ _$insert(_el$19, label);
312
+ _$effect(_$p => _$setProp(_el$19, "fg", context.theme.textMuted, _$p));
313
+ return _el$19;
314
+ }
315
+ }), null);
316
+ _$insert(_el$18, _$createComponent(Show, {
317
+ get when() {
318
+ return hasReading();
319
+ },
320
+ get fallback() {
321
+ return (() => {
322
+ var _el$23 = _$createElement("text");
323
+ _$insertNode(_el$23, _$createTextNode(`no reading`));
324
+ _$setProp(_el$23, "wrapMode", 'none');
325
+ _$effect(_$p => _$setProp(_el$23, "fg", context.theme.textMuted, _$p));
326
+ return _el$23;
327
+ })();
328
+ },
329
+ get children() {
330
+ return _$createComponent(Show, {
331
+ get when() {
332
+ return !props.part.resetSinceReading;
333
+ },
334
+ get fallback() {
335
+ return (() => {
336
+ var _el$25 = _$createElement("text");
337
+ _$insertNode(_el$25, _$createTextNode(`reset since last check`));
338
+ _$setProp(_el$25, "wrapMode", 'none');
339
+ _$effect(_$p => _$setProp(_el$25, "fg", context.theme.textMuted, _$p));
340
+ return _el$25;
341
+ })();
342
+ },
343
+ get children() {
344
+ return [_$createComponent(Show, {
345
+ get when() {
346
+ return props.barCells > 0;
347
+ },
348
+ get children() {
349
+ return _$createComponent(Index, {
350
+ get each() {
351
+ return segments();
352
+ },
353
+ children: segment => (() => {
354
+ var _el$27 = _$createElement("text");
355
+ _$setProp(_el$27, "wrapMode", 'none');
356
+ _$insert(_el$27, () => segment().text);
357
+ _$effect(_p$ => {
358
+ var _v$9 = context.theme[segment().tone],
359
+ _v$0 = segment().text ? undefined : 0;
360
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$27, "fg", _v$9, _p$.e));
361
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$27, "width", _v$0, _p$.t));
362
+ return _p$;
363
+ }, {
364
+ e: undefined,
365
+ t: undefined
366
+ });
367
+ return _el$27;
368
+ })()
369
+ });
370
+ }
371
+ }), (() => {
372
+ var _el$20 = _$createElement("text");
373
+ _$setProp(_el$20, "wrapMode", 'none');
374
+ _$insert(_el$20, figure);
375
+ _$effect(_$p => _$setProp(_el$20, "fg", context.theme[usageTone(used(), context.appearance)], _$p));
376
+ return _el$20;
377
+ })()];
378
+ }
379
+ });
380
+ }
381
+ }), null);
382
+ _$insert(_el$17, _$createComponent(Show, {
383
+ get when() {
384
+ return _$memo(() => !!(hasReading() && props.showReset))() && !props.part.resetSinceReading;
385
+ },
386
+ get children() {
387
+ var _el$21 = _$createElement("box");
388
+ _$setProp(_el$21, "flexShrink", 0);
389
+ _$setProp(_el$21, "flexDirection", 'row');
390
+ _$setProp(_el$21, "justifyContent", 'flex-end');
391
+ _$insert(_el$21, _$createComponent(Show, {
392
+ get when() {
393
+ return props.part.resetIn;
394
+ },
395
+ get children() {
396
+ var _el$22 = _$createElement("text");
397
+ _$setProp(_el$22, "wrapMode", 'none');
398
+ _$insert(_el$22, () => props.part.resetIn);
399
+ _$effect(_$p => _$setProp(_el$22, "fg", context.theme.textMuted, _$p));
400
+ return _el$22;
401
+ }
402
+ }));
403
+ _$effect(_$p => _$setProp(_el$21, "width", props.resetWidth, _$p));
404
+ return _el$21;
405
+ }
406
+ }), null);
407
+ _$effect(_$p => _$setProp(_el$18, "flexShrink", props.part.resetSinceReading ? 1 : 0, _$p));
408
+ return _el$17;
409
+ })(), _$createComponent(Show, {
410
+ get when() {
411
+ return _$memo(() => !!(hasReading() && !props.part.resetSinceReading))() && paceLine();
412
+ },
413
+ keyed: true,
414
+ children: line => (() => {
415
+ var _el$28 = _$createElement("box"),
416
+ _el$30 = _$createElement("text");
417
+ _$insertNode(_el$28, _el$30);
418
+ _$setProp(_el$28, "width", '100%');
419
+ _$setProp(_el$28, "flexDirection", 'row');
420
+ _$insert(_el$28, _$createComponent(Show, {
421
+ get when() {
422
+ return props.labelWidth > 0;
423
+ },
424
+ get children() {
425
+ var _el$29 = _$createElement("text");
426
+ _$insert(_el$29, () => ''.padEnd(props.labelWidth));
427
+ _$effect(_$p => _$setProp(_el$29, "fg", context.theme.textMuted, _$p));
428
+ return _el$29;
429
+ }
430
+ }), _el$30);
431
+ _$setProp(_el$30, "wrapMode", 'none');
432
+ _$insert(_el$30, line);
433
+ _$effect(_$p => _$setProp(_el$30, "fg", context.theme[props.pace?.tone ?? 'textMuted'], _$p));
434
+ return _el$28;
435
+ })()
436
+ })];
437
+ }
438
+ function CollapsedAccountRow(props) {
439
+ const context = frameContext();
440
+ const [width, setWidth] = createSignal(0);
441
+ let row;
442
+ const hasQuota = () => props.account.hasQuota;
443
+ const text = () => hasQuota() ? props.account.collapsedQuotaText ?? '' : props.account.emptyQuotaText?.collapsed ?? '\u2014';
444
+ // Reserve an ellipsis, a separator and the trailing indicator before keeping quota.
445
+ const showQuota = () => width() <= 0 || displayWidth(text()) + 4 <= width();
446
+ const label = () => {
447
+ if (width() <= 0) return props.account.label;
448
+ return fitText(props.account.label, width() - (showQuota() ? displayWidth(text()) + 3 : 2));
449
+ };
450
+ const muted = () => props.account.collapsed?.muted ?? false;
451
+ const quotaTone = () => muted() ? 'textMuted' : props.account.collapsed?.quotaTone ?? 'success';
452
+ return (() => {
453
+ var _el$31 = _$createElement("box"),
454
+ _el$33 = _$createElement("box"),
455
+ _el$36 = _$createElement("text");
456
+ _$insertNode(_el$31, _el$33);
457
+ _$use(element => {
458
+ row = element;
459
+ }, _el$31);
460
+ _$setProp(_el$31, "onSizeChange", () => setWidth(row?.width ?? 0));
461
+ _$setProp(_el$31, "width", '100%');
462
+ _$setProp(_el$31, "flexDirection", 'row');
463
+ _$setProp(_el$31, "justifyContent", 'space-between');
464
+ _$insert(_el$31, _$createComponent(Show, {
465
+ get when() {
466
+ return label();
467
+ },
468
+ get children() {
469
+ var _el$32 = _$createElement("text");
470
+ _$setProp(_el$32, "wrapMode", 'none');
471
+ _$setProp(_el$32, "flexShrink", 1);
472
+ _$insert(_el$32, label);
473
+ _$effect(_$p => _$setProp(_el$32, "fg", context.theme[muted() ? 'textMuted' : 'text'], _$p));
474
+ return _el$32;
475
+ }
476
+ }), _el$33);
477
+ _$insertNode(_el$33, _el$36);
478
+ _$setProp(_el$33, "flexDirection", 'row');
479
+ _$setProp(_el$33, "flexShrink", 0);
480
+ _$insert(_el$33, _$createComponent(Show, {
481
+ get when() {
482
+ return showQuota();
483
+ },
484
+ get children() {
485
+ return _$createComponent(Show, {
486
+ get when() {
487
+ return _$memo(() => !!hasQuota())() && !muted();
488
+ },
489
+ get fallback() {
490
+ return (() => {
491
+ var _el$37 = _$createElement("text");
492
+ _$insert(_el$37, text);
493
+ _$effect(_$p => _$setProp(_el$37, "fg", context.theme.textMuted, _$p));
494
+ return _el$37;
495
+ })();
496
+ },
497
+ get children() {
498
+ var _el$34 = _$createElement("text"),
499
+ _el$35 = _$createElement("b");
500
+ _$insertNode(_el$34, _el$35);
501
+ _$insert(_el$35, text);
502
+ _$effect(_$p => _$setProp(_el$34, "fg", context.theme[quotaTone()], _$p));
503
+ return _el$34;
504
+ }
505
+ });
506
+ }
507
+ }), _el$36);
508
+ _$insert(_el$36, () => showQuota() ? ' ' : '', null);
509
+ _$insert(_el$36, () => props.account.blocked ? '\u2298' : '\u25cf', null);
510
+ _$effect(_p$ => {
511
+ var _v$1 = label() ? 1 : 0,
512
+ _v$10 = context.theme[props.account.indicatorTone];
513
+ _v$1 !== _p$.e && (_p$.e = _$setProp(_el$31, "columnGap", _v$1, _p$.e));
514
+ _v$10 !== _p$.t && (_p$.t = _$setProp(_el$36, "fg", _v$10, _p$.t));
515
+ return _p$;
516
+ }, {
517
+ e: undefined,
518
+ t: undefined
519
+ });
520
+ return _el$31;
521
+ })();
522
+ }
523
+ function AccountBlock(props) {
524
+ const context = frameContext();
525
+ const [columns, setColumns] = createSignal(0);
526
+ let block;
527
+ const rows = () => props.account.groups.flatMap(group => group.rows);
528
+ const labelWidth = () => Math.max(0, ...rows().map(({
529
+ part
530
+ }) => part.name ? Math.max(3, displayWidth(part.name) + 1) : 0));
531
+ const resetWidth = () => Math.max(0, ...rows().map(({
532
+ part
533
+ }) => part.reading === 'unknown' || part.resetSinceReading ? 0 : displayWidth(part.resetIn ?? '')));
534
+ const figureWidth = createMemo(() => Math.max(10, ...rows().filter(({
535
+ part
536
+ }) => part.reading === 'known' && !part.resetSinceReading).map(({
537
+ part
538
+ }) => displayWidth(` ${part.kind === 'budget' && part.figure.startsWith(`${part.name} `) ? part.figure.slice(part.name.length + 1) : part.figure}`))));
539
+ const fit = rowLabelWidth => {
540
+ const desired = context.appearance.barWidth;
541
+ // Four cells still distinguish quarters of a window without sacrificing its reset.
542
+ const minimum = Math.min(4, desired);
543
+ if (columns() <= 0) return {
544
+ barCells: desired,
545
+ showReset: resetWidth() > 0
546
+ };
547
+ const room = columns() - rowLabelWidth - figureWidth();
548
+ const withReset = room - resetWidth() - 1;
549
+ const showReset = resetWidth() > 0 && withReset >= minimum;
550
+ const available = showReset ? withReset : room;
551
+ return {
552
+ barCells: available >= minimum ? Math.min(desired, available) : 0,
553
+ showReset
554
+ };
555
+ };
556
+ const label = () => {
557
+ const full = `${props.account.label}${props.account.titleSuffix ? ` · ${props.account.titleSuffix}` : ''}`;
558
+ return columns() <= 0 ? full : fitText(full, columns() - displayWidth(props.account.status.text) - 1);
559
+ };
560
+ const age = () => props.account.quotaAge;
561
+ const hasRows = () => props.account.groups.some(group => group.rows.length > 0);
562
+ return (() => {
563
+ var _el$38 = _$createElement("box"),
564
+ _el$39 = _$createElement("box"),
565
+ _el$40 = _$createElement("text"),
566
+ _el$41 = _$createElement("b"),
567
+ _el$42 = _$createElement("text"),
568
+ _el$43 = _$createElement("b");
569
+ _$insertNode(_el$38, _el$39);
570
+ _$use(element => {
571
+ block = element;
572
+ }, _el$38);
573
+ _$setProp(_el$38, "onSizeChange", () => setColumns(block?.width ?? 0));
574
+ _$setProp(_el$38, "width", '100%');
575
+ _$setProp(_el$38, "flexDirection", 'column');
576
+ _$insertNode(_el$39, _el$40);
577
+ _$insertNode(_el$39, _el$42);
578
+ _$setProp(_el$39, "width", '100%');
579
+ _$setProp(_el$39, "flexDirection", 'row');
580
+ _$setProp(_el$39, "justifyContent", 'space-between');
581
+ _$setProp(_el$39, "columnGap", 1);
582
+ _$insertNode(_el$40, _el$41);
583
+ _$setProp(_el$40, "wrapMode", 'none');
584
+ _$setProp(_el$40, "flexShrink", 1);
585
+ _$insert(_el$41, label);
586
+ _$insertNode(_el$42, _el$43);
587
+ _$setProp(_el$42, "wrapMode", 'none');
588
+ _$setProp(_el$42, "flexShrink", 0);
589
+ _$insert(_el$43, () => props.account.status.text);
590
+ _$insert(_el$38, () => context.beforeQuota?.(props.account), null);
591
+ _$insert(_el$38, _$createComponent(Show, {
592
+ get when() {
593
+ return hasRows();
594
+ },
595
+ get fallback() {
596
+ return _$createComponent(SidebarTextRow, {
597
+ get text() {
598
+ return props.account.emptyQuotaText?.expanded ?? '\u2014';
599
+ },
600
+ wrapMode: "none"
601
+ });
602
+ },
603
+ get children() {
604
+ return _$createComponent(For, {
605
+ get each() {
606
+ return props.account.groups;
607
+ },
608
+ children: group => [_$createComponent(Show, {
609
+ get when() {
610
+ return group.title;
611
+ },
612
+ get children() {
613
+ return _$createComponent(SidebarTextRow, {
614
+ get text() {
615
+ return group.title ?? '';
616
+ },
617
+ tone: "text",
618
+ wrapMode: "none"
619
+ });
620
+ }
621
+ }), _$createComponent(For, {
622
+ get each() {
623
+ return group.rows;
624
+ },
625
+ children: row => {
626
+ const rowLabelWidth = () => row.part.name ? labelWidth() : 0;
627
+ const rowFit = createMemo(() => fit(rowLabelWidth()));
628
+ return _$createComponent(QuotaRow, {
629
+ get part() {
630
+ return row.part;
631
+ },
632
+ get pace() {
633
+ return row.pace;
634
+ },
635
+ get labelWidth() {
636
+ return rowLabelWidth();
637
+ },
638
+ get barCells() {
639
+ return rowFit().barCells;
640
+ },
641
+ get resetWidth() {
642
+ return resetWidth();
643
+ },
644
+ get showReset() {
645
+ return rowFit().showReset;
646
+ }
647
+ });
648
+ }
649
+ })]
650
+ });
651
+ }
652
+ }), null);
653
+ _$insert(_el$38, _$createComponent(Show, {
654
+ get when() {
655
+ return age();
656
+ },
657
+ children: text => _$createComponent(SidebarTextRow, {
658
+ get text() {
659
+ return text();
660
+ },
661
+ wrapMode: "none"
662
+ })
663
+ }), null);
664
+ _$insert(_el$38, () => context.afterQuota?.(props.account), null);
665
+ _$effect(_p$ => {
666
+ var _v$11 = props.marginTop,
667
+ _v$12 = context.theme.text,
668
+ _v$13 = context.theme[props.account.status.tone];
669
+ _v$11 !== _p$.e && (_p$.e = _$setProp(_el$38, "marginTop", _v$11, _p$.e));
670
+ _v$12 !== _p$.t && (_p$.t = _$setProp(_el$40, "fg", _v$12, _p$.t));
671
+ _v$13 !== _p$.a && (_p$.a = _$setProp(_el$42, "fg", _v$13, _p$.a));
672
+ return _p$;
673
+ }, {
674
+ e: undefined,
675
+ t: undefined,
676
+ a: undefined
677
+ });
678
+ return _el$38;
679
+ })();
680
+ }
681
+
682
+ /** Borderless, controlled presentation; host registration and provider policy stay in the plugin. */
683
+ export function SidebarFrame(props) {
684
+ const hasData = () => props.accounts.length > 0;
685
+ const canCollapse = () => props.collapsible ?? hasData();
686
+ const headerLabel = () => !canCollapse() ? props.header.badge : `${props.collapsed ? '\u25b6' : '\u25bc'} ${props.header.badge}`;
687
+ // Missing semantic colors inherit normal text; an unset palette uses terminal defaults.
688
+ const context = mergeProps(props, {
689
+ get theme() {
690
+ const theme = props.theme;
691
+ return {
692
+ ...theme,
693
+ textMuted: theme?.textMuted ?? theme?.text,
694
+ accent: theme?.accent ?? theme?.text,
695
+ success: theme?.success ?? theme?.text,
696
+ warning: theme?.warning ?? theme?.text,
697
+ error: theme?.error ?? theme?.text
698
+ };
699
+ }
700
+ });
701
+ // Inverting an incomplete badge palette can put light text on a light background.
702
+ const invertBadge = () => props.theme?.accent !== undefined && props.theme?.background !== undefined;
703
+ function HeaderBadge() {
704
+ return (() => {
705
+ var _el$44 = _$createElement("box"),
706
+ _el$45 = _$createElement("text"),
707
+ _el$46 = _$createElement("b");
708
+ _$insertNode(_el$44, _el$45);
709
+ _$setProp(_el$44, "paddingLeft", 1);
710
+ _$setProp(_el$44, "paddingRight", 1);
711
+ _$insertNode(_el$45, _el$46);
712
+ _$insert(_el$46, headerLabel);
713
+ _$effect(_p$ => {
714
+ var _v$14 = invertBadge() ? props.theme?.accent : undefined,
715
+ _v$15 = invertBadge() ? props.theme?.background : context.theme.accent;
716
+ _v$14 !== _p$.e && (_p$.e = _$setProp(_el$44, "backgroundColor", _v$14, _p$.e));
717
+ _v$15 !== _p$.t && (_p$.t = _$setProp(_el$45, "fg", _v$15, _p$.t));
718
+ return _p$;
719
+ }, {
720
+ e: undefined,
721
+ t: undefined
722
+ });
723
+ return _el$44;
724
+ })();
725
+ }
726
+ return _$createComponent(FrameContext.Provider, {
727
+ value: context,
728
+ get children() {
729
+ var _el$47 = _$createElement("box"),
730
+ _el$48 = _$createElement("box");
731
+ _$insertNode(_el$47, _el$48);
732
+ _$setProp(_el$47, "width", '100%');
733
+ _$setProp(_el$47, "flexDirection", 'column');
734
+ _$setProp(_el$48, "width", '100%');
735
+ _$setProp(_el$48, "flexDirection", 'row');
736
+ _$setProp(_el$48, "justifyContent", 'space-between');
737
+ _$setProp(_el$48, "alignItems", 'center');
738
+ _$setProp(_el$48, "onMouseDown", () => {
739
+ if (canCollapse()) props.onToggle();
740
+ });
741
+ _$insert(_el$48, _$createComponent(Show, {
742
+ get when() {
743
+ return props.header.afterBadge;
744
+ },
745
+ get fallback() {
746
+ return _$createComponent(HeaderBadge, {});
747
+ },
748
+ get children() {
749
+ var _el$49 = _$createElement("box");
750
+ _$setProp(_el$49, "flexDirection", 'row');
751
+ _$setProp(_el$49, "alignItems", 'center');
752
+ _$insert(_el$49, _$createComponent(HeaderBadge, {}), null);
753
+ _$insert(_el$49, () => props.header.afterBadge?.(), null);
754
+ return _el$49;
755
+ }
756
+ }), null);
757
+ _$insert(_el$48, _$createComponent(Show, {
758
+ get when() {
759
+ return props.header.version;
760
+ },
761
+ get children() {
762
+ var _el$50 = _$createElement("text");
763
+ _$insert(_el$50, () => props.header.version);
764
+ _$effect(_$p => _$setProp(_el$50, "fg", context.theme.textMuted, _$p));
765
+ return _el$50;
766
+ }
767
+ }), null);
768
+ _$insert(_el$47, _$createComponent(Show, {
769
+ get when() {
770
+ return _$memo(() => !!props.collapsed)() && canCollapse();
771
+ },
772
+ get children() {
773
+ return [_$createComponent(For, {
774
+ get each() {
775
+ return props.accounts;
776
+ },
777
+ children: account => _$createComponent(CollapsedAccountRow, {
778
+ account: account
779
+ })
780
+ }), _$memo(() => props.collapsedSections?.())];
781
+ }
782
+ }), null);
783
+ _$insert(_el$47, _$createComponent(Show, {
784
+ get when() {
785
+ return !props.collapsed || !canCollapse();
786
+ },
787
+ get children() {
788
+ return [_$createComponent(Show, {
789
+ get when() {
790
+ return _$memo(() => !!props.showQuota)() && hasData();
791
+ },
792
+ get children() {
793
+ return _$createComponent(SidebarSection, {
794
+ title: "Quota",
795
+ get children() {
796
+ return _$createComponent(For, {
797
+ get each() {
798
+ return props.accounts;
799
+ },
800
+ children: (account, index) => _$createComponent(AccountBlock, {
801
+ account: account,
802
+ get marginTop() {
803
+ return index() === 0 ? 0 : 1;
804
+ }
805
+ })
806
+ });
807
+ }
808
+ });
809
+ }
810
+ }), _$memo(() => props.sections?.())];
811
+ }
812
+ }), null);
813
+ return _el$47;
814
+ }
815
+ });
816
+ }