omp-fabric 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +28 -0
- package/dist/commands/fabric.d.ts.map +1 -1
- package/dist/compaction/lcm-maintenance.d.ts +8 -1
- package/dist/compaction/lcm-maintenance.d.ts.map +1 -1
- package/dist/compaction/lcm-runtime.d.ts +26 -0
- package/dist/compaction/lcm-runtime.d.ts.map +1 -1
- package/dist/compaction/lcm-runtime.js +83 -5
- package/dist/compaction/lcm-runtime.js.map +2 -2
- package/dist/fabric-runtime-state.d.ts +47 -0
- package/dist/fabric-runtime-state.d.ts.map +1 -1
- package/dist/fabric-runtime-state.js.map +2 -2
- package/dist/index.js +2 -51
- package/dist/index.js.map +3 -3
- package/dist/storage/lcm-ledger.d.ts +8 -0
- package/dist/storage/lcm-ledger.d.ts.map +1 -1
- package/dist/storage/lcm-migration.d.ts +2 -0
- package/dist/storage/lcm-migration.d.ts.map +1 -1
- package/dist/ui/controller.d.ts.map +1 -1
- package/dist/ui/dashboard-model.d.ts +1 -1
- package/dist/ui/dashboard-model.d.ts.map +1 -1
- package/dist/ui/dashboard.d.ts +18 -0
- package/dist/ui/dashboard.d.ts.map +1 -1
- package/dist/ui/dashboard.js +498 -64
- package/dist/ui/dashboard.js.map +4 -4
- package/dist/ui/lcm-panel.d.ts +38 -0
- package/dist/ui/lcm-panel.d.ts.map +1 -0
- package/docs/compaction.md +1 -1
- package/package.json +3 -3
package/dist/ui/dashboard.js
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
formatActorDataPreview,
|
|
27
27
|
formatClock,
|
|
28
|
+
formatCost,
|
|
28
29
|
formatDuration,
|
|
29
30
|
formatTokens,
|
|
30
31
|
padToWidth,
|
|
@@ -53,8 +54,8 @@ import {
|
|
|
53
54
|
getKeybindings as getKeybindings3,
|
|
54
55
|
Key,
|
|
55
56
|
matchesKey,
|
|
56
|
-
truncateToWidth as
|
|
57
|
-
visibleWidth as
|
|
57
|
+
truncateToWidth as truncateToWidth5,
|
|
58
|
+
visibleWidth as visibleWidth5
|
|
58
59
|
} from "@oh-my-pi/pi-tui";
|
|
59
60
|
|
|
60
61
|
// src/ui/topology.ts
|
|
@@ -988,13 +989,257 @@ var entityTail = (entity, now) => {
|
|
|
988
989
|
return [entity.value.owner, entity.value.detail, `v${entity.value.version}`].filter((value) => Boolean(value)).join(" \xB7 ");
|
|
989
990
|
};
|
|
990
991
|
|
|
992
|
+
// src/ui/lcm-panel.ts
|
|
993
|
+
import { truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
994
|
+
var LCM_NODE_PAGE = 200;
|
|
995
|
+
var COVERAGE_COMPLETE_RATIO = 0.95;
|
|
996
|
+
var EMERGENCY_MODEL_HASH = "emergency";
|
|
997
|
+
var readLcmSnapshot = (source, offset) => {
|
|
998
|
+
const nodes = source.nodes(LCM_NODE_PAGE, offset);
|
|
999
|
+
return {
|
|
1000
|
+
report: source.report(),
|
|
1001
|
+
coverage: source.coverage(),
|
|
1002
|
+
preview: source.preview(),
|
|
1003
|
+
coverageMap: source.coverageMap(),
|
|
1004
|
+
nodes,
|
|
1005
|
+
offset,
|
|
1006
|
+
hasMore: nodes.length === LCM_NODE_PAGE
|
|
1007
|
+
};
|
|
1008
|
+
};
|
|
1009
|
+
var lcmNodeDetail = (source, nodeId) => {
|
|
1010
|
+
const detail = source.node(nodeId);
|
|
1011
|
+
if (!detail) return void 0;
|
|
1012
|
+
return {
|
|
1013
|
+
node: detail.node,
|
|
1014
|
+
revisions: [...detail.revisions].sort((left, right) => left.revision - right.revision),
|
|
1015
|
+
ancestors: detail.ancestors
|
|
1016
|
+
};
|
|
1017
|
+
};
|
|
1018
|
+
var formatBytes = (bytes) => {
|
|
1019
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
1020
|
+
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
|
1021
|
+
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} kB`;
|
|
1022
|
+
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
1023
|
+
};
|
|
1024
|
+
var formatBudget = (used, limit) => Number.isFinite(limit) ? `${used}/${limit}` : `${used}/no limit`;
|
|
1025
|
+
var formatPercent = (value) => value > 0 && value < 1 ? `${value.toFixed(value < 0.1 ? 2 : 1)}%` : `${Math.round(value)}%`;
|
|
1026
|
+
var isEmergency = (modelHash) => modelHash === EMERGENCY_MODEL_HASH;
|
|
1027
|
+
var originLabel = (modelHash) => isEmergency(modelHash) ? "excerpt" : "model";
|
|
1028
|
+
var shortId = (value) => value.length > 10 ? `${value.slice(0, 10)}\u2026` : value;
|
|
1029
|
+
var textBytes = (text) => text ? Buffer.byteLength(text, "utf8") : 0;
|
|
1030
|
+
var lcmUnavailableLine = () => "LCM ledger unavailable \xB7 set compaction.engine to lcm and start a session to populate it";
|
|
1031
|
+
var renderLcmHeaderLines = (theme, snapshot, width) => {
|
|
1032
|
+
const { report } = snapshot;
|
|
1033
|
+
const summary = [
|
|
1034
|
+
report.state,
|
|
1035
|
+
report.degraded ? `degraded ${report.degraded}` : void 0,
|
|
1036
|
+
`session ${report.sessionId ?? "none"} \xB7 ${report.sessionEntries} entries`,
|
|
1037
|
+
`ledger ${report.rawEntries} entries`,
|
|
1038
|
+
`model ${report.summaryModel || "inherit"}`,
|
|
1039
|
+
`${report.modelNodes} model \xB7 ${report.emergencyNodes} excerpt \xB7 ${report.pendingNodes} pending`,
|
|
1040
|
+
`today ${formatBudget(report.usage.calls, report.budget.calls)} calls \xB7 ${formatCost(report.usage.cost)}`
|
|
1041
|
+
].filter((value) => Boolean(value)).join(" \xB7 ");
|
|
1042
|
+
return wrapPlainText(summary, width, 2).map((line) => theme.fg("dim", line));
|
|
1043
|
+
};
|
|
1044
|
+
var bandRun = (theme, char, length) => {
|
|
1045
|
+
const color = char === "\u2588" ? "success" : char === "\u2593" ? "warning" : "dim";
|
|
1046
|
+
return theme.fg(color, char.repeat(length));
|
|
1047
|
+
};
|
|
1048
|
+
var coverageBand = (theme, map, cells) => {
|
|
1049
|
+
if (cells <= 0) return "";
|
|
1050
|
+
if (map.length === 0) return theme.fg("dim", "\xB7".repeat(cells));
|
|
1051
|
+
let band = "";
|
|
1052
|
+
let runChar = "";
|
|
1053
|
+
let runLength = 0;
|
|
1054
|
+
for (let cell = 0; cell < cells; cell++) {
|
|
1055
|
+
const start = Math.min(map.length - 1, Math.floor(cell * map.length / cells));
|
|
1056
|
+
const end = Math.min(map.length, Math.max(start + 1, Math.floor((cell + 1) * map.length / cells)));
|
|
1057
|
+
let covered = 0;
|
|
1058
|
+
for (let index = start; index < end; index++) {
|
|
1059
|
+
if (map[index].covered) covered++;
|
|
1060
|
+
}
|
|
1061
|
+
const char = covered === 0 ? "\u2591" : covered === end - start ? "\u2588" : "\u2593";
|
|
1062
|
+
if (char === runChar) {
|
|
1063
|
+
runLength++;
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
if (runLength > 0) band += bandRun(theme, runChar, runLength);
|
|
1067
|
+
runChar = char;
|
|
1068
|
+
runLength = 1;
|
|
1069
|
+
}
|
|
1070
|
+
if (runLength > 0) band += bandRun(theme, runChar, runLength);
|
|
1071
|
+
return band;
|
|
1072
|
+
};
|
|
1073
|
+
var renderLcmCoverageLine = (theme, snapshot, width) => {
|
|
1074
|
+
const { active, covered } = snapshot.coverage;
|
|
1075
|
+
const label = `${covered}/${active} covered ${formatPercent(active > 0 ? covered / active * 100 : 0)}`;
|
|
1076
|
+
const cells = Math.max(0, width - visibleWidth(label) - 1);
|
|
1077
|
+
const band = coverageBand(theme, snapshot.coverageMap, cells);
|
|
1078
|
+
return truncateToWidth(`${band} ${theme.fg("muted", label)}`, width, "");
|
|
1079
|
+
};
|
|
1080
|
+
var nodeRow = (node) => {
|
|
1081
|
+
const indent = " ".repeat(Math.min(node.depth, 8));
|
|
1082
|
+
const glyph = node.kind === "condensed" ? "\u25C6" : "\u25C7";
|
|
1083
|
+
return safeText(
|
|
1084
|
+
`${indent}${glyph} ${node.kind} ${originLabel(node.modelHash)} \xB7 ${node.sources.length} src \xB7 ${node.children.length} ch \xB7 ${formatBytes(textBytes(node.text))} \xB7 ${shortId(node.nodeId)}`
|
|
1085
|
+
);
|
|
1086
|
+
};
|
|
1087
|
+
var renderLcmNodeLines = (theme, snapshot, width, height, selectedIndex) => {
|
|
1088
|
+
const available = Math.max(1, height);
|
|
1089
|
+
const total = snapshot.nodes.length;
|
|
1090
|
+
const heading = `nodes ${total === 0 ? 0 : snapshot.offset + 1}-${snapshot.offset + total}${snapshot.hasMore ? "+" : ""}`;
|
|
1091
|
+
const lines = [theme.fg("accent", truncateToWidth(heading, width, ""))];
|
|
1092
|
+
if (total === 0) {
|
|
1093
|
+
lines.push(theme.fg("dim", " (no summary nodes on the active branch yet)"));
|
|
1094
|
+
while (lines.length < available) lines.push("");
|
|
1095
|
+
return lines.slice(0, available);
|
|
1096
|
+
}
|
|
1097
|
+
const rows = Math.max(1, available - 1);
|
|
1098
|
+
const start = Math.max(
|
|
1099
|
+
0,
|
|
1100
|
+
Math.min(selectedIndex - Math.floor(rows / 2), Math.max(0, total - rows))
|
|
1101
|
+
);
|
|
1102
|
+
for (let index = start; index < Math.min(total, start + rows); index++) {
|
|
1103
|
+
const node = snapshot.nodes[index];
|
|
1104
|
+
const emergency = isEmergency(node.modelHash);
|
|
1105
|
+
let line = theme.fg(emergency ? "warning" : "muted", nodeRow(node));
|
|
1106
|
+
if (index === selectedIndex) line = theme.bg("selectedBg", padToWidth(line, width));
|
|
1107
|
+
lines.push(truncateToWidth(line, width, ""));
|
|
1108
|
+
}
|
|
1109
|
+
while (lines.length < available) lines.push("");
|
|
1110
|
+
return lines.slice(0, available);
|
|
1111
|
+
};
|
|
1112
|
+
var renderLcmPreviewLines = (theme, snapshot, width, height) => {
|
|
1113
|
+
const available = Math.max(1, height);
|
|
1114
|
+
const preview = snapshot.preview;
|
|
1115
|
+
const complete = preview.activeSources > 0 && preview.coveredSources / preview.activeSources >= COVERAGE_COMPLETE_RATIO;
|
|
1116
|
+
const ratio = complete && preview.sourceBytes > 0 ? ` \xB7 assembled text is ${formatPercent(preview.summaryBytes / preview.sourceBytes * 100)} of the session payload it replaces` : "";
|
|
1117
|
+
const headline = `preview \xB7 frontier holds ${preview.coveredSources}/${preview.activeSources} sources \xB7 ${preview.nodes} nodes assemble to ${formatBytes(preview.summaryBytes)} \xB7 session stored payload ${formatBytes(preview.sourceBytes)}${ratio}`;
|
|
1118
|
+
const lines = wrapPlainText(headline, width, 2).map((line) => theme.fg("accent", line));
|
|
1119
|
+
const body = wrapPlainText(preview.text, width, Math.max(1, available - lines.length));
|
|
1120
|
+
for (const line of body) lines.push(theme.fg("dim", line));
|
|
1121
|
+
if (body.length === 0) lines.push(theme.fg("dim", "(the frontier assembles no text yet)"));
|
|
1122
|
+
while (lines.length < available) lines.push("");
|
|
1123
|
+
return lines.slice(0, available);
|
|
1124
|
+
};
|
|
1125
|
+
var revisionColumns = (theme, detail, width, rows) => {
|
|
1126
|
+
const current = detail.revisions[detail.revisions.length - 1];
|
|
1127
|
+
const previous = detail.revisions[detail.revisions.length - 2];
|
|
1128
|
+
if (!current) return [];
|
|
1129
|
+
if (!previous) {
|
|
1130
|
+
const lines2 = [theme.fg("accent", `text \xB7 current revision ${current.revision} \xB7 ${originLabel(current.modelHash)}`)];
|
|
1131
|
+
for (const line of wrapPlainText(current.text, width, Math.max(1, rows - 1))) {
|
|
1132
|
+
lines2.push(theme.fg("dim", line));
|
|
1133
|
+
}
|
|
1134
|
+
return lines2;
|
|
1135
|
+
}
|
|
1136
|
+
const label = (revision, name) => `${name} revision ${revision.revision} \xB7 ${originLabel(revision.modelHash)}`;
|
|
1137
|
+
if (width < 60) {
|
|
1138
|
+
const lines2 = [theme.fg("accent", label(current, "current"))];
|
|
1139
|
+
for (const line of wrapPlainText(current.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {
|
|
1140
|
+
lines2.push(theme.fg("dim", line));
|
|
1141
|
+
}
|
|
1142
|
+
lines2.push(theme.fg("accent", label(previous, "previous")));
|
|
1143
|
+
for (const line of wrapPlainText(previous.text, width, Math.max(1, Math.floor((rows - 2) / 2)))) {
|
|
1144
|
+
lines2.push(theme.fg("muted", line));
|
|
1145
|
+
}
|
|
1146
|
+
return lines2;
|
|
1147
|
+
}
|
|
1148
|
+
const columnWidth = Math.floor((width - 1) / 2);
|
|
1149
|
+
const body = Math.max(1, rows - 1);
|
|
1150
|
+
const previousBody = wrapPlainText(previous.text, columnWidth, body);
|
|
1151
|
+
const currentBody = wrapPlainText(current.text, columnWidth, body);
|
|
1152
|
+
const lines = [
|
|
1153
|
+
`${padToWidth(theme.fg("accent", label(previous, "previous")), columnWidth)}${theme.fg("borderMuted", "\u2502")}${theme.fg("accent", label(current, "current"))}`
|
|
1154
|
+
];
|
|
1155
|
+
for (let index = 0; index < body; index++) {
|
|
1156
|
+
const left = previousBody[index];
|
|
1157
|
+
const right = currentBody[index];
|
|
1158
|
+
if (left === void 0 && right === void 0) break;
|
|
1159
|
+
lines.push(
|
|
1160
|
+
`${padToWidth(theme.fg("muted", left ?? ""), columnWidth)}${theme.fg("borderMuted", "\u2502")}${theme.fg("dim", right ?? "")}`
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
return lines;
|
|
1164
|
+
};
|
|
1165
|
+
var renderLcmDetailLines = (theme, detail, sourceIndex, entry, width, height) => {
|
|
1166
|
+
const available = Math.max(1, height);
|
|
1167
|
+
const node = detail.node;
|
|
1168
|
+
const lines = [
|
|
1169
|
+
theme.fg(
|
|
1170
|
+
isEmergency(node.modelHash) ? "warning" : "accent",
|
|
1171
|
+
truncateToWidth(
|
|
1172
|
+
safeText(
|
|
1173
|
+
`${node.kind} \xB7 ${originLabel(node.modelHash)} \xB7 depth ${node.depth} \xB7 ${node.state} \xB7 ${formatClock(node.createdAt)} \xB7 ${node.nodeId}`
|
|
1174
|
+
),
|
|
1175
|
+
width,
|
|
1176
|
+
""
|
|
1177
|
+
)
|
|
1178
|
+
),
|
|
1179
|
+
theme.fg(
|
|
1180
|
+
"dim",
|
|
1181
|
+
truncateToWidth(safeText(`model hash ${node.modelHash} \xB7 branch ${node.branch ?? "none"}`), width, "")
|
|
1182
|
+
),
|
|
1183
|
+
theme.fg(
|
|
1184
|
+
"dim",
|
|
1185
|
+
truncateToWidth(
|
|
1186
|
+
safeText(`ancestors ${detail.ancestors.length > 0 ? detail.ancestors.join(" \u203A ") : "none"}`),
|
|
1187
|
+
width,
|
|
1188
|
+
""
|
|
1189
|
+
)
|
|
1190
|
+
),
|
|
1191
|
+
theme.fg(
|
|
1192
|
+
"dim",
|
|
1193
|
+
truncateToWidth(
|
|
1194
|
+
safeText(
|
|
1195
|
+
`children (${node.children.length}) ${node.children.length > 0 ? node.children.map(shortId).join(" ") : "none"}`
|
|
1196
|
+
),
|
|
1197
|
+
width,
|
|
1198
|
+
""
|
|
1199
|
+
)
|
|
1200
|
+
),
|
|
1201
|
+
theme.fg("accent", truncateToWidth(`sources (${node.sources.length})`, width, ""))
|
|
1202
|
+
];
|
|
1203
|
+
const sourceRows = Math.min(node.sources.length, Math.max(1, Math.floor(available / 4)));
|
|
1204
|
+
const sourceStart = Math.max(
|
|
1205
|
+
0,
|
|
1206
|
+
Math.min(sourceIndex - Math.floor(sourceRows / 2), Math.max(0, node.sources.length - sourceRows))
|
|
1207
|
+
);
|
|
1208
|
+
for (let index = sourceStart; index < Math.min(node.sources.length, sourceStart + sourceRows); index++) {
|
|
1209
|
+
const source = node.sources[index];
|
|
1210
|
+
const text = ` ${source.sessionId}/${source.entryId}@${source.revision}`;
|
|
1211
|
+
const line = index === sourceIndex ? theme.bg("selectedBg", padToWidth(theme.fg("muted", text), width)) : theme.fg("muted", text);
|
|
1212
|
+
lines.push(truncateToWidth(line, width, ""));
|
|
1213
|
+
}
|
|
1214
|
+
if (node.sources.length === 0) lines.push(theme.fg("dim", " (no sources)"));
|
|
1215
|
+
if (entry) {
|
|
1216
|
+
lines.push(
|
|
1217
|
+
theme.fg(
|
|
1218
|
+
"accent",
|
|
1219
|
+
truncateToWidth(
|
|
1220
|
+
safeText(`raw entry \xB7 ${entry.role} \xB7 ${formatClock(entry.createdAt)} \xB7 ${formatBytes(Buffer.byteLength(entry.payloadJson, "utf8"))} stored`),
|
|
1221
|
+
width,
|
|
1222
|
+
""
|
|
1223
|
+
)
|
|
1224
|
+
)
|
|
1225
|
+
);
|
|
1226
|
+
for (const line of wrapPlainText(entry.content, width, Math.max(1, Math.floor(available / 4)))) {
|
|
1227
|
+
lines.push(theme.fg("dim", line));
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
const remaining = Math.max(1, available - lines.length);
|
|
1231
|
+
for (const line of revisionColumns(theme, detail, width, remaining)) lines.push(line);
|
|
1232
|
+
while (lines.length < available) lines.push("");
|
|
1233
|
+
return lines.slice(0, available);
|
|
1234
|
+
};
|
|
1235
|
+
|
|
991
1236
|
// src/ui/dashboard-detail.ts
|
|
992
|
-
import { Markdown, truncateToWidth as
|
|
1237
|
+
import { Markdown, truncateToWidth as truncateToWidth3, wrapTextWithAnsi, visibleWidth as visibleWidth3 } from "@oh-my-pi/pi-tui";
|
|
993
1238
|
|
|
994
1239
|
// src/ui/state-file-preview.ts
|
|
995
1240
|
import fs from "node:fs";
|
|
996
1241
|
import path from "node:path";
|
|
997
|
-
import { truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
1242
|
+
import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@oh-my-pi/pi-tui";
|
|
998
1243
|
var MAX_FILE_BYTES = 64 * 1024;
|
|
999
1244
|
var MAX_FILE_LINES = 240;
|
|
1000
1245
|
var CACHE_LIMIT = 64;
|
|
@@ -1070,14 +1315,14 @@ var renderStateFilePreview = (preview, theme, width, maxLines, invalidate) => {
|
|
|
1070
1315
|
const digits = String(Math.max(1, shown.length)).length;
|
|
1071
1316
|
const output = highlighted.map((line, index) => {
|
|
1072
1317
|
const gutter = theme.fg("dim", `${String(index + 1).padStart(digits)} \u2502 `);
|
|
1073
|
-
return
|
|
1318
|
+
return truncateToWidth2(gutter + line, width, "");
|
|
1074
1319
|
});
|
|
1075
1320
|
if ((preview.truncated || preview.lines.length > shown.length) && output.length < maxLines) {
|
|
1076
1321
|
const omitted = Math.max(0, preview.lines.length - shown.length);
|
|
1077
1322
|
const label = omitted > 0 ? `\u2026 ${omitted}+ lines omitted` : "\u2026 file truncated";
|
|
1078
|
-
output.push(
|
|
1323
|
+
output.push(truncateToWidth2(theme.fg("dim", label), width, ""));
|
|
1079
1324
|
}
|
|
1080
|
-
return output.filter((line) =>
|
|
1325
|
+
return output.filter((line) => visibleWidth2(line) <= width);
|
|
1081
1326
|
};
|
|
1082
1327
|
|
|
1083
1328
|
// src/ui/dashboard-detail.ts
|
|
@@ -1209,7 +1454,7 @@ var DashboardDetailRenderer = class {
|
|
|
1209
1454
|
lines.push(this.row(width, this.theme.fg("muted", ` ${actionLine}`)));
|
|
1210
1455
|
}
|
|
1211
1456
|
lines.push(this.bottomBorder(width));
|
|
1212
|
-
return lines.map((line) =>
|
|
1457
|
+
return lines.map((line) => truncateToWidth3(line, width, ""));
|
|
1213
1458
|
}
|
|
1214
1459
|
transcriptLines(entity, width) {
|
|
1215
1460
|
const transcript = this.transcriptFor(entity);
|
|
@@ -1240,7 +1485,7 @@ var DashboardDetailRenderer = class {
|
|
|
1240
1485
|
statusGlyph(entry.status ?? "completed")
|
|
1241
1486
|
);
|
|
1242
1487
|
lines.push(
|
|
1243
|
-
|
|
1488
|
+
truncateToWidth3(
|
|
1244
1489
|
`${glyph} ${this.theme.fg(
|
|
1245
1490
|
entry.kind === "assistant" ? "accent" : "muted",
|
|
1246
1491
|
safeText(entry.label)
|
|
@@ -1263,7 +1508,7 @@ var DashboardDetailRenderer = class {
|
|
|
1263
1508
|
}
|
|
1264
1509
|
for (const paragraph of entry.text.split("\n")) {
|
|
1265
1510
|
const wrapped = wrapPlainText(paragraph, Math.max(1, width - 2), 1e4);
|
|
1266
|
-
for (const line of wrapped) lines.push(
|
|
1511
|
+
for (const line of wrapped) lines.push(truncateToWidth3(` ${line}`, width, ""));
|
|
1267
1512
|
}
|
|
1268
1513
|
}
|
|
1269
1514
|
if (transcript.hasNewer) {
|
|
@@ -1291,7 +1536,7 @@ var DashboardDetailRenderer = class {
|
|
|
1291
1536
|
const headline = title ?? this.theme.fg("toolTitle", this.theme.bold(entry.toolName ?? entry.label));
|
|
1292
1537
|
const collapsedSummary = !this.transcriptToolsExpanded && entry.text ? ` \xB7 ${safeText(entry.text).replace(/\s+/g, " ").trim()}` : "";
|
|
1293
1538
|
const lines = [
|
|
1294
|
-
|
|
1539
|
+
truncateToWidth3(
|
|
1295
1540
|
`${padding}${glyph} ${headline}${this.theme.fg("dim", `${status}${collapsedSummary}`)}`,
|
|
1296
1541
|
width,
|
|
1297
1542
|
""
|
|
@@ -1308,8 +1553,8 @@ var DashboardDetailRenderer = class {
|
|
|
1308
1553
|
rendered.lines,
|
|
1309
1554
|
this.theme,
|
|
1310
1555
|
this.codePreviewSettings?.diffIntensity ?? "off"
|
|
1311
|
-
).render(Math.max(1, width -
|
|
1312
|
-
lines.push(
|
|
1556
|
+
).render(Math.max(1, width - visibleWidth3(bodyPadding)))) {
|
|
1557
|
+
lines.push(truncateToWidth3(`${bodyPadding}${row}`, width, ""));
|
|
1313
1558
|
}
|
|
1314
1559
|
if (rendered.hidden > 0) {
|
|
1315
1560
|
lines.push(this.theme.fg("dim", `${bodyPadding}\u2026 ${rendered.hidden} more lines`));
|
|
@@ -1321,10 +1566,10 @@ var DashboardDetailRenderer = class {
|
|
|
1321
1566
|
} else if (entry.text) {
|
|
1322
1567
|
for (const row of wrapPlainText(
|
|
1323
1568
|
entry.text,
|
|
1324
|
-
Math.max(1, width -
|
|
1569
|
+
Math.max(1, width - visibleWidth3(bodyPadding)),
|
|
1325
1570
|
1e4
|
|
1326
1571
|
)) {
|
|
1327
|
-
lines.push(
|
|
1572
|
+
lines.push(truncateToWidth3(`${bodyPadding}${row}`, width, ""));
|
|
1328
1573
|
}
|
|
1329
1574
|
}
|
|
1330
1575
|
if (entry.result !== void 0) {
|
|
@@ -1361,11 +1606,11 @@ var DashboardDetailRenderer = class {
|
|
|
1361
1606
|
const yamlLines = yaml.split("\n");
|
|
1362
1607
|
const shownYamlLines = yamlLines.slice(0, TRANSCRIPT_STRUCTURED_LINES);
|
|
1363
1608
|
const highlighted = highlightCode(shownYamlLines.join("\n"), "yaml", this.highlightInvalidate) ?? shownYamlLines.map((line) => this.theme.fg("mdCodeBlock", line || " "));
|
|
1364
|
-
const lines = [
|
|
1609
|
+
const lines = [truncateToWidth3(`${padding}${this.theme.fg("dim", `${label}:`)}`, width, "")];
|
|
1365
1610
|
const nestedPadding = `${padding} `;
|
|
1366
1611
|
for (const row of highlighted) {
|
|
1367
|
-
for (const wrapped of wrapTextWithAnsi(row, Math.max(1, width -
|
|
1368
|
-
lines.push(
|
|
1612
|
+
for (const wrapped of wrapTextWithAnsi(row, Math.max(1, width - visibleWidth3(nestedPadding)))) {
|
|
1613
|
+
lines.push(truncateToWidth3(`${nestedPadding}${wrapped}`, width, ""));
|
|
1369
1614
|
if (lines.length > TRANSCRIPT_STRUCTURED_LINES) break;
|
|
1370
1615
|
}
|
|
1371
1616
|
if (lines.length > TRANSCRIPT_STRUCTURED_LINES) break;
|
|
@@ -1405,7 +1650,7 @@ var DashboardDetailRenderer = class {
|
|
|
1405
1650
|
}
|
|
1406
1651
|
}
|
|
1407
1652
|
const padding = " ".repeat(Math.max(0, indent));
|
|
1408
|
-
return cached.component.render(Math.max(1, width -
|
|
1653
|
+
return cached.component.render(Math.max(1, width - visibleWidth3(padding))).map((line) => truncateToWidth3(`${padding}${line}`, width, ""));
|
|
1409
1654
|
}
|
|
1410
1655
|
detailLines(entity, width, now, cwd) {
|
|
1411
1656
|
const lines = [];
|
|
@@ -1413,10 +1658,10 @@ var DashboardDetailRenderer = class {
|
|
|
1413
1658
|
const text = safeText(value);
|
|
1414
1659
|
if (!text) return;
|
|
1415
1660
|
const prefix = `${this.theme.fg("dim", `${label}:`)} `;
|
|
1416
|
-
const wrapped = wrapPlainText(text, Math.max(1, width -
|
|
1417
|
-
if (wrapped[0]) lines.push(
|
|
1661
|
+
const wrapped = wrapPlainText(text, Math.max(1, width - visibleWidth3(prefix)), 12);
|
|
1662
|
+
if (wrapped[0]) lines.push(truncateToWidth3(prefix + wrapped[0], width));
|
|
1418
1663
|
for (const continuation of wrapped.slice(1)) {
|
|
1419
|
-
lines.push(
|
|
1664
|
+
lines.push(truncateToWidth3(" ".repeat(visibleWidth3(prefix)) + continuation, width));
|
|
1420
1665
|
}
|
|
1421
1666
|
};
|
|
1422
1667
|
const markdownField = (label, value, key) => {
|
|
@@ -1435,7 +1680,7 @@ var DashboardDetailRenderer = class {
|
|
|
1435
1680
|
const highlighted = highlightCode(yaml, "yaml", this.highlightInvalidate) ?? yaml.split("\n").map((line) => this.theme.fg("mdCodeBlock", line || " "));
|
|
1436
1681
|
for (const highlightedLine of highlighted) {
|
|
1437
1682
|
for (const wrapped of wrapTextWithAnsi(highlightedLine, Math.max(1, width - 2))) {
|
|
1438
|
-
lines.push(
|
|
1683
|
+
lines.push(truncateToWidth3(` ${wrapped}`, width, ""));
|
|
1439
1684
|
}
|
|
1440
1685
|
}
|
|
1441
1686
|
};
|
|
@@ -1498,7 +1743,7 @@ var DashboardDetailRenderer = class {
|
|
|
1498
1743
|
this.theme,
|
|
1499
1744
|
settings.diffIntensity
|
|
1500
1745
|
).render(Math.max(1, width - 2));
|
|
1501
|
-
for (const row of body) lines.push(
|
|
1746
|
+
for (const row of body) lines.push(truncateToWidth3(` ${row}`, width, ""));
|
|
1502
1747
|
if (rendered.hidden > 0) {
|
|
1503
1748
|
lines.push(this.theme.fg("muted", ` \u2026 ${rendered.hidden} more lines`));
|
|
1504
1749
|
}
|
|
@@ -1529,7 +1774,7 @@ ${command}
|
|
|
1529
1774
|
this.highlightInvalidate
|
|
1530
1775
|
);
|
|
1531
1776
|
if (diff) {
|
|
1532
|
-
for (const line of diff) lines.push(
|
|
1777
|
+
for (const line of diff) lines.push(truncateToWidth3(` ${line}`, width, ""));
|
|
1533
1778
|
} else {
|
|
1534
1779
|
structuredField("Edits", edits);
|
|
1535
1780
|
}
|
|
@@ -1777,13 +2022,13 @@ ${command}
|
|
|
1777
2022
|
}
|
|
1778
2023
|
const title = `${entity.label}${transcriptView ? " \xB7 transcript" : ""}`;
|
|
1779
2024
|
const hint = transcriptView ? `${this.transcriptToolToggleHint()} \xB7 g page top \xB7 G follow:${this.transcriptFollowing ? "on" : "off"}/tail \xB7 t summary \xB7 esc` : `${this.hasTranscript(entity) ? "t transcript \xB7 " : ""}esc`;
|
|
1780
|
-
return [title, ...content.slice(this.detailScroll, this.detailScroll + maxBody), hint].map((line) =>
|
|
2025
|
+
return [title, ...content.slice(this.detailScroll, this.detailScroll + maxBody), hint].map((line) => truncateToWidth3(line, width, "")).filter((line) => visibleWidth3(line) > 0);
|
|
1781
2026
|
}
|
|
1782
2027
|
topBorder(width, title) {
|
|
1783
2028
|
const border = (value) => this.theme.fg("borderMuted", value);
|
|
1784
|
-
const safeTitle =
|
|
2029
|
+
const safeTitle = truncateToWidth3(safeText(title), Math.max(0, width - 6));
|
|
1785
2030
|
const styledTitle = ` ${this.theme.fg("accent", safeTitle)} `;
|
|
1786
|
-
const remaining = Math.max(0, width - 2 -
|
|
2031
|
+
const remaining = Math.max(0, width - 2 - visibleWidth3(styledTitle));
|
|
1787
2032
|
const left = Math.floor(remaining / 2);
|
|
1788
2033
|
const right = remaining - left;
|
|
1789
2034
|
return `${border(`\u256D${"\u2500".repeat(left)}`)}${styledTitle}${border(`${"\u2500".repeat(right)}\u256E`)}`;
|
|
@@ -1805,7 +2050,7 @@ ${command}
|
|
|
1805
2050
|
|
|
1806
2051
|
// src/ui/dashboard-fabric-graph.ts
|
|
1807
2052
|
import { Ellipsis as Ellipsis2 } from "@oh-my-pi/pi-tui";
|
|
1808
|
-
import { truncateToWidth as
|
|
2053
|
+
import { truncateToWidth as truncateToWidth4, visibleWidth as visibleWidth4 } from "@oh-my-pi/pi-tui";
|
|
1809
2054
|
var kindRank = {
|
|
1810
2055
|
agent: 0,
|
|
1811
2056
|
actor: 1,
|
|
@@ -1848,13 +2093,13 @@ var graphLabel = (value, maxWidth) => {
|
|
|
1848
2093
|
let output = "";
|
|
1849
2094
|
let width = 0;
|
|
1850
2095
|
for (const char of value) {
|
|
1851
|
-
const charWidth =
|
|
2096
|
+
const charWidth = visibleWidth4(char);
|
|
1852
2097
|
if (width + charWidth > maxWidth) {
|
|
1853
2098
|
while (width + 1 > maxWidth && output.length > 0) {
|
|
1854
2099
|
const parts = [...output];
|
|
1855
2100
|
const removed = parts.pop();
|
|
1856
2101
|
output = parts.join("");
|
|
1857
|
-
width -= removed ?
|
|
2102
|
+
width -= removed ? visibleWidth4(removed) : 0;
|
|
1858
2103
|
}
|
|
1859
2104
|
return output + "\u2026";
|
|
1860
2105
|
}
|
|
@@ -2259,7 +2504,7 @@ var renderCanvas = (theme, layout, selectedEntityId, width, height, camera, anim
|
|
|
2259
2504
|
}
|
|
2260
2505
|
};
|
|
2261
2506
|
const edgePathPoints = (from, to) => {
|
|
2262
|
-
const fromEnd = from.x + Math.min(16,
|
|
2507
|
+
const fromEnd = from.x + Math.min(16, visibleWidth4(safeText(from.label)) + 3);
|
|
2263
2508
|
const toStart = to.x - 2;
|
|
2264
2509
|
const bend = Math.max(fromEnd + 1, Math.floor((fromEnd + toStart) / 2));
|
|
2265
2510
|
const points = [];
|
|
@@ -2296,7 +2541,7 @@ var renderCanvas = (theme, layout, selectedEntityId, width, height, camera, anim
|
|
|
2296
2541
|
const from = nodeById.get(edge.from);
|
|
2297
2542
|
const to = nodeById.get(edge.to);
|
|
2298
2543
|
if (!from || !to) continue;
|
|
2299
|
-
const fromEnd = from.x + Math.min(16,
|
|
2544
|
+
const fromEnd = from.x + Math.min(16, visibleWidth4(safeText(from.label)) + 3);
|
|
2300
2545
|
const toStart = to.x - 2;
|
|
2301
2546
|
const bend = Math.max(fromEnd + 1, Math.floor((fromEnd + toStart) / 2));
|
|
2302
2547
|
horizontal(fromEnd, bend, from.y);
|
|
@@ -2366,7 +2611,7 @@ var renderCanvas = (theme, layout, selectedEntityId, width, height, camera, anim
|
|
|
2366
2611
|
let offset = 2;
|
|
2367
2612
|
for (const char of label) {
|
|
2368
2613
|
setCell(point.x + offset, point.y, char, routeStyle(route));
|
|
2369
|
-
offset +=
|
|
2614
|
+
offset += visibleWidth4(char);
|
|
2370
2615
|
}
|
|
2371
2616
|
} else {
|
|
2372
2617
|
const count = group.routes.reduce((total, candidate) => total + candidate.count, 0);
|
|
@@ -2390,7 +2635,7 @@ var renderCanvas = (theme, layout, selectedEntityId, width, height, camera, anim
|
|
|
2390
2635
|
let offset = 0;
|
|
2391
2636
|
for (const char of label) {
|
|
2392
2637
|
setCell(node.x + 2 + offset, node.y, char, selected ? "accent" : spotlighted ? "muted" : "dim");
|
|
2393
|
-
offset +=
|
|
2638
|
+
offset += visibleWidth4(char);
|
|
2394
2639
|
}
|
|
2395
2640
|
}
|
|
2396
2641
|
const apply = (style, value) => {
|
|
@@ -2416,12 +2661,12 @@ var renderCanvas = (theme, layout, selectedEntityId, width, height, camera, anim
|
|
|
2416
2661
|
run += cell.char;
|
|
2417
2662
|
}
|
|
2418
2663
|
rendered += apply(style, run);
|
|
2419
|
-
return
|
|
2664
|
+
return truncateToWidth4(rendered, width, "");
|
|
2420
2665
|
});
|
|
2421
2666
|
};
|
|
2422
2667
|
var wrapInspector = (theme, label, value, width) => {
|
|
2423
2668
|
const clean = safeText(value);
|
|
2424
|
-
const first =
|
|
2669
|
+
const first = truncateToWidth4(clean, Math.max(1, width - label.length - 1), Ellipsis2.Unicode);
|
|
2425
2670
|
return [theme.fg("muted", `${label} ${first}`)];
|
|
2426
2671
|
};
|
|
2427
2672
|
var inspectorLines = (theme, entity, snapshot, run, width, height, invalidate) => {
|
|
@@ -2429,10 +2674,10 @@ var inspectorLines = (theme, entity, snapshot, run, width, height, invalidate) =
|
|
|
2429
2674
|
const border = (value) => theme.fg("borderMuted", value);
|
|
2430
2675
|
const content = [];
|
|
2431
2676
|
if (entity) {
|
|
2432
|
-
content.push(theme.fg("accent", theme.bold(
|
|
2677
|
+
content.push(theme.fg("accent", theme.bold(truncateToWidth4(safeText(entity.label), inner - 2, Ellipsis2.Unicode))));
|
|
2433
2678
|
content.push(colorStatus(theme, entity.status, `${statusGlyph(entity.status)} ${entity.kind} \xB7 ${entity.status}`));
|
|
2434
2679
|
content.push("");
|
|
2435
|
-
content.push(theme.fg("dim",
|
|
2680
|
+
content.push(theme.fg("dim", truncateToWidth4(safeText(entityTail(entity, snapshot.now)), inner - 2, Ellipsis2.Unicode)));
|
|
2436
2681
|
if (entity.kind === "agent") {
|
|
2437
2682
|
const agentRun = snapshot.runs.find((candidate) => candidate.id === entity.value.runId) ?? run;
|
|
2438
2683
|
const phase = agentRun?.phases.find((candidate) => candidate.id === entity.value.phaseId);
|
|
@@ -2498,7 +2743,7 @@ var inspectorLines = (theme, entity, snapshot, run, width, height, invalidate) =
|
|
|
2498
2743
|
content.push(theme.fg("dim", "No node selected"));
|
|
2499
2744
|
}
|
|
2500
2745
|
const title = " selected ";
|
|
2501
|
-
const rows = [border(`\u256D${title}${"\u2500".repeat(Math.max(0, inner -
|
|
2746
|
+
const rows = [border(`\u256D${title}${"\u2500".repeat(Math.max(0, inner - visibleWidth4(title)))}\u256E`)];
|
|
2502
2747
|
for (let index = 0; index < height - 2; index++) {
|
|
2503
2748
|
rows.push(`${border("\u2502")}${padToWidth(` ${content[index] ?? ""}`, inner)}${border("\u2502")}`);
|
|
2504
2749
|
}
|
|
@@ -2584,8 +2829,8 @@ var renderFabricTopologyPanel = ({
|
|
|
2584
2829
|
animation.reducedMotion ? "reduced motion" : void 0,
|
|
2585
2830
|
filter !== "all" ? `${entities.length}/${allEntities.length} ${filter}` : void 0
|
|
2586
2831
|
].filter((value) => Boolean(value)).join(" \xB7 ");
|
|
2587
|
-
const graphLegend = padToWidth(theme.fg("dim",
|
|
2588
|
-
lines[0] =
|
|
2832
|
+
const graphLegend = padToWidth(theme.fg("dim", truncateToWidth4(legend, graphWidth, "")), graphWidth);
|
|
2833
|
+
lines[0] = truncateToWidth4(
|
|
2589
2834
|
graphLegend + (inspectorWidth > 0 ? inspector[0] ?? "" : ""),
|
|
2590
2835
|
width,
|
|
2591
2836
|
""
|
|
@@ -2988,6 +3233,7 @@ var FabricDashboard = class {
|
|
|
2988
3233
|
this.done = done;
|
|
2989
3234
|
this.focused = true;
|
|
2990
3235
|
this.modelSource = options.modelSource;
|
|
3236
|
+
this.lcmStatus = options.lcmStatus;
|
|
2991
3237
|
this.codePreviewSettings = options.codePreviewSettings;
|
|
2992
3238
|
this.keybindings = options.keybindings;
|
|
2993
3239
|
this.claudeModelSource = options.claudeModelSource;
|
|
@@ -3024,6 +3270,13 @@ var FabricDashboard = class {
|
|
|
3024
3270
|
focused = false;
|
|
3025
3271
|
pane = "phases";
|
|
3026
3272
|
overviewView = "activity";
|
|
3273
|
+
lcmSnapshot;
|
|
3274
|
+
lcmSnapshotTick;
|
|
3275
|
+
lcmOffset = 0;
|
|
3276
|
+
lcmIndex = 0;
|
|
3277
|
+
lcmDetail;
|
|
3278
|
+
lcmSourceIndex = 0;
|
|
3279
|
+
lcmSourceEntry;
|
|
3027
3280
|
graphPositions = /* @__PURE__ */ new Map();
|
|
3028
3281
|
graphCamera = { x: 0, y: 0 };
|
|
3029
3282
|
graphCameraTarget = { x: 0, y: 0 };
|
|
@@ -3065,6 +3318,7 @@ var FabricDashboard = class {
|
|
|
3065
3318
|
editorActorName;
|
|
3066
3319
|
agentMessageTarget;
|
|
3067
3320
|
pendingStop;
|
|
3321
|
+
lcmStatus;
|
|
3068
3322
|
modelSource;
|
|
3069
3323
|
claudeModelSource;
|
|
3070
3324
|
onAgentSteer;
|
|
@@ -3152,6 +3406,17 @@ var FabricDashboard = class {
|
|
|
3152
3406
|
this.tui.requestRender();
|
|
3153
3407
|
return;
|
|
3154
3408
|
}
|
|
3409
|
+
if (this.overviewView === "lcm") {
|
|
3410
|
+
const outcome = this.handleLcmInput(data);
|
|
3411
|
+
if (outcome === "exit") {
|
|
3412
|
+
this.done();
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
if (outcome === "handled") {
|
|
3416
|
+
this.tui.requestRender();
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3155
3420
|
if (this.detailId) {
|
|
3156
3421
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.left) || data === "h") {
|
|
3157
3422
|
this.closeDetail();
|
|
@@ -3264,10 +3529,10 @@ var FabricDashboard = class {
|
|
|
3264
3529
|
this.tui.requestRender();
|
|
3265
3530
|
return;
|
|
3266
3531
|
}
|
|
3267
|
-
if (data === "1" || data === "2") {
|
|
3268
|
-
const nextOverview = data === "1" ? "activity" : "topology";
|
|
3532
|
+
if (data === "1" || data === "2" || data === "3") {
|
|
3533
|
+
const nextOverview = data === "1" ? "activity" : data === "2" ? "topology" : "lcm";
|
|
3269
3534
|
if (nextOverview !== this.overviewView) {
|
|
3270
|
-
if (nextOverview
|
|
3535
|
+
if (nextOverview !== "topology") {
|
|
3271
3536
|
this.stopGraphAnimation();
|
|
3272
3537
|
this.stopGraphEffectsAnimation();
|
|
3273
3538
|
}
|
|
@@ -3276,6 +3541,8 @@ var FabricDashboard = class {
|
|
|
3276
3541
|
this.entityIndex = 0;
|
|
3277
3542
|
this.selectedEntityId = void 0;
|
|
3278
3543
|
this.pendingStop = void 0;
|
|
3544
|
+
if (nextOverview === "lcm") this.enterLcmView();
|
|
3545
|
+
else this.closeLcmDetail();
|
|
3279
3546
|
}
|
|
3280
3547
|
this.tui.requestRender();
|
|
3281
3548
|
return;
|
|
@@ -3481,6 +3748,7 @@ var FabricDashboard = class {
|
|
|
3481
3748
|
if ((this.mode === "modelPicker" || this.mode === "thinkingPicker" || this.mode === "deliveryPicker" || this.mode === "eventsPicker" || this.mode === "toolsPicker") && this.picker) {
|
|
3482
3749
|
return this.renderPicker(width);
|
|
3483
3750
|
}
|
|
3751
|
+
if (this.overviewView === "lcm") return this.renderLcmView(width);
|
|
3484
3752
|
const snapshot = this.snapshot();
|
|
3485
3753
|
const run = this.selectRun(snapshot);
|
|
3486
3754
|
const panels = phasePanels(snapshot, run);
|
|
@@ -3647,7 +3915,7 @@ var FabricDashboard = class {
|
|
|
3647
3915
|
)
|
|
3648
3916
|
);
|
|
3649
3917
|
lines.push(this.bottomBorder(width));
|
|
3650
|
-
return lines.map((line) =>
|
|
3918
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
3651
3919
|
}
|
|
3652
3920
|
renderHelp(width) {
|
|
3653
3921
|
if (width < 24) return this.renderNarrowFallback(width, "dashboard help", "? or esc close");
|
|
@@ -3684,7 +3952,11 @@ var FabricDashboard = class {
|
|
|
3684
3952
|
].filter((value) => Boolean(value));
|
|
3685
3953
|
const help = [
|
|
3686
3954
|
["Navigate", "Topology: arrows/h/l move spatially \xB7 j/k ordered selection \xB7 tab next \xB7 enter inspect \xB7 esc back"],
|
|
3687
|
-
["Views", "1 Activity \xB7 2 unified Topology"],
|
|
3955
|
+
["Views", "1 Activity \xB7 2 unified Topology \xB7 3 LCM ledger"],
|
|
3956
|
+
[
|
|
3957
|
+
"LCM",
|
|
3958
|
+
"\u2191\u2193/jk node \xB7 g/G first/last node \xB7 [ ] page 200 nodes \xB7 enter inspect node \xB7 in a node: \u2191\u2193/jk source \xB7 enter open/close the stored raw entry \xB7 esc back"
|
|
3959
|
+
],
|
|
3688
3960
|
...this.onConversation ? [["Chat", "Shift+C opens the selected participant conversation \xB7 Esc returns to Main"]] : [],
|
|
3689
3961
|
["Topology", "Main branches into Participants (sessions, agents, actors) and Mesh (namespaced topics and hierarchical state); traffic travels on decaying edges"],
|
|
3690
3962
|
["Motion", "r replay/live \xB7 space pause/play \xB7 \u2190/\u2192 step \xB7 +/- speed \xB7 H history \xB7 M reduced motion"],
|
|
@@ -3700,16 +3972,16 @@ var FabricDashboard = class {
|
|
|
3700
3972
|
];
|
|
3701
3973
|
for (const [label, value] of help) {
|
|
3702
3974
|
const prefix = `${this.theme.fg("accent", `${label}:`)} `;
|
|
3703
|
-
const wrapped = wrapPlainText(value ?? "", Math.max(1, width - 2 -
|
|
3975
|
+
const wrapped = wrapPlainText(value ?? "", Math.max(1, width - 2 - visibleWidth5(prefix)), 3);
|
|
3704
3976
|
if (wrapped[0]) lines.push(this.row(width, prefix + wrapped[0]));
|
|
3705
3977
|
for (const continuation of wrapped.slice(1)) {
|
|
3706
|
-
lines.push(this.row(width, " ".repeat(
|
|
3978
|
+
lines.push(this.row(width, " ".repeat(visibleWidth5(prefix)) + continuation));
|
|
3707
3979
|
}
|
|
3708
3980
|
}
|
|
3709
3981
|
lines.push(this.middleBorder(width));
|
|
3710
3982
|
lines.push(this.row(width, this.theme.fg("dim", " ? or esc close")));
|
|
3711
3983
|
lines.push(this.bottomBorder(width));
|
|
3712
|
-
return lines.map((line) =>
|
|
3984
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
3713
3985
|
}
|
|
3714
3986
|
modelSourceForActor(actor) {
|
|
3715
3987
|
return actor.runner === "claude" ? this.claudeModelSource : this.modelSource;
|
|
@@ -3886,7 +4158,7 @@ var FabricDashboard = class {
|
|
|
3886
4158
|
)
|
|
3887
4159
|
);
|
|
3888
4160
|
lines.push(this.bottomBorder(width));
|
|
3889
|
-
return lines.map((line) =>
|
|
4161
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
3890
4162
|
}
|
|
3891
4163
|
renderInstructionsEditor(width) {
|
|
3892
4164
|
if (!this.editor) return [];
|
|
@@ -3904,7 +4176,7 @@ var FabricDashboard = class {
|
|
|
3904
4176
|
)
|
|
3905
4177
|
);
|
|
3906
4178
|
lines.push(this.bottomBorder(width));
|
|
3907
|
-
return lines.map((line) =>
|
|
4179
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
3908
4180
|
}
|
|
3909
4181
|
projectMesh(snapshot) {
|
|
3910
4182
|
if (this.overviewView !== "topology") return void 0;
|
|
@@ -4029,7 +4301,7 @@ var FabricDashboard = class {
|
|
|
4029
4301
|
}
|
|
4030
4302
|
renderOverview(width, snapshot, run, panels, entities, allEntities, meshModel) {
|
|
4031
4303
|
if (width < 24) {
|
|
4032
|
-
return [
|
|
4304
|
+
return [truncateToWidth5("too narrow \xB7 need 24 cols", width)];
|
|
4033
4305
|
}
|
|
4034
4306
|
const innerWidth = width - 2;
|
|
4035
4307
|
const terminalRows = Math.max(
|
|
@@ -4068,7 +4340,7 @@ var FabricDashboard = class {
|
|
|
4068
4340
|
let headerLine = summaryText;
|
|
4069
4341
|
if (run?.description && this.overviewView === "activity") {
|
|
4070
4342
|
const gap = " ";
|
|
4071
|
-
const availableDescription = innerWidth -
|
|
4343
|
+
const availableDescription = innerWidth - visibleWidth5(summaryText) - gap.length;
|
|
4072
4344
|
headerLine = availableDescription >= 12 ? `${padToWidth(
|
|
4073
4345
|
this.theme.fg("muted", safeText(run.description)),
|
|
4074
4346
|
availableDescription
|
|
@@ -4082,7 +4354,7 @@ var FabricDashboard = class {
|
|
|
4082
4354
|
title,
|
|
4083
4355
|
this.theme.fg("dim", summaryText || "No Fabric activity yet"),
|
|
4084
4356
|
this.theme.fg("dim", "1 activity \xB7 2 topology \xB7 arrows move \xB7 esc close")
|
|
4085
|
-
].slice(0, overlayRows).map((line) =>
|
|
4357
|
+
].slice(0, overlayRows).map((line) => truncateToWidth5(line, width, ""));
|
|
4086
4358
|
}
|
|
4087
4359
|
lines.push(this.row(width, headerLine || this.theme.fg("muted", "No Fabric activity yet")));
|
|
4088
4360
|
lines.push(this.middleBorder(width));
|
|
@@ -4219,7 +4491,7 @@ var FabricDashboard = class {
|
|
|
4219
4491
|
const actionHint = this.pane === "entities" && selectedEntity ? this.theme.fg("muted", ` ${this.overviewActionHint(selectedEntity)}`) : "";
|
|
4220
4492
|
lines.push(this.row(width, actionHint));
|
|
4221
4493
|
lines.push(this.bottomBorder(width));
|
|
4222
|
-
return lines.map((line) =>
|
|
4494
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
4223
4495
|
}
|
|
4224
4496
|
overviewActionHint(entity) {
|
|
4225
4497
|
if (entity.kind === "main") {
|
|
@@ -4289,7 +4561,7 @@ var FabricDashboard = class {
|
|
|
4289
4561
|
}
|
|
4290
4562
|
renderPhasePanel(panels, width, height) {
|
|
4291
4563
|
const lines = [
|
|
4292
|
-
|
|
4564
|
+
truncateToWidth5(
|
|
4293
4565
|
`${this.pane === "phases" ? this.theme.fg("accent", "\u25B8 ") : " "}${this.theme.fg(
|
|
4294
4566
|
"accent",
|
|
4295
4567
|
"Activity"
|
|
@@ -4311,13 +4583,13 @@ var FabricDashboard = class {
|
|
|
4311
4583
|
const raw = `${prefix}${colorStatus(this.theme, panel.status, statusGlyph(panel.status))} ${this.theme.fg("muted", safeText(
|
|
4312
4584
|
panel.name
|
|
4313
4585
|
))}`;
|
|
4314
|
-
const countWidth =
|
|
4586
|
+
const countWidth = visibleWidth5(count);
|
|
4315
4587
|
const contentWidth = Math.max(0, width - countWidth - (count ? 1 : 0));
|
|
4316
4588
|
let line = `${padToWidth(raw, contentWidth)}${count ? ` ${this.theme.fg("dim", count)}` : ""}`;
|
|
4317
4589
|
if (selected && this.pane === "phases") {
|
|
4318
4590
|
line = this.theme.bg("selectedBg", padToWidth(line, width));
|
|
4319
4591
|
}
|
|
4320
|
-
lines.push(
|
|
4592
|
+
lines.push(truncateToWidth5(line, width, ""));
|
|
4321
4593
|
}
|
|
4322
4594
|
while (lines.length < height) lines.push("");
|
|
4323
4595
|
return lines.slice(0, height);
|
|
@@ -4357,7 +4629,7 @@ var FabricDashboard = class {
|
|
|
4357
4629
|
}
|
|
4358
4630
|
if (row.type === "group") {
|
|
4359
4631
|
lines.push(
|
|
4360
|
-
|
|
4632
|
+
truncateToWidth5(
|
|
4361
4633
|
this.theme.fg(
|
|
4362
4634
|
"muted",
|
|
4363
4635
|
` ${this.theme.bold(row.group.label)} (${row.group.entries.length})`
|
|
@@ -4379,7 +4651,7 @@ var FabricDashboard = class {
|
|
|
4379
4651
|
if (selected && this.pane === "entities") {
|
|
4380
4652
|
line = this.theme.bg("selectedBg", padToWidth(line, width));
|
|
4381
4653
|
}
|
|
4382
|
-
lines.push(
|
|
4654
|
+
lines.push(truncateToWidth5(line, width, ""));
|
|
4383
4655
|
}
|
|
4384
4656
|
if (entities.length === 0 && available > 0) {
|
|
4385
4657
|
const label = this.filter === "all" ? "activity" : `${this.filter} activity`;
|
|
@@ -4572,14 +4844,176 @@ var FabricDashboard = class {
|
|
|
4572
4844
|
this.detailView = "summary";
|
|
4573
4845
|
this.transcriptFollowing = true;
|
|
4574
4846
|
}
|
|
4847
|
+
enterLcmView() {
|
|
4848
|
+
this.lcmOffset = 0;
|
|
4849
|
+
this.lcmIndex = 0;
|
|
4850
|
+
this.closeLcmDetail();
|
|
4851
|
+
this.refreshLcm();
|
|
4852
|
+
}
|
|
4853
|
+
closeLcmDetail() {
|
|
4854
|
+
this.lcmDetail = void 0;
|
|
4855
|
+
this.lcmSourceIndex = 0;
|
|
4856
|
+
this.lcmSourceEntry = void 0;
|
|
4857
|
+
}
|
|
4858
|
+
refreshLcm() {
|
|
4859
|
+
this.lcmSnapshotTick = this.snapshot().now;
|
|
4860
|
+
const source = this.lcmStatus?.();
|
|
4861
|
+
this.lcmSnapshot = source ? readLcmSnapshot(source, this.lcmOffset) : void 0;
|
|
4862
|
+
const total = this.lcmSnapshot?.nodes.length ?? 0;
|
|
4863
|
+
this.lcmIndex = total === 0 ? 0 : Math.max(0, Math.min(this.lcmIndex, total - 1));
|
|
4864
|
+
}
|
|
4865
|
+
openLcmNode() {
|
|
4866
|
+
const nodeId = this.lcmSnapshot?.nodes[this.lcmIndex]?.nodeId;
|
|
4867
|
+
const source = nodeId ? this.lcmStatus?.() : void 0;
|
|
4868
|
+
this.lcmDetail = source && nodeId ? lcmNodeDetail(source, nodeId) : void 0;
|
|
4869
|
+
this.lcmSourceIndex = 0;
|
|
4870
|
+
this.lcmSourceEntry = void 0;
|
|
4871
|
+
}
|
|
4872
|
+
openLcmSource() {
|
|
4873
|
+
const selected = this.lcmDetail?.node.sources[this.lcmSourceIndex];
|
|
4874
|
+
if (!selected) return;
|
|
4875
|
+
if (this.lcmSourceEntry) {
|
|
4876
|
+
this.lcmSourceEntry = void 0;
|
|
4877
|
+
return;
|
|
4878
|
+
}
|
|
4879
|
+
this.lcmSourceEntry = this.lcmStatus?.()?.source(
|
|
4880
|
+
selected.sessionId,
|
|
4881
|
+
selected.entryId,
|
|
4882
|
+
selected.revision
|
|
4883
|
+
);
|
|
4884
|
+
}
|
|
4885
|
+
pageLcmNodes(direction) {
|
|
4886
|
+
const snapshot = this.lcmSnapshot;
|
|
4887
|
+
if (!snapshot) return;
|
|
4888
|
+
if (direction === 1 && !snapshot.hasMore) return;
|
|
4889
|
+
const next = Math.max(0, snapshot.offset + direction * LCM_NODE_PAGE);
|
|
4890
|
+
if (next === snapshot.offset) return;
|
|
4891
|
+
this.lcmOffset = next;
|
|
4892
|
+
this.lcmIndex = 0;
|
|
4893
|
+
this.refreshLcm();
|
|
4894
|
+
}
|
|
4895
|
+
handleLcmInput(data) {
|
|
4896
|
+
if (data === "1" || data === "2" || data === "3") return "ignored";
|
|
4897
|
+
const back = matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"));
|
|
4898
|
+
if (this.lcmDetail) {
|
|
4899
|
+
const sources = this.lcmDetail.node.sources.length;
|
|
4900
|
+
if (back || matchesKey(data, Key.left) || data === "h") {
|
|
4901
|
+
if (this.lcmSourceEntry) this.lcmSourceEntry = void 0;
|
|
4902
|
+
else this.closeLcmDetail();
|
|
4903
|
+
} else if (matchesKey(data, Key.up) || data === "k") {
|
|
4904
|
+
this.lcmSourceIndex = Math.max(0, this.lcmSourceIndex - 1);
|
|
4905
|
+
this.lcmSourceEntry = void 0;
|
|
4906
|
+
} else if (matchesKey(data, Key.down) || data === "j") {
|
|
4907
|
+
this.lcmSourceIndex = Math.min(Math.max(0, sources - 1), this.lcmSourceIndex + 1);
|
|
4908
|
+
this.lcmSourceEntry = void 0;
|
|
4909
|
+
} else if (matchesKey(data, Key.enter)) {
|
|
4910
|
+
this.openLcmSource();
|
|
4911
|
+
}
|
|
4912
|
+
return "handled";
|
|
4913
|
+
}
|
|
4914
|
+
if (back) return "exit";
|
|
4915
|
+
const total = this.lcmSnapshot?.nodes.length ?? 0;
|
|
4916
|
+
if (matchesKey(data, Key.up) || data === "k") {
|
|
4917
|
+
this.lcmIndex = Math.max(0, this.lcmIndex - 1);
|
|
4918
|
+
} else if (matchesKey(data, Key.down) || data === "j") {
|
|
4919
|
+
this.lcmIndex = Math.min(Math.max(0, total - 1), this.lcmIndex + 1);
|
|
4920
|
+
} else if (data === "g") {
|
|
4921
|
+
this.lcmIndex = 0;
|
|
4922
|
+
} else if (data === "G") {
|
|
4923
|
+
this.lcmIndex = Math.max(0, total - 1);
|
|
4924
|
+
} else if (data === "[") {
|
|
4925
|
+
this.pageLcmNodes(-1);
|
|
4926
|
+
} else if (data === "]") {
|
|
4927
|
+
this.pageLcmNodes(1);
|
|
4928
|
+
} else if (matchesKey(data, Key.enter)) {
|
|
4929
|
+
this.openLcmNode();
|
|
4930
|
+
}
|
|
4931
|
+
return "handled";
|
|
4932
|
+
}
|
|
4933
|
+
renderLcmView(width) {
|
|
4934
|
+
if (width < 24) return this.renderNarrowFallback(width, "Fabric \xB7 LCM", "1 activity \xB7 esc close");
|
|
4935
|
+
if (this.snapshot().now !== this.lcmSnapshotTick) this.refreshLcm();
|
|
4936
|
+
const innerWidth = width - 2;
|
|
4937
|
+
const overlayRows = dashboardOverlayRows(
|
|
4938
|
+
Math.max(1, this.tui.terminal?.rows ?? process.stdout.rows ?? 28)
|
|
4939
|
+
);
|
|
4940
|
+
const lines = [this.topBorder(width, "Fabric \xB7 LCM")];
|
|
4941
|
+
const snapshot = this.lcmSnapshot;
|
|
4942
|
+
if (!snapshot) {
|
|
4943
|
+
lines.push(this.row(width, this.theme.fg("muted", lcmUnavailableLine())));
|
|
4944
|
+
lines.push(this.middleBorder(width));
|
|
4945
|
+
lines.push(
|
|
4946
|
+
this.row(width, this.theme.fg("dim", "1 activity \xB7 2 topology \xB7 ? help \xB7 esc close"))
|
|
4947
|
+
);
|
|
4948
|
+
lines.push(this.bottomBorder(width));
|
|
4949
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
4950
|
+
}
|
|
4951
|
+
const maxBody = Math.max(3, Math.min(34, overlayRows - 5));
|
|
4952
|
+
if (this.lcmDetail) {
|
|
4953
|
+
for (const line of renderLcmDetailLines(
|
|
4954
|
+
this.theme,
|
|
4955
|
+
this.lcmDetail,
|
|
4956
|
+
this.lcmSourceIndex,
|
|
4957
|
+
this.lcmSourceEntry,
|
|
4958
|
+
innerWidth,
|
|
4959
|
+
maxBody
|
|
4960
|
+
)) {
|
|
4961
|
+
lines.push(this.row(width, line));
|
|
4962
|
+
}
|
|
4963
|
+
lines.push(this.middleBorder(width));
|
|
4964
|
+
lines.push(
|
|
4965
|
+
this.row(
|
|
4966
|
+
width,
|
|
4967
|
+
this.theme.fg(
|
|
4968
|
+
"dim",
|
|
4969
|
+
`\u2191\u2193/jk source \xB7 enter ${this.lcmSourceEntry ? "close" : "open"} raw entry \xB7 esc back \xB7 ? help`
|
|
4970
|
+
)
|
|
4971
|
+
)
|
|
4972
|
+
);
|
|
4973
|
+
lines.push(this.bottomBorder(width));
|
|
4974
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
4975
|
+
}
|
|
4976
|
+
const headerLines = renderLcmHeaderLines(this.theme, snapshot, innerWidth);
|
|
4977
|
+
for (const line of headerLines) lines.push(this.row(width, line));
|
|
4978
|
+
lines.push(this.row(width, renderLcmCoverageLine(this.theme, snapshot, innerWidth)));
|
|
4979
|
+
lines.push(this.middleBorder(width));
|
|
4980
|
+
const body = Math.max(2, maxBody - headerLines.length - 2);
|
|
4981
|
+
const previewRows = Math.max(2, Math.min(6, Math.floor(body / 3)));
|
|
4982
|
+
const nodeRows = Math.max(1, body - previewRows - 1);
|
|
4983
|
+
for (const line of renderLcmNodeLines(
|
|
4984
|
+
this.theme,
|
|
4985
|
+
snapshot,
|
|
4986
|
+
innerWidth,
|
|
4987
|
+
nodeRows,
|
|
4988
|
+
this.lcmIndex
|
|
4989
|
+
)) {
|
|
4990
|
+
lines.push(this.row(width, line));
|
|
4991
|
+
}
|
|
4992
|
+
lines.push(this.middleBorder(width));
|
|
4993
|
+
for (const line of renderLcmPreviewLines(this.theme, snapshot, innerWidth, previewRows)) {
|
|
4994
|
+
lines.push(this.row(width, line));
|
|
4995
|
+
}
|
|
4996
|
+
lines.push(this.middleBorder(width));
|
|
4997
|
+
lines.push(
|
|
4998
|
+
this.row(
|
|
4999
|
+
width,
|
|
5000
|
+
this.theme.fg(
|
|
5001
|
+
"dim",
|
|
5002
|
+
`\u2191\u2193/jk node \xB7 g/G first/last \xB7 [ ] node page \xB7 enter inspect \xB7 1 activity \xB7 2 topology \xB7 ? help`
|
|
5003
|
+
)
|
|
5004
|
+
)
|
|
5005
|
+
);
|
|
5006
|
+
lines.push(this.bottomBorder(width));
|
|
5007
|
+
return lines.map((line) => truncateToWidth5(line, width, ""));
|
|
5008
|
+
}
|
|
4575
5009
|
renderNarrowFallback(width, label, hint) {
|
|
4576
|
-
return [safeText(label), hint].map((line) =>
|
|
5010
|
+
return [safeText(label), hint].map((line) => truncateToWidth5(line, width, "")).filter((line) => visibleWidth5(line) > 0);
|
|
4577
5011
|
}
|
|
4578
5012
|
topBorder(width, title) {
|
|
4579
5013
|
const border = (value) => this.theme.fg("borderMuted", value);
|
|
4580
|
-
const safeTitle =
|
|
5014
|
+
const safeTitle = truncateToWidth5(safeText(title), Math.max(0, width - 6));
|
|
4581
5015
|
const styledTitle = ` ${this.theme.fg("accent", safeTitle)} `;
|
|
4582
|
-
const remaining = Math.max(0, width - 2 -
|
|
5016
|
+
const remaining = Math.max(0, width - 2 - visibleWidth5(styledTitle));
|
|
4583
5017
|
const left = Math.floor(remaining / 2);
|
|
4584
5018
|
const right = remaining - left;
|
|
4585
5019
|
return `${border(`\u256D${"\u2500".repeat(left)}`)}${styledTitle}${border(`${"\u2500".repeat(right)}\u256E`)}`;
|