@cortexkit/aft-opencode 0.56.2 → 0.57.1

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 (39) hide show
  1. package/dist/bg-notifications.d.ts +1 -0
  2. package/dist/bg-notifications.d.ts.map +1 -1
  3. package/dist/entry/server.js +2650 -267
  4. package/dist/index.js +420 -228
  5. package/dist/lsp-github-install.d.ts.map +1 -1
  6. package/dist/permissions/v2-service.d.ts +30 -0
  7. package/dist/permissions/v2-service.d.ts.map +1 -0
  8. package/dist/permissions/v2.d.ts +121 -9
  9. package/dist/permissions/v2.d.ts.map +1 -1
  10. package/dist/shared/rpc-server.d.ts.map +1 -1
  11. package/dist/shared/status.d.ts +8 -1
  12. package/dist/shared/status.d.ts.map +1 -1
  13. package/dist/subc-tool-schemas.d.ts +11 -0
  14. package/dist/subc-tool-schemas.d.ts.map +1 -1
  15. package/dist/tool-registration.d.ts.map +1 -1
  16. package/dist/tools/definitions/v2.d.ts +6 -0
  17. package/dist/tools/definitions/v2.d.ts.map +1 -1
  18. package/dist/tools/hoisted/v2.d.ts +12 -2
  19. package/dist/tools/hoisted/v2.d.ts.map +1 -1
  20. package/dist/tools/hoisted.d.ts.map +1 -1
  21. package/dist/tools/semantic.d.ts.map +1 -1
  22. package/dist/tui/v2-status.d.ts +8 -0
  23. package/dist/tui/v2-status.d.ts.map +1 -1
  24. package/index.js +14 -0
  25. package/package.json +14 -10
  26. package/server.js +8 -0
  27. package/src/shared/rpc-server.ts +5 -0
  28. package/src/shared/status.ts +16 -9
  29. package/src/tui/index.tsx +1 -0
  30. package/src/tui/sidebar-view.tsx +722 -0
  31. package/src/tui/sidebar.tsx +42 -560
  32. package/src/tui/v2-status.ts +8 -1
  33. package/src/tui/v2.tsx +173 -30
  34. package/src/tui-compiled/index.tsx +1 -1
  35. package/src/tui-compiled/sidebar-view.tsx +815 -0
  36. package/src/tui-compiled/sidebar.tsx +45 -693
  37. package/src/tui-compiled/v2-status.ts +8 -1
  38. package/src/tui-compiled/v2.tsx +163 -54
  39. package/tui.js +7 -0
@@ -0,0 +1,815 @@
1
+ import { createComponent as _$createComponent } from "opentui:runtime-module:%40opentui%2Fsolid";
2
+ import { createTextNode as _$createTextNode } from "opentui:runtime-module:%40opentui%2Fsolid";
3
+ import { memo as _$memo } from "opentui:runtime-module:%40opentui%2Fsolid";
4
+ import { effect as _$effect } from "opentui:runtime-module:%40opentui%2Fsolid";
5
+ import { insertNode as _$insertNode } from "opentui:runtime-module:%40opentui%2Fsolid";
6
+ import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid";
7
+ import { setProp as _$setProp } from "opentui:runtime-module:%40opentui%2Fsolid";
8
+ import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid";
9
+ /** @jsxImportSource @opentui/solid */
10
+ // @ts-nocheck
11
+
12
+ // The AFT sidebar panel, shared by both OpenCode hosts.
13
+ //
14
+ // Everything that decides what the panel *draws* lives here and takes the
15
+ // status snapshot, the user's preferences and a colour palette as plain
16
+ // inputs. The two hosts differ only in how a component is mounted and how the
17
+ // theme arrives, so those two things stay at the edge (sidebar.tsx for the
18
+ // slot-plugin host, v2.tsx for the GA host) and the body below is rendered
19
+ // identically by both. Without this split the second host drifts into a
20
+ // smaller, differently-worded panel, which is exactly what happened before.
21
+
22
+ import { createMemo, createSignal, onCleanup } from "opentui:runtime-module:solid-js";
23
+ import { formatSemanticIndexStatus, formatSemanticRefreshing, semanticIndexStatusKind, worktreeCacheRoleNote } from "../shared/status";
24
+ import { badgeTextColor } from "./badge-contrast";
25
+ import { DEFAULT_PREFS, persistCollapsedIfEnabled, readTuiPreferencesFile, resolveAftPrefs, seedCollapsedFromPrefs, watchTuiPreferences } from "./preferences";
26
+ const SINGLE_BORDER = {
27
+ type: "single"
28
+ };
29
+
30
+ /**
31
+ * A colour is whatever the host's theme hands us: OpenTUI accepts both its own
32
+ * RGBA instances and hex strings, and the two hosts spell their themes
33
+ * differently, so the panel never looks inside a colour — it only passes it on.
34
+ */
35
+
36
+ /**
37
+ * The colours the panel draws with, named after their role rather than after
38
+ * either host's theme keys. Each host resolves its own theme into this shape
39
+ * before rendering; see resolveV1Palette / resolveV2Palette at the edges.
40
+ */
41
+
42
+ function hexChannels(value) {
43
+ const hex = value.trim().replace(/^#/, "");
44
+ if (hex.length !== 3 && hex.length !== 6 && hex.length !== 8) return null;
45
+ const wide = hex.length === 3 ? [...hex].map(c => c + c).join("") : hex;
46
+ const int = Number.parseInt(wide.slice(0, 6), 16);
47
+ if (Number.isNaN(int)) return null;
48
+ const alpha = wide.length === 8 ? Number.parseInt(wide.slice(6, 8), 16) / 255 : 1;
49
+ return {
50
+ r: (int >> 16 & 0xff) / 255,
51
+ g: (int >> 8 & 0xff) / 255,
52
+ b: (int & 0xff) / 255,
53
+ a: alpha
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Read a colour's 0..1 channels for the badge contrast decision only. OpenTUI's
59
+ * RGBA already exposes r/g/b/a getters in that range; a hex string has to be
60
+ * parsed. Anything else returns null and the caller keeps its existing choice
61
+ * rather than guessing.
62
+ */
63
+ function colorChannels(value) {
64
+ if (typeof value === "string") return hexChannels(value);
65
+ if (value && typeof value === "object") {
66
+ const candidate = value;
67
+ if (typeof candidate.r === "number" && typeof candidate.g === "number") {
68
+ if (typeof candidate.b !== "number") return null;
69
+ return {
70
+ r: candidate.r,
71
+ g: candidate.g,
72
+ b: candidate.b,
73
+ a: candidate.a
74
+ };
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Text colour for a label drawn on the accent badge. badgeTextColor works on
82
+ * plain channels, but the value handed back to OpenTUI has to be the host's
83
+ * original colour object, so the decision is made on the channels and then
84
+ * mapped back.
85
+ */
86
+ export function badgeForeground(accent, background) {
87
+ const accentChannels = colorChannels(accent);
88
+ const backgroundChannels = colorChannels(background);
89
+ if (!accentChannels || !backgroundChannels) return background;
90
+ const picked = badgeTextColor(accentChannels, backgroundChannels);
91
+ return picked === backgroundChannels ? background : picked;
92
+ }
93
+ function formatBytes(n) {
94
+ if (!Number.isFinite(n) || n <= 0) return "—";
95
+ if (n >= 1_073_741_824) return `${(n / 1_073_741_824).toFixed(1)} GB`;
96
+ if (n >= 1_048_576) return `${(n / 1_048_576).toFixed(1)} MB`;
97
+ if (n >= 1_024) return `${Math.round(n / 1_024)} KB`;
98
+ return `${n} B`;
99
+ }
100
+ function formatCount(n) {
101
+ if (n == null || !Number.isFinite(n)) return "—";
102
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
103
+ if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
104
+ return String(n);
105
+ }
106
+
107
+ /** Tagged rows for the Compression section. Each scope (Session / Project)
108
+ * emits a "scope" header followed by two "stat" rows — Tokens Saved and
109
+ * Compression Ratio — so the renderer can use the same StatRow layout as
110
+ * Search Index / Semantic Index above. Pi's monospace overlay and the
111
+ * OpenCode TUI dialog/sidebar all consume this same shape. */
112
+
113
+ function appendScope(rows, label, scope) {
114
+ const savings = scope.savings_tokens;
115
+ const pct = scope.original_tokens > 0 ? Math.round(savings / scope.original_tokens * 100) : 0;
116
+ rows.push({
117
+ kind: "scope",
118
+ label
119
+ });
120
+ rows.push({
121
+ kind: "stat",
122
+ label: "Tokens Saved",
123
+ value: savings.toLocaleString("en-US")
124
+ });
125
+ rows.push({
126
+ kind: "stat",
127
+ label: "Compression Ratio",
128
+ value: `${pct}%`
129
+ });
130
+ }
131
+ export function formatCompressionSidebarRows(compression) {
132
+ if (!compression || compression.project.events <= 0) return [];
133
+ const rows = [];
134
+ if (compression.session.events > 0) {
135
+ appendScope(rows, "Session", compression.session);
136
+ }
137
+ appendScope(rows, "Project", compression.project);
138
+ return rows;
139
+ }
140
+
141
+ // Map an index status word to (label, theme color name). The label is what we
142
+ // want the user to see; the color encodes severity so the eye lands on trouble.
143
+ //
144
+ // The tone comes from the same classification the label uses
145
+ // (`semanticIndexStatusKind` in @cortexkit/aft-bridge), so a word cannot be
146
+ // readable in one column and unrecognised grey in the other.
147
+ // `backend_unavailable` used to fall through to the default arm: a real
148
+ // embedding-backend outage was drawn in the same muted grey as a word the
149
+ // renderer has no idea about, which reads as noise rather than as a condition
150
+ // the user has to act on.
151
+ export function statusDisplay(status) {
152
+ const label = status || "unknown";
153
+ switch (semanticIndexStatusKind(status)) {
154
+ case "ready":
155
+ return {
156
+ label,
157
+ tone: "ok"
158
+ };
159
+ case "progress":
160
+ return {
161
+ label,
162
+ tone: "warn"
163
+ };
164
+ case "failure":
165
+ return {
166
+ label,
167
+ tone: "err"
168
+ };
169
+ case "inactive":
170
+ return {
171
+ label,
172
+ tone: "muted"
173
+ };
174
+ default:
175
+ // Nothing is known about this word, so grey is honest here: it says the
176
+ // renderer has no reading to offer, not that everything is fine.
177
+ return {
178
+ label,
179
+ tone: "muted"
180
+ };
181
+ }
182
+ }
183
+ const StatRow = props => {
184
+ const fg = createMemo(() => {
185
+ switch (props.tone) {
186
+ case "ok":
187
+ return props.palette.success;
188
+ case "warn":
189
+ return props.palette.warning;
190
+ case "err":
191
+ return props.palette.error;
192
+ case "muted":
193
+ return props.palette.textMuted;
194
+ case "accent":
195
+ return props.palette.accent;
196
+ default:
197
+ return props.palette.text;
198
+ }
199
+ });
200
+ return (() => {
201
+ var _el$ = _$createElement("box"),
202
+ _el$2 = _$createElement("text"),
203
+ _el$3 = _$createElement("text"),
204
+ _el$4 = _$createElement("b");
205
+ _$insertNode(_el$, _el$2);
206
+ _$insertNode(_el$, _el$3);
207
+ _$setProp(_el$, "width", "100%");
208
+ _$setProp(_el$, "flexDirection", "row");
209
+ _$setProp(_el$, "justifyContent", "space-between");
210
+ _$insert(_el$2, () => props.label);
211
+ _$insertNode(_el$3, _el$4);
212
+ _$insert(_el$4, () => props.value);
213
+ _$effect(_p$ => {
214
+ var _v$ = props.palette.textMuted,
215
+ _v$2 = fg();
216
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$2, "fg", _v$, _p$.e));
217
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$3, "fg", _v$2, _p$.t));
218
+ return _p$;
219
+ }, {
220
+ e: undefined,
221
+ t: undefined
222
+ });
223
+ return _el$;
224
+ })();
225
+ };
226
+ const SectionHeader = props => (() => {
227
+ var _el$5 = _$createElement("box"),
228
+ _el$6 = _$createElement("text"),
229
+ _el$7 = _$createElement("b");
230
+ _$insertNode(_el$5, _el$6);
231
+ _$setProp(_el$5, "width", "100%");
232
+ _$insertNode(_el$6, _el$7);
233
+ _$insert(_el$7, () => props.title);
234
+ _$effect(_p$ => {
235
+ var _v$3 = props.marginTop ?? 1,
236
+ _v$4 = props.palette.text;
237
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$5, "marginTop", _v$3, _p$.e));
238
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$6, "fg", _v$4, _p$.t));
239
+ return _p$;
240
+ }, {
241
+ e: undefined,
242
+ t: undefined
243
+ });
244
+ return _el$5;
245
+ })();
246
+
247
+ // Map a status tone to a palette color — used for the collapsed-view status dots.
248
+ function toneColor(palette, tone) {
249
+ switch (tone) {
250
+ case "ok":
251
+ return palette.success;
252
+ case "warn":
253
+ return palette.warning;
254
+ case "err":
255
+ return palette.error;
256
+ default:
257
+ return palette.textMuted;
258
+ }
259
+ }
260
+
261
+ // Collapsed-view row: label on the left, a status dot (or compact value) on the
262
+ // right. Mirrors the expanded StatRow layout so the columns line up.
263
+ const CollapsedRow = props => (() => {
264
+ var _el$8 = _$createElement("box"),
265
+ _el$9 = _$createElement("text");
266
+ _$insertNode(_el$8, _el$9);
267
+ _$setProp(_el$8, "width", "100%");
268
+ _$setProp(_el$8, "flexDirection", "row");
269
+ _$setProp(_el$8, "justifyContent", "space-between");
270
+ _$insert(_el$9, () => props.label);
271
+ _$insert(_el$8, () => props.children, null);
272
+ _$effect(_$p => _$setProp(_el$9, "fg", props.palette.textMuted, _$p));
273
+ return _el$8;
274
+ })();
275
+
276
+ // Compact "saved / ratio" string for the collapsed Compression row — e.g.
277
+ // "7.6M / 64%". Uses the local `formatCount` (not the aft-bridge token
278
+ // formatter) so the TUI bundle doesn't pull the bridge barrel, which exports
279
+ // URL-fetch helpers unsuitable for Bun's TUI runtime. Returns null when no
280
+ // compression has been recorded yet.
281
+ export function collapsedCompressionValue(compression) {
282
+ if (!compression || compression.project.events <= 0) return null;
283
+ const {
284
+ savings_tokens,
285
+ original_tokens
286
+ } = compression.project;
287
+ const pct = original_tokens > 0 ? Math.round(savings_tokens / original_tokens * 100) : 0;
288
+ return `${formatCount(savings_tokens)} / ${pct}%`;
289
+ }
290
+ // Degraded-mode reason → human-readable hint. Distinct strings per reason
291
+ // because the UX direction is different: "home_root" tells the user to open a
292
+ // real project subdirectory, "search_too_many_files" tells them the tree is too
293
+ // big for full indexing, and "watcher_unavailable" is an honest soft
294
+ // degradation (AFT continues without live external-change invalidation).
295
+ export function degradedReasonLabel(reason) {
296
+ if (reason === "home_root") {
297
+ return "project root is your home directory";
298
+ }
299
+ if (reason.startsWith("search_too_many_files:")) {
300
+ const threshold = reason.split(":")[1] ?? "20000";
301
+ return `project exceeds ${threshold} files`;
302
+ }
303
+ if (reason === "watcher_unavailable") {
304
+ return "file watcher unavailable; continuing without live external-change invalidation";
305
+ }
306
+ return reason; // unknown reason — surface verbatim so users can grep logs
307
+ }
308
+ // Missing categories are intentionally muted: a green light requires an
309
+ // explicit zero for every category that feeds that light.
310
+ export function collapsedHealthLights(statusBar) {
311
+ if (!statusBar) return null;
312
+ const diagnostics = statusBar.errors !== undefined && statusBar.errors > 0 ? "err" : statusBar.warnings !== undefined && statusBar.warnings > 0 ? "warn" : statusBar.errors === 0 && statusBar.warnings === 0 ? "ok" : "muted";
313
+ const codeValues = [statusBar.dead_code, statusBar.unused_exports, statusBar.duplicates];
314
+ const code = codeValues.some(value => value !== undefined && value > 0) ? "warn" : codeValues.every(value => value === 0) ? "ok" : "muted";
315
+ const todos = statusBar.todos === undefined ? "muted" : statusBar.todos > 0 ? "warn" : "ok";
316
+ return {
317
+ diagnostics,
318
+ code,
319
+ todos
320
+ };
321
+ }
322
+ /**
323
+ * The preference-backed state the panel needs: which sections are enabled, the
324
+ * header label, and whether the panel is collapsed. Both hosts read the same
325
+ * shared `tui-preferences.jsonc`, so this lives here rather than being wired
326
+ * up twice.
327
+ *
328
+ * `onChange` lets a host that does not repaint on its own ask for a frame
329
+ * after preferences or the collapsed flag change.
330
+ */
331
+ export function createSidebarPreferences(onChange) {
332
+ const [prefs, setPrefs] = createSignal(structuredClone(DEFAULT_PREFS));
333
+ const [collapsed, setCollapsed] = createSignal(seedCollapsedFromPrefs(DEFAULT_PREFS));
334
+ const reload = async () => {
335
+ const root = await readTuiPreferencesFile();
336
+ const next = resolveAftPrefs(root);
337
+ setPrefs(next);
338
+ setCollapsed(seedCollapsedFromPrefs(next));
339
+ onChange?.();
340
+ };
341
+ void reload();
342
+ const unwatch = watchTuiPreferences(() => {
343
+ void reload();
344
+ });
345
+ onCleanup(unwatch);
346
+ const toggleCollapsed = () => {
347
+ setCollapsed(current => {
348
+ const next = !current;
349
+ persistCollapsedIfEnabled(prefs(), next);
350
+ return next;
351
+ });
352
+ onChange?.();
353
+ };
354
+ return {
355
+ prefs,
356
+ collapsed,
357
+ toggleCollapsed
358
+ };
359
+ }
360
+
361
+ /**
362
+ * The whole sidebar body. Both hosts render this component; neither adds rows
363
+ * of its own, so a section added or removed here shows up in both at once.
364
+ */
365
+ export const AftSidebarPanel = props => {
366
+ const s = () => props.snapshot;
367
+
368
+ // Lazy-bridge: while AFT has no live bridge yet, the RPC server returns a
369
+ // synthetic snapshot with `cache_role === "not_initialized"`. In that state
370
+ // every metric is unknown by design — not "disabled" — so we hide the
371
+ // version line and the entire Search Index / Semantic Index / Compression
372
+ // grid until a first tool call warms the bridge. Users were reading the
373
+ // pre-init `vunknown` + `Status: unknown` rows as broken state instead of
374
+ // "AFT has not been used yet for this project".
375
+ const notInitialized = () => s()?.cache_role === "not_initialized";
376
+
377
+ // Pre-compute display values so the JSX stays readable. createMemo for
378
+ // each derived field would be overkill — these are cheap derivations.
379
+ const searchStatus = () => statusDisplay(s()?.search_index?.status ?? "disabled");
380
+ const semanticStatus = () => {
381
+ const rawStatus = s()?.semantic_index?.status ?? "disabled";
382
+ const display = statusDisplay(rawStatus);
383
+ return {
384
+ ...display,
385
+ // The error is passed through so a capability failure (a missing ONNX
386
+ // Runtime) is named here instead of arriving as a bare status word.
387
+ label: formatSemanticIndexStatus(rawStatus, s()?.semantic_index?.stage, s()?.semantic_index?.error)
388
+ };
389
+ };
390
+ const semanticRefreshing = () => formatSemanticRefreshing(s()?.semantic_index?.refreshing_count ?? 0);
391
+ const trigramBytes = () => s()?.disk?.trigram_disk_bytes ?? 0;
392
+ const semanticBytes = () => s()?.disk?.semantic_disk_bytes ?? 0;
393
+ const compressionRows = () => formatCompressionSidebarRows(s()?.compression);
394
+ const statusBar = () => s()?.status_bar;
395
+ const degradedSummary = () => {
396
+ const snap = s();
397
+ if (!snap?.degraded) return null;
398
+ const reasons = snap.degraded_reasons ?? [];
399
+ if (reasons.length === 0) return null;
400
+ return reasons.map(degradedReasonLabel).join("; ");
401
+ };
402
+
403
+ // Worktree borrow is a shared-index arrangement, not a degraded_reasons
404
+ // entry. Keep this muted and separate from the DEGRADED badge above.
405
+ const worktreeNote = () => worktreeCacheRoleNote(s()?.cache_role);
406
+ return (() => {
407
+ var _el$0 = _$createElement("box"),
408
+ _el$1 = _$createElement("box"),
409
+ _el$10 = _$createElement("box"),
410
+ _el$11 = _$createElement("box"),
411
+ _el$12 = _$createElement("text"),
412
+ _el$13 = _$createElement("b");
413
+ _$insertNode(_el$0, _el$1);
414
+ _$setProp(_el$0, "width", "100%");
415
+ _$setProp(_el$0, "flexDirection", "column");
416
+ _$setProp(_el$0, "border", SINGLE_BORDER);
417
+ _$setProp(_el$0, "paddingTop", 1);
418
+ _$setProp(_el$0, "paddingBottom", 1);
419
+ _$setProp(_el$0, "paddingLeft", 1);
420
+ _$setProp(_el$0, "paddingRight", 1);
421
+ _$insertNode(_el$1, _el$10);
422
+ _$setProp(_el$1, "flexDirection", "row");
423
+ _$setProp(_el$1, "justifyContent", "space-between");
424
+ _$setProp(_el$1, "alignItems", "center");
425
+ _$setProp(_el$1, "onMouseDown", () => {
426
+ if (notInitialized()) return;
427
+ props.onToggleCollapsed();
428
+ });
429
+ _$insertNode(_el$10, _el$11);
430
+ _$setProp(_el$10, "flexDirection", "row");
431
+ _$setProp(_el$10, "alignItems", "center");
432
+ _$insertNode(_el$11, _el$12);
433
+ _$setProp(_el$11, "paddingLeft", 1);
434
+ _$setProp(_el$11, "paddingRight", 1);
435
+ _$insertNode(_el$12, _el$13);
436
+ _$insert(_el$13, (() => {
437
+ var _c$ = _$memo(() => !!notInitialized());
438
+ return () => _c$() ? "" : props.collapsed ? "▶ " : "▼ ";
439
+ })(), null);
440
+ _$insert(_el$13, () => props.prefs.header.label, null);
441
+ _$insert(_el$10, (() => {
442
+ var _c$2 = _$memo(() => !!s()?.degraded);
443
+ return () => _c$2() && (() => {
444
+ var _el$14 = _$createElement("box"),
445
+ _el$15 = _$createElement("text"),
446
+ _el$16 = _$createElement("b");
447
+ _$insertNode(_el$14, _el$15);
448
+ _$setProp(_el$14, "paddingLeft", 1);
449
+ _$setProp(_el$14, "paddingRight", 1);
450
+ _$setProp(_el$14, "marginLeft", 1);
451
+ _$insertNode(_el$15, _el$16);
452
+ _$insertNode(_el$16, _$createTextNode(`DEGRADED`));
453
+ _$effect(_p$ => {
454
+ var _v$8 = props.palette.warning,
455
+ _v$9 = badgeForeground(props.palette.warning, props.palette.background);
456
+ _v$8 !== _p$.e && (_p$.e = _$setProp(_el$14, "backgroundColor", _v$8, _p$.e));
457
+ _v$9 !== _p$.t && (_p$.t = _$setProp(_el$15, "fg", _v$9, _p$.t));
458
+ return _p$;
459
+ }, {
460
+ e: undefined,
461
+ t: undefined
462
+ });
463
+ return _el$14;
464
+ })();
465
+ })(), null);
466
+ _$insert(_el$1, (() => {
467
+ var _c$3 = _$memo(() => !!(!notInitialized() && props.prefs.header.showVersion));
468
+ return () => _c$3() && (() => {
469
+ var _el$18 = _$createElement("text"),
470
+ _el$19 = _$createTextNode(`v`);
471
+ _$insertNode(_el$18, _el$19);
472
+ _$insert(_el$18, () => s()?.version ?? props.pluginVersion, null);
473
+ _$effect(_$p => _$setProp(_el$18, "fg", props.palette.textMuted, _$p));
474
+ return _el$18;
475
+ })();
476
+ })(), null);
477
+ _$insert(_el$0, (() => {
478
+ var _c$4 = _$memo(() => !!(s()?.degraded && degradedSummary()));
479
+ return () => _c$4() && (() => {
480
+ var _el$20 = _$createElement("box"),
481
+ _el$21 = _$createElement("text"),
482
+ _el$22 = _$createTextNode(`⚠ `);
483
+ _$insertNode(_el$20, _el$21);
484
+ _$setProp(_el$20, "marginTop", 1);
485
+ _$setProp(_el$20, "width", "100%");
486
+ _$insertNode(_el$21, _el$22);
487
+ _$insert(_el$21, degradedSummary, null);
488
+ _$effect(_$p => _$setProp(_el$21, "fg", props.palette.warning, _$p));
489
+ return _el$20;
490
+ })();
491
+ })(), null);
492
+ _$insert(_el$0, (() => {
493
+ var _c$5 = _$memo(() => !!(!notInitialized() && worktreeNote()));
494
+ return () => _c$5() && (() => {
495
+ var _el$23 = _$createElement("box"),
496
+ _el$24 = _$createElement("text");
497
+ _$insertNode(_el$23, _el$24);
498
+ _$setProp(_el$23, "marginTop", 1);
499
+ _$setProp(_el$23, "width", "100%");
500
+ _$insert(_el$24, worktreeNote);
501
+ _$effect(_$p => _$setProp(_el$24, "fg", props.palette.textMuted, _$p));
502
+ return _el$23;
503
+ })();
504
+ })(), null);
505
+ _$insert(_el$0, (() => {
506
+ var _c$6 = _$memo(() => !!notInitialized());
507
+ return () => _c$6() && (() => {
508
+ var _el$25 = _$createElement("box"),
509
+ _el$26 = _$createElement("text");
510
+ _$insertNode(_el$25, _el$26);
511
+ _$setProp(_el$25, "marginTop", 1);
512
+ _$setProp(_el$25, "width", "100%");
513
+ _$insert(_el$26, () => s().message || "AFT bridge is now spawned lazily, information here will be populated after first tool call.");
514
+ _$effect(_$p => _$setProp(_el$26, "fg", props.palette.textMuted, _$p));
515
+ return _el$25;
516
+ })();
517
+ })(), null);
518
+ _$insert(_el$0, (() => {
519
+ var _c$7 = _$memo(() => !!(!notInitialized() && props.collapsed));
520
+ return () => _c$7() && (() => {
521
+ var _el$27 = _$createElement("box");
522
+ _$setProp(_el$27, "width", "100%");
523
+ _$setProp(_el$27, "flexDirection", "column");
524
+ _$insert(_el$27, (() => {
525
+ var _c$9 = _$memo(() => !!props.prefs.sections.searchIndex);
526
+ return () => _c$9() && _$createComponent(CollapsedRow, {
527
+ get palette() {
528
+ return props.palette;
529
+ },
530
+ label: "Search Index",
531
+ get children() {
532
+ var _el$28 = _$createElement("text");
533
+ _$insertNode(_el$28, _$createTextNode(`●`));
534
+ _$effect(_$p => _$setProp(_el$28, "fg", toneColor(props.palette, searchStatus().tone), _$p));
535
+ return _el$28;
536
+ }
537
+ });
538
+ })(), null);
539
+ _$insert(_el$27, (() => {
540
+ var _c$0 = _$memo(() => !!props.prefs.sections.semanticIndex);
541
+ return () => _c$0() && _$createComponent(CollapsedRow, {
542
+ get palette() {
543
+ return props.palette;
544
+ },
545
+ label: "Semantic Index",
546
+ get children() {
547
+ var _el$30 = _$createElement("text");
548
+ _$insertNode(_el$30, _$createTextNode(`●`));
549
+ _$effect(_$p => _$setProp(_el$30, "fg", toneColor(props.palette, semanticStatus().tone), _$p));
550
+ return _el$30;
551
+ }
552
+ });
553
+ })(), null);
554
+ _$insert(_el$27, (() => {
555
+ var _c$1 = _$memo(() => !!(props.prefs.sections.codeHealth && collapsedHealthLights(statusBar())));
556
+ return () => _c$1() && _$createComponent(CollapsedRow, {
557
+ get palette() {
558
+ return props.palette;
559
+ },
560
+ label: "Code Health",
561
+ get children() {
562
+ var _el$32 = _$createElement("box"),
563
+ _el$33 = _$createElement("text"),
564
+ _el$35 = _$createElement("text"),
565
+ _el$37 = _$createElement("text");
566
+ _$insertNode(_el$32, _el$33);
567
+ _$insertNode(_el$32, _el$35);
568
+ _$insertNode(_el$32, _el$37);
569
+ _$setProp(_el$32, "flexDirection", "row");
570
+ _$setProp(_el$32, "gap", 1);
571
+ _$insertNode(_el$33, _$createTextNode(`●`));
572
+ _$insertNode(_el$35, _$createTextNode(`●`));
573
+ _$insertNode(_el$37, _$createTextNode(`●`));
574
+ _$effect(_p$ => {
575
+ var _v$0 = toneColor(props.palette, collapsedHealthLights(statusBar()).diagnostics),
576
+ _v$1 = toneColor(props.palette, collapsedHealthLights(statusBar()).code),
577
+ _v$10 = toneColor(props.palette, collapsedHealthLights(statusBar()).todos);
578
+ _v$0 !== _p$.e && (_p$.e = _$setProp(_el$33, "fg", _v$0, _p$.e));
579
+ _v$1 !== _p$.t && (_p$.t = _$setProp(_el$35, "fg", _v$1, _p$.t));
580
+ _v$10 !== _p$.a && (_p$.a = _$setProp(_el$37, "fg", _v$10, _p$.a));
581
+ return _p$;
582
+ }, {
583
+ e: undefined,
584
+ t: undefined,
585
+ a: undefined
586
+ });
587
+ return _el$32;
588
+ }
589
+ });
590
+ })(), null);
591
+ _$insert(_el$27, (() => {
592
+ var _c$10 = _$memo(() => !!(props.prefs.sections.compression && collapsedCompressionValue(s()?.compression)));
593
+ return () => _c$10() && _$createComponent(CollapsedRow, {
594
+ get palette() {
595
+ return props.palette;
596
+ },
597
+ label: "Compression",
598
+ get children() {
599
+ var _el$39 = _$createElement("text"),
600
+ _el$40 = _$createElement("b");
601
+ _$insertNode(_el$39, _el$40);
602
+ _$insert(_el$40, () => collapsedCompressionValue(s()?.compression));
603
+ _$effect(_$p => _$setProp(_el$39, "fg", props.palette.textMuted, _$p));
604
+ return _el$39;
605
+ }
606
+ });
607
+ })(), null);
608
+ return _el$27;
609
+ })();
610
+ })(), null);
611
+ _$insert(_el$0, (() => {
612
+ var _c$8 = _$memo(() => !!(!notInitialized() && !props.collapsed));
613
+ return () => _c$8() && [_$memo(() => _$memo(() => !!props.prefs.sections.searchIndex)() && [_$createComponent(SectionHeader, {
614
+ get palette() {
615
+ return props.palette;
616
+ },
617
+ title: "Search Index"
618
+ }), _$createComponent(StatRow, {
619
+ get palette() {
620
+ return props.palette;
621
+ },
622
+ label: "Status",
623
+ get value() {
624
+ return searchStatus().label;
625
+ },
626
+ get tone() {
627
+ return searchStatus().tone;
628
+ }
629
+ }), _$memo(() => _$memo(() => (s()?.search_index?.files ?? null) != null)() && _$createComponent(StatRow, {
630
+ get palette() {
631
+ return props.palette;
632
+ },
633
+ label: "Files",
634
+ get value() {
635
+ return formatCount(s().search_index.files);
636
+ },
637
+ tone: "muted"
638
+ })), _$createComponent(StatRow, {
639
+ get palette() {
640
+ return props.palette;
641
+ },
642
+ label: "Disk",
643
+ get value() {
644
+ return formatBytes(trigramBytes());
645
+ },
646
+ tone: "muted"
647
+ })]), _$memo(() => _$memo(() => !!props.prefs.sections.semanticIndex)() && [_$createComponent(SectionHeader, {
648
+ get palette() {
649
+ return props.palette;
650
+ },
651
+ title: "Semantic Index"
652
+ }), _$createComponent(StatRow, {
653
+ get palette() {
654
+ return props.palette;
655
+ },
656
+ label: "Status",
657
+ get value() {
658
+ return semanticStatus().label;
659
+ },
660
+ get tone() {
661
+ return semanticStatus().tone;
662
+ }
663
+ }), _$memo(() => _$memo(() => !!semanticRefreshing())() && (() => {
664
+ var _el$41 = _$createElement("box"),
665
+ _el$42 = _$createElement("text");
666
+ _$insertNode(_el$41, _el$42);
667
+ _$setProp(_el$41, "width", "100%");
668
+ _$insert(_el$42, semanticRefreshing);
669
+ _$effect(_$p => _$setProp(_el$42, "fg", props.palette.textMuted, _$p));
670
+ return _el$41;
671
+ })()), _$memo(() => _$memo(() => !!(s()?.semantic_index?.status === "loading" && s()?.semantic_index?.entries_total != null && s().semantic_index.entries_total > 0))() && _$createComponent(StatRow, {
672
+ get palette() {
673
+ return props.palette;
674
+ },
675
+ label: "Progress",
676
+ get value() {
677
+ return `${formatCount(s().semantic_index.entries_done)} / ${formatCount(s().semantic_index.entries_total)}`;
678
+ },
679
+ tone: "warn"
680
+ })), _$memo(() => _$memo(() => (s()?.semantic_index?.entries ?? null) != null)() && _$createComponent(StatRow, {
681
+ get palette() {
682
+ return props.palette;
683
+ },
684
+ label: "Entries",
685
+ get value() {
686
+ return formatCount(s().semantic_index.entries);
687
+ },
688
+ tone: "muted"
689
+ })), _$createComponent(StatRow, {
690
+ get palette() {
691
+ return props.palette;
692
+ },
693
+ label: "Disk",
694
+ get value() {
695
+ return formatBytes(semanticBytes());
696
+ },
697
+ tone: "muted"
698
+ })]), _$memo(() => _$memo(() => !!(props.prefs.sections.codeHealth && statusBar()))() && [_$createComponent(SectionHeader, {
699
+ get palette() {
700
+ return props.palette;
701
+ },
702
+ get title() {
703
+ return statusBar().tier2_stale ? "Code Health ~" : "Code Health";
704
+ }
705
+ }), _$memo(() => _$memo(() => statusBar().errors !== undefined)() && _$createComponent(StatRow, {
706
+ get palette() {
707
+ return props.palette;
708
+ },
709
+ label: "Errors",
710
+ get value() {
711
+ return formatCount(statusBar().errors);
712
+ },
713
+ get tone() {
714
+ return statusBar().errors > 0 ? "err" : "muted";
715
+ }
716
+ })), _$memo(() => _$memo(() => statusBar().warnings !== undefined)() && _$createComponent(StatRow, {
717
+ get palette() {
718
+ return props.palette;
719
+ },
720
+ label: "Warnings",
721
+ get value() {
722
+ return formatCount(statusBar().warnings);
723
+ },
724
+ get tone() {
725
+ return statusBar().warnings > 0 ? "warn" : "muted";
726
+ }
727
+ })), _$memo(() => _$memo(() => statusBar().dead_code !== undefined)() && _$createComponent(StatRow, {
728
+ get palette() {
729
+ return props.palette;
730
+ },
731
+ label: "Dead Code",
732
+ get value() {
733
+ return formatCount(statusBar().dead_code);
734
+ },
735
+ tone: "muted"
736
+ })), _$memo(() => _$memo(() => statusBar().unused_exports !== undefined)() && _$createComponent(StatRow, {
737
+ get palette() {
738
+ return props.palette;
739
+ },
740
+ label: "Unused Exports",
741
+ get value() {
742
+ return formatCount(statusBar().unused_exports);
743
+ },
744
+ tone: "muted"
745
+ })), _$memo(() => _$memo(() => statusBar().duplicates !== undefined)() && _$createComponent(StatRow, {
746
+ get palette() {
747
+ return props.palette;
748
+ },
749
+ label: "Duplicates",
750
+ get value() {
751
+ return formatCount(statusBar().duplicates);
752
+ },
753
+ tone: "muted"
754
+ })), _$memo(() => _$memo(() => statusBar().todos !== undefined)() && _$createComponent(StatRow, {
755
+ get palette() {
756
+ return props.palette;
757
+ },
758
+ label: "TODOs",
759
+ get value() {
760
+ return formatCount(statusBar().todos);
761
+ },
762
+ tone: "muted"
763
+ }))]), _$memo(() => _$memo(() => !!(props.prefs.sections.compression && compressionRows().length > 0))() && [_$createComponent(SectionHeader, {
764
+ get palette() {
765
+ return props.palette;
766
+ },
767
+ title: "Compression"
768
+ }), _$memo(() => compressionRows().map(row => row.kind === "scope" ? (() => {
769
+ var _el$43 = _$createElement("box"),
770
+ _el$44 = _$createElement("text");
771
+ _$insertNode(_el$43, _el$44);
772
+ _$setProp(_el$43, "width", "100%");
773
+ _$insert(_el$44, () => row.label);
774
+ _$effect(_$p => _$setProp(_el$44, "fg", props.palette.text, _$p));
775
+ return _el$43;
776
+ })() : _$createComponent(StatRow, {
777
+ get palette() {
778
+ return props.palette;
779
+ },
780
+ get label() {
781
+ return row.label;
782
+ },
783
+ get value() {
784
+ return row.value;
785
+ },
786
+ tone: "muted"
787
+ })))]), _$memo(() => _$memo(() => !!(s()?.semantic_index?.status === "failed" && s()?.semantic_index?.error))() && (() => {
788
+ var _el$45 = _$createElement("box"),
789
+ _el$46 = _$createElement("text"),
790
+ _el$47 = _$createTextNode(`⚠ `);
791
+ _$insertNode(_el$45, _el$46);
792
+ _$setProp(_el$45, "marginTop", 1);
793
+ _$setProp(_el$45, "width", "100%");
794
+ _$insertNode(_el$46, _el$47);
795
+ _$insert(_el$46, () => s().semantic_index.error, null);
796
+ _$effect(_$p => _$setProp(_el$46, "fg", props.palette.error, _$p));
797
+ return _el$45;
798
+ })())];
799
+ })(), null);
800
+ _$effect(_p$ => {
801
+ var _v$5 = props.palette.border,
802
+ _v$6 = props.palette.accent,
803
+ _v$7 = badgeForeground(props.palette.accent, props.palette.background);
804
+ _v$5 !== _p$.e && (_p$.e = _$setProp(_el$0, "borderColor", _v$5, _p$.e));
805
+ _v$6 !== _p$.t && (_p$.t = _$setProp(_el$11, "backgroundColor", _v$6, _p$.t));
806
+ _v$7 !== _p$.a && (_p$.a = _$setProp(_el$12, "fg", _v$7, _p$.a));
807
+ return _p$;
808
+ }, {
809
+ e: undefined,
810
+ t: undefined,
811
+ a: undefined
812
+ });
813
+ return _el$0;
814
+ })();
815
+ };