opencode-context-tree 0.1.1 → 0.2.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.
- package/CHANGELOG.md +38 -0
- package/README.md +35 -25
- package/dist/server.js +6 -2
- package/dist/tui.js +1310 -490
- package/docs/USAGE.md +52 -21
- package/package.json +1 -1
- package/src/core/actions.ts +1 -0
- package/src/core/consumers.ts +55 -13
- package/src/core/decision.ts +62 -0
- package/src/core/lanes.ts +166 -5
- package/src/core/navigation.ts +39 -10
- package/src/core/tokens.ts +22 -4
- package/src/core/tree.ts +98 -5
- package/src/server/index.ts +3 -2
- package/src/shared/version.ts +3 -0
- package/src/tui/actions.ts +131 -11
- package/src/tui/index.tsx +14 -6
- package/src/tui/route.tsx +556 -181
package/dist/tui.js
CHANGED
|
@@ -66,7 +66,17 @@ function contextSizeOf(messages) {
|
|
|
66
66
|
}
|
|
67
67
|
return { tokens: lastAssistantInput + newer, estimated: newer > 0 };
|
|
68
68
|
}
|
|
69
|
-
function bandFor(tokens) {
|
|
69
|
+
function bandFor(tokens, limit) {
|
|
70
|
+
if (limit && limit > 0) {
|
|
71
|
+
const r = tokens / limit;
|
|
72
|
+
if (r < 0.25)
|
|
73
|
+
return "low";
|
|
74
|
+
if (r < 0.6)
|
|
75
|
+
return "healthy";
|
|
76
|
+
if (r < 0.85)
|
|
77
|
+
return "filling";
|
|
78
|
+
return "red";
|
|
79
|
+
}
|
|
70
80
|
if (tokens < 8000)
|
|
71
81
|
return "low";
|
|
72
82
|
if (tokens < 32000)
|
|
@@ -75,6 +85,12 @@ function bandFor(tokens) {
|
|
|
75
85
|
return "filling";
|
|
76
86
|
return "red";
|
|
77
87
|
}
|
|
88
|
+
function contextBar(tokens, limit, cells = 5) {
|
|
89
|
+
if (!(limit > 0))
|
|
90
|
+
return "";
|
|
91
|
+
const filled = tokens <= 0 ? 0 : Math.min(cells, Math.max(1, Math.round(tokens / limit * cells)));
|
|
92
|
+
return "\u2593".repeat(filled) + "\u2591".repeat(cells - filled);
|
|
93
|
+
}
|
|
78
94
|
function formatK(tokens) {
|
|
79
95
|
if (tokens < 1000)
|
|
80
96
|
return String(tokens);
|
|
@@ -82,7 +98,8 @@ function formatK(tokens) {
|
|
|
82
98
|
return `${k.endsWith(".0") ? k.slice(0, -2) : k}k`;
|
|
83
99
|
}
|
|
84
100
|
function formatContext(size, limit) {
|
|
85
|
-
|
|
101
|
+
const bar = limit ? `${contextBar(size.tokens, limit)} ` : "";
|
|
102
|
+
return `ctx ${bar}${size.estimated ? "~" : ""}${formatK(size.tokens)}${limit ? `/${formatK(limit)}` : ""} \xB7 ${bandFor(size.tokens, limit)}`;
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
// src/shared/store.ts
|
|
@@ -19113,6 +19130,8 @@ function debug(event, data) {
|
|
|
19113
19130
|
|
|
19114
19131
|
// src/tui/actions.ts
|
|
19115
19132
|
import { createSignal } from "solid-js";
|
|
19133
|
+
import fs4 from "fs";
|
|
19134
|
+
import path3 from "path";
|
|
19116
19135
|
|
|
19117
19136
|
// src/core/decision.ts
|
|
19118
19137
|
var DECISION_SYSTEM = "You write concise engineering decision records. You are given the transcript of a side branch of a coding session. Do NOT continue the conversation. Output ONLY the record in the exact markdown template requested, nothing else.";
|
|
@@ -19181,6 +19200,55 @@ function decisionMessageText(record2, branchName) {
|
|
|
19181
19200
|
return body.startsWith("## Decision:") ? `\u25C6 ${body}` : `\u25C6 ## Decision: ${branchName}
|
|
19182
19201
|
${body}`;
|
|
19183
19202
|
}
|
|
19203
|
+
var BULLET = /^(\s*)[-*\u2022]\s+/;
|
|
19204
|
+
var HEADING = /^(\s*(?:\u25C6\s*)?)#{1,6}\s*/;
|
|
19205
|
+
function stripMarkdown(text) {
|
|
19206
|
+
return text.replace(/[*`]/g, "");
|
|
19207
|
+
}
|
|
19208
|
+
function wrapLine(body, width, marker) {
|
|
19209
|
+
const room = Math.max(1, width - marker.length);
|
|
19210
|
+
const indent = " ".repeat(marker.length);
|
|
19211
|
+
const lines = [];
|
|
19212
|
+
let current = "";
|
|
19213
|
+
for (const raw of body.split(/\s+/).filter(Boolean)) {
|
|
19214
|
+
const word = raw.length > room ? `${raw.slice(0, Math.max(1, room - 1))}\u2026` : raw;
|
|
19215
|
+
if (current === "")
|
|
19216
|
+
current = word;
|
|
19217
|
+
else if (current.length + 1 + word.length <= room)
|
|
19218
|
+
current += ` ${word}`;
|
|
19219
|
+
else {
|
|
19220
|
+
lines.push(current);
|
|
19221
|
+
current = word;
|
|
19222
|
+
}
|
|
19223
|
+
}
|
|
19224
|
+
lines.push(current);
|
|
19225
|
+
return lines.map((line, i) => `${i === 0 ? marker : indent}${line}`.trimEnd());
|
|
19226
|
+
}
|
|
19227
|
+
function renderDecision(text, width) {
|
|
19228
|
+
const out = [];
|
|
19229
|
+
for (const raw of text.replace(/\r/g, "").trim().split(`
|
|
19230
|
+
`)) {
|
|
19231
|
+
const line = raw.trimEnd();
|
|
19232
|
+
if (line.trim() === "") {
|
|
19233
|
+
if (out.length > 0 && out[out.length - 1] !== "")
|
|
19234
|
+
out.push("");
|
|
19235
|
+
continue;
|
|
19236
|
+
}
|
|
19237
|
+
const bullet = BULLET.exec(line);
|
|
19238
|
+
const marker = bullet ? `${bullet[1]}- ` : "";
|
|
19239
|
+
const body = stripMarkdown(bullet ? line.slice(bullet[0].length) : line.replace(HEADING, "$1"));
|
|
19240
|
+
out.push(...wrapLine(body, width, marker));
|
|
19241
|
+
}
|
|
19242
|
+
return out;
|
|
19243
|
+
}
|
|
19244
|
+
function decisionSummary(text) {
|
|
19245
|
+
const lines = text.replace(/\r/g, "").split(`
|
|
19246
|
+
`).map((l) => stripMarkdown(l.replace(/^\s*\u25C6\s*/, "").replace(HEADING, "$1").replace(BULLET, "")).trim());
|
|
19247
|
+
const heading = lines.find((l) => /^Decision:\s*\S/i.test(l));
|
|
19248
|
+
const title = heading ? heading.replace(/^Decision:\s*/i, "").trim() : lines.find((l) => l !== "") ?? "";
|
|
19249
|
+
const outcome = lines.find((l) => /^Outcome:\s*\S/i.test(l))?.replace(/^Outcome:\s*/i, "").trim();
|
|
19250
|
+
return outcome ? { title, outcome } : { title };
|
|
19251
|
+
}
|
|
19184
19252
|
function openSiblings(state, sessionID) {
|
|
19185
19253
|
const me = state.sessions[sessionID];
|
|
19186
19254
|
if (!me)
|
|
@@ -19562,19 +19630,67 @@ async function draftWithHelper(ctx, input2) {
|
|
|
19562
19630
|
}
|
|
19563
19631
|
}
|
|
19564
19632
|
var MERGE_TRUST = "Your transcript is never rewritten; the record is appended to the trunk as a normal message.";
|
|
19633
|
+
var UNDO_KEY = "u";
|
|
19565
19634
|
var MERGE_GATE_NOTICE = `Edit the \u25C6 decision record, then save to confirm (empty file or a non-zero exit aborts the merge).
|
|
19566
19635
|
${MERGE_TRUST}`;
|
|
19567
|
-
|
|
19568
|
-
|
|
19636
|
+
var DISCARD_NOTICE = `${MERGE_TRUST}
|
|
19637
|
+
The branch is only marked rejected \u2014 ${UNDO_KEY} (alias x) undoes it.`;
|
|
19638
|
+
var TRUNK_LABEL = "trunk";
|
|
19639
|
+
function mergeTargetOf(label, messages) {
|
|
19640
|
+
const minimal = messages.map((m) => ({ info: m.role === "assistant" ? { role: "assistant", tokens: m.tokens } : { role: "user" }, parts: m.parts }));
|
|
19641
|
+
return { label, turns: messages.filter((m) => m.role === "user").length, tokens: contextSizeOf(minimal).tokens };
|
|
19642
|
+
}
|
|
19643
|
+
function ownTurnCount(messages, anchor2) {
|
|
19644
|
+
const anchorIndex = anchor2.messageID ? anchor2.parentMessageIDs.indexOf(anchor2.messageID) : -1;
|
|
19645
|
+
return messages.slice(anchorIndex + 1).filter((m) => m.role === "user").length;
|
|
19646
|
+
}
|
|
19647
|
+
function mergeDialogTitle(branchName, target2) {
|
|
19648
|
+
if (!target2 || typeof target2 === "string")
|
|
19649
|
+
return `Merge \u2387 ${branchName} \u2192 the trunk`;
|
|
19650
|
+
const where = target2.label === TRUNK_LABEL ? TRUNK_LABEL : `\u2387 ${clip(target2.label, 24)}`;
|
|
19651
|
+
return `Merge \u2387 ${branchName} \u2192 ${where} (${plural(target2.turns, "turn")}, ~${formatK(target2.tokens)})`;
|
|
19569
19652
|
}
|
|
19570
19653
|
function mergeDialogOptions(input2) {
|
|
19654
|
+
const folds = input2.turns === undefined ? "the branch" : plural(input2.turns, "turn");
|
|
19571
19655
|
return [
|
|
19572
|
-
{ title: "Squash", value: "squash", description:
|
|
19573
|
-
{ title: "Squash without LLM", value: "squash-no-llm", description: "you write
|
|
19574
|
-
{ title: "Discard", value: "discard", description: "rejected
|
|
19656
|
+
{ title: "Squash", value: "squash", description: `1 model call \xB7 folds ${folds} into one \u25C6 record` },
|
|
19657
|
+
{ title: "Squash without LLM", value: "squash-no-llm", description: "you write it \xB7 no model call" },
|
|
19658
|
+
{ title: "Discard", value: "discard", description: "rejected \xB7 nothing lands in the trunk" },
|
|
19575
19659
|
...input2.siblings > 0 ? [{ title: "Tournament", value: "tournament", description: "compare sibling branches and keep one" }] : []
|
|
19576
19660
|
];
|
|
19577
19661
|
}
|
|
19662
|
+
function confirmDialog(ctx, title, message) {
|
|
19663
|
+
return new Promise((resolve) => {
|
|
19664
|
+
ctx.api.ui.dialog.replace(() => ctx.api.ui.DialogConfirm({
|
|
19665
|
+
title,
|
|
19666
|
+
message,
|
|
19667
|
+
onConfirm: () => {
|
|
19668
|
+
resolve(true);
|
|
19669
|
+
ctx.api.ui.dialog.clear();
|
|
19670
|
+
},
|
|
19671
|
+
onCancel: () => {
|
|
19672
|
+
resolve(false);
|
|
19673
|
+
ctx.api.ui.dialog.clear();
|
|
19674
|
+
}
|
|
19675
|
+
}), () => resolve(false));
|
|
19676
|
+
});
|
|
19677
|
+
}
|
|
19678
|
+
function promptDialog(ctx, title, placeholder) {
|
|
19679
|
+
return new Promise((resolve) => {
|
|
19680
|
+
ctx.api.ui.dialog.replace(() => ctx.api.ui.DialogPrompt({
|
|
19681
|
+
title,
|
|
19682
|
+
placeholder,
|
|
19683
|
+
onConfirm: (value) => {
|
|
19684
|
+
resolve(value);
|
|
19685
|
+
ctx.api.ui.dialog.clear();
|
|
19686
|
+
},
|
|
19687
|
+
onCancel: () => {
|
|
19688
|
+
resolve(undefined);
|
|
19689
|
+
ctx.api.ui.dialog.clear();
|
|
19690
|
+
}
|
|
19691
|
+
}), () => resolve(undefined));
|
|
19692
|
+
});
|
|
19693
|
+
}
|
|
19578
19694
|
async function mergeBranch(ctx, input2) {
|
|
19579
19695
|
const treeId = ctx.store.ensureTree(input2.sessionID, "tui");
|
|
19580
19696
|
const state = ctx.store.stateFor(treeId);
|
|
@@ -19585,16 +19701,33 @@ async function mergeBranch(ctx, input2) {
|
|
|
19585
19701
|
const name = branch.name ?? "branch";
|
|
19586
19702
|
debug("merge.start", { mode: input2.mode, sessionID: input2.sessionID, parentID });
|
|
19587
19703
|
await abortIfBusy(ctx, input2.sessionID);
|
|
19704
|
+
const parentMsgs = await ctx.api.client.session.messages({ sessionID: parentID, directory: ctx.directory }).catch(() => {
|
|
19705
|
+
return;
|
|
19706
|
+
});
|
|
19707
|
+
const parentMessageIDs = (parentMsgs?.data ?? []).map((m) => String(m.info.id));
|
|
19708
|
+
const own2 = await fetchOwnTranscript(ctx, input2.sessionID);
|
|
19709
|
+
const turns = ownTurnCount(own2.messages, { messageID: branch.anchorMessageID, parentMessageIDs });
|
|
19588
19710
|
if (input2.mode === "discard") {
|
|
19589
|
-
|
|
19711
|
+
const ok = await confirmDialog(ctx, `Discard \u2387 ${name} (${plural(turns, "turn")})?`, DISCARD_NOTICE);
|
|
19712
|
+
if (!ok) {
|
|
19713
|
+
ctx.api.ui.toast({ variant: "warning", message: `\u2387 ${name} kept \u2014 nothing discarded` });
|
|
19714
|
+
return;
|
|
19715
|
+
}
|
|
19716
|
+
let note = input2.note;
|
|
19717
|
+
if (note === undefined) {
|
|
19718
|
+
const answer = await promptDialog(ctx, "Why? (optional note on the close marker)", "dead end");
|
|
19719
|
+
if (answer === undefined) {
|
|
19720
|
+
ctx.api.ui.toast({ variant: "warning", message: `\u2387 ${name} kept \u2014 nothing discarded` });
|
|
19721
|
+
return;
|
|
19722
|
+
}
|
|
19723
|
+
note = answer.trim() || undefined;
|
|
19724
|
+
}
|
|
19725
|
+
record2(ctx, treeId, "branch.closed", { sessionID: input2.sessionID, status: "rejected", note });
|
|
19590
19726
|
await mirrorMetadata(ctx, input2.sessionID, { status: "rejected" });
|
|
19591
19727
|
navigateToSession(ctx, parentID);
|
|
19592
19728
|
ctx.api.ui.toast({ variant: "success", message: `\u2387 ${name} discarded \u2014 back on the trunk` });
|
|
19593
19729
|
return parentID;
|
|
19594
19730
|
}
|
|
19595
|
-
const parentMsgs = await ctx.api.client.session.messages({ sessionID: parentID, directory: ctx.directory });
|
|
19596
|
-
const parentMessageIDs = (parentMsgs.data ?? []).map((m) => String(m.info.id));
|
|
19597
|
-
const own2 = await fetchOwnTranscript(ctx, input2.sessionID);
|
|
19598
19731
|
const transcript = branchTranscriptText(own2, { messageID: branch.anchorMessageID, parentMessageIDs });
|
|
19599
19732
|
const model = branch.branchModel ?? branch.trunkModel;
|
|
19600
19733
|
const modelRef = model ? { providerID: model.split("/")[0], modelID: model.split("/").slice(1).join("/") } : undefined;
|
|
@@ -19654,23 +19787,43 @@ async function fetchOwnTranscript(ctx, sessionID) {
|
|
|
19654
19787
|
return { sessionID, title: sessionID, status: "available", messages };
|
|
19655
19788
|
}
|
|
19656
19789
|
var BRANCH_DIALOG = { title: "Branch here \u2192 new OpenCode session", placeholder: "name, e.g. try-redis", modelTitle: "Model for this branch (Enter keeps the current one)" };
|
|
19790
|
+
var COPY_HINT = ".opencode/context-tree/last-copy.txt";
|
|
19791
|
+
function copyText(api2, text, directory) {
|
|
19792
|
+
const renderer = api2.renderer;
|
|
19793
|
+
if (text && renderer?.copyToClipboardOSC52?.(text))
|
|
19794
|
+
return { target: "clipboard", hint: "clipboard" };
|
|
19795
|
+
const file2 = path3.join(directory, COPY_HINT);
|
|
19796
|
+
fs4.mkdirSync(path3.dirname(file2), { recursive: true });
|
|
19797
|
+
fs4.writeFileSync(file2, text);
|
|
19798
|
+
return { target: "file", hint: COPY_HINT };
|
|
19799
|
+
}
|
|
19657
19800
|
function clip(text, max) {
|
|
19658
19801
|
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
19659
19802
|
}
|
|
19803
|
+
function plural(n, noun) {
|
|
19804
|
+
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
19805
|
+
}
|
|
19660
19806
|
|
|
19661
19807
|
// src/tui/route.tsx
|
|
19662
|
-
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
19663
|
-
import { effect as _$effect } from "@opentui/solid";
|
|
19664
|
-
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
19665
19808
|
import { memo as _$memo } from "@opentui/solid";
|
|
19809
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
19810
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
19811
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
19666
19812
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
19667
19813
|
import { insert as _$insert } from "@opentui/solid";
|
|
19668
19814
|
import { setProp as _$setProp } from "@opentui/solid";
|
|
19669
19815
|
import { createElement as _$createElement } from "@opentui/solid";
|
|
19816
|
+
|
|
19817
|
+
// src/shared/version.ts
|
|
19818
|
+
var PLUGIN_VERSION = "0.2.1";
|
|
19819
|
+
|
|
19820
|
+
// src/tui/route.tsx
|
|
19670
19821
|
import { For, Show, createEffect, createMemo, createSignal as createSignal2, on, onCleanup } from "solid-js";
|
|
19671
19822
|
|
|
19672
19823
|
// src/core/actions.ts
|
|
19673
19824
|
function planJump(row, ctx) {
|
|
19825
|
+
if (row.kind === "separator")
|
|
19826
|
+
return { kind: "noop", reason: "nothing to go to" };
|
|
19674
19827
|
if (row.kind === "branch")
|
|
19675
19828
|
return row.sessionID === ctx.currentSessionID ? { kind: "noop", reason: "you are here" } : { kind: "switch", sessionID: row.sessionID };
|
|
19676
19829
|
const tr = ctx.transcripts[row.sessionID];
|
|
@@ -19698,15 +19851,33 @@ function planJump(row, ctx) {
|
|
|
19698
19851
|
}
|
|
19699
19852
|
|
|
19700
19853
|
// src/core/navigation.ts
|
|
19854
|
+
function scan(rows, index, dir) {
|
|
19855
|
+
for (let i = index;i >= 0 && i < rows.length; i += dir)
|
|
19856
|
+
if (rows[i].kind !== "separator")
|
|
19857
|
+
return i;
|
|
19858
|
+
return -1;
|
|
19859
|
+
}
|
|
19701
19860
|
function moveSelection(rows, index, delta) {
|
|
19702
19861
|
if (rows.length === 0)
|
|
19703
19862
|
return -1;
|
|
19704
|
-
const
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19709
|
-
|
|
19863
|
+
const dir = delta < 0 ? -1 : 1;
|
|
19864
|
+
let i = Math.min(Math.max(index, 0), rows.length - 1);
|
|
19865
|
+
for (let step = Math.abs(delta);step > 0; step--) {
|
|
19866
|
+
const next = scan(rows, i + dir, dir);
|
|
19867
|
+
if (next === -1)
|
|
19868
|
+
break;
|
|
19869
|
+
i = next;
|
|
19870
|
+
}
|
|
19871
|
+
if (rows[i].kind !== "separator")
|
|
19872
|
+
return i;
|
|
19873
|
+
const ahead = scan(rows, i, dir);
|
|
19874
|
+
return ahead === -1 ? scan(rows, i, dir === 1 ? -1 : 1) : ahead;
|
|
19875
|
+
}
|
|
19876
|
+
function firstIndex(rows) {
|
|
19877
|
+
return scan(rows, 0, 1);
|
|
19878
|
+
}
|
|
19879
|
+
function lastIndex(rows) {
|
|
19880
|
+
return scan(rows, rows.length - 1, -1);
|
|
19710
19881
|
}
|
|
19711
19882
|
function nextBranchIndex(rows, index, dir) {
|
|
19712
19883
|
if (rows.length === 0)
|
|
@@ -19729,39 +19900,40 @@ function toggleExpanded(expanded, sessionID) {
|
|
|
19729
19900
|
next.add(sessionID);
|
|
19730
19901
|
return next;
|
|
19731
19902
|
}
|
|
19732
|
-
var FILTER_ORDER = ["default", "no-tools", "user-only", "labeled", "all"];
|
|
19733
|
-
function cycleFilter(filter) {
|
|
19734
|
-
const idx = FILTER_ORDER.indexOf(filter);
|
|
19735
|
-
return FILTER_ORDER[(idx + 1) % FILTER_ORDER.length];
|
|
19736
|
-
}
|
|
19737
19903
|
function resolveSelection(view, preferredId, currentRowId, previousIndex) {
|
|
19738
19904
|
if (view.rows.length === 0)
|
|
19739
19905
|
return -1;
|
|
19906
|
+
const land = (i) => {
|
|
19907
|
+
const ahead = scan(view.rows, i, 1);
|
|
19908
|
+
return ahead === -1 ? scan(view.rows, i, -1) : ahead;
|
|
19909
|
+
};
|
|
19740
19910
|
if (preferredId !== undefined) {
|
|
19741
19911
|
const exact = view.indexById[preferredId];
|
|
19742
19912
|
if (exact !== undefined)
|
|
19743
|
-
return exact;
|
|
19913
|
+
return land(exact);
|
|
19744
19914
|
const owner = preferredId.split(":").slice(0, 2).join(":");
|
|
19745
19915
|
for (let i = 0;i < view.rows.length; i++) {
|
|
19746
19916
|
const row = view.rows[i];
|
|
19917
|
+
if (row.kind === "separator")
|
|
19918
|
+
continue;
|
|
19747
19919
|
const rowOwner = row.kind === "branch" ? row.id : `${row.sessionID}:${row.messageID}`;
|
|
19748
19920
|
if (rowOwner === owner)
|
|
19749
19921
|
return i;
|
|
19750
19922
|
}
|
|
19751
19923
|
}
|
|
19752
19924
|
if (previousIndex !== undefined && previousIndex >= 0)
|
|
19753
|
-
return Math.min(previousIndex, view.rows.length - 1);
|
|
19925
|
+
return land(Math.min(previousIndex, view.rows.length - 1));
|
|
19754
19926
|
if (currentRowId !== undefined) {
|
|
19755
19927
|
const idx = view.indexById[currentRowId];
|
|
19756
19928
|
if (idx !== undefined)
|
|
19757
|
-
return idx;
|
|
19929
|
+
return land(idx);
|
|
19758
19930
|
}
|
|
19759
19931
|
if (view.currentRowId !== undefined) {
|
|
19760
19932
|
const idx = view.indexById[view.currentRowId];
|
|
19761
19933
|
if (idx !== undefined)
|
|
19762
|
-
return idx;
|
|
19934
|
+
return land(idx);
|
|
19763
19935
|
}
|
|
19764
|
-
return 0;
|
|
19936
|
+
return land(0);
|
|
19765
19937
|
}
|
|
19766
19938
|
|
|
19767
19939
|
// src/core/transcript.ts
|
|
@@ -19833,6 +20005,7 @@ function messagePreview(message) {
|
|
|
19833
20005
|
}
|
|
19834
20006
|
|
|
19835
20007
|
// src/core/tree.ts
|
|
20008
|
+
var OFF_PATH_TEXT = "\u2500\u2500 not in this branch's context \u2500\u2500";
|
|
19836
20009
|
var WARN_TOKENS = 1e4;
|
|
19837
20010
|
function userText(message) {
|
|
19838
20011
|
return message.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
|
|
@@ -19959,7 +20132,7 @@ function branchAllowed(filter) {
|
|
|
19959
20132
|
function isCropped(ctx, messageID, partID) {
|
|
19960
20133
|
return ctx.crops.some((c) => c.messageID === messageID && (c.partID === undefined || c.partID === partID));
|
|
19961
20134
|
}
|
|
19962
|
-
function emitAssistantRows(ctx, sessionID, message, depth, gutter, out) {
|
|
20135
|
+
function emitAssistantRows(ctx, sessionID, message, depth, gutter, inContext, out) {
|
|
19963
20136
|
if (message.summary) {
|
|
19964
20137
|
if (!stepAllowed(ctx.filter, "text"))
|
|
19965
20138
|
return;
|
|
@@ -19985,19 +20158,29 @@ function emitAssistantRows(ctx, sessionID, message, depth, gutter, out) {
|
|
|
19985
20158
|
durationMs: durationOfParts(message.parts),
|
|
19986
20159
|
isError: false,
|
|
19987
20160
|
isCropped: false,
|
|
19988
|
-
warn: tokens >= WARN_TOKENS
|
|
20161
|
+
warn: tokens >= WARN_TOKENS,
|
|
20162
|
+
inContext
|
|
19989
20163
|
});
|
|
19990
20164
|
return;
|
|
19991
20165
|
}
|
|
20166
|
+
const collapseThinking = ctx.filter !== "all";
|
|
20167
|
+
const rows = [];
|
|
20168
|
+
let thinkingMs;
|
|
19992
20169
|
let first = true;
|
|
19993
20170
|
for (const part of message.parts) {
|
|
19994
20171
|
const kind = stepKind(part);
|
|
20172
|
+
if (collapseThinking && kind === "reasoning") {
|
|
20173
|
+
const ms = durationOfPart(part);
|
|
20174
|
+
if (ms !== undefined)
|
|
20175
|
+
thinkingMs = (thinkingMs ?? 0) + ms;
|
|
20176
|
+
continue;
|
|
20177
|
+
}
|
|
19995
20178
|
const label = first ? ctx.labels[message.id] : undefined;
|
|
19996
20179
|
if (!stepAllowed(ctx.filter, kind, Boolean(label)))
|
|
19997
20180
|
continue;
|
|
19998
20181
|
const { tokens, estimated } = stepTokensFor(part, message);
|
|
19999
20182
|
first = false;
|
|
20000
|
-
|
|
20183
|
+
rows.push({
|
|
20001
20184
|
kind: "step",
|
|
20002
20185
|
id: `${sessionID}:${message.id}:${part.id}`,
|
|
20003
20186
|
sessionID,
|
|
@@ -20013,9 +20196,40 @@ function emitAssistantRows(ctx, sessionID, message, depth, gutter, out) {
|
|
|
20013
20196
|
isError: part.state?.status === "error",
|
|
20014
20197
|
isCropped: isCropped(ctx, message.id, part.id),
|
|
20015
20198
|
warn: tokens >= WARN_TOKENS,
|
|
20016
|
-
label
|
|
20199
|
+
label,
|
|
20200
|
+
inContext
|
|
20017
20201
|
});
|
|
20018
20202
|
}
|
|
20203
|
+
if (rows.length > 0) {
|
|
20204
|
+
if (thinkingMs !== undefined)
|
|
20205
|
+
rows[0].thinkingMs = thinkingMs;
|
|
20206
|
+
} else if (collapseThinking) {
|
|
20207
|
+
const thinking = message.parts.filter((p) => stepKind(p) === "reasoning");
|
|
20208
|
+
const label = ctx.labels[message.id];
|
|
20209
|
+
if (thinking.length > 0 && stepAllowed(ctx.filter, "reasoning", Boolean(label))) {
|
|
20210
|
+
const tokens = thinking.reduce((sum, p) => sum + stepTokensFor(p, message).tokens, 0);
|
|
20211
|
+
rows.push({
|
|
20212
|
+
kind: "step",
|
|
20213
|
+
id: `${sessionID}:${message.id}:${thinking[0].id}`,
|
|
20214
|
+
sessionID,
|
|
20215
|
+
messageID: message.id,
|
|
20216
|
+
partID: thinking[0].id,
|
|
20217
|
+
depth,
|
|
20218
|
+
gutter,
|
|
20219
|
+
glyph: "\u25CB",
|
|
20220
|
+
preview: partPreview(thinking[0]),
|
|
20221
|
+
tokens,
|
|
20222
|
+
estimated: thinking.some((p) => stepTokensFor(p, message).estimated),
|
|
20223
|
+
durationMs: thinkingMs,
|
|
20224
|
+
isError: false,
|
|
20225
|
+
isCropped: isCropped(ctx, message.id, thinking[0].id),
|
|
20226
|
+
warn: tokens >= WARN_TOKENS,
|
|
20227
|
+
label,
|
|
20228
|
+
inContext
|
|
20229
|
+
});
|
|
20230
|
+
}
|
|
20231
|
+
}
|
|
20232
|
+
out.push(...rows);
|
|
20019
20233
|
}
|
|
20020
20234
|
function shownExpanded(ctx, sessionID) {
|
|
20021
20235
|
const flagged = ctx.expanded.has(sessionID);
|
|
@@ -20076,11 +20290,21 @@ function emitChildBranches(ctx, sessionID, anchorMessageID, depth, gutter, out)
|
|
|
20076
20290
|
pushBranch(ctx, branch, depth, gutter, i === visible.length - 1, showHeaders, out);
|
|
20077
20291
|
});
|
|
20078
20292
|
}
|
|
20293
|
+
function forkAnchorOf(ctx, sessionID) {
|
|
20294
|
+
const childID = ctx.onPathChild.get(sessionID);
|
|
20295
|
+
return childID ? ctx.state.sessions[childID]?.anchorMessageID : undefined;
|
|
20296
|
+
}
|
|
20079
20297
|
function walkSession(ctx, sessionID, messages, depth, gutter, turnStart, out) {
|
|
20080
20298
|
const lastUserIndex = findLastUserIndex(messages, ctx.filter);
|
|
20081
20299
|
const counter = { turn: turnStart };
|
|
20082
20300
|
let inPluginCommand = false;
|
|
20301
|
+
const forkAnchor = forkAnchorOf(ctx, sessionID);
|
|
20302
|
+
let inContext = ctx.onPath.has(sessionID);
|
|
20303
|
+
let separatorDue = inContext && forkAnchor === "";
|
|
20304
|
+
if (separatorDue)
|
|
20305
|
+
inContext = false;
|
|
20083
20306
|
messages.forEach((message, i) => {
|
|
20307
|
+
const before = out.length;
|
|
20084
20308
|
if (message.role === "user") {
|
|
20085
20309
|
inPluginCommand = hiddenPluginTurn(ctx.filter, message);
|
|
20086
20310
|
if (!inPluginCommand) {
|
|
@@ -20106,14 +20330,23 @@ function walkSession(ctx, sessionID, messages, depth, gutter, turnStart, out) {
|
|
|
20106
20330
|
isCurrent: sessionID === ctx.currentSessionID,
|
|
20107
20331
|
isTip: i === lastUserIndex,
|
|
20108
20332
|
isDecision,
|
|
20109
|
-
isSummary
|
|
20333
|
+
isSummary,
|
|
20334
|
+
inContext
|
|
20110
20335
|
});
|
|
20111
20336
|
}
|
|
20112
20337
|
}
|
|
20113
20338
|
} else if (!inPluginCommand) {
|
|
20114
|
-
emitAssistantRows(ctx, sessionID, message, depth, gutter, out);
|
|
20339
|
+
emitAssistantRows(ctx, sessionID, message, depth, gutter, inContext, out);
|
|
20115
20340
|
}
|
|
20116
20341
|
emitChildBranches(ctx, sessionID, message.id, depth, gutter, out);
|
|
20342
|
+
if (separatorDue && out.length > before) {
|
|
20343
|
+
out.splice(before, 0, { kind: "separator", id: `separator:${sessionID}`, depth, gutter, text: OFF_PATH_TEXT });
|
|
20344
|
+
separatorDue = false;
|
|
20345
|
+
}
|
|
20346
|
+
if (inContext && message.id === forkAnchor) {
|
|
20347
|
+
inContext = false;
|
|
20348
|
+
separatorDue = true;
|
|
20349
|
+
}
|
|
20117
20350
|
});
|
|
20118
20351
|
}
|
|
20119
20352
|
function rowSearchFields(row) {
|
|
@@ -20124,6 +20357,8 @@ function rowSearchFields(row) {
|
|
|
20124
20357
|
return row.label ? [row.preview, row.label] : [row.preview];
|
|
20125
20358
|
case "branch":
|
|
20126
20359
|
return row.model ? [row.name, row.model, row.status] : [row.name, row.status];
|
|
20360
|
+
case "separator":
|
|
20361
|
+
return [];
|
|
20127
20362
|
}
|
|
20128
20363
|
}
|
|
20129
20364
|
function rowMatches(row, needle) {
|
|
@@ -20156,17 +20391,17 @@ function applySearch(rows, search) {
|
|
|
20156
20391
|
return rows.filter((_, i) => keep[i]);
|
|
20157
20392
|
}
|
|
20158
20393
|
function computeTotalTokens(transcript) {
|
|
20159
|
-
let
|
|
20394
|
+
let lastIndex2 = -1;
|
|
20160
20395
|
let lastInput = 0;
|
|
20161
20396
|
transcript.messages.forEach((m, idx) => {
|
|
20162
20397
|
if (m.role === "assistant" && typeof m.tokens?.input === "number") {
|
|
20163
|
-
|
|
20398
|
+
lastIndex2 = idx;
|
|
20164
20399
|
lastInput = m.tokens.input;
|
|
20165
20400
|
}
|
|
20166
20401
|
});
|
|
20167
20402
|
let counted = 0;
|
|
20168
20403
|
let guessed = 0;
|
|
20169
|
-
for (let i = Math.max(
|
|
20404
|
+
for (let i = Math.max(lastIndex2, 0);i < transcript.messages.length; i++) {
|
|
20170
20405
|
const m = transcript.messages[i];
|
|
20171
20406
|
if (m.role === "user") {
|
|
20172
20407
|
guessed += estimateTokens(userText(m));
|
|
@@ -20179,7 +20414,7 @@ function computeTotalTokens(transcript) {
|
|
|
20179
20414
|
if (p.type === "tool" || typeof output2 !== "number")
|
|
20180
20415
|
guessed += estimateTokens(partText2(p));
|
|
20181
20416
|
}
|
|
20182
|
-
return { tokens: lastInput + counted + guessed, estimated:
|
|
20417
|
+
return { tokens: lastInput + counted + guessed, estimated: lastIndex2 === -1 || guessed > 0 };
|
|
20183
20418
|
}
|
|
20184
20419
|
function currentChainOf(state, currentSessionID) {
|
|
20185
20420
|
return [...ancestorChainOf(state, currentSessionID), currentSessionID];
|
|
@@ -20231,7 +20466,7 @@ function buildTreeView(o) {
|
|
|
20231
20466
|
let currentRowId;
|
|
20232
20467
|
for (let i = rows.length - 1;i >= 0; i--) {
|
|
20233
20468
|
const r = rows[i];
|
|
20234
|
-
if (r.kind
|
|
20469
|
+
if ((r.kind === "turn" || r.kind === "step") && r.sessionID === o.currentSessionID) {
|
|
20235
20470
|
currentRowId = r.id;
|
|
20236
20471
|
break;
|
|
20237
20472
|
}
|
|
@@ -20323,111 +20558,141 @@ function spanOf(m) {
|
|
|
20323
20558
|
const end = m.time.completed ?? m.parts.reduce((e, p) => Math.max(e, p.state?.time?.end ?? p.time?.end ?? 0), 0);
|
|
20324
20559
|
return end > m.time.created ? end - m.time.created : 0;
|
|
20325
20560
|
}
|
|
20326
|
-
|
|
20327
|
-
|
|
20561
|
+
var EVENT_GLYPH = "\u25AC";
|
|
20562
|
+
function ctreeKindOf2(message) {
|
|
20563
|
+
for (const p of message.parts) {
|
|
20564
|
+
const ctree = p.metadata?.["ctree"];
|
|
20565
|
+
if (ctree?.kind)
|
|
20566
|
+
return ctree.kind;
|
|
20567
|
+
}
|
|
20568
|
+
return;
|
|
20569
|
+
}
|
|
20570
|
+
function isContextMessage(message) {
|
|
20571
|
+
return message.summary === true || ctreeKindOf2(message) === "summary";
|
|
20572
|
+
}
|
|
20573
|
+
function messageEvent(message, turn) {
|
|
20574
|
+
const ms = spanOf(message);
|
|
20575
|
+
return {
|
|
20576
|
+
lane: "input",
|
|
20577
|
+
kind: isContextMessage(message) ? "context" : "user",
|
|
20578
|
+
messageID: message.id,
|
|
20579
|
+
turn,
|
|
20580
|
+
startMs: message.time.created,
|
|
20581
|
+
...ms > 0 ? { durationMs: ms } : {},
|
|
20582
|
+
tokens: message.parts.reduce((s, p) => s + estimateTokens(p.text ?? ""), 0)
|
|
20583
|
+
};
|
|
20584
|
+
}
|
|
20585
|
+
function partEvent(message, part, turn) {
|
|
20586
|
+
const t = part.state?.time ?? part.time;
|
|
20587
|
+
const base = {
|
|
20588
|
+
messageID: message.id,
|
|
20589
|
+
partID: part.id,
|
|
20590
|
+
turn,
|
|
20591
|
+
startMs: t?.start ?? message.time.created,
|
|
20592
|
+
...t?.start !== undefined && t?.end !== undefined ? { durationMs: Math.max(0, t.end - t.start) } : {}
|
|
20593
|
+
};
|
|
20594
|
+
if (part.type === "tool")
|
|
20595
|
+
return { ...base, lane: "tools", kind: "tool", error: part.state?.status === "error", tokens: estimateTokens(part.state?.output ?? "") + estimateTokens(JSON.stringify(part.state?.input ?? "")) };
|
|
20596
|
+
return { ...base, lane: "model", kind: part.type === "reasoning" ? "reasoning" : "text", tokens: estimateTokens(part.text ?? "") };
|
|
20597
|
+
}
|
|
20598
|
+
function eventsOf(transcript) {
|
|
20599
|
+
const out = [];
|
|
20328
20600
|
for (const turn of turnsOf(transcript.messages)) {
|
|
20329
|
-
if (
|
|
20330
|
-
|
|
20331
|
-
for (const m of turn.assistants) {
|
|
20332
|
-
for (const p of m.parts) {
|
|
20333
|
-
if (p.type !== "tool")
|
|
20334
|
-
continue;
|
|
20335
|
-
any2 = true;
|
|
20336
|
-
const out = p.state?.output ?? "";
|
|
20337
|
-
const t = p.state?.time;
|
|
20338
|
-
columns.push({ messageID: m.id, userMessageID: turn.user?.id, partID: p.id, turn: turn.index, input: m.tokens?.input ?? 0, output: 0, tool: estimateTokens(out), toolError: p.state?.status === "error", ms: t?.start !== undefined && t?.end !== undefined ? t.end - t.start : 0 });
|
|
20339
|
-
}
|
|
20340
|
-
}
|
|
20341
|
-
if (!any2) {
|
|
20342
|
-
const last2 = turn.assistants.at(-1);
|
|
20343
|
-
columns.push({ messageID: last2?.id ?? turn.user?.id ?? "", userMessageID: turn.user?.id, turn: turn.index, input: last2?.tokens?.input ?? 0, output: last2?.tokens?.output ?? 0, tool: 0, toolError: false, ms: last2 ? spanOf(last2) : 0 });
|
|
20344
|
-
}
|
|
20345
|
-
continue;
|
|
20346
|
-
}
|
|
20347
|
-
const last = turn.assistants.at(-1);
|
|
20348
|
-
let tool = 0;
|
|
20349
|
-
let toolError = false;
|
|
20350
|
-
let ms = 0;
|
|
20351
|
-
let output2 = 0;
|
|
20601
|
+
if (turn.user)
|
|
20602
|
+
out.push(messageEvent(turn.user, turn.index));
|
|
20352
20603
|
for (const m of turn.assistants) {
|
|
20353
|
-
|
|
20354
|
-
|
|
20604
|
+
const context = isContextMessage(m);
|
|
20605
|
+
if (context)
|
|
20606
|
+
out.push(messageEvent(m, turn.index));
|
|
20355
20607
|
for (const p of m.parts) {
|
|
20356
|
-
|
|
20608
|
+
const kind = stepKind(p);
|
|
20609
|
+
if (kind === "other" || context && kind !== "tool")
|
|
20357
20610
|
continue;
|
|
20358
|
-
|
|
20359
|
-
if (p.state?.status === "error")
|
|
20360
|
-
toolError = true;
|
|
20611
|
+
out.push(partEvent(m, p, turn.index));
|
|
20361
20612
|
}
|
|
20362
20613
|
}
|
|
20363
|
-
columns.push({ messageID: last?.id ?? turn.user?.id ?? "", userMessageID: turn.user?.id, turn: turn.index, input: last?.tokens?.input ?? 0, output: output2, tool, toolError, ms });
|
|
20364
20614
|
}
|
|
20365
|
-
return
|
|
20615
|
+
return out;
|
|
20366
20616
|
}
|
|
20367
|
-
|
|
20368
|
-
|
|
20369
|
-
if (values.length === 0 || width <= 0)
|
|
20370
|
-
return "";
|
|
20371
|
-
const cells = fitColumns(values, width);
|
|
20372
|
-
const max = Math.max(1, scale ?? 0, ...cells);
|
|
20373
|
-
return cells.map((v) => v <= 0 ? " " : BLOCKS[Math.min(7, Math.floor(v / max * 7.999))]).join("");
|
|
20617
|
+
function gapBefore(events, i, mode) {
|
|
20618
|
+
return mode === "turns" && events[i].turn !== events[i - 1].turn ? 2 : 1;
|
|
20374
20619
|
}
|
|
20375
|
-
function
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
|
|
20382
|
-
const per = values.length / width;
|
|
20383
|
-
for (let i = 0;i < width; i++) {
|
|
20384
|
-
const a = Math.floor(i * per);
|
|
20385
|
-
const b = Math.max(a + 1, Math.floor((i + 1) * per));
|
|
20386
|
-
out.push(Math.max(...values.slice(a, b)));
|
|
20387
|
-
}
|
|
20388
|
-
return out;
|
|
20620
|
+
function firstFitting(events, mode, width) {
|
|
20621
|
+
let cells = 0;
|
|
20622
|
+
for (let i = events.length - 1;i >= 0; i--) {
|
|
20623
|
+
const next = cells === 0 ? 1 : cells + 1 + gapBefore(events, i + 1, mode);
|
|
20624
|
+
if (next > width)
|
|
20625
|
+
return i + 1;
|
|
20626
|
+
cells = next;
|
|
20389
20627
|
}
|
|
20390
|
-
|
|
20391
|
-
for (let i = 0;i < width; i++)
|
|
20392
|
-
out.push(values[Math.min(values.length - 1, Math.floor(i / rep))]);
|
|
20393
|
-
return out;
|
|
20628
|
+
return 0;
|
|
20394
20629
|
}
|
|
20395
|
-
function
|
|
20396
|
-
const total =
|
|
20397
|
-
|
|
20398
|
-
|
|
20399
|
-
const
|
|
20400
|
-
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20410
|
-
|
|
20630
|
+
function durationWidths(events, budget) {
|
|
20631
|
+
const total = events.reduce((s, e) => s + Math.max(0, e.durationMs ?? 0), 0);
|
|
20632
|
+
if (total <= 0)
|
|
20633
|
+
return events.map(() => 1);
|
|
20634
|
+
const widths = events.map((e) => Math.max(1, Math.round(Math.max(0, e.durationMs ?? 0) / total * budget)));
|
|
20635
|
+
let over = widths.reduce((s, v) => s + v, 0) - budget;
|
|
20636
|
+
while (over > 0) {
|
|
20637
|
+
let widest = -1;
|
|
20638
|
+
for (let i = 0;i < widths.length; i++)
|
|
20639
|
+
if ((widths[i] ?? 0) > 1 && (widest < 0 || (widths[i] ?? 0) > (widths[widest] ?? 0)))
|
|
20640
|
+
widest = i;
|
|
20641
|
+
if (widest < 0)
|
|
20642
|
+
break;
|
|
20643
|
+
widths[widest] = (widths[widest] ?? 1) - 1;
|
|
20644
|
+
over--;
|
|
20645
|
+
}
|
|
20646
|
+
return widths;
|
|
20647
|
+
}
|
|
20648
|
+
function buildEventStrip(transcript, mode, width) {
|
|
20649
|
+
const w = Math.max(0, Math.floor(width));
|
|
20650
|
+
const all = eventsOf(transcript);
|
|
20651
|
+
const truncatedLeft = firstFitting(all, mode, w);
|
|
20652
|
+
const events = all.slice(truncatedLeft);
|
|
20653
|
+
const gaps = events.map((_, i) => i === 0 ? 0 : gapBefore(events, i, mode));
|
|
20654
|
+
const widths = mode === "duration" ? durationWidths(events, w - gaps.reduce((s, g) => s + g, 0)) : events.map(() => 1);
|
|
20655
|
+
const blank = () => Array.from({ length: w }, () => null);
|
|
20656
|
+
const lanes = { input: blank(), model: blank(), tools: blank() };
|
|
20657
|
+
const empty = { input: true, model: true, tools: true };
|
|
20658
|
+
const spans = [];
|
|
20659
|
+
let cursor = 0;
|
|
20660
|
+
events.forEach((e, i) => {
|
|
20661
|
+
const start = cursor + (gaps[i] ?? 0);
|
|
20662
|
+
const end = Math.min(w, start + (widths[i] ?? 1));
|
|
20663
|
+
for (let c = start;c < end; c++)
|
|
20664
|
+
lanes[e.lane][c] = { lane: e.lane, eventIndex: i, glyph: EVENT_GLYPH, ...e.error ? { error: true } : {} };
|
|
20665
|
+
spans.push({ start, end });
|
|
20666
|
+
empty[e.lane] = false;
|
|
20667
|
+
cursor = start + (widths[i] ?? 1);
|
|
20411
20668
|
});
|
|
20412
|
-
return {
|
|
20669
|
+
return { events, width: w, lanes, spans, empty, truncatedLeft };
|
|
20413
20670
|
}
|
|
20414
|
-
function
|
|
20671
|
+
function stripIndexFor(strip, messageID, partID) {
|
|
20415
20672
|
if (partID) {
|
|
20416
|
-
const
|
|
20417
|
-
if (
|
|
20418
|
-
return
|
|
20673
|
+
const byPart = strip.events.findIndex((e) => e.partID === partID);
|
|
20674
|
+
if (byPart >= 0)
|
|
20675
|
+
return byPart;
|
|
20419
20676
|
}
|
|
20420
|
-
|
|
20421
|
-
return i >= 0 ? i : -1;
|
|
20677
|
+
return strip.events.findIndex((e) => e.messageID === messageID);
|
|
20422
20678
|
}
|
|
20423
20679
|
|
|
20424
20680
|
// src/core/consumers.ts
|
|
20681
|
+
var THINKING = "(thinking)";
|
|
20682
|
+
var THINKING_NOTE = "provider reasoning \xB7 not croppable";
|
|
20425
20683
|
function consumers(transcript, opts = {}) {
|
|
20426
20684
|
const acc = new Map;
|
|
20427
|
-
const add = (source, kind, tokens) => {
|
|
20428
|
-
const c = acc.get(source) ?? { source, kind, tokens: 0, count: 0, share: 0 };
|
|
20685
|
+
const add = (source, kind, tokens, message, part) => {
|
|
20686
|
+
const c = acc.get(source) ?? { source, kind, tokens: 0, count: 0, share: 0, entries: [] };
|
|
20429
20687
|
c.tokens += tokens;
|
|
20430
20688
|
c.count += 1;
|
|
20689
|
+
c.entries.push({
|
|
20690
|
+
messageID: message.id,
|
|
20691
|
+
partID: part.id,
|
|
20692
|
+
tokens,
|
|
20693
|
+
preview: partPreview(part),
|
|
20694
|
+
croppable: part.type === "tool" && part.state?.status === "completed"
|
|
20695
|
+
});
|
|
20431
20696
|
acc.set(source, c);
|
|
20432
20697
|
};
|
|
20433
20698
|
for (const m of transcript.messages) {
|
|
@@ -20435,25 +20700,32 @@ function consumers(transcript, opts = {}) {
|
|
|
20435
20700
|
if (opts.cropped?.has(p.id))
|
|
20436
20701
|
continue;
|
|
20437
20702
|
if (p.type === "tool")
|
|
20438
|
-
add(p.tool ?? "tool", "tool", estimateTokens(p.state?.output ?? "") + estimateTokens(JSON.stringify(p.state?.input ?? "")));
|
|
20703
|
+
add(p.tool ?? "tool", "tool", estimateTokens(p.state?.output ?? "") + estimateTokens(JSON.stringify(p.state?.input ?? "")), m, p);
|
|
20439
20704
|
else if (p.type === "text") {
|
|
20440
20705
|
const kind = p.metadata?.["ctree"]?.kind;
|
|
20441
20706
|
if (kind === "decision")
|
|
20442
|
-
add("\u25C6 decisions", "decision", estimateTokens(p.text ?? ""));
|
|
20707
|
+
add("\u25C6 decisions", "decision", estimateTokens(p.text ?? ""), m, p);
|
|
20443
20708
|
else if (kind === "summary")
|
|
20444
|
-
add("\u25C7 branch summaries", "summary", estimateTokens(p.text ?? ""));
|
|
20709
|
+
add("\u25C7 branch summaries", "summary", estimateTokens(p.text ?? ""), m, p);
|
|
20445
20710
|
else if (m.role === "user")
|
|
20446
|
-
add("\u25CF user prompts", "user", estimateTokens(p.text ?? ""));
|
|
20711
|
+
add("\u25CF user prompts", "user", estimateTokens(p.text ?? ""), m, p);
|
|
20447
20712
|
else if (m.summary)
|
|
20448
|
-
add("\u25C7 compaction summaries", "summary", estimateTokens(p.text ?? ""));
|
|
20713
|
+
add("\u25C7 compaction summaries", "summary", estimateTokens(p.text ?? ""), m, p);
|
|
20449
20714
|
else
|
|
20450
|
-
add("\u25CB assistant text", "assistant", estimateTokens(p.text ?? ""));
|
|
20715
|
+
add("\u25CB assistant text", "assistant", estimateTokens(p.text ?? ""), m, p);
|
|
20451
20716
|
} else if (p.type === "reasoning")
|
|
20452
|
-
add(
|
|
20717
|
+
add(THINKING, "reasoning", estimateTokens(p.text ?? ""), m, p);
|
|
20453
20718
|
}
|
|
20454
20719
|
}
|
|
20455
20720
|
const total = [...acc.values()].reduce((s, c) => s + c.tokens, 0) || 1;
|
|
20456
|
-
|
|
20721
|
+
const limit = opts.limit !== undefined && opts.limit > 0 ? opts.limit : undefined;
|
|
20722
|
+
return [...acc.values()].map((c) => ({
|
|
20723
|
+
...c,
|
|
20724
|
+
share: c.tokens / total,
|
|
20725
|
+
...limit === undefined ? {} : { shareOfWindow: c.tokens / limit },
|
|
20726
|
+
...c.kind === "reasoning" ? { note: THINKING_NOTE } : {},
|
|
20727
|
+
entries: c.entries.sort((a, b) => b.tokens - a.tokens)
|
|
20728
|
+
})).sort((a, b) => b.tokens - a.tokens);
|
|
20457
20729
|
}
|
|
20458
20730
|
function bar(share, width) {
|
|
20459
20731
|
const n = Math.round(share * width);
|
|
@@ -20461,8 +20733,8 @@ function bar(share, width) {
|
|
|
20461
20733
|
}
|
|
20462
20734
|
|
|
20463
20735
|
// src/tui/route.tsx
|
|
20464
|
-
import
|
|
20465
|
-
import
|
|
20736
|
+
import fs5 from "fs";
|
|
20737
|
+
import path4 from "path";
|
|
20466
20738
|
|
|
20467
20739
|
// src/core/cropplan.ts
|
|
20468
20740
|
function sha8(text) {
|
|
@@ -20692,7 +20964,14 @@ function textOf(row) {
|
|
|
20692
20964
|
return plain(row.preview);
|
|
20693
20965
|
return `assistant: ${plain(row.preview)}`;
|
|
20694
20966
|
}
|
|
20967
|
+
function thoughtOf(row) {
|
|
20968
|
+
if (row.kind !== "step" || row.thinkingMs === undefined)
|
|
20969
|
+
return "";
|
|
20970
|
+
return ` \xB7 ${(row.thinkingMs / 1000).toFixed(row.thinkingMs < 1e4 ? 1 : 0)}s thought`;
|
|
20971
|
+
}
|
|
20695
20972
|
function rowLine(row, width, here) {
|
|
20973
|
+
if (row.kind === "separator")
|
|
20974
|
+
return `${row.gutter}${row.text}`;
|
|
20696
20975
|
const tokens = `${row.kind !== "branch" && row.estimated ? "~" : ""}${formatK(row.tokens)}`;
|
|
20697
20976
|
const marker = here ? " \u2190 here" : "";
|
|
20698
20977
|
let body;
|
|
@@ -20705,32 +20984,82 @@ function rowLine(row, width, here) {
|
|
|
20705
20984
|
} else {
|
|
20706
20985
|
const flags = row.kind === "step" ? `${row.label ? ` [${row.label}]` : ""}${row.isCropped ? " \u2702" : ""}${row.warn ? " \u26A0" : ""}${row.isError ? " \u2717" : ""}` : row.label ? ` [${row.label}]` : "";
|
|
20707
20986
|
const dur = row.kind === "step" && row.durationMs !== undefined ? ` ${(row.durationMs / 1000).toFixed(row.durationMs < 1e4 ? 1 : 0)}s` : "";
|
|
20708
|
-
body = `${row.gutter}${glyphOf(row)} ${textOf(row)}${flags}${dur}${marker}`;
|
|
20987
|
+
body = `${row.gutter}${glyphOf(row)} ${textOf(row)}${flags}${dur}${thoughtOf(row)}${marker}`;
|
|
20709
20988
|
}
|
|
20710
20989
|
return fitRow(body, tokens, width);
|
|
20711
20990
|
}
|
|
20991
|
+
function segmentsOf(line, query, thought) {
|
|
20992
|
+
const ranges = [];
|
|
20993
|
+
if (query) {
|
|
20994
|
+
const at = line.toLowerCase().indexOf(query.toLowerCase());
|
|
20995
|
+
if (at >= 0)
|
|
20996
|
+
ranges.push({
|
|
20997
|
+
at,
|
|
20998
|
+
len: query.length,
|
|
20999
|
+
kind: "match"
|
|
21000
|
+
});
|
|
21001
|
+
}
|
|
21002
|
+
if (thought) {
|
|
21003
|
+
const at = line.lastIndexOf(thought);
|
|
21004
|
+
if (at >= 0)
|
|
21005
|
+
ranges.push({
|
|
21006
|
+
at,
|
|
21007
|
+
len: thought.length,
|
|
21008
|
+
kind: "dim"
|
|
21009
|
+
});
|
|
21010
|
+
}
|
|
21011
|
+
if (ranges.length === 0)
|
|
21012
|
+
return [{
|
|
21013
|
+
text: line,
|
|
21014
|
+
kind: "plain"
|
|
21015
|
+
}];
|
|
21016
|
+
const out = [];
|
|
21017
|
+
let cursor = 0;
|
|
21018
|
+
for (const r of ranges.sort((a, b) => a.at - b.at)) {
|
|
21019
|
+
if (r.at < cursor)
|
|
21020
|
+
continue;
|
|
21021
|
+
if (r.at > cursor)
|
|
21022
|
+
out.push({
|
|
21023
|
+
text: line.slice(cursor, r.at),
|
|
21024
|
+
kind: "plain"
|
|
21025
|
+
});
|
|
21026
|
+
out.push({
|
|
21027
|
+
text: line.slice(r.at, r.at + r.len),
|
|
21028
|
+
kind: r.kind
|
|
21029
|
+
});
|
|
21030
|
+
cursor = r.at + r.len;
|
|
21031
|
+
}
|
|
21032
|
+
if (cursor < line.length)
|
|
21033
|
+
out.push({
|
|
21034
|
+
text: line.slice(cursor),
|
|
21035
|
+
kind: "plain"
|
|
21036
|
+
});
|
|
21037
|
+
return out;
|
|
21038
|
+
}
|
|
20712
21039
|
var DEFAULT_KEYS = {
|
|
20713
21040
|
up: ["up", "k"],
|
|
20714
21041
|
down: ["down", "j"],
|
|
20715
21042
|
jump_up: ["shift+up", "shift+k"],
|
|
20716
21043
|
jump_down: ["shift+down", "shift+j"],
|
|
20717
|
-
|
|
21044
|
+
half_up: ["ctrl+u"],
|
|
21045
|
+
half_down: ["ctrl+d"],
|
|
21046
|
+
first: ["gg"],
|
|
20718
21047
|
last: ["shift+g"],
|
|
20719
21048
|
prev_branch: ["["],
|
|
20720
21049
|
next_branch: ["]"],
|
|
20721
21050
|
fold: ["left", "h"],
|
|
20722
21051
|
unfold: ["right", "l"],
|
|
20723
|
-
toggle: ["e"],
|
|
21052
|
+
toggle: ["tab", "e"],
|
|
20724
21053
|
go: ["return"],
|
|
20725
21054
|
branch: ["b"],
|
|
20726
21055
|
crop: ["c"],
|
|
20727
21056
|
crop_toggle_mode: ["t"],
|
|
20728
21057
|
mark: ["space"],
|
|
20729
21058
|
auto: ["a"],
|
|
20730
|
-
undo: ["x"],
|
|
21059
|
+
undo: ["u", "x"],
|
|
20731
21060
|
merge: ["m"],
|
|
20732
21061
|
inspector: ["i"],
|
|
20733
|
-
consumers: ["
|
|
21062
|
+
consumers: ["s"],
|
|
20734
21063
|
copy: ["y"],
|
|
20735
21064
|
mode_duration: ["1"],
|
|
20736
21065
|
mode_turns: ["2"],
|
|
@@ -20739,13 +21068,43 @@ var DEFAULT_KEYS = {
|
|
|
20739
21068
|
decisions: ["shift+d"],
|
|
20740
21069
|
export: ["shift+e"],
|
|
20741
21070
|
label: ["shift+l"],
|
|
20742
|
-
|
|
21071
|
+
filter_pick: ["f"],
|
|
21072
|
+
filter_prev: ["shift+f"],
|
|
20743
21073
|
search: ["/"],
|
|
21074
|
+
search_next: ["n"],
|
|
21075
|
+
search_prev: ["shift+n"],
|
|
20744
21076
|
help: ["?", "shift+/"],
|
|
20745
21077
|
back: ["q", "escape"]
|
|
20746
21078
|
};
|
|
21079
|
+
var EMPTY_TRANSCRIPT = {
|
|
21080
|
+
sessionID: "",
|
|
21081
|
+
title: "",
|
|
21082
|
+
status: "available",
|
|
21083
|
+
messages: []
|
|
21084
|
+
};
|
|
20747
21085
|
var NO_BRANCHES = "No branches yet \xB7 b forks here into a real OpenCode session; nothing is copied or deleted.";
|
|
20748
|
-
var HELP = [
|
|
21086
|
+
var HELP = [`? help \xB7 ? or esc closes \xB7 opencode-context-tree ${PLUGIN_VERSION}`, "Move", " \u2191\u2193 j k \xB7 J K by 20 \xB7 ctrl+d ctrl+u half page \xB7 gg top \xB7 G bottom \xB7 [ ] branch rows", " h l \u2190 \u2192 fold/unfold a branch \xB7 Tab (or e) toggle \xB7 / live search \xB7 n N next/prev match", "Act", " \u23CE go \u2014 a \u2387 header switches to it \xB7 a user turn forks & prefills it \xB7 a step forks after it", " b branch \xB7 m merge \xB7 c crop mode (space mark \xB7 a auto \xB7 t result\u21C4turn \xB7 \u23CE apply \xB7 esc leave)", " u undo (alias x) \xB7 L label \xB7 y copy \xB7 E export decisions", "Views", " i inspector \xB7 1 2 3 lanes (duration/turns/calls) \xB7 0 off \xB7 s consumers \xB7 D decisions \xB7 f F filter", "Legend", " \u25CF user \xB7 \u25CB assistant \xB7 \u2699 tool step \xB7 \u25C6 decision \xB7 \u2263 summary \xB7 \u2387 branch (a real OpenCode session)", " \u2502 \u251C \u2570 draw the topology \xB7 \u25BE open \u25B8 folded \xB7 \u2190 here is the session you are in", " dim rows are not sent to the model; \u2500\u2500 not in this branch's context \u2500\u2500 is where your path forked", " right column is tokens; ~ estimated \xB7 \u26A0 \u226510k \xB7 \u2702 cropped \xB7 \u2717 tool error", " \u2387 colours: open green \xB7 squashed blue \xB7 rejected/discarded red \xB7 abandoned grey", " lanes: Input green you / grey context \xB7 Model purple answer / grey thinking \xB7 Tools orange call / red failed"];
|
|
21087
|
+
var FILTERS = [{
|
|
21088
|
+
title: "default",
|
|
21089
|
+
value: "default",
|
|
21090
|
+
description: "user turns, assistant text, tool steps"
|
|
21091
|
+
}, {
|
|
21092
|
+
title: "no-tools",
|
|
21093
|
+
value: "no-tools",
|
|
21094
|
+
description: "hide \u2699 tool steps"
|
|
21095
|
+
}, {
|
|
21096
|
+
title: "user-only",
|
|
21097
|
+
value: "user-only",
|
|
21098
|
+
description: "\u25CF user turns only"
|
|
21099
|
+
}, {
|
|
21100
|
+
title: "labeled",
|
|
21101
|
+
value: "labeled",
|
|
21102
|
+
description: "labelled rows only"
|
|
21103
|
+
}, {
|
|
21104
|
+
title: "all",
|
|
21105
|
+
value: "all",
|
|
21106
|
+
description: "everything, thinking parts included"
|
|
21107
|
+
}];
|
|
20749
21108
|
function bindingsFor(overrides) {
|
|
20750
21109
|
const out = [];
|
|
20751
21110
|
for (const [cmd, keys] of Object.entries(DEFAULT_KEYS))
|
|
@@ -20777,6 +21136,8 @@ function TreeRoute(props) {
|
|
|
20777
21136
|
const [expanded, setExpanded] = createSignal2(new Set(api2.kv.get(`ctree.expanded.${sessionID}`, [])));
|
|
20778
21137
|
const [filter, setFilter] = createSignal2(api2.kv.get("ctree.filter", "default"));
|
|
20779
21138
|
const [search, setSearch] = createSignal2("");
|
|
21139
|
+
const [searchMode, setSearchMode] = createSignal2(false);
|
|
21140
|
+
let searchBefore = "";
|
|
20780
21141
|
const [selected, setSelected] = createSignal2(0);
|
|
20781
21142
|
const [others, setOthers] = createSignal2({});
|
|
20782
21143
|
const [busy, setBusy] = createSignal2();
|
|
@@ -20786,7 +21147,9 @@ function TreeRoute(props) {
|
|
|
20786
21147
|
const [lanesOn, setLanesOn] = createSignal2(api2.kv.get("ctree.lanesOn", false));
|
|
20787
21148
|
const [inspector, setInspector] = createSignal2(api2.kv.get("ctree.inspector", false));
|
|
20788
21149
|
const [consumerIndex, setConsumerIndex] = createSignal2(0);
|
|
21150
|
+
const [consumerOpen, setConsumerOpen] = createSignal2(new Set);
|
|
20789
21151
|
const [decisionIndex, setDecisionIndex] = createSignal2(0);
|
|
21152
|
+
const [decisionScroll, setDecisionScroll] = createSignal2(0);
|
|
20790
21153
|
const [marked, setMarked] = createSignal2(new Set);
|
|
20791
21154
|
const state = createMemo(() => {
|
|
20792
21155
|
tick();
|
|
@@ -20901,19 +21264,23 @@ function TreeRoute(props) {
|
|
|
20901
21264
|
api2.renderer.on("resize", onResize);
|
|
20902
21265
|
onCleanup(() => void api2.renderer.off("resize", onResize));
|
|
20903
21266
|
const cols = () => size().cols;
|
|
20904
|
-
const
|
|
21267
|
+
const helpHeight = () => panel() === "help" ? Math.min(HELP.length, Math.max(0, size().rows - 12)) : 0;
|
|
21268
|
+
const height = () => Math.max(4, size().rows - 8 - (lanesOn() && size().rows >= 12 ? 3 : 0) - helpHeight());
|
|
20905
21269
|
const width = () => Math.max(60, cols() - 4);
|
|
21270
|
+
const overflow = () => view().rows.length > height() - 2;
|
|
21271
|
+
const rowsHeight = () => overflow() ? height() - 2 : height();
|
|
20906
21272
|
const windowStart = createMemo(() => {
|
|
20907
|
-
const h =
|
|
21273
|
+
const h = rowsHeight();
|
|
20908
21274
|
const s = selected();
|
|
20909
21275
|
const n = view().rows.length;
|
|
20910
|
-
|
|
20911
|
-
return start;
|
|
21276
|
+
return Math.max(0, Math.min(s - Math.floor(h / 2), n - h));
|
|
20912
21277
|
});
|
|
20913
|
-
const visible = createMemo(() => view().rows.slice(windowStart(), windowStart() +
|
|
21278
|
+
const visible = createMemo(() => view().rows.slice(windowStart(), windowStart() + rowsHeight()));
|
|
21279
|
+
const hiddenAbove = () => windowStart();
|
|
21280
|
+
const hiddenBelow = () => Math.max(0, view().rows.length - windowStart() - rowsHeight());
|
|
20914
21281
|
const live = () => sessionID ? liveTranscript(api2, sessionID) : undefined;
|
|
20915
21282
|
const currentMessageOf = (row) => {
|
|
20916
|
-
if (row.kind === "branch")
|
|
21283
|
+
if (row.kind === "branch" || row.kind === "separator")
|
|
20917
21284
|
return;
|
|
20918
21285
|
if (row.sessionID === sessionID)
|
|
20919
21286
|
return row.messageID;
|
|
@@ -20958,10 +21325,35 @@ function TreeRoute(props) {
|
|
|
20958
21325
|
const list = cropMode() === "result" ? resultCands() : turnCands();
|
|
20959
21326
|
return list.filter((c) => m.has(markKey(c)));
|
|
20960
21327
|
});
|
|
21328
|
+
function modeForRow(row) {
|
|
21329
|
+
if (row.kind === "step" && resultCands().some((c) => c.partID === (currentPartOf(row) ?? row.partID)))
|
|
21330
|
+
return "result";
|
|
21331
|
+
if (row.kind !== "branch" && row.kind !== "separator" && turnCands().some((c) => c.anchorMessageID === currentMessageOf(row)))
|
|
21332
|
+
return "turn";
|
|
21333
|
+
return;
|
|
21334
|
+
}
|
|
21335
|
+
const armed = () => {
|
|
21336
|
+
const row = current();
|
|
21337
|
+
const c = row ? candidateOf(row) : undefined;
|
|
21338
|
+
if (!c)
|
|
21339
|
+
return false;
|
|
21340
|
+
const key = markKey(c);
|
|
21341
|
+
return marked().has(`${key}:warned`) && !marked().has(key);
|
|
21342
|
+
};
|
|
20961
21343
|
function toggleMark() {
|
|
20962
21344
|
const row = current();
|
|
20963
21345
|
if (!row)
|
|
20964
21346
|
return;
|
|
21347
|
+
if (!cropMode()) {
|
|
21348
|
+
const mode = modeForRow(row);
|
|
21349
|
+
if (!mode) {
|
|
21350
|
+
api2.ui.toast({
|
|
21351
|
+
message: "nothing croppable on this row \u2014 c opens crop mode"
|
|
21352
|
+
});
|
|
21353
|
+
return;
|
|
21354
|
+
}
|
|
21355
|
+
setCropMode(mode);
|
|
21356
|
+
}
|
|
20965
21357
|
const c = candidateOf(row);
|
|
20966
21358
|
debug("crop.mark", {
|
|
20967
21359
|
row: row.id,
|
|
@@ -20993,6 +21385,13 @@ function TreeRoute(props) {
|
|
|
20993
21385
|
next.add(key);
|
|
20994
21386
|
setMarked(next);
|
|
20995
21387
|
}
|
|
21388
|
+
async function leaveCropMode() {
|
|
21389
|
+
const n = selectedCandidates().length;
|
|
21390
|
+
if (n > 0 && !await confirm(`Drop ${n} mark${n === 1 ? "" : "s"}?`, "Nothing has been cropped yet \u2014 the marks are lost, the transcript is untouched."))
|
|
21391
|
+
return;
|
|
21392
|
+
setCropMode(undefined);
|
|
21393
|
+
setMarked(new Set);
|
|
21394
|
+
}
|
|
20996
21395
|
function autoMarkAll() {
|
|
20997
21396
|
if (cropMode() !== "result")
|
|
20998
21397
|
return;
|
|
@@ -21070,69 +21469,64 @@ function TreeRoute(props) {
|
|
|
21070
21469
|
},
|
|
21071
21470
|
parts: m.parts
|
|
21072
21471
|
}))));
|
|
21073
|
-
const band = () => bandFor(contextSize().tokens);
|
|
21472
|
+
const band = () => bandFor(contextSize().tokens, contextLimit());
|
|
21074
21473
|
const branchOfCurrent = () => sessionID ? state().sessions[sessionID] : undefined;
|
|
21075
21474
|
const userTurns = () => (live()?.messages ?? []).filter((m) => m.role === "user").length;
|
|
21076
|
-
const lanes = createMemo(() => live() ? buildLanes(live(), laneMode() === "duration" ? "turns" : laneMode()) : {
|
|
21077
|
-
mode: laneMode(),
|
|
21078
|
-
columns: []
|
|
21079
|
-
});
|
|
21080
21475
|
const laneWidth = () => Math.max(10, Math.min(width() - 46, 80));
|
|
21081
|
-
const
|
|
21082
|
-
|
|
21083
|
-
if (laneMode() === "duration") {
|
|
21084
|
-
const w = durationWeighted(l, laneWidth());
|
|
21085
|
-
return {
|
|
21086
|
-
input: w.input,
|
|
21087
|
-
output: w.output,
|
|
21088
|
-
tool: w.tool,
|
|
21089
|
-
toolError: w.toolError,
|
|
21090
|
-
cellFor: (col) => w.input.findIndex((_, i) => w.columnAt(i) === col)
|
|
21091
|
-
};
|
|
21092
|
-
}
|
|
21093
|
-
const n = l.columns.length;
|
|
21094
|
-
const cellFor = (col) => n === 0 ? -1 : Math.floor(col * laneWidth() / Math.max(n, laneWidth())) + (n < laneWidth() ? Math.floor(laneWidth() / n / 2) : 0);
|
|
21095
|
-
return {
|
|
21096
|
-
input: l.columns.map((c) => c.input),
|
|
21097
|
-
output: l.columns.map((c) => c.output),
|
|
21098
|
-
tool: l.columns.map((c) => c.tool),
|
|
21099
|
-
toolError: l.columns.map((c) => c.toolError),
|
|
21100
|
-
cellFor
|
|
21101
|
-
};
|
|
21102
|
-
});
|
|
21103
|
-
const cursorCell = createMemo(() => {
|
|
21476
|
+
const strip = createMemo(() => buildEventStrip(live() ?? EMPTY_TRANSCRIPT, laneMode(), laneWidth()));
|
|
21477
|
+
const cursorEvents = createMemo(() => {
|
|
21104
21478
|
const row = current();
|
|
21105
|
-
|
|
21106
|
-
|
|
21479
|
+
const hit = new Set;
|
|
21480
|
+
if (!row || row.kind === "branch" || row.kind === "separator")
|
|
21481
|
+
return hit;
|
|
21107
21482
|
const mid = currentMessageOf(row) ?? row.messageID;
|
|
21108
21483
|
const pid = row.kind === "step" ? currentPartOf(row) ?? row.partID : undefined;
|
|
21109
|
-
const
|
|
21110
|
-
|
|
21484
|
+
const own2 = stripIndexFor(strip(), mid, pid);
|
|
21485
|
+
if (own2 >= 0)
|
|
21486
|
+
hit.add(own2);
|
|
21487
|
+
strip().events.forEach((e, i) => {
|
|
21488
|
+
if (e.messageID !== mid)
|
|
21489
|
+
return;
|
|
21490
|
+
if (row.kind === "turn" ? e.lane === "input" : e.kind === "reasoning")
|
|
21491
|
+
hit.add(i);
|
|
21492
|
+
});
|
|
21493
|
+
return hit;
|
|
21111
21494
|
});
|
|
21112
|
-
const
|
|
21113
|
-
|
|
21114
|
-
|
|
21115
|
-
|
|
21116
|
-
|
|
21117
|
-
|
|
21118
|
-
|
|
21119
|
-
|
|
21120
|
-
|
|
21121
|
-
|
|
21495
|
+
const cellColor = (cell) => {
|
|
21496
|
+
if (cell.error)
|
|
21497
|
+
return t.error;
|
|
21498
|
+
const e = strip().events[cell.eventIndex];
|
|
21499
|
+
if (!e)
|
|
21500
|
+
return t.textMuted;
|
|
21501
|
+
if (e.lane === "tools")
|
|
21502
|
+
return t.warning;
|
|
21503
|
+
if (e.lane === "input")
|
|
21504
|
+
return e.kind === "user" ? t.success : t.textMuted;
|
|
21505
|
+
return e.kind === "reasoning" ? t.textMuted : t.accent;
|
|
21506
|
+
};
|
|
21507
|
+
const laneRuns = (lane) => {
|
|
21508
|
+
const cur = cursorEvents();
|
|
21122
21509
|
const runs = [];
|
|
21123
|
-
for (
|
|
21124
|
-
const
|
|
21510
|
+
for (const cell of strip().lanes[lane]) {
|
|
21511
|
+
const sel = cell !== null && cur.has(cell.eventIndex);
|
|
21512
|
+
const color = cell === null ? t.textMuted : cellColor(cell);
|
|
21513
|
+
const fg = sel ? t.background : color;
|
|
21514
|
+
const bg = sel ? color : undefined;
|
|
21125
21515
|
const last = runs[runs.length - 1];
|
|
21126
|
-
if (last && last.
|
|
21127
|
-
last.text +=
|
|
21516
|
+
if (last && last.fg === fg && last.bg === bg)
|
|
21517
|
+
last.text += cell?.glyph ?? " ";
|
|
21128
21518
|
else
|
|
21129
21519
|
runs.push({
|
|
21130
|
-
text:
|
|
21131
|
-
|
|
21520
|
+
text: cell?.glyph ?? " ",
|
|
21521
|
+
fg,
|
|
21522
|
+
bg
|
|
21132
21523
|
});
|
|
21133
21524
|
}
|
|
21134
21525
|
return runs;
|
|
21135
|
-
}
|
|
21526
|
+
};
|
|
21527
|
+
const inputRuns = createMemo(() => laneRuns("input"));
|
|
21528
|
+
const modelRuns = createMemo(() => laneRuns("model"));
|
|
21529
|
+
const toolRuns = createMemo(() => laneRuns("tools"));
|
|
21136
21530
|
const contextLimit = createMemo(() => sessionID ? modelContextLimit(api2, sessionID) : undefined);
|
|
21137
21531
|
const laneRoom = () => height() >= 12 && panel() === "tree";
|
|
21138
21532
|
const showLanes = () => laneRoom() && lanesOn() && userTurns() >= 3;
|
|
@@ -21154,7 +21548,7 @@ function TreeRoute(props) {
|
|
|
21154
21548
|
const noBranchesLines = () => NO_BRANCHES.length + 2 <= rowWidth() ? [NO_BRANCHES] : NO_BRANCHES.split(/(?<=;) /);
|
|
21155
21549
|
const inspectorLines = createMemo(() => {
|
|
21156
21550
|
const row = current();
|
|
21157
|
-
if (!row)
|
|
21551
|
+
if (!row || row.kind === "separator")
|
|
21158
21552
|
return [];
|
|
21159
21553
|
const w = inspectorWidth() - 3;
|
|
21160
21554
|
const clip2 = (x) => x.length > w ? `${x.slice(0, w - 1)}\u2026` : x;
|
|
@@ -21201,19 +21595,37 @@ function TreeRoute(props) {
|
|
|
21201
21595
|
const msg = tr?.messages.find((m) => m.id === row.messageID);
|
|
21202
21596
|
const turn = view().rows.slice(0, view().indexById[row.id] + 1).filter((r) => r.kind === "turn").at(-1);
|
|
21203
21597
|
if (row.kind === "turn") {
|
|
21204
|
-
|
|
21598
|
+
const text = msg?.parts.map((p) => p.text ?? "").join(`
|
|
21599
|
+
`) ?? row.preview;
|
|
21600
|
+
if (row.isDecision) {
|
|
21601
|
+
head(`\u25C6 ${decisionSummary(text).title}`);
|
|
21602
|
+
kv("Tokens", `~${formatK(row.tokens)}`);
|
|
21603
|
+
const lines = renderDecision(text, w);
|
|
21604
|
+
for (const l of lines.slice(0, 16))
|
|
21605
|
+
out.push({
|
|
21606
|
+
fg: t.text,
|
|
21607
|
+
text: l
|
|
21608
|
+
});
|
|
21609
|
+
if (lines.length > 16)
|
|
21610
|
+
muted(`\u2026 ${lines.length - 16} more lines (y to copy)`);
|
|
21611
|
+
return out;
|
|
21612
|
+
}
|
|
21613
|
+
head(`${row.isSummary ? "\u25C7 summary" : "\u25CF user"} \xB7 T${row.turn}`);
|
|
21205
21614
|
if (row.label)
|
|
21206
21615
|
kv("Label", row.label);
|
|
21207
21616
|
kv("Tokens", `~${formatK(row.tokens)}`);
|
|
21208
21617
|
kv("At", msg ? new Date(msg.time.created).toISOString().slice(11, 19) : "?");
|
|
21209
|
-
|
|
21210
|
-
|
|
21618
|
+
if (!row.inContext)
|
|
21619
|
+
muted("not in this branch's context");
|
|
21620
|
+
block("Text", text, 14);
|
|
21211
21621
|
return out;
|
|
21212
21622
|
}
|
|
21213
21623
|
const part = msg?.parts.find((p) => p.id === row.partID);
|
|
21214
21624
|
const stepNo = msg ? msg.parts.filter((p) => p.type === "tool" || p.type === "text").findIndex((p) => p.id === row.partID) + 1 : 0;
|
|
21215
21625
|
head(`${row.glyph} ${part?.type === "tool" ? part.tool : row.glyph === "\u25C7" ? "compaction" : "assistant"} \xB7 T${turn?.kind === "turn" ? turn.turn : "?"} \xB7 step ${stepNo}`);
|
|
21216
21626
|
kv("Hierarchy", `T${turn?.kind === "turn" ? turn.turn : "?"} \u203A assistant \u203A step ${stepNo}`);
|
|
21627
|
+
if (!row.inContext)
|
|
21628
|
+
muted("not in this branch's context");
|
|
21217
21629
|
if (part?.type === "tool") {
|
|
21218
21630
|
const st = part.state;
|
|
21219
21631
|
const dur = st?.time?.start !== undefined && st?.time?.end !== undefined ? `${st.time.end - st.time.start} ms` : "?";
|
|
@@ -21223,25 +21635,66 @@ function TreeRoute(props) {
|
|
|
21223
21635
|
block("Result", String(st?.output ?? ""), 10);
|
|
21224
21636
|
kv("Timing", st?.time?.start ? `started ${new Date(st.time.start).toISOString().slice(11, 23)} \xB7 ${dur} \xB7 session ts` : "n/a");
|
|
21225
21637
|
const cand = resultCands().find((c) => c.partID === (currentPartOf(row) ?? row.partID));
|
|
21226
|
-
kv("Crop", row.isCropped ?
|
|
21638
|
+
kv("Crop", row.isCropped ? `\u2702 cropped (${UNDO_KEY} to restore)` : cand ? cand.protections.length ? `protected: ${cand.protections.join(", ")}` : "c then space to stub this result" : "n/a");
|
|
21227
21639
|
} else {
|
|
21228
21640
|
kv("Tokens", `~${formatK(row.tokens)}`);
|
|
21229
21641
|
if (row.durationMs !== undefined)
|
|
21230
21642
|
kv("Duration", `${(row.durationMs / 1000).toFixed(1)} s`);
|
|
21643
|
+
if (row.thinkingMs !== undefined)
|
|
21644
|
+
kv("Thought", `${(row.thinkingMs / 1000).toFixed(1)} s`);
|
|
21231
21645
|
block("Text", part?.text ?? row.preview, 14);
|
|
21232
21646
|
}
|
|
21233
21647
|
return out;
|
|
21234
21648
|
});
|
|
21235
21649
|
const consumerRows = createMemo(() => live() ? consumers(live(), {
|
|
21236
|
-
cropped: alreadyCropped()
|
|
21650
|
+
cropped: alreadyCropped(),
|
|
21651
|
+
limit: contextLimit()
|
|
21237
21652
|
}) : []);
|
|
21653
|
+
const consumerLines = createMemo(() => consumerRows().flatMap((c) => [{
|
|
21654
|
+
bucket: c
|
|
21655
|
+
}, ...consumerOpen().has(c.source) ? c.entries.map((e) => ({
|
|
21656
|
+
bucket: c,
|
|
21657
|
+
entry: e
|
|
21658
|
+
})) : []]));
|
|
21659
|
+
const consumerLine = () => consumerLines()[Math.min(consumerIndex(), consumerLines().length - 1)];
|
|
21660
|
+
const consumerMax = () => Math.max(1, ...consumerRows().map((c) => c.tokens));
|
|
21661
|
+
function toggleConsumer(open2) {
|
|
21662
|
+
const line = consumerLine();
|
|
21663
|
+
if (!line)
|
|
21664
|
+
return;
|
|
21665
|
+
const next = new Set(consumerOpen());
|
|
21666
|
+
if (open2)
|
|
21667
|
+
next.add(line.bucket.source);
|
|
21668
|
+
else
|
|
21669
|
+
next.delete(line.bucket.source);
|
|
21670
|
+
setConsumerOpen(next);
|
|
21671
|
+
}
|
|
21672
|
+
function markConsumerEntry() {
|
|
21673
|
+
const line = consumerLine();
|
|
21674
|
+
if (!line)
|
|
21675
|
+
return;
|
|
21676
|
+
if (!line.entry) {
|
|
21677
|
+
toggleConsumer(true);
|
|
21678
|
+
return;
|
|
21679
|
+
}
|
|
21680
|
+
const cand = line.entry.croppable ? resultCands().find((r) => r.partID === line.entry?.partID) : undefined;
|
|
21681
|
+
if (!cand)
|
|
21682
|
+
return;
|
|
21683
|
+
setCropMode("result");
|
|
21684
|
+
const next = new Set(marked());
|
|
21685
|
+
if (next.has(cand.partID))
|
|
21686
|
+
next.delete(cand.partID);
|
|
21687
|
+
else
|
|
21688
|
+
next.add(cand.partID);
|
|
21689
|
+
setMarked(next);
|
|
21690
|
+
}
|
|
21238
21691
|
function cropConsumer() {
|
|
21239
|
-
const c =
|
|
21692
|
+
const c = consumerLine()?.bucket;
|
|
21240
21693
|
setPanel("tree");
|
|
21241
21694
|
if (!c || c.kind !== "tool") {
|
|
21242
21695
|
setCropMode("result");
|
|
21243
21696
|
api2.ui.toast({
|
|
21244
|
-
message: c ? `${c.source} is not a tool result; mark rows by hand` : "nothing to crop"
|
|
21697
|
+
message: c ? c.note ?? `${c.source} is not a tool result; mark rows by hand` : "nothing to crop"
|
|
21245
21698
|
});
|
|
21246
21699
|
return;
|
|
21247
21700
|
}
|
|
@@ -21254,20 +21707,18 @@ function TreeRoute(props) {
|
|
|
21254
21707
|
}
|
|
21255
21708
|
function copySelected() {
|
|
21256
21709
|
const row = current();
|
|
21257
|
-
if (!row || row.kind === "branch")
|
|
21710
|
+
if (!row || row.kind === "branch" || row.kind === "separator")
|
|
21258
21711
|
return;
|
|
21259
21712
|
const tr = row.sessionID === sessionID ? live() : others()[row.sessionID];
|
|
21260
21713
|
const msg = tr?.messages.find((m) => m.id === row.messageID);
|
|
21261
21714
|
const text = row.kind === "step" ? String(msg?.parts.find((p) => p.id === row.partID)?.state?.output ?? msg?.parts.find((p) => p.id === row.partID)?.text ?? "") : msg?.parts.map((p) => p.text ?? "").join(`
|
|
21262
21715
|
`) ?? "";
|
|
21263
|
-
const file2 = path3.join(directory, ".opencode", "context-tree", "last-copy.txt");
|
|
21264
21716
|
try {
|
|
21265
|
-
|
|
21266
|
-
|
|
21267
|
-
});
|
|
21268
|
-
fs4.writeFileSync(file2, text);
|
|
21717
|
+
const {
|
|
21718
|
+
hint
|
|
21719
|
+
} = copyText(api2, text, directory);
|
|
21269
21720
|
api2.ui.toast({
|
|
21270
|
-
message: `
|
|
21721
|
+
message: `copied ${text.length} chars \u2192 ${hint}`
|
|
21271
21722
|
});
|
|
21272
21723
|
} catch (e) {
|
|
21273
21724
|
api2.ui.toast({
|
|
@@ -21393,6 +21844,10 @@ function TreeRoute(props) {
|
|
|
21393
21844
|
bump();
|
|
21394
21845
|
}
|
|
21395
21846
|
}
|
|
21847
|
+
const sessionLabel = (id) => {
|
|
21848
|
+
const name = state().sessions[id]?.name;
|
|
21849
|
+
return name ? `\u2387 ${name}` : others()[id]?.title ?? api2.state.session.get(id)?.title ?? id;
|
|
21850
|
+
};
|
|
21396
21851
|
async function jump() {
|
|
21397
21852
|
const row = current();
|
|
21398
21853
|
if (!row || !sessionID)
|
|
@@ -21415,16 +21870,19 @@ function TreeRoute(props) {
|
|
|
21415
21870
|
});
|
|
21416
21871
|
return;
|
|
21417
21872
|
}
|
|
21418
|
-
|
|
21419
|
-
|
|
21420
|
-
|
|
21421
|
-
|
|
21422
|
-
}
|
|
21873
|
+
const from = sessionLabel(plan.sessionID);
|
|
21874
|
+
const ok = await confirm(plan.kind === "switch" ? `Switch to ${from}?` : plan.mode === "redo" ? "Fork & prefill this turn?" : "Fork after this step?", plan.kind === "switch" ? `The session you are on now stays exactly as it is. ${UNDO_KEY} undoes this.` : `A new OpenCode session forks from ${from} at this point; nothing is deleted. ${UNDO_KEY} undoes this.`);
|
|
21875
|
+
if (!ok)
|
|
21876
|
+
return;
|
|
21423
21877
|
const summary = await askSummary();
|
|
21424
|
-
await executeJump(ctx, plan, {
|
|
21878
|
+
const target2 = await executeJump(ctx, plan, {
|
|
21425
21879
|
currentSessionID: sessionID,
|
|
21426
21880
|
summary
|
|
21427
21881
|
});
|
|
21882
|
+
if (target2)
|
|
21883
|
+
api2.ui.toast({
|
|
21884
|
+
message: `moved to ${sessionLabel(target2)} \xB7 ${UNDO_KEY} undoes it`
|
|
21885
|
+
});
|
|
21428
21886
|
});
|
|
21429
21887
|
}
|
|
21430
21888
|
async function branch() {
|
|
@@ -21477,7 +21935,7 @@ function TreeRoute(props) {
|
|
|
21477
21935
|
}
|
|
21478
21936
|
async function label() {
|
|
21479
21937
|
const row = current();
|
|
21480
|
-
if (!row || row.kind === "branch")
|
|
21938
|
+
if (!row || row.kind === "branch" || row.kind === "separator")
|
|
21481
21939
|
return;
|
|
21482
21940
|
const st = state();
|
|
21483
21941
|
const existing = st.labels[row.messageID]?.label;
|
|
@@ -21495,7 +21953,7 @@ function TreeRoute(props) {
|
|
|
21495
21953
|
const row = current();
|
|
21496
21954
|
if (!row)
|
|
21497
21955
|
return;
|
|
21498
|
-
const target2 = row.kind === "
|
|
21956
|
+
const target2 = row.kind === "separator" ? undefined : row.kind === "branch" || row.depth > 0 ? row.sessionID : undefined;
|
|
21499
21957
|
if (!target2)
|
|
21500
21958
|
return;
|
|
21501
21959
|
const shown = row.kind === "branch" ? row.expanded : true;
|
|
@@ -21529,14 +21987,18 @@ function TreeRoute(props) {
|
|
|
21529
21987
|
return;
|
|
21530
21988
|
}
|
|
21531
21989
|
const siblings = Object.values(state().sessions).filter((x) => x.parentSessionID === b.parentSessionID && x.sessionID !== sessionID && x.status === "open").length;
|
|
21532
|
-
const
|
|
21533
|
-
|
|
21990
|
+
const parent = others()[b.parentSessionID];
|
|
21991
|
+
const target2 = mergeTargetOf(b.parentSessionID === state().root ? TRUNK_LABEL : state().sessions[b.parentSessionID]?.name ?? TRUNK_LABEL, parent?.messages ?? []);
|
|
21992
|
+
const turns = ownTurnCount(live()?.messages ?? [], {
|
|
21993
|
+
messageID: b.anchorMessageID,
|
|
21994
|
+
parentMessageIDs: parent?.messages.map((m) => m.id) ?? []
|
|
21995
|
+
});
|
|
21996
|
+
const mode = await select(mergeDialogTitle(b.name ?? "branch", target2), mergeDialogOptions({
|
|
21997
|
+
siblings,
|
|
21998
|
+
turns
|
|
21534
21999
|
}));
|
|
21535
22000
|
if (!mode)
|
|
21536
22001
|
return;
|
|
21537
|
-
let note;
|
|
21538
|
-
if (mode === "discard")
|
|
21539
|
-
note = await prompt("Why? (optional note on the close marker)", "dead end") ?? undefined;
|
|
21540
22002
|
const inApp = !hasEditor() ? async (draft) => {
|
|
21541
22003
|
const ok = await confirm("Accept the drafted record as-is?", `${draft.slice(0, 400)}${draft.length > 400 ? "\u2026" : ""}
|
|
21542
22004
|
|
|
@@ -21549,7 +22011,6 @@ ${MERGE_TRUST}
|
|
|
21549
22011
|
await mergeBranch(ctx, {
|
|
21550
22012
|
sessionID,
|
|
21551
22013
|
mode,
|
|
21552
|
-
note,
|
|
21553
22014
|
confirm: inApp
|
|
21554
22015
|
});
|
|
21555
22016
|
});
|
|
@@ -21561,9 +22022,9 @@ ${MERGE_TRUST}
|
|
|
21561
22022
|
sessionID: d.sessionID,
|
|
21562
22023
|
at: d.recordedAt
|
|
21563
22024
|
}));
|
|
21564
|
-
const file2 =
|
|
22025
|
+
const file2 = path4.join(directory, "ctree-decisions.md");
|
|
21565
22026
|
try {
|
|
21566
|
-
|
|
22027
|
+
fs5.writeFileSync(file2, exportDecisions(records));
|
|
21567
22028
|
api2.ui.toast({
|
|
21568
22029
|
variant: "success",
|
|
21569
22030
|
message: `wrote ${records.length} record${records.length === 1 ? "" : "s"} \u2192 ${file2}`
|
|
@@ -21579,7 +22040,7 @@ ${MERGE_TRUST}
|
|
|
21579
22040
|
const d = decisions()[decisionIndex()];
|
|
21580
22041
|
if (!d)
|
|
21581
22042
|
return;
|
|
21582
|
-
const idx = view().rows.findIndex((r) => r.kind
|
|
22043
|
+
const idx = view().rows.findIndex((r) => (r.kind === "turn" || r.kind === "step") && r.messageID === d.messageID);
|
|
21583
22044
|
setPanel("tree");
|
|
21584
22045
|
if (idx >= 0)
|
|
21585
22046
|
setSelected(idx);
|
|
@@ -21588,125 +22049,251 @@ ${MERGE_TRUST}
|
|
|
21588
22049
|
message: "that record lives in another session"
|
|
21589
22050
|
});
|
|
21590
22051
|
}
|
|
22052
|
+
const treePanel = () => panel() === "tree";
|
|
22053
|
+
const inCrop = () => cropMode() !== undefined;
|
|
22054
|
+
const treeIdle = () => treePanel() && !inCrop();
|
|
22055
|
+
const listPanel = () => treePanel() || panel() === "consumers";
|
|
22056
|
+
function setFilterTo(next) {
|
|
22057
|
+
setFilter(next);
|
|
22058
|
+
api2.kv.set("ctree.filter", next);
|
|
22059
|
+
}
|
|
22060
|
+
async function pickFilter() {
|
|
22061
|
+
const next = await select("Filter rows", FILTERS.map((f) => ({
|
|
22062
|
+
title: `${f.value === filter() ? "\u25CF" : " "} ${f.title}`,
|
|
22063
|
+
value: f.value,
|
|
22064
|
+
description: f.description
|
|
22065
|
+
})));
|
|
22066
|
+
if (next)
|
|
22067
|
+
setFilterTo(next);
|
|
22068
|
+
}
|
|
22069
|
+
function moveIndex(delta) {
|
|
22070
|
+
if (panel() === "decisions") {
|
|
22071
|
+
setDecisionIndex((i) => Math.min(Math.max(0, decisions().length - 1), Math.max(0, i + delta)));
|
|
22072
|
+
setDecisionScroll(0);
|
|
22073
|
+
return;
|
|
22074
|
+
}
|
|
22075
|
+
if (panel() === "consumers") {
|
|
22076
|
+
setConsumerIndex((i) => Math.min(Math.max(0, consumerLines().length - 1), Math.max(0, i + delta)));
|
|
22077
|
+
return;
|
|
22078
|
+
}
|
|
22079
|
+
setSelected((i) => moveSelection(view().rows, i, delta));
|
|
22080
|
+
}
|
|
22081
|
+
function halfPage(dir) {
|
|
22082
|
+
const half = Math.max(1, Math.floor(height() / 2));
|
|
22083
|
+
if (panel() === "decisions")
|
|
22084
|
+
setDecisionScroll((s) => Math.max(0, s + dir * half));
|
|
22085
|
+
else
|
|
22086
|
+
moveIndex(dir * half);
|
|
22087
|
+
}
|
|
22088
|
+
function gotoEdge(dir) {
|
|
22089
|
+
if (panel() === "decisions") {
|
|
22090
|
+
setDecisionIndex(dir === -1 ? 0 : Math.max(0, decisions().length - 1));
|
|
22091
|
+
setDecisionScroll(0);
|
|
22092
|
+
return;
|
|
22093
|
+
}
|
|
22094
|
+
if (panel() === "consumers") {
|
|
22095
|
+
setConsumerIndex(dir === -1 ? 0 : Math.max(0, consumerLines().length - 1));
|
|
22096
|
+
return;
|
|
22097
|
+
}
|
|
22098
|
+
const i = dir === -1 ? firstIndex(view().rows) : lastIndex(view().rows);
|
|
22099
|
+
if (i >= 0)
|
|
22100
|
+
setSelected(i);
|
|
22101
|
+
}
|
|
22102
|
+
const matchIn = (line) => {
|
|
22103
|
+
const q = search().trim().toLowerCase();
|
|
22104
|
+
return q ? line.toLowerCase().indexOf(q) : -1;
|
|
22105
|
+
};
|
|
22106
|
+
function moveMatch(dir) {
|
|
22107
|
+
const rows = view().rows;
|
|
22108
|
+
const q = search().trim();
|
|
22109
|
+
if (rows.length === 0 || !q)
|
|
22110
|
+
return;
|
|
22111
|
+
for (let step = 1;step <= rows.length; step++) {
|
|
22112
|
+
const i = ((selected() + dir * step) % rows.length + rows.length) % rows.length;
|
|
22113
|
+
if (rows[i].kind !== "separator" && matchIn(rowLine(rows[i], rowWidth(), false)) >= 0) {
|
|
22114
|
+
setSelected(i);
|
|
22115
|
+
return;
|
|
22116
|
+
}
|
|
22117
|
+
}
|
|
22118
|
+
api2.ui.toast({
|
|
22119
|
+
message: `no other row matches "${q}"`
|
|
22120
|
+
});
|
|
22121
|
+
}
|
|
22122
|
+
function enterSearch() {
|
|
22123
|
+
searchBefore = search();
|
|
22124
|
+
setSearchMode(true);
|
|
22125
|
+
}
|
|
22126
|
+
function exitSearch(commit) {
|
|
22127
|
+
if (!commit)
|
|
22128
|
+
setSearch(searchBefore);
|
|
22129
|
+
setSearchMode(false);
|
|
22130
|
+
}
|
|
22131
|
+
const stopTyping = api2.keymap.intercept("key", (input2) => {
|
|
22132
|
+
if (!searchMode())
|
|
22133
|
+
return;
|
|
22134
|
+
const ev = input2.event;
|
|
22135
|
+
if (ev.eventType === "release" || ev.ctrl || ev.meta)
|
|
22136
|
+
return;
|
|
22137
|
+
const take = () => input2.consume({
|
|
22138
|
+
preventDefault: true,
|
|
22139
|
+
stopPropagation: true
|
|
22140
|
+
});
|
|
22141
|
+
if (ev.name === "escape")
|
|
22142
|
+
return void (exitSearch(false), take());
|
|
22143
|
+
if (ev.name === "return" || ev.name === "enter")
|
|
22144
|
+
return void (exitSearch(true), take());
|
|
22145
|
+
if (ev.name === "backspace")
|
|
22146
|
+
return void (setSearch((q) => q.slice(0, -1)), take());
|
|
22147
|
+
const char = ev.sequence?.length === 1 && ev.sequence >= " " && ev.sequence !== "\x7F" ? ev.sequence : ev.name.length === 1 ? ev.name : undefined;
|
|
22148
|
+
if (char === undefined)
|
|
22149
|
+
return;
|
|
22150
|
+
setSearch((q) => q + char);
|
|
22151
|
+
take();
|
|
22152
|
+
});
|
|
22153
|
+
onCleanup(() => stopTyping());
|
|
21591
22154
|
const off = api2.keymap.registerLayer({
|
|
21592
22155
|
mode: "base",
|
|
21593
22156
|
commands: [{
|
|
21594
22157
|
name: "ctree.up",
|
|
21595
22158
|
hidden: true,
|
|
21596
|
-
run: () =>
|
|
22159
|
+
run: () => moveIndex(-1)
|
|
21597
22160
|
}, {
|
|
21598
22161
|
name: "ctree.down",
|
|
21599
22162
|
hidden: true,
|
|
21600
|
-
run: () =>
|
|
22163
|
+
run: () => moveIndex(1)
|
|
21601
22164
|
}, {
|
|
21602
22165
|
name: "ctree.jump_up",
|
|
21603
22166
|
hidden: true,
|
|
21604
|
-
|
|
22167
|
+
enabled: treePanel,
|
|
22168
|
+
run: () => moveIndex(-20)
|
|
21605
22169
|
}, {
|
|
21606
22170
|
name: "ctree.jump_down",
|
|
21607
22171
|
hidden: true,
|
|
21608
|
-
|
|
22172
|
+
enabled: treePanel,
|
|
22173
|
+
run: () => moveIndex(20)
|
|
22174
|
+
}, {
|
|
22175
|
+
name: "ctree.half_up",
|
|
22176
|
+
hidden: true,
|
|
22177
|
+
run: () => halfPage(-1)
|
|
22178
|
+
}, {
|
|
22179
|
+
name: "ctree.half_down",
|
|
22180
|
+
hidden: true,
|
|
22181
|
+
run: () => halfPage(1)
|
|
21609
22182
|
}, {
|
|
21610
22183
|
name: "ctree.first",
|
|
21611
22184
|
hidden: true,
|
|
21612
|
-
run: () =>
|
|
22185
|
+
run: () => gotoEdge(-1)
|
|
21613
22186
|
}, {
|
|
21614
22187
|
name: "ctree.last",
|
|
21615
22188
|
hidden: true,
|
|
21616
|
-
run: () =>
|
|
22189
|
+
run: () => gotoEdge(1)
|
|
21617
22190
|
}, {
|
|
21618
22191
|
name: "ctree.prev_branch",
|
|
21619
22192
|
hidden: true,
|
|
22193
|
+
enabled: treePanel,
|
|
21620
22194
|
run: () => setSelected((i) => nextBranchIndex(view().rows, i, -1))
|
|
21621
22195
|
}, {
|
|
21622
22196
|
name: "ctree.next_branch",
|
|
21623
22197
|
hidden: true,
|
|
22198
|
+
enabled: treePanel,
|
|
21624
22199
|
run: () => setSelected((i) => nextBranchIndex(view().rows, i, 1))
|
|
21625
22200
|
}, {
|
|
21626
22201
|
name: "ctree.fold",
|
|
21627
22202
|
hidden: true,
|
|
21628
|
-
|
|
22203
|
+
enabled: listPanel,
|
|
22204
|
+
run: () => panel() === "consumers" ? toggleConsumer(false) : foldOrUnfold(false)
|
|
21629
22205
|
}, {
|
|
21630
22206
|
name: "ctree.unfold",
|
|
21631
22207
|
hidden: true,
|
|
21632
|
-
|
|
22208
|
+
enabled: listPanel,
|
|
22209
|
+
run: () => panel() === "consumers" ? toggleConsumer(true) : foldOrUnfold(true)
|
|
21633
22210
|
}, {
|
|
21634
22211
|
name: "ctree.toggle",
|
|
21635
22212
|
hidden: true,
|
|
22213
|
+
enabled: treePanel,
|
|
21636
22214
|
run: () => foldOrUnfold(!(current()?.kind === "branch" && current().expanded))
|
|
21637
22215
|
}, {
|
|
21638
22216
|
name: "ctree.go",
|
|
21639
22217
|
hidden: true,
|
|
21640
|
-
run: () => void (panel() === "decisions" ? jumpToDecision() : panel() === "consumers" ?
|
|
22218
|
+
run: () => void (panel() === "decisions" ? jumpToDecision() : panel() === "consumers" ? toggleConsumer(!consumerOpen().has(consumerLine()?.bucket.source ?? "")) : cropMode() ? applyMarked() : jump())
|
|
21641
22219
|
}, {
|
|
21642
22220
|
name: "ctree.branch",
|
|
21643
22221
|
hidden: true,
|
|
22222
|
+
enabled: treeIdle,
|
|
21644
22223
|
run: () => void branch()
|
|
21645
22224
|
}, {
|
|
21646
22225
|
name: "ctree.label",
|
|
21647
22226
|
hidden: true,
|
|
22227
|
+
enabled: treeIdle,
|
|
21648
22228
|
run: () => void label()
|
|
21649
22229
|
}, {
|
|
21650
|
-
name: "ctree.
|
|
22230
|
+
name: "ctree.filter_pick",
|
|
21651
22231
|
hidden: true,
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
22232
|
+
enabled: () => !inCrop(),
|
|
22233
|
+
run: () => void pickFilter()
|
|
22234
|
+
}, {
|
|
22235
|
+
name: "ctree.filter_prev",
|
|
22236
|
+
hidden: true,
|
|
22237
|
+
enabled: () => !inCrop(),
|
|
22238
|
+
run: () => setFilterTo(FILTERS[(FILTERS.findIndex((f) => f.value === filter()) - 1 + FILTERS.length) % FILTERS.length].value)
|
|
21657
22239
|
}, {
|
|
21658
22240
|
name: "ctree.search",
|
|
21659
22241
|
hidden: true,
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
|
|
21663
|
-
|
|
22242
|
+
enabled: treeIdle,
|
|
22243
|
+
run: () => enterSearch()
|
|
22244
|
+
}, {
|
|
22245
|
+
name: "ctree.search_next",
|
|
22246
|
+
hidden: true,
|
|
22247
|
+
enabled: treePanel,
|
|
22248
|
+
run: () => moveMatch(1)
|
|
22249
|
+
}, {
|
|
22250
|
+
name: "ctree.search_prev",
|
|
22251
|
+
hidden: true,
|
|
22252
|
+
enabled: treePanel,
|
|
22253
|
+
run: () => moveMatch(-1)
|
|
21664
22254
|
}, {
|
|
21665
22255
|
name: "ctree.crop",
|
|
21666
22256
|
hidden: true,
|
|
22257
|
+
enabled: listPanel,
|
|
21667
22258
|
run: () => {
|
|
21668
22259
|
if (panel() === "consumers") {
|
|
21669
22260
|
cropConsumer();
|
|
21670
22261
|
return;
|
|
21671
22262
|
}
|
|
21672
|
-
if (
|
|
21673
|
-
|
|
21674
|
-
|
|
21675
|
-
setCropMode(undefined);
|
|
21676
|
-
setMarked(new Set);
|
|
21677
|
-
} else
|
|
22263
|
+
if (cropMode())
|
|
22264
|
+
leaveCropMode();
|
|
22265
|
+
else
|
|
21678
22266
|
setCropMode("result");
|
|
21679
22267
|
}
|
|
21680
22268
|
}, {
|
|
21681
22269
|
name: "ctree.crop_toggle_mode",
|
|
21682
22270
|
hidden: true,
|
|
21683
|
-
|
|
21684
|
-
|
|
21685
|
-
return;
|
|
21686
|
-
setCropMode(cropMode() === "result" ? "turn" : "result");
|
|
21687
|
-
setMarked(new Set);
|
|
21688
|
-
}
|
|
22271
|
+
enabled: inCrop,
|
|
22272
|
+
run: () => setCropMode(cropMode() === "result" ? "turn" : "result")
|
|
21689
22273
|
}, {
|
|
21690
22274
|
name: "ctree.mark",
|
|
21691
22275
|
hidden: true,
|
|
21692
|
-
enabled:
|
|
21693
|
-
run: () => toggleMark()
|
|
22276
|
+
enabled: listPanel,
|
|
22277
|
+
run: () => panel() === "consumers" ? markConsumerEntry() : toggleMark()
|
|
21694
22278
|
}, {
|
|
21695
22279
|
name: "ctree.auto",
|
|
21696
22280
|
hidden: true,
|
|
21697
|
-
enabled:
|
|
22281
|
+
enabled: inCrop,
|
|
21698
22282
|
run: () => autoMarkAll()
|
|
21699
22283
|
}, {
|
|
21700
22284
|
name: "ctree.undo",
|
|
21701
22285
|
hidden: true,
|
|
22286
|
+
enabled: treeIdle,
|
|
21702
22287
|
run: () => void undo()
|
|
21703
22288
|
}, {
|
|
21704
22289
|
name: "ctree.merge",
|
|
21705
22290
|
hidden: true,
|
|
22291
|
+
enabled: treeIdle,
|
|
21706
22292
|
run: () => void merge2()
|
|
21707
22293
|
}, {
|
|
21708
22294
|
name: "ctree.inspector",
|
|
21709
22295
|
hidden: true,
|
|
22296
|
+
enabled: () => !inCrop(),
|
|
21710
22297
|
run: () => {
|
|
21711
22298
|
setInspector(!inspector());
|
|
21712
22299
|
api2.kv.set("ctree.inspector", inspector());
|
|
@@ -21714,26 +22301,32 @@ ${MERGE_TRUST}
|
|
|
21714
22301
|
}, {
|
|
21715
22302
|
name: "ctree.consumers",
|
|
21716
22303
|
hidden: true,
|
|
22304
|
+
enabled: () => !inCrop(),
|
|
21717
22305
|
run: () => setPanel(panel() === "consumers" ? "tree" : "consumers")
|
|
21718
22306
|
}, {
|
|
21719
22307
|
name: "ctree.copy",
|
|
21720
22308
|
hidden: true,
|
|
22309
|
+
enabled: treeIdle,
|
|
21721
22310
|
run: () => copySelected()
|
|
21722
22311
|
}, {
|
|
21723
22312
|
name: "ctree.mode_duration",
|
|
21724
22313
|
hidden: true,
|
|
22314
|
+
enabled: treePanel,
|
|
21725
22315
|
run: () => setLane("duration")
|
|
21726
22316
|
}, {
|
|
21727
22317
|
name: "ctree.mode_turns",
|
|
21728
22318
|
hidden: true,
|
|
22319
|
+
enabled: treePanel,
|
|
21729
22320
|
run: () => setLane("turns")
|
|
21730
22321
|
}, {
|
|
21731
22322
|
name: "ctree.mode_calls",
|
|
21732
22323
|
hidden: true,
|
|
22324
|
+
enabled: treePanel,
|
|
21733
22325
|
run: () => setLane("calls")
|
|
21734
22326
|
}, {
|
|
21735
22327
|
name: "ctree.lanes_off",
|
|
21736
22328
|
hidden: true,
|
|
22329
|
+
enabled: treePanel,
|
|
21737
22330
|
run: () => {
|
|
21738
22331
|
setLanesOn(false);
|
|
21739
22332
|
api2.kv.set("ctree.lanesOn", false);
|
|
@@ -21741,6 +22334,7 @@ ${MERGE_TRUST}
|
|
|
21741
22334
|
}, {
|
|
21742
22335
|
name: "ctree.decisions",
|
|
21743
22336
|
hidden: true,
|
|
22337
|
+
enabled: () => !inCrop(),
|
|
21744
22338
|
run: () => setPanel(panel() === "decisions" ? "tree" : "decisions")
|
|
21745
22339
|
}, {
|
|
21746
22340
|
name: "ctree.export",
|
|
@@ -21760,8 +22354,11 @@ ${MERGE_TRUST}
|
|
|
21760
22354
|
return;
|
|
21761
22355
|
}
|
|
21762
22356
|
if (cropMode()) {
|
|
21763
|
-
|
|
21764
|
-
|
|
22357
|
+
leaveCropMode();
|
|
22358
|
+
return;
|
|
22359
|
+
}
|
|
22360
|
+
if (search()) {
|
|
22361
|
+
setSearch("");
|
|
21765
22362
|
return;
|
|
21766
22363
|
}
|
|
21767
22364
|
back();
|
|
@@ -21775,18 +22372,64 @@ ${MERGE_TRUST}
|
|
|
21775
22372
|
const root = state().root;
|
|
21776
22373
|
return (root && root !== sessionID ? others()[root]?.title : undefined) ?? sessionTitle();
|
|
21777
22374
|
};
|
|
21778
|
-
const
|
|
22375
|
+
const modeTag = () => cropMode() ? " \xB7 crop mode" : searchMode() ? " \xB7 search" : "";
|
|
22376
|
+
const headLine = () => {
|
|
21779
22377
|
const b = branchOfCurrent();
|
|
21780
|
-
|
|
21781
|
-
|
|
21782
|
-
|
|
22378
|
+
const lead = "\u250C Context tree \xB7 ";
|
|
22379
|
+
const where = b ? `\u2387 ${clip(b.name ?? sessionTitle(), 28)}${b.status === "open" ? "" : ` (${b.status})`} \u2190 ` : "";
|
|
22380
|
+
const tail = b ? "" : " \xB7 trunk";
|
|
22381
|
+
const room = cols() - 4 - formatContext(contextSize(), contextLimit()).length - modeTag().length - lead.length - where.length - tail.length - 3;
|
|
22382
|
+
return `${lead}${where}${clip(title(), Math.max(8, room))}${tail}${modeTag()} `;
|
|
22383
|
+
};
|
|
22384
|
+
const statusLine = () => {
|
|
22385
|
+
const n = view().rows.length;
|
|
22386
|
+
const pos = `${n ? Math.min(selected() + 1, n) : 0}/${n}`;
|
|
22387
|
+
if (cropMode()) {
|
|
22388
|
+
const a = armed();
|
|
22389
|
+
return `\u2702 crop mode (${cropMode()}) \xB7 space mark \xB7 a auto \xB7 t result\u21C4turn \xB7 \u23CE apply \xB7 esc leave \xB7 marked ${selectedCandidates().length} ~${formatK(reclaimed(selectedCandidates()))}${a ? " \xB7 armed \u2014 space again to override" : ""}`;
|
|
22390
|
+
}
|
|
22391
|
+
if (searchMode())
|
|
22392
|
+
return `search: ${search()}\u258F \xB7 ${pos} rows \xB7 \u23CE keeps it \xB7 esc clears`;
|
|
22393
|
+
return `filter: ${filter()}${search() ? ` search: "${search()}"` : ""}${busy() ? ` \u2026 ${busy()}` : ""} ${pos} rows`;
|
|
22394
|
+
};
|
|
22395
|
+
const goVerb = () => {
|
|
22396
|
+
const row = current();
|
|
22397
|
+
if (!row)
|
|
22398
|
+
return "\u23CE go";
|
|
22399
|
+
if (row.kind === "branch")
|
|
22400
|
+
return row.isCurrent ? "\u23CE you are here" : `\u23CE switch to \u2387 ${clip(row.name, 20)}`;
|
|
22401
|
+
if (row.kind === "separator")
|
|
22402
|
+
return "\u23CE go";
|
|
22403
|
+
if (row.id === view().currentRowId)
|
|
22404
|
+
return "\u23CE you are here";
|
|
22405
|
+
return row.kind === "turn" ? "\u23CE fork & prefill this turn" : "\u23CE fork after this step";
|
|
22406
|
+
};
|
|
22407
|
+
const footer = () => {
|
|
22408
|
+
if (cropMode())
|
|
22409
|
+
return "space mark a auto t result\u21C4turn \u23CE apply esc leave";
|
|
22410
|
+
if (panel() === "decisions")
|
|
22411
|
+
return "\u23CE jump to record E export q back";
|
|
22412
|
+
if (panel() === "consumers")
|
|
22413
|
+
return "\u23CE expand space mark c crop q back";
|
|
22414
|
+
if (panel() === "help")
|
|
22415
|
+
return "esc/q back";
|
|
22416
|
+
return `${goVerb()} b branch m merge c crop ${UNDO_KEY} undo s consumers ? help q back`;
|
|
22417
|
+
};
|
|
22418
|
+
const showsTree = () => panel() === "tree" || panel() === "help";
|
|
22419
|
+
const emptyText = () => {
|
|
22420
|
+
const q = search().trim();
|
|
22421
|
+
if (q)
|
|
22422
|
+
return `no rows match "${q}" \xB7 esc clears`;
|
|
22423
|
+
if (filter() !== "default")
|
|
22424
|
+
return `no rows match filter: ${filter()} \xB7 f changes it`;
|
|
22425
|
+
return "(no messages yet \u2014 chat first, then open the tree)";
|
|
21783
22426
|
};
|
|
21784
22427
|
return (() => {
|
|
21785
|
-
var _el$ = _$createElement("box"), _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$4 = _$createElement("text"), _el$
|
|
22428
|
+
var _el$ = _$createElement("box"), _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$4 = _$createElement("text"), _el$16 = _$createElement("text"), _el$17 = _$createTextNode(`\u2502 `), _el$28 = _$createElement("box"), _el$29 = _$createElement("box"), _el$35 = _$createElement("text"), _el$36 = _$createTextNode(`\u2514 `);
|
|
21786
22429
|
_$insertNode(_el$, _el$2);
|
|
21787
|
-
_$insertNode(_el$, _el$
|
|
21788
|
-
_$insertNode(_el$, _el$
|
|
21789
|
-
_$insertNode(_el$, _el$
|
|
22430
|
+
_$insertNode(_el$, _el$16);
|
|
22431
|
+
_$insertNode(_el$, _el$28);
|
|
22432
|
+
_$insertNode(_el$, _el$35);
|
|
21790
22433
|
_$setProp(_el$, "flexDirection", "column");
|
|
21791
22434
|
_$setProp(_el$, "padding", 1);
|
|
21792
22435
|
_$setProp(_el$, "width", "100%");
|
|
@@ -21794,7 +22437,7 @@ ${MERGE_TRUST}
|
|
|
21794
22437
|
_$insertNode(_el$2, _el$3);
|
|
21795
22438
|
_$insertNode(_el$2, _el$4);
|
|
21796
22439
|
_$setProp(_el$2, "flexDirection", "row");
|
|
21797
|
-
_$insert(_el$3,
|
|
22440
|
+
_$insert(_el$3, headLine);
|
|
21798
22441
|
_$insert(_el$4, () => formatContext(contextSize(), contextLimit()));
|
|
21799
22442
|
_$insert(_el$, _$createComponent(Show, {
|
|
21800
22443
|
get when() {
|
|
@@ -21802,16 +22445,55 @@ ${MERGE_TRUST}
|
|
|
21802
22445
|
},
|
|
21803
22446
|
get children() {
|
|
21804
22447
|
return [(() => {
|
|
21805
|
-
var _el$5 = _$createElement("
|
|
22448
|
+
var _el$5 = _$createElement("box"), _el$6 = _$createElement("text"), _el$7 = _$createElement("text");
|
|
21806
22449
|
_$insertNode(_el$5, _el$6);
|
|
21807
22450
|
_$insertNode(_el$5, _el$7);
|
|
21808
|
-
_$
|
|
21809
|
-
_$
|
|
21810
|
-
_$insert(_el$5, (
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
22451
|
+
_$setProp(_el$5, "flexDirection", "row");
|
|
22452
|
+
_$insert(_el$6, () => `\u2502 Input ${strip().truncatedLeft > 0 ? `\u2026${strip().truncatedLeft}` : ""}`);
|
|
22453
|
+
_$insert(_el$5, _$createComponent(Show, {
|
|
22454
|
+
get when() {
|
|
22455
|
+
return !strip().empty.input;
|
|
22456
|
+
},
|
|
22457
|
+
get fallback() {
|
|
22458
|
+
return (() => {
|
|
22459
|
+
var _el$37 = _$createElement("text");
|
|
22460
|
+
_$insert(_el$37, () => "no input".padEnd(laneWidth()));
|
|
22461
|
+
_$effect((_$p) => _$setProp(_el$37, "fg", t.textMuted, _$p));
|
|
22462
|
+
return _el$37;
|
|
22463
|
+
})();
|
|
22464
|
+
},
|
|
22465
|
+
get children() {
|
|
22466
|
+
return _$createComponent(For, {
|
|
22467
|
+
get each() {
|
|
22468
|
+
return inputRuns();
|
|
22469
|
+
},
|
|
22470
|
+
children: (r) => (() => {
|
|
22471
|
+
var _el$38 = _$createElement("text");
|
|
22472
|
+
_$insert(_el$38, () => r.text);
|
|
22473
|
+
_$effect((_p$) => {
|
|
22474
|
+
var { fg: _v$0, bg: _v$1 } = r;
|
|
22475
|
+
_v$0 !== _p$.e && (_p$.e = _$setProp(_el$38, "fg", _v$0, _p$.e));
|
|
22476
|
+
_v$1 !== _p$.t && (_p$.t = _$setProp(_el$38, "bg", _v$1, _p$.t));
|
|
22477
|
+
return _p$;
|
|
22478
|
+
}, {
|
|
22479
|
+
e: undefined,
|
|
22480
|
+
t: undefined
|
|
22481
|
+
});
|
|
22482
|
+
return _el$38;
|
|
22483
|
+
})()
|
|
22484
|
+
});
|
|
22485
|
+
}
|
|
22486
|
+
}), _el$7);
|
|
22487
|
+
_$insert(_el$7, () => ` ${laneMode() === "duration" ? "[1] Duration" : " 1 duration"} \xB7 ${laneMode() === "turns" ? "[2] Turns" : " 2 turns"} \xB7 ${laneMode() === "calls" ? "[3] Calls" : " 3 calls"} \xB7 0 off`);
|
|
22488
|
+
_$effect((_p$) => {
|
|
22489
|
+
var { textMuted: _v$, textMuted: _v$2 } = t;
|
|
22490
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$6, "fg", _v$, _p$.e));
|
|
22491
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$7, "fg", _v$2, _p$.t));
|
|
22492
|
+
return _p$;
|
|
22493
|
+
}, {
|
|
22494
|
+
e: undefined,
|
|
22495
|
+
t: undefined
|
|
22496
|
+
});
|
|
21815
22497
|
return _el$5;
|
|
21816
22498
|
})(), _$createComponent(Show, {
|
|
21817
22499
|
get when() {
|
|
@@ -21819,92 +22501,136 @@ ${MERGE_TRUST}
|
|
|
21819
22501
|
},
|
|
21820
22502
|
get children() {
|
|
21821
22503
|
return [(() => {
|
|
21822
|
-
var _el$
|
|
21823
|
-
_$insertNode(_el$
|
|
21824
|
-
_$
|
|
21825
|
-
_$
|
|
21826
|
-
|
|
22504
|
+
var _el$8 = _$createElement("box"), _el$9 = _$createElement("text");
|
|
22505
|
+
_$insertNode(_el$8, _el$9);
|
|
22506
|
+
_$setProp(_el$8, "flexDirection", "row");
|
|
22507
|
+
_$insertNode(_el$9, _$createTextNode(`\u2502 Model `));
|
|
22508
|
+
_$insert(_el$8, _$createComponent(Show, {
|
|
22509
|
+
get when() {
|
|
22510
|
+
return !strip().empty.model;
|
|
22511
|
+
},
|
|
22512
|
+
get fallback() {
|
|
22513
|
+
return (() => {
|
|
22514
|
+
var _el$39 = _$createElement("text");
|
|
22515
|
+
_$insert(_el$39, () => "no model steps".padEnd(laneWidth()));
|
|
22516
|
+
_$effect((_$p) => _$setProp(_el$39, "fg", t.textMuted, _$p));
|
|
22517
|
+
return _el$39;
|
|
22518
|
+
})();
|
|
22519
|
+
},
|
|
22520
|
+
get children() {
|
|
22521
|
+
return _$createComponent(For, {
|
|
22522
|
+
get each() {
|
|
22523
|
+
return modelRuns();
|
|
22524
|
+
},
|
|
22525
|
+
children: (r) => (() => {
|
|
22526
|
+
var _el$40 = _$createElement("text");
|
|
22527
|
+
_$insert(_el$40, () => r.text);
|
|
22528
|
+
_$effect((_p$) => {
|
|
22529
|
+
var { fg: _v$10, bg: _v$11 } = r;
|
|
22530
|
+
_v$10 !== _p$.e && (_p$.e = _$setProp(_el$40, "fg", _v$10, _p$.e));
|
|
22531
|
+
_v$11 !== _p$.t && (_p$.t = _$setProp(_el$40, "bg", _v$11, _p$.t));
|
|
22532
|
+
return _p$;
|
|
22533
|
+
}, {
|
|
22534
|
+
e: undefined,
|
|
22535
|
+
t: undefined
|
|
22536
|
+
});
|
|
22537
|
+
return _el$40;
|
|
22538
|
+
})()
|
|
22539
|
+
});
|
|
22540
|
+
}
|
|
22541
|
+
}), null);
|
|
22542
|
+
_$effect((_$p) => _$setProp(_el$9, "fg", t.textMuted, _$p));
|
|
22543
|
+
return _el$8;
|
|
21827
22544
|
})(), (() => {
|
|
21828
|
-
var _el$
|
|
21829
|
-
_$insertNode(_el$
|
|
21830
|
-
_$insertNode(_el$
|
|
21831
|
-
_$setProp(_el$
|
|
21832
|
-
_$insertNode(_el$
|
|
21833
|
-
_$insert(_el$
|
|
21834
|
-
get
|
|
21835
|
-
return
|
|
22545
|
+
var _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$12 = _$createElement("text");
|
|
22546
|
+
_$insertNode(_el$1, _el$10);
|
|
22547
|
+
_$insertNode(_el$1, _el$12);
|
|
22548
|
+
_$setProp(_el$1, "flexDirection", "row");
|
|
22549
|
+
_$insertNode(_el$10, _$createTextNode(`\u2502 Tools `));
|
|
22550
|
+
_$insert(_el$1, _$createComponent(Show, {
|
|
22551
|
+
get when() {
|
|
22552
|
+
return !strip().empty.tools;
|
|
21836
22553
|
},
|
|
21837
|
-
|
|
21838
|
-
|
|
21839
|
-
|
|
21840
|
-
|
|
21841
|
-
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
|
|
22554
|
+
get fallback() {
|
|
22555
|
+
return (() => {
|
|
22556
|
+
var _el$41 = _$createElement("text");
|
|
22557
|
+
_$insert(_el$41, () => "no tool calls".padEnd(laneWidth()));
|
|
22558
|
+
_$effect((_$p) => _$setProp(_el$41, "fg", t.textMuted, _$p));
|
|
22559
|
+
return _el$41;
|
|
22560
|
+
})();
|
|
22561
|
+
},
|
|
22562
|
+
get children() {
|
|
22563
|
+
return _$createComponent(For, {
|
|
22564
|
+
get each() {
|
|
22565
|
+
return toolRuns();
|
|
22566
|
+
},
|
|
22567
|
+
children: (r) => (() => {
|
|
22568
|
+
var _el$42 = _$createElement("text");
|
|
22569
|
+
_$insert(_el$42, () => r.text);
|
|
22570
|
+
_$effect((_p$) => {
|
|
22571
|
+
var { fg: _v$12, bg: _v$13 } = r;
|
|
22572
|
+
_v$12 !== _p$.e && (_p$.e = _$setProp(_el$42, "fg", _v$12, _p$.e));
|
|
22573
|
+
_v$13 !== _p$.t && (_p$.t = _$setProp(_el$42, "bg", _v$13, _p$.t));
|
|
22574
|
+
return _p$;
|
|
22575
|
+
}, {
|
|
22576
|
+
e: undefined,
|
|
22577
|
+
t: undefined
|
|
22578
|
+
});
|
|
22579
|
+
return _el$42;
|
|
22580
|
+
})()
|
|
22581
|
+
});
|
|
22582
|
+
}
|
|
22583
|
+
}), _el$12);
|
|
22584
|
+
_$insertNode(_el$12, _$createTextNode(` i inspector \xB7 s consumers`));
|
|
21845
22585
|
_$effect((_p$) => {
|
|
21846
|
-
var {
|
|
21847
|
-
_v$ !== _p$.e && (_p$.e = _$setProp(_el$
|
|
21848
|
-
_v$
|
|
22586
|
+
var { textMuted: _v$3, textMuted: _v$4 } = t;
|
|
22587
|
+
_v$3 !== _p$.e && (_p$.e = _$setProp(_el$10, "fg", _v$3, _p$.e));
|
|
22588
|
+
_v$4 !== _p$.t && (_p$.t = _$setProp(_el$12, "fg", _v$4, _p$.t));
|
|
21849
22589
|
return _p$;
|
|
21850
22590
|
}, {
|
|
21851
22591
|
e: undefined,
|
|
21852
22592
|
t: undefined
|
|
21853
22593
|
});
|
|
21854
|
-
return _el$
|
|
22594
|
+
return _el$1;
|
|
21855
22595
|
})()];
|
|
21856
22596
|
}
|
|
21857
22597
|
})];
|
|
21858
22598
|
}
|
|
21859
|
-
}), _el$
|
|
22599
|
+
}), _el$16);
|
|
21860
22600
|
_$insert(_el$, _$createComponent(Show, {
|
|
21861
22601
|
get when() {
|
|
21862
22602
|
return _$memo(() => !!(laneRoom() && lanesOn()))() && !showLanes();
|
|
21863
22603
|
},
|
|
21864
22604
|
get children() {
|
|
21865
|
-
var _el$
|
|
21866
|
-
_$insertNode(_el$
|
|
21867
|
-
_$effect((_$p) => _$setProp(_el$
|
|
21868
|
-
return _el$
|
|
21869
|
-
}
|
|
21870
|
-
}), _el$
|
|
21871
|
-
_$insertNode(_el$
|
|
21872
|
-
_$
|
|
21873
|
-
_$insertNode(_el$17, _el$20);
|
|
21874
|
-
_$insert(_el$17, (() => {
|
|
21875
|
-
var _c$ = _$memo(() => !!cropMode());
|
|
21876
|
-
return () => _c$() ? `\u2702 crop mode (${cropMode()}) \xB7 space mark \xB7 a auto \xB7 t result\u21C4turn \xB7 \u23CE apply \xB7 esc leave \xB7 marked ${selectedCandidates().length} ~${formatK(reclaimed(selectedCandidates()))}` : `filter: ${filter()}`;
|
|
21877
|
-
})(), _el$19);
|
|
21878
|
-
_$insert(_el$17, (() => {
|
|
21879
|
-
var _c$2 = _$memo(() => !!search());
|
|
21880
|
-
return () => _c$2() ? ` search: "${search()}"` : "";
|
|
21881
|
-
})(), _el$19);
|
|
21882
|
-
_$insert(_el$17, (() => {
|
|
21883
|
-
var _c$3 = _$memo(() => !!busy());
|
|
21884
|
-
return () => _c$3() ? ` \u2026 ${busy()}` : "";
|
|
21885
|
-
})(), _el$19);
|
|
21886
|
-
_$insert(_el$17, () => view().rows.length, _el$20);
|
|
22605
|
+
var _el$14 = _$createElement("text");
|
|
22606
|
+
_$insertNode(_el$14, _$createTextNode(`\u2502 lanes appear after 3 turns`));
|
|
22607
|
+
_$effect((_$p) => _$setProp(_el$14, "fg", t.textMuted, _$p));
|
|
22608
|
+
return _el$14;
|
|
22609
|
+
}
|
|
22610
|
+
}), _el$16);
|
|
22611
|
+
_$insertNode(_el$16, _el$17);
|
|
22612
|
+
_$insert(_el$16, statusLine, null);
|
|
21887
22613
|
_$insert(_el$, _$createComponent(Show, {
|
|
21888
22614
|
get when() {
|
|
21889
22615
|
return panel() === "decisions";
|
|
21890
22616
|
},
|
|
21891
22617
|
get children() {
|
|
21892
22618
|
return [(() => {
|
|
21893
|
-
var _el$
|
|
21894
|
-
_$insertNode(_el$
|
|
21895
|
-
_$insertNode(_el$
|
|
21896
|
-
_$insert(_el$
|
|
21897
|
-
_$effect((_$p) => _$setProp(_el$
|
|
21898
|
-
return _el$
|
|
22619
|
+
var _el$18 = _$createElement("text"), _el$19 = _$createTextNode(`\u2502 \u25C6 decisions on this tree (`), _el$20 = _$createTextNode(`) \xB7 \u23CE jump to record \xB7 E export markdown \xB7 q back`);
|
|
22620
|
+
_$insertNode(_el$18, _el$19);
|
|
22621
|
+
_$insertNode(_el$18, _el$20);
|
|
22622
|
+
_$insert(_el$18, () => decisions().length, _el$20);
|
|
22623
|
+
_$effect((_$p) => _$setProp(_el$18, "fg", t.accent, _$p));
|
|
22624
|
+
return _el$18;
|
|
21899
22625
|
})(), _$createComponent(Show, {
|
|
21900
22626
|
get when() {
|
|
21901
22627
|
return decisions().length === 0;
|
|
21902
22628
|
},
|
|
21903
22629
|
get children() {
|
|
21904
|
-
var _el$
|
|
21905
|
-
_$insertNode(_el$
|
|
21906
|
-
_$effect((_$p) => _$setProp(_el$
|
|
21907
|
-
return _el$
|
|
22630
|
+
var _el$21 = _$createElement("text");
|
|
22631
|
+
_$insertNode(_el$21, _$createTextNode(`\u2502 (none yet \u2014 /merge a branch to write one)`));
|
|
22632
|
+
_$effect((_$p) => _$setProp(_el$21, "fg", t.textMuted, _$p));
|
|
22633
|
+
return _el$21;
|
|
21908
22634
|
}
|
|
21909
22635
|
}), _$createComponent(For, {
|
|
21910
22636
|
get each() {
|
|
@@ -21912,132 +22638,142 @@ ${MERGE_TRUST}
|
|
|
21912
22638
|
},
|
|
21913
22639
|
children: (d, i) => {
|
|
21914
22640
|
const sel = () => i() === decisionIndex();
|
|
21915
|
-
const
|
|
21916
|
-
|
|
22641
|
+
const body = () => renderDecision(d.text ?? "", width() - 6);
|
|
22642
|
+
const room = () => Math.max(3, height() - decisions().length);
|
|
22643
|
+
const start = () => Math.min(decisionScroll(), Math.max(0, body().length - room()));
|
|
22644
|
+
const more = () => Math.max(0, body().length - start() - room());
|
|
21917
22645
|
return (() => {
|
|
21918
|
-
var _el$
|
|
21919
|
-
_$insertNode(_el$
|
|
21920
|
-
_$setProp(_el$
|
|
21921
|
-
_$insertNode(_el$
|
|
21922
|
-
_$insertNode(_el$
|
|
21923
|
-
_$insert(_el$
|
|
21924
|
-
_$insert(_el$
|
|
21925
|
-
_$insert(_el$
|
|
21926
|
-
_$insert(_el$
|
|
21927
|
-
_$insert(_el$
|
|
21928
|
-
var _c$
|
|
21929
|
-
return () => _c$
|
|
22646
|
+
var _el$43 = _$createElement("box"), _el$44 = _$createElement("text"), _el$45 = _$createTextNode(` `), _el$46 = _$createTextNode(` \xB7 `);
|
|
22647
|
+
_$insertNode(_el$43, _el$44);
|
|
22648
|
+
_$setProp(_el$43, "flexDirection", "column");
|
|
22649
|
+
_$insertNode(_el$44, _el$45);
|
|
22650
|
+
_$insertNode(_el$44, _el$46);
|
|
22651
|
+
_$insert(_el$44, () => sel() ? "\u203A" : "\u2502", _el$45);
|
|
22652
|
+
_$insert(_el$44, () => d.hidden ? "\u25C7 (hidden from model) " : "\u25C6 ", _el$46);
|
|
22653
|
+
_$insert(_el$44, () => clip(decisionSummary(d.text ?? "").title || d.branchName, 48), _el$46);
|
|
22654
|
+
_$insert(_el$44, () => new Date(d.recordedAt).toISOString().slice(0, 16).replace("T", " "), null);
|
|
22655
|
+
_$insert(_el$44, (() => {
|
|
22656
|
+
var _c$3 = _$memo(() => !!d.siblings.length);
|
|
22657
|
+
return () => _c$3() ? ` \xB7 \u2717 ${d.siblings.map((x) => x.name).join(", ")}` : "";
|
|
21930
22658
|
})(), null);
|
|
21931
|
-
_$insert(_el$
|
|
22659
|
+
_$insert(_el$43, _$createComponent(For, {
|
|
21932
22660
|
get each() {
|
|
21933
|
-
return _$memo(() => !!sel())() ?
|
|
22661
|
+
return _$memo(() => !!sel())() ? body().slice(start(), start() + room()) : [];
|
|
21934
22662
|
},
|
|
21935
22663
|
children: (l) => (() => {
|
|
21936
|
-
var _el$
|
|
21937
|
-
_$
|
|
21938
|
-
_$
|
|
21939
|
-
|
|
21940
|
-
return _el$41;
|
|
22664
|
+
var _el$48 = _$createElement("text");
|
|
22665
|
+
_$insert(_el$48, `\u2502 ${l}`);
|
|
22666
|
+
_$effect((_$p) => _$setProp(_el$48, "fg", t.text, _$p));
|
|
22667
|
+
return _el$48;
|
|
21941
22668
|
})()
|
|
21942
22669
|
}), null);
|
|
22670
|
+
_$insert(_el$43, _$createComponent(Show, {
|
|
22671
|
+
get when() {
|
|
22672
|
+
return _$memo(() => !!sel())() && more() > 0;
|
|
22673
|
+
},
|
|
22674
|
+
get children() {
|
|
22675
|
+
var _el$47 = _$createElement("text");
|
|
22676
|
+
_$insert(_el$47, () => `\u2502 \u2026 ${more()} more lines \u2193 (ctrl+d)`);
|
|
22677
|
+
_$effect((_$p) => _$setProp(_el$47, "fg", t.textMuted, _$p));
|
|
22678
|
+
return _el$47;
|
|
22679
|
+
}
|
|
22680
|
+
}), null);
|
|
21943
22681
|
_$effect((_p$) => {
|
|
21944
|
-
var _v$
|
|
21945
|
-
_v$
|
|
21946
|
-
_v$
|
|
22682
|
+
var _v$14 = sel() ? t.background : t.accent, _v$15 = sel() ? t.primary : undefined;
|
|
22683
|
+
_v$14 !== _p$.e && (_p$.e = _$setProp(_el$44, "fg", _v$14, _p$.e));
|
|
22684
|
+
_v$15 !== _p$.t && (_p$.t = _$setProp(_el$44, "bg", _v$15, _p$.t));
|
|
21947
22685
|
return _p$;
|
|
21948
22686
|
}, {
|
|
21949
22687
|
e: undefined,
|
|
21950
22688
|
t: undefined
|
|
21951
22689
|
});
|
|
21952
|
-
return _el$
|
|
22690
|
+
return _el$43;
|
|
21953
22691
|
})();
|
|
21954
22692
|
}
|
|
21955
22693
|
})];
|
|
21956
22694
|
}
|
|
21957
|
-
}), _el$
|
|
22695
|
+
}), _el$28);
|
|
21958
22696
|
_$insert(_el$, _$createComponent(Show, {
|
|
21959
22697
|
get when() {
|
|
21960
22698
|
return panel() === "consumers";
|
|
21961
22699
|
},
|
|
21962
22700
|
get children() {
|
|
21963
22701
|
return [(() => {
|
|
21964
|
-
var _el$
|
|
21965
|
-
_$insertNode(_el$
|
|
21966
|
-
_$insertNode(_el$
|
|
21967
|
-
_$insert(_el$
|
|
21968
|
-
_$effect((_$p) => _$setProp(_el$
|
|
21969
|
-
return _el$
|
|
22702
|
+
var _el$23 = _$createElement("text"), _el$24 = _$createTextNode(`\u2502 what's filling the context \xB7 `), _el$25 = _$createTextNode(` total \xB7 source \xB7 %tree \xB7 %window \xB7 tokens \xB7 entries`);
|
|
22703
|
+
_$insertNode(_el$23, _el$24);
|
|
22704
|
+
_$insertNode(_el$23, _el$25);
|
|
22705
|
+
_$insert(_el$23, () => formatK(view().totalTokens), _el$25);
|
|
22706
|
+
_$effect((_$p) => _$setProp(_el$23, "fg", t.accent, _$p));
|
|
22707
|
+
return _el$23;
|
|
21970
22708
|
})(), _$createComponent(For, {
|
|
21971
22709
|
get each() {
|
|
21972
|
-
return
|
|
22710
|
+
return consumerLines();
|
|
21973
22711
|
},
|
|
21974
|
-
children: (
|
|
22712
|
+
children: (line, i) => {
|
|
21975
22713
|
const sel = () => i() === consumerIndex();
|
|
22714
|
+
const c = line.bucket;
|
|
22715
|
+
const fg = () => sel() ? t.background : line.entry ? t.textMuted : c.kind === "tool" ? t.warning : t.text;
|
|
22716
|
+
const window = () => c.shareOfWindow === undefined ? "\u2013" : `${(c.shareOfWindow * 100).toFixed(0)}%`;
|
|
22717
|
+
const entry = line.entry;
|
|
21976
22718
|
return (() => {
|
|
21977
|
-
var _el$
|
|
21978
|
-
_$insertNode(_el$
|
|
21979
|
-
_$
|
|
21980
|
-
_$
|
|
21981
|
-
_$insertNode(_el$43, _el$47);
|
|
21982
|
-
_$insertNode(_el$43, _el$48);
|
|
21983
|
-
_$insertNode(_el$43, _el$49);
|
|
21984
|
-
_$insert(_el$43, () => sel() ? "\u203A" : "\u2502", _el$44);
|
|
21985
|
-
_$insert(_el$43, () => c.source.padEnd(22).slice(0, 22), _el$45);
|
|
21986
|
-
_$insert(_el$43, () => `${(c.share * 100).toFixed(0)}%`.padStart(4), _el$46);
|
|
21987
|
-
_$insert(_el$43, () => bar(c.share, 24), _el$47);
|
|
21988
|
-
_$insert(_el$43, () => formatK(c.tokens).padStart(6), _el$48);
|
|
21989
|
-
_$insert(_el$43, () => c.count, _el$49);
|
|
21990
|
-
_$insert(_el$43, () => c.count === 1 ? "y" : "ies", null);
|
|
22719
|
+
var _el$49 = _$createElement("text"), _el$50 = _$createTextNode(` `);
|
|
22720
|
+
_$insertNode(_el$49, _el$50);
|
|
22721
|
+
_$insert(_el$49, () => sel() ? "\u203A" : "\u2502", _el$50);
|
|
22722
|
+
_$insert(_el$49, () => entry ? fitRow(` ${entry.croppable ? marked().has(entry.partID ?? "") ? "[x]" : "[ ]" : " "} ${plain(entry.preview)}${entry.croppable ? "" : ` \xB7 ${c.note ?? "not a completed tool result"}`}`, formatK(entry.tokens), width() - 4) : `${consumerOpen().has(c.source) ? "\u25BE" : "\u25B8"} ${c.source.padEnd(20).slice(0, 20)} ${`${(c.share * 100).toFixed(0)}%`.padStart(4)} ${window().padStart(5)} ${bar(c.tokens / consumerMax(), 18)} ${formatK(c.tokens).padStart(6)} \xB7 ${c.count} entr${c.count === 1 ? "y" : "ies"}${c.note ? ` \xB7 ${c.note}` : ""}`, null);
|
|
21991
22723
|
_$effect((_p$) => {
|
|
21992
|
-
var _v$
|
|
21993
|
-
_v$
|
|
21994
|
-
_v$
|
|
22724
|
+
var _v$16 = fg(), _v$17 = sel() ? t.primary : undefined;
|
|
22725
|
+
_v$16 !== _p$.e && (_p$.e = _$setProp(_el$49, "fg", _v$16, _p$.e));
|
|
22726
|
+
_v$17 !== _p$.t && (_p$.t = _$setProp(_el$49, "bg", _v$17, _p$.t));
|
|
21995
22727
|
return _p$;
|
|
21996
22728
|
}, {
|
|
21997
22729
|
e: undefined,
|
|
21998
22730
|
t: undefined
|
|
21999
22731
|
});
|
|
22000
|
-
return _el$
|
|
22732
|
+
return _el$49;
|
|
22001
22733
|
})();
|
|
22002
22734
|
}
|
|
22003
22735
|
})];
|
|
22004
22736
|
}
|
|
22005
|
-
}), _el$
|
|
22006
|
-
_$insert(_el$, _$createComponent(For, {
|
|
22007
|
-
get each() {
|
|
22008
|
-
return _$memo(() => panel() === "help")() ? HELP.slice(0, Math.max(6, size().rows - 5)) : [];
|
|
22009
|
-
},
|
|
22010
|
-
children: (l) => (() => {
|
|
22011
|
-
var _el$50 = _$createElement("text"), _el$51 = _$createTextNode(`\u2502 `);
|
|
22012
|
-
_$insertNode(_el$50, _el$51);
|
|
22013
|
-
_$insert(_el$50, l, null);
|
|
22014
|
-
_$effect((_$p) => _$setProp(_el$50, "fg", l.startsWith(" ") ? t.textMuted : t.accent, _$p));
|
|
22015
|
-
return _el$50;
|
|
22016
|
-
})()
|
|
22017
|
-
}), _el$31);
|
|
22737
|
+
}), _el$28);
|
|
22018
22738
|
_$insert(_el$, _$createComponent(Show, {
|
|
22019
22739
|
get when() {
|
|
22020
|
-
return _$memo(() =>
|
|
22740
|
+
return _$memo(() => !!showsTree())() && view().rows.length === 0;
|
|
22741
|
+
},
|
|
22742
|
+
get children() {
|
|
22743
|
+
var _el$26 = _$createElement("text"), _el$27 = _$createTextNode(`\u2502 `);
|
|
22744
|
+
_$insertNode(_el$26, _el$27);
|
|
22745
|
+
_$insert(_el$26, emptyText, null);
|
|
22746
|
+
_$effect((_$p) => _$setProp(_el$26, "fg", t.textMuted, _$p));
|
|
22747
|
+
return _el$26;
|
|
22748
|
+
}
|
|
22749
|
+
}), _el$28);
|
|
22750
|
+
_$insertNode(_el$28, _el$29);
|
|
22751
|
+
_$setProp(_el$28, "flexDirection", "row");
|
|
22752
|
+
_$setProp(_el$28, "flexGrow", 1);
|
|
22753
|
+
_$setProp(_el$29, "flexDirection", "column");
|
|
22754
|
+
_$setProp(_el$29, "flexGrow", 1);
|
|
22755
|
+
_$insert(_el$29, _$createComponent(Show, {
|
|
22756
|
+
get when() {
|
|
22757
|
+
return _$memo(() => !!showsTree())() && overflow();
|
|
22021
22758
|
},
|
|
22022
22759
|
get children() {
|
|
22023
|
-
var _el$
|
|
22024
|
-
_$insertNode(_el$
|
|
22025
|
-
_$
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
_$
|
|
22034
|
-
_$insert(_el$32, _$createComponent(For, {
|
|
22760
|
+
var _el$30 = _$createElement("text"), _el$31 = _$createTextNode(`\u2502 `);
|
|
22761
|
+
_$insertNode(_el$30, _el$31);
|
|
22762
|
+
_$insert(_el$30, (() => {
|
|
22763
|
+
var _c$ = _$memo(() => hiddenAbove() > 0);
|
|
22764
|
+
return () => _c$() ? `\u2191 ${hiddenAbove()} more` : "";
|
|
22765
|
+
})(), null);
|
|
22766
|
+
_$effect((_$p) => _$setProp(_el$30, "fg", t.textMuted, _$p));
|
|
22767
|
+
return _el$30;
|
|
22768
|
+
}
|
|
22769
|
+
}), null);
|
|
22770
|
+
_$insert(_el$29, _$createComponent(For, {
|
|
22035
22771
|
get each() {
|
|
22036
|
-
return _$memo(() =>
|
|
22772
|
+
return _$memo(() => !!showsTree())() ? visible() : [];
|
|
22037
22773
|
},
|
|
22038
22774
|
children: (row, i) => {
|
|
22039
22775
|
const isSel = () => windowStart() + i() === selected();
|
|
22040
|
-
const color = () => row.kind === "branch" ? statusColor(t, row) : row.kind === "turn" ? row.isDecision ? t.accent : t.text : row.isError ? t.error : row.warn ? t.warning : t.textMuted;
|
|
22776
|
+
const color = () => row.kind === "separator" ? t.textMuted : row.kind === "branch" ? statusColor(t, row) : !row.inContext ? t.textMuted : row.kind === "turn" ? row.isDecision ? t.accent : t.text : row.isError ? t.error : row.warn ? t.warning : t.textMuted;
|
|
22041
22777
|
const mark = () => {
|
|
22042
22778
|
if (!cropMode())
|
|
22043
22779
|
return "";
|
|
@@ -22048,70 +22784,139 @@ ${MERGE_TRUST}
|
|
|
22048
22784
|
const prot = c.protections.filter((p) => p !== "too-small");
|
|
22049
22785
|
return `${on2 ? "[x]" : "[ ]"}${prot.length ? "!" : " "}`;
|
|
22050
22786
|
};
|
|
22051
|
-
|
|
22052
|
-
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
|
|
22060
|
-
|
|
22061
|
-
|
|
22062
|
-
|
|
22063
|
-
|
|
22064
|
-
|
|
22065
|
-
|
|
22066
|
-
|
|
22067
|
-
|
|
22787
|
+
const prefix = () => `${isSel() ? "\u203A" : "\u2502"} ${mark()}`;
|
|
22788
|
+
const segs = () => segmentsOf(rowLine(row, rowWidth(), row.id === view().currentRowId), search().trim(), thoughtOf(row));
|
|
22789
|
+
return _$createComponent(Show, {
|
|
22790
|
+
get when() {
|
|
22791
|
+
return segs().length > 1;
|
|
22792
|
+
},
|
|
22793
|
+
get fallback() {
|
|
22794
|
+
return (() => {
|
|
22795
|
+
var _el$53 = _$createElement("text");
|
|
22796
|
+
_$insert(_el$53, prefix, null);
|
|
22797
|
+
_$insert(_el$53, () => segs()[0]?.text, null);
|
|
22798
|
+
_$effect((_p$) => {
|
|
22799
|
+
var _v$20 = isSel() ? t.background : color(), _v$21 = isSel() ? t.primary : undefined;
|
|
22800
|
+
_v$20 !== _p$.e && (_p$.e = _$setProp(_el$53, "fg", _v$20, _p$.e));
|
|
22801
|
+
_v$21 !== _p$.t && (_p$.t = _$setProp(_el$53, "bg", _v$21, _p$.t));
|
|
22802
|
+
return _p$;
|
|
22803
|
+
}, {
|
|
22804
|
+
e: undefined,
|
|
22805
|
+
t: undefined
|
|
22806
|
+
});
|
|
22807
|
+
return _el$53;
|
|
22808
|
+
})();
|
|
22809
|
+
},
|
|
22810
|
+
get children() {
|
|
22811
|
+
var _el$51 = _$createElement("box"), _el$52 = _$createElement("text");
|
|
22812
|
+
_$insertNode(_el$51, _el$52);
|
|
22813
|
+
_$setProp(_el$51, "flexDirection", "row");
|
|
22814
|
+
_$insert(_el$52, prefix);
|
|
22815
|
+
_$insert(_el$51, _$createComponent(For, {
|
|
22816
|
+
get each() {
|
|
22817
|
+
return segs();
|
|
22818
|
+
},
|
|
22819
|
+
children: (s) => (() => {
|
|
22820
|
+
var _el$54 = _$createElement("text");
|
|
22821
|
+
_$insert(_el$54, () => s.text);
|
|
22822
|
+
_$effect((_p$) => {
|
|
22823
|
+
var _v$22 = s.kind === "match" ? t.background : s.kind === "dim" ? t.textMuted : isSel() ? t.background : color(), _v$23 = s.kind === "match" ? t.accent : isSel() ? t.primary : undefined;
|
|
22824
|
+
_v$22 !== _p$.e && (_p$.e = _$setProp(_el$54, "fg", _v$22, _p$.e));
|
|
22825
|
+
_v$23 !== _p$.t && (_p$.t = _$setProp(_el$54, "bg", _v$23, _p$.t));
|
|
22826
|
+
return _p$;
|
|
22827
|
+
}, {
|
|
22828
|
+
e: undefined,
|
|
22829
|
+
t: undefined
|
|
22830
|
+
});
|
|
22831
|
+
return _el$54;
|
|
22832
|
+
})()
|
|
22833
|
+
}), null);
|
|
22834
|
+
_$effect((_p$) => {
|
|
22835
|
+
var _v$18 = isSel() ? t.background : color(), _v$19 = isSel() ? t.primary : undefined;
|
|
22836
|
+
_v$18 !== _p$.e && (_p$.e = _$setProp(_el$52, "fg", _v$18, _p$.e));
|
|
22837
|
+
_v$19 !== _p$.t && (_p$.t = _$setProp(_el$52, "bg", _v$19, _p$.t));
|
|
22838
|
+
return _p$;
|
|
22839
|
+
}, {
|
|
22840
|
+
e: undefined,
|
|
22841
|
+
t: undefined
|
|
22842
|
+
});
|
|
22843
|
+
return _el$51;
|
|
22844
|
+
}
|
|
22845
|
+
});
|
|
22846
|
+
}
|
|
22847
|
+
}), null);
|
|
22848
|
+
_$insert(_el$29, _$createComponent(Show, {
|
|
22849
|
+
get when() {
|
|
22850
|
+
return _$memo(() => !!showsTree())() && overflow();
|
|
22851
|
+
},
|
|
22852
|
+
get children() {
|
|
22853
|
+
var _el$32 = _$createElement("text"), _el$33 = _$createTextNode(`\u2502 `);
|
|
22854
|
+
_$insertNode(_el$32, _el$33);
|
|
22855
|
+
_$insert(_el$32, (() => {
|
|
22856
|
+
var _c$2 = _$memo(() => hiddenBelow() > 0);
|
|
22857
|
+
return () => _c$2() ? `\u2026 ${hiddenBelow()} more \u2193` : "";
|
|
22858
|
+
})(), null);
|
|
22859
|
+
_$effect((_$p) => _$setProp(_el$32, "fg", t.textMuted, _$p));
|
|
22860
|
+
return _el$32;
|
|
22068
22861
|
}
|
|
22069
22862
|
}), null);
|
|
22070
|
-
_$insert(_el$
|
|
22863
|
+
_$insert(_el$29, _$createComponent(For, {
|
|
22864
|
+
get each() {
|
|
22865
|
+
return _$memo(() => !!(showsTree() && view().rows.length > 0 && !view().rows.some((r) => r.kind === "branch")))() ? noBranchesLines() : [];
|
|
22866
|
+
},
|
|
22867
|
+
children: (l) => (() => {
|
|
22868
|
+
var _el$55 = _$createElement("text"), _el$56 = _$createTextNode(`\u2502 `);
|
|
22869
|
+
_$insertNode(_el$55, _el$56);
|
|
22870
|
+
_$insert(_el$55, l, null);
|
|
22871
|
+
_$effect((_$p) => _$setProp(_el$55, "fg", t.textMuted, _$p));
|
|
22872
|
+
return _el$55;
|
|
22873
|
+
})()
|
|
22874
|
+
}), null);
|
|
22875
|
+
_$insert(_el$29, _$createComponent(For, {
|
|
22071
22876
|
get each() {
|
|
22072
|
-
return _$memo(() =>
|
|
22877
|
+
return _$memo(() => panel() === "help")() ? HELP.slice(0, helpHeight()) : [];
|
|
22073
22878
|
},
|
|
22074
22879
|
children: (l) => (() => {
|
|
22075
|
-
var _el$
|
|
22076
|
-
_$insertNode(_el$
|
|
22077
|
-
_$insert(_el$
|
|
22078
|
-
_$effect((_$p) => _$setProp(_el$
|
|
22079
|
-
return _el$
|
|
22880
|
+
var _el$57 = _$createElement("text"), _el$58 = _$createTextNode(`\u2502 `);
|
|
22881
|
+
_$insertNode(_el$57, _el$58);
|
|
22882
|
+
_$insert(_el$57, l, null);
|
|
22883
|
+
_$effect((_$p) => _$setProp(_el$57, "fg", l.startsWith(" ") ? t.textMuted : t.accent, _$p));
|
|
22884
|
+
return _el$57;
|
|
22080
22885
|
})()
|
|
22081
22886
|
}), null);
|
|
22082
|
-
_$insert(_el$
|
|
22887
|
+
_$insert(_el$28, _$createComponent(Show, {
|
|
22083
22888
|
get when() {
|
|
22084
22889
|
return showInspector();
|
|
22085
22890
|
},
|
|
22086
22891
|
get children() {
|
|
22087
|
-
var _el$
|
|
22088
|
-
_$setProp(_el$
|
|
22089
|
-
_$setProp(_el$
|
|
22090
|
-
_$insert(_el$
|
|
22892
|
+
var _el$34 = _$createElement("box");
|
|
22893
|
+
_$setProp(_el$34, "flexDirection", "column");
|
|
22894
|
+
_$setProp(_el$34, "paddingLeft", 1);
|
|
22895
|
+
_$insert(_el$34, _$createComponent(For, {
|
|
22091
22896
|
get each() {
|
|
22092
22897
|
return inspectorLines();
|
|
22093
22898
|
},
|
|
22094
22899
|
children: (l) => (() => {
|
|
22095
|
-
var _el$
|
|
22096
|
-
_$insertNode(_el$
|
|
22097
|
-
_$insert(_el$
|
|
22098
|
-
_$effect((_$p) => _$setProp(_el$
|
|
22099
|
-
return _el$
|
|
22900
|
+
var _el$59 = _$createElement("text"), _el$60 = _$createTextNode(`\u2503 `);
|
|
22901
|
+
_$insertNode(_el$59, _el$60);
|
|
22902
|
+
_$insert(_el$59, () => l.text, null);
|
|
22903
|
+
_$effect((_$p) => _$setProp(_el$59, "fg", l.fg, _$p));
|
|
22904
|
+
return _el$59;
|
|
22100
22905
|
})()
|
|
22101
22906
|
}));
|
|
22102
|
-
_$effect((_$p) => _$setProp(_el$
|
|
22103
|
-
return _el$
|
|
22907
|
+
_$effect((_$p) => _$setProp(_el$34, "width", inspectorWidth(), _$p));
|
|
22908
|
+
return _el$34;
|
|
22104
22909
|
}
|
|
22105
22910
|
}), null);
|
|
22106
|
-
_$insertNode(_el$
|
|
22107
|
-
_$insert(_el$
|
|
22911
|
+
_$insertNode(_el$35, _el$36);
|
|
22912
|
+
_$insert(_el$35, footer, null);
|
|
22108
22913
|
_$effect((_p$) => {
|
|
22109
|
-
var { background: _v$
|
|
22110
|
-
_v$
|
|
22111
|
-
_v$
|
|
22112
|
-
_v$
|
|
22113
|
-
_v$
|
|
22114
|
-
_v$
|
|
22914
|
+
var { background: _v$5, primary: _v$6 } = t, _v$7 = t[BAND_KEY[band()]], _v$8 = cropMode() ? t.warning : searchMode() ? t.accent : t.textMuted, _v$9 = cropMode() ? t.warning : t.textMuted;
|
|
22915
|
+
_v$5 !== _p$.e && (_p$.e = _$setProp(_el$, "backgroundColor", _v$5, _p$.e));
|
|
22916
|
+
_v$6 !== _p$.t && (_p$.t = _$setProp(_el$3, "fg", _v$6, _p$.t));
|
|
22917
|
+
_v$7 !== _p$.a && (_p$.a = _$setProp(_el$4, "fg", _v$7, _p$.a));
|
|
22918
|
+
_v$8 !== _p$.o && (_p$.o = _$setProp(_el$16, "fg", _v$8, _p$.o));
|
|
22919
|
+
_v$9 !== _p$.i && (_p$.i = _$setProp(_el$35, "fg", _v$9, _p$.i));
|
|
22115
22920
|
return _p$;
|
|
22116
22921
|
}, {
|
|
22117
22922
|
e: undefined,
|
|
@@ -22342,7 +23147,7 @@ var tui = async (api2, rawOptions) => {
|
|
|
22342
23147
|
adoptSoon();
|
|
22343
23148
|
});
|
|
22344
23149
|
api2.lifecycle?.onDispose(offCreated);
|
|
22345
|
-
const
|
|
23150
|
+
const promptDialog2 = (title, placeholder) => new Promise((resolve) => {
|
|
22346
23151
|
api2.ui.dialog.replace(() => api2.ui.DialogPrompt({
|
|
22347
23152
|
title,
|
|
22348
23153
|
placeholder,
|
|
@@ -22399,7 +23204,7 @@ var tui = async (api2, rawOptions) => {
|
|
|
22399
23204
|
if (!sessionID)
|
|
22400
23205
|
return;
|
|
22401
23206
|
await new Promise((r) => setTimeout(r, 30));
|
|
22402
|
-
const name = await
|
|
23207
|
+
const name = await promptDialog2(BRANCH_DIALOG.title, BRANCH_DIALOG.placeholder);
|
|
22403
23208
|
debug("branch.named", {
|
|
22404
23209
|
name
|
|
22405
23210
|
});
|
|
@@ -22437,7 +23242,7 @@ var tui = async (api2, rawOptions) => {
|
|
|
22437
23242
|
const last = api2.state.session.messages(sessionID).at(-1);
|
|
22438
23243
|
if (!last)
|
|
22439
23244
|
return;
|
|
22440
|
-
const value = await
|
|
23245
|
+
const value = await promptDialog2("Label (empty to remove)", "checkpoint");
|
|
22441
23246
|
if (value === undefined)
|
|
22442
23247
|
return;
|
|
22443
23248
|
setLabel({
|
|
@@ -22482,11 +23287,20 @@ var tui = async (api2, rawOptions) => {
|
|
|
22482
23287
|
});
|
|
22483
23288
|
return;
|
|
22484
23289
|
}
|
|
23290
|
+
const parent = await fetchTranscript(api2, branch.parentSessionID, directory).catch(() => {
|
|
23291
|
+
return;
|
|
23292
|
+
});
|
|
23293
|
+
const parentLabel = branch.parentSessionID === state.root ? TRUNK_LABEL : state.sessions[branch.parentSessionID]?.name ?? TRUNK_LABEL;
|
|
23294
|
+
const turns = ownTurnCount(api2.state.session.messages(sessionID), {
|
|
23295
|
+
messageID: branch.anchorMessageID,
|
|
23296
|
+
parentMessageIDs: parent?.messages.map((m) => m.id) ?? []
|
|
23297
|
+
});
|
|
22485
23298
|
const mode = await new Promise((resolve) => {
|
|
22486
23299
|
api2.ui.dialog.replace(() => api2.ui.DialogSelect({
|
|
22487
|
-
title: mergeDialogTitle(branch.name ?? "branch",
|
|
23300
|
+
title: mergeDialogTitle(branch.name ?? "branch", parent ? mergeTargetOf(parentLabel, parent.messages) : undefined),
|
|
22488
23301
|
options: mergeDialogOptions({
|
|
22489
|
-
siblings: openSiblings(state, sessionID).length
|
|
23302
|
+
siblings: openSiblings(state, sessionID).length,
|
|
23303
|
+
turns
|
|
22490
23304
|
}),
|
|
22491
23305
|
onSelect: (o) => {
|
|
22492
23306
|
resolve(o.value);
|
|
@@ -22581,6 +23395,8 @@ ${MERGE_TRUST}`,
|
|
|
22581
23395
|
return store.stateForSession(props.session_id);
|
|
22582
23396
|
});
|
|
22583
23397
|
const branch = () => st()?.sessions[props.session_id];
|
|
23398
|
+
const size = createMemo2(() => contextSizeOf(toMinimalMessages(api2.state.session.messages(props.session_id), api2.state.part)));
|
|
23399
|
+
const limit = createMemo2(() => modelContextLimit(api2, props.session_id));
|
|
22584
23400
|
const crops = () => st()?.activeCrops(props.session_id) ?? [];
|
|
22585
23401
|
const hidden = () => crops().reduce((s, c) => s + c.targets.reduce((x, y) => x + y.estTokens, 0), 0);
|
|
22586
23402
|
const siblings = () => Object.values(st()?.sessions ?? {}).filter((b) => b.parentSessionID === props.session_id && b.status === "open").length;
|
|
@@ -22591,9 +23407,10 @@ ${MERGE_TRUST}`,
|
|
|
22591
23407
|
return `${b.status}${title && room > 3 ? ` \xB7 from "${clip(title, room)}"` : ""}`;
|
|
22592
23408
|
};
|
|
22593
23409
|
return (() => {
|
|
22594
|
-
var _el$ = _$createElement2("box"), _el$2 = _$createElement2("text"), _el$3 = _$createElement2("b"), _el$
|
|
23410
|
+
var _el$ = _$createElement2("box"), _el$2 = _$createElement2("text"), _el$3 = _$createElement2("b"), _el$7 = _$createElement2("text"), _el$9 = _$createElement2("text");
|
|
22595
23411
|
_$insertNode2(_el$, _el$2);
|
|
22596
|
-
_$insertNode2(_el$, _el$
|
|
23412
|
+
_$insertNode2(_el$, _el$7);
|
|
23413
|
+
_$insertNode2(_el$, _el$9);
|
|
22597
23414
|
_$setProp2(_el$, "flexDirection", "column");
|
|
22598
23415
|
_$insertNode2(_el$2, _el$3);
|
|
22599
23416
|
_$insertNode2(_el$3, _$createTextNode2(`Context tree`));
|
|
@@ -22603,10 +23420,10 @@ ${MERGE_TRUST}`,
|
|
|
22603
23420
|
},
|
|
22604
23421
|
get fallback() {
|
|
22605
23422
|
return (() => {
|
|
22606
|
-
var _el$
|
|
22607
|
-
_$insert2(_el$
|
|
22608
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
22609
|
-
return _el$
|
|
23423
|
+
var _el$1 = _$createElement2("text");
|
|
23424
|
+
_$insert2(_el$1, () => `trunk${siblings() ? ` \xB7 ${siblings()} branch${siblings() === 1 ? "" : "es"}` : ""}`);
|
|
23425
|
+
_$effect2((_$p) => _$setProp2(_el$1, "fg", t.text, _$p));
|
|
23426
|
+
return _el$1;
|
|
22610
23427
|
})();
|
|
22611
23428
|
},
|
|
22612
23429
|
get children() {
|
|
@@ -22622,27 +23439,30 @@ ${MERGE_TRUST}`,
|
|
|
22622
23439
|
return _el$6;
|
|
22623
23440
|
})()];
|
|
22624
23441
|
}
|
|
22625
|
-
}), _el$
|
|
23442
|
+
}), _el$7);
|
|
23443
|
+
_$insert2(_el$7, () => formatContext(size(), limit()));
|
|
22626
23444
|
_$insert2(_el$, _$createComponent2(Show2, {
|
|
22627
23445
|
get when() {
|
|
22628
23446
|
return crops().length;
|
|
22629
23447
|
},
|
|
22630
23448
|
get children() {
|
|
22631
|
-
var _el$
|
|
22632
|
-
_$insert2(_el$
|
|
22633
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
22634
|
-
return _el$
|
|
23449
|
+
var _el$8 = _$createElement2("text");
|
|
23450
|
+
_$insert2(_el$8, () => `\u2702 ${crops().length} crop${crops().length === 1 ? "" : "s"} \xB7 ~${formatK(hidden())} hidden`);
|
|
23451
|
+
_$effect2((_$p) => _$setProp2(_el$8, "fg", t.warning, _$p));
|
|
23452
|
+
return _el$8;
|
|
22635
23453
|
}
|
|
22636
|
-
}), _el$
|
|
22637
|
-
_$insertNode2(_el$
|
|
23454
|
+
}), _el$9);
|
|
23455
|
+
_$insertNode2(_el$9, _$createTextNode2(`/tree \xB7 ctrl+q`));
|
|
22638
23456
|
_$effect2((_p$) => {
|
|
22639
|
-
var
|
|
23457
|
+
var _v$ = t.text, _v$2 = t[BAND_COLOR[bandFor(size().tokens, limit())]], _v$3 = t.textMuted;
|
|
22640
23458
|
_v$ !== _p$.e && (_p$.e = _$setProp2(_el$2, "fg", _v$, _p$.e));
|
|
22641
|
-
_v$2 !== _p$.t && (_p$.t = _$setProp2(_el$
|
|
23459
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp2(_el$7, "fg", _v$2, _p$.t));
|
|
23460
|
+
_v$3 !== _p$.a && (_p$.a = _$setProp2(_el$9, "fg", _v$3, _p$.a));
|
|
22642
23461
|
return _p$;
|
|
22643
23462
|
}, {
|
|
22644
23463
|
e: undefined,
|
|
22645
|
-
t: undefined
|
|
23464
|
+
t: undefined,
|
|
23465
|
+
a: undefined
|
|
22646
23466
|
});
|
|
22647
23467
|
return _el$;
|
|
22648
23468
|
})();
|
|
@@ -22650,12 +23470,12 @@ ${MERGE_TRUST}`,
|
|
|
22650
23470
|
session_prompt_right: (_ctx, props) => {
|
|
22651
23471
|
const t = api2.theme.current;
|
|
22652
23472
|
const size = createMemo2(() => contextSizeOf(toMinimalMessages(api2.state.session.messages(props.session_id), api2.state.part)));
|
|
22653
|
-
const band = () => bandFor(size().tokens);
|
|
22654
23473
|
const branch = () => {
|
|
22655
23474
|
journalRevision();
|
|
22656
23475
|
return store.stateForSession(props.session_id)?.sessions[props.session_id];
|
|
22657
23476
|
};
|
|
22658
23477
|
const limit = createMemo2(() => modelContextLimit(api2, props.session_id));
|
|
23478
|
+
const band = () => bandFor(size().tokens, limit());
|
|
22659
23479
|
const reserve = () => api2.state.config.compaction?.reserved ?? 16384;
|
|
22660
23480
|
const [trend, setTrend] = createSignal3("");
|
|
22661
23481
|
let prevTokens = 0;
|
|
@@ -22711,7 +23531,7 @@ ${MERGE_TRUST}`,
|
|
|
22711
23531
|
redNudged = true;
|
|
22712
23532
|
api2.ui.toast({
|
|
22713
23533
|
variant: "warning",
|
|
22714
|
-
message:
|
|
23534
|
+
message: `context is in the red band (${limit() ? "\u226585% of the window" : "\u226564k"}) \u2014 consider /tree \u2192 c crop, or /merge a branch`,
|
|
22715
23535
|
duration: 6000
|
|
22716
23536
|
});
|
|
22717
23537
|
} else if (b === "low" || b === "healthy")
|
|
@@ -22728,10 +23548,10 @@ ${MERGE_TRUST}`,
|
|
|
22728
23548
|
guardNudged = false;
|
|
22729
23549
|
});
|
|
22730
23550
|
return (() => {
|
|
22731
|
-
var _el$
|
|
22732
|
-
_$insert2(_el$
|
|
22733
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
22734
|
-
return _el$
|
|
23551
|
+
var _el$10 = _$createElement2("text");
|
|
23552
|
+
_$insert2(_el$10, () => `${branch() ? `\u2387 ${branchLabel(api2, props.session_id, branch().name, 24)} \xB7 ` : ""}${formatContext(size(), limit())}${trend()}`);
|
|
23553
|
+
_$effect2((_$p) => _$setProp2(_el$10, "fg", t[BAND_COLOR[band()]], _$p));
|
|
23554
|
+
return _el$10;
|
|
22735
23555
|
})();
|
|
22736
23556
|
}
|
|
22737
23557
|
}
|