pilotswarm 0.5.35 → 0.5.37
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/package.json +2 -2
- package/tui/src/app.js +0 -4
- package/tui/src/index.js +0 -9
- package/ui/core/src/canvas-actions.js +148 -0
- package/ui/core/src/commands.js +0 -1
- package/ui/core/src/controller.js +217 -19
- package/ui/core/src/history.js +134 -1
- package/ui/core/src/index.js +1 -0
- package/ui/core/src/reducer.js +209 -19
- package/ui/core/src/selectors.js +143 -154
- package/ui/core/src/state.js +51 -2
- package/ui/core/src/themes/duroxide.js +6 -1
- package/ui/core/src/themes/win95.js +4 -1
- package/ui/react/src/components.js +1 -7
- package/ui/react/src/web-app.js +853 -260
- package/web/dist/assets/{abnfDiagram-VRR7QNED-BwJEohLG.js → abnfDiagram-VRR7QNED-DlvPH-8O.js} +1 -1
- package/web/dist/assets/{arc-CWUJ_S5s.js → arc-r1liz5e5.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-i6mrLxuE.js → architectureDiagram-ZJ3FMSHR-Bci5dNH3.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-C_KG-qPQ.js → blockDiagram-677ZJIJ3-Bug32AXp.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-BBJH7hbB.js → c4Diagram-LMCZKHZV-DwxSFqgE.js} +1 -1
- package/web/dist/assets/channel-jVpSYat3.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-CXwaFcaM.js → chunk-2Q5K7J3B-B_dbI_xg.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-C8ujjkK-.js → chunk-32BRIVSS-BkF68W-R.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-M9rz7YYk.js → chunk-5VM5RSS4-BSbNIbJu.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-bk6tRxOA.js → chunk-EX3LRPZG-Blv_Iqa2.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-DZ4WSHGH.js → chunk-JWPE2WC7-Dk_dBFKd.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-B5-y6tMF.js → chunk-MOJQB5TN-DqNFBrfw.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-yKcnHxm0.js → chunk-RYQCIY6F-Dlv7Svpb.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-CpoNcATD.js → chunk-V7JOEXUC-DozGikwu.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-Bf1yD2fn.js → chunk-VR4S4FIN-CmkqvGT4.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-EnPvDVhP.js → chunk-XXDRQBXY-DyByai5N.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-C78zSajE.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-C78zSajE.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-qhKoA2qj.js → cose-bilkent-JH36ORCC-Bv-gRVvA.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-jcyYelJS.js → cynefin-VYW2F7L2-DRnAsyZU.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-D4RLufLP.js → cynefinDiagram-TSTJHNR4-B1fnUtjo.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-C9BA3GTO.js → dagre-VKFMJZFB-DM6-QENB.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-DbSSrVUJ.js → diagram-FQU43EPY-BPxxnLdU.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-3egISQXQ.js → diagram-G47NLZAW-BgtJT3qF.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-8IKEMGRg.js → diagram-NH7WQ7WH-uekeIzsy.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-VnaogqtK.js → diagram-OA4YK3LP-BVy3JZWE.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-DxigXH0T.js → diagram-WEI45ONY-BWdjUr32.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-BSkNwJ9x.js → ebnfDiagram-CCIWWBDH-DD4fwhNK.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-CKN8JXl6.js → erDiagram-Q63AITRT-BllD_6Kh.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-DDaD5ExP.js → flowDiagram-23GEKE2U-QEYKkAUN.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-tOrBOuoI.js → ganttDiagram-NO4QXBWP-uZcfKTSW.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-Cdp867ON.js → gitGraphDiagram-IHSO6WYX-XPdbPaH-.js} +1 -1
- package/web/dist/assets/index-BuDSoRIL.css +1 -0
- package/web/dist/assets/{index-BmwU43_u.js → index-D1itTxvB.js} +4 -4
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-DwcKHD2h.js → infoDiagram-FWYZ7A6U-DXCnQOJs.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-CWffK5kz.js → ishikawaDiagram-FXEZZL3T-687_4FBy.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-BEAgS-EZ.js → journeyDiagram-5HDEW3XC-GXlKuda8.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-xDm-mRRj.js → kanban-definition-HUTT4EX6-C38-2DvN.js} +1 -1
- package/web/dist/assets/{linear-oP9MA0as.js → linear-lGPXiBcz.js} +1 -1
- package/web/dist/assets/{mermaid.core-DgXXt1OS.js → mermaid.core-C_neEtab.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-BBQgVrYC.js → mindmap-definition-LN4V7U3C-BaJ9tpEG.js} +1 -1
- package/web/dist/assets/{papaparse.min-CR6JmBWm.js → papaparse.min-rDwKYbBf.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-C2O0cf3j.js → pegDiagram-2B236MQR-BdXJLYgY.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-D08pUtYU.js → pieDiagram-ENE6RG2P-DU19t6fM.js} +1 -1
- package/web/dist/assets/pilotswarm-BNKGG936.js +96 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-D-Z7YoEK.js → quadrantDiagram-ABIIQ3AL-CBkOdPp0.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-ClkdveLI.js → railroadDiagram-RFXS5EU6-D2DMcGDw.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-IY4xbn-Q.js → requirementDiagram-TGXJPOKE-Bx9tF1pr.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-Cpt5i7KM.js → sankeyDiagram-HTMAVEWB-DcLlRfsi.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-c07lALcz.js → sequenceDiagram-DBY2YBRQ-xr6UVRBO.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-CmCDiNbI.js → sizeCapture-X5ZJPWSS-0fUkSXmg.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-Dueu17zX.js → stateDiagram-2N3HPSRC-BWgJGGpg.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DmAC8DdB.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-Csc2Fup8.js → swimlanes-5IMT3BWC-CZKya4_i.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-CVYx20_X.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-4-lf702e.js → timeline-definition-FHXFAJF6-Df8BpeAu.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-CmlaKpLI.js → vennDiagram-L72KCM5P-ChX5bjT6.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-B9Qe44pi.js → wardleyDiagram-EHGQE667-Dr48BgaJ.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-BFcc0CLn.js → xychartDiagram-FW5EYKEG-Cia_MFfc.js} +1 -1
- package/web/dist/index.html +4 -4
- package/web/runtime.js +20 -1
- package/web/dist/assets/channel-5Ddqi9ib.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-B8u1fZiB.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-B8u1fZiB.js +0 -1
- package/web/dist/assets/index-D-nA7gUe.css +0 -1
- package/web/dist/assets/pilotswarm-D7NLCbuc.js +0 -99
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-AGpIV7wW.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BTGZSV0N.js +0 -8
package/ui/core/src/selectors.js
CHANGED
|
@@ -106,11 +106,6 @@ function sessionStatusLabelForVisualStatus(session, status) {
|
|
|
106
106
|
return status;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function getSessionSummaryStatusLabel(session) {
|
|
110
|
-
const status = getSessionVisualStatus(session);
|
|
111
|
-
if (!status || status === "unknown") return "";
|
|
112
|
-
return sessionStatusLabelForVisualStatus(session, status);
|
|
113
|
-
}
|
|
114
109
|
|
|
115
110
|
/**
|
|
116
111
|
* The label + timestamp the session-detail box shows, as ONE derivation shared
|
|
@@ -1176,7 +1171,7 @@ export function selectSessionRows(state) {
|
|
|
1176
1171
|
const ownerSearchText = effectiveOwner
|
|
1177
1172
|
? `${ownerDisplayName(effectiveOwner, "")} ${effectiveOwner.email || ""}`
|
|
1178
1173
|
: "";
|
|
1179
|
-
const summarySearchText =
|
|
1174
|
+
const summarySearchText = "";
|
|
1180
1175
|
return matchesSearchQuery(row.text, query)
|
|
1181
1176
|
|| matchesSearchQuery(row.sessionId, query)
|
|
1182
1177
|
|| matchesSearchQuery(ownerSearchText, query)
|
|
@@ -1557,7 +1552,7 @@ export function selectActiveChat(state) {
|
|
|
1557
1552
|
.replace(/[\r\n]+/g, " ")
|
|
1558
1553
|
.trim() || " ";
|
|
1559
1554
|
const memberRows = memberSessions.map((candidate) => (
|
|
1560
|
-
`| ${markdownCell(candidate.title || candidate.sessionId)} | ${markdownCell(candidate.status || "unknown")}
|
|
1555
|
+
`| ${markdownCell(candidate.title || candidate.sessionId)} | ${markdownCell(candidate.status || "unknown")} |`
|
|
1561
1556
|
));
|
|
1562
1557
|
const lines = [
|
|
1563
1558
|
`# ${session.title || session.groupId}`,
|
|
@@ -1575,8 +1570,8 @@ export function selectActiveChat(state) {
|
|
|
1575
1570
|
"## Members",
|
|
1576
1571
|
...(memberRows.length > 0
|
|
1577
1572
|
? [
|
|
1578
|
-
"| Session | Status |
|
|
1579
|
-
"
|
|
1573
|
+
"| Session | Status |",
|
|
1574
|
+
"|---|---|",
|
|
1580
1575
|
...memberRows,
|
|
1581
1576
|
]
|
|
1582
1577
|
: ["_No top-level members._"]),
|
|
@@ -1589,9 +1584,6 @@ export function selectActiveChat(state) {
|
|
|
1589
1584
|
createdAt: session.updatedAt || Date.now(),
|
|
1590
1585
|
}];
|
|
1591
1586
|
}
|
|
1592
|
-
if (state.ui?.chatViewMode === "summary") {
|
|
1593
|
-
return buildSessionSummaryCards(session);
|
|
1594
|
-
}
|
|
1595
1587
|
const history = state.history.bySessionId.get(sessionId);
|
|
1596
1588
|
const chat = history?.chat || [];
|
|
1597
1589
|
const pendingQuestionMessage = session?.pendingQuestion?.question
|
|
@@ -1618,123 +1610,6 @@ export function selectActiveChat(state) {
|
|
|
1618
1610
|
return messages;
|
|
1619
1611
|
}
|
|
1620
1612
|
|
|
1621
|
-
function buildSessionSummaryCards(session) {
|
|
1622
|
-
const normalizeSummaryMarkdownText = (value) => {
|
|
1623
|
-
const source = String(value || "");
|
|
1624
|
-
if (!source.includes("\\n")) return source;
|
|
1625
|
-
return source
|
|
1626
|
-
.replace(/\\\\n/g, "\n")
|
|
1627
|
-
.replace(/\\n/g, "\n");
|
|
1628
|
-
};
|
|
1629
|
-
|
|
1630
|
-
const summaryState = session?.summaryState && typeof session.summaryState === "object" && !Array.isArray(session.summaryState)
|
|
1631
|
-
? session.summaryState
|
|
1632
|
-
: null;
|
|
1633
|
-
const stateJson = summaryState?.state && typeof summaryState.state === "object" && !Array.isArray(summaryState.state)
|
|
1634
|
-
? summaryState.state
|
|
1635
|
-
: null;
|
|
1636
|
-
const title = buildSessionDisplayTitle(session) || session?.title || session?.sessionId || "Session";
|
|
1637
|
-
const lines = [];
|
|
1638
|
-
lines.push(`# ${title}`);
|
|
1639
|
-
lines.push("");
|
|
1640
|
-
|
|
1641
|
-
// Compact metadata header — status / intent / updated on their own
|
|
1642
|
-
// lines, each as a bold-labeled value. Skipped when empty.
|
|
1643
|
-
const metaPairs = [];
|
|
1644
|
-
const statusLabel = getSessionSummaryStatusLabel(session);
|
|
1645
|
-
if (statusLabel) metaPairs.push(["Status", statusLabel]);
|
|
1646
|
-
if (summaryState?.intent) metaPairs.push(["Intent", String(summaryState.intent)]);
|
|
1647
|
-
if (session?.summaryUpdatedAt) {
|
|
1648
|
-
const ts = formatTimestamp(session.summaryUpdatedAt);
|
|
1649
|
-
if (ts) metaPairs.push(["Updated", ts]);
|
|
1650
|
-
}
|
|
1651
|
-
for (const [label, value] of metaPairs) {
|
|
1652
|
-
lines.push(`**${label}:** ${value}`);
|
|
1653
|
-
}
|
|
1654
|
-
if (metaPairs.length > 0) lines.push("");
|
|
1655
|
-
|
|
1656
|
-
// Free-form summary paragraph (uses the LLM-authored summary if present,
|
|
1657
|
-
// otherwise the short summary the CLI rolls up).
|
|
1658
|
-
const narrative = normalizeSummaryMarkdownText(summaryState?.summary || session?.shortSummary || "").trim();
|
|
1659
|
-
if (narrative) {
|
|
1660
|
-
lines.push(narrative);
|
|
1661
|
-
lines.push("");
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
// Structured state — each key:value renders as a bold-labeled bullet.
|
|
1665
|
-
// Lists/objects are rendered as nested bullets so arrays of "progress"
|
|
1666
|
-
// steps don't appear as JSON blobs.
|
|
1667
|
-
if (stateJson) {
|
|
1668
|
-
const entries = Object.entries(stateJson)
|
|
1669
|
-
.filter(([, value]) => value !== null && value !== undefined && !(typeof value === "string" && !value.trim()));
|
|
1670
|
-
if (entries.length > 0) {
|
|
1671
|
-
lines.push("## State");
|
|
1672
|
-
for (const [key, value] of entries) {
|
|
1673
|
-
if (Array.isArray(value)) {
|
|
1674
|
-
if (value.length === 0) {
|
|
1675
|
-
lines.push(`- **${key}:** _(empty)_`);
|
|
1676
|
-
} else {
|
|
1677
|
-
lines.push(`- **${key}:**`);
|
|
1678
|
-
for (const item of value.slice(0, 16)) {
|
|
1679
|
-
const text = typeof item === "string"
|
|
1680
|
-
? normalizeSummaryMarkdownText(item)
|
|
1681
|
-
: JSON.stringify(item);
|
|
1682
|
-
lines.push(` - ${text}`);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
} else if (value && typeof value === "object") {
|
|
1686
|
-
lines.push(`- **${key}:**`);
|
|
1687
|
-
for (const [subKey, subValue] of Object.entries(value).slice(0, 16)) {
|
|
1688
|
-
const text = typeof subValue === "string"
|
|
1689
|
-
? normalizeSummaryMarkdownText(subValue)
|
|
1690
|
-
: JSON.stringify(subValue);
|
|
1691
|
-
lines.push(` - **${subKey}:** ${text}`);
|
|
1692
|
-
}
|
|
1693
|
-
} else {
|
|
1694
|
-
lines.push(`- **${key}:** ${normalizeSummaryMarkdownText(String(value))}`);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
lines.push("");
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
// Structured tail sections. Each section renders only if it actually
|
|
1702
|
-
// has items; skipped sections do not occupy vertical space.
|
|
1703
|
-
for (const [heading, field] of [["Blockers", "blockers"], ["Open Questions", "openQuestions"], ["Next Actions", "nextActions"]]) {
|
|
1704
|
-
const items = Array.isArray(summaryState?.[field]) ? summaryState[field].filter(Boolean).slice(0, 12) : [];
|
|
1705
|
-
if (items.length === 0) continue;
|
|
1706
|
-
lines.push(`## ${heading}`);
|
|
1707
|
-
for (const item of items) {
|
|
1708
|
-
lines.push(`- ${typeof item === "string" ? normalizeSummaryMarkdownText(item) : JSON.stringify(item)}`);
|
|
1709
|
-
}
|
|
1710
|
-
lines.push("");
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
if (!summaryState && !narrative) {
|
|
1714
|
-
lines.push("_No summary has been written yet for this session._");
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// Collapse trailing blank lines so the pane doesn't render empty rows
|
|
1718
|
-
// at the bottom.
|
|
1719
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
1720
|
-
|
|
1721
|
-
return [{
|
|
1722
|
-
id: `summary:${session?.sessionId || "none"}`,
|
|
1723
|
-
role: "system",
|
|
1724
|
-
noChrome: true,
|
|
1725
|
-
text: lines.join("\n"),
|
|
1726
|
-
createdAt: session?.summaryUpdatedAt || session?.updatedAt || Date.now(),
|
|
1727
|
-
}];
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
export function selectActiveOutboxMessages(state) {
|
|
1731
|
-
const sessionId = state.sessions.activeSessionId;
|
|
1732
|
-
if (!sessionId) return [];
|
|
1733
|
-
return Array.isArray(state.outbox?.bySessionId?.[sessionId])
|
|
1734
|
-
? state.outbox.bySessionId[sessionId].map((item) => buildPendingOutboxMessage(sessionId, item)).filter(Boolean)
|
|
1735
|
-
: [];
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
1613
|
function prefixRuns(text, color = "gray", options = {}) {
|
|
1739
1614
|
return [{
|
|
1740
1615
|
text,
|
|
@@ -2593,7 +2468,6 @@ function liveActivityPhaseText(item) {
|
|
|
2593
2468
|
// so visibility is governed by session status rather than transcript role.
|
|
2594
2469
|
// `options.spinnerFrame` animates the marker.
|
|
2595
2470
|
export function selectLiveActivityLines(state, options = {}) {
|
|
2596
|
-
if (state?.ui?.chatViewMode === "summary") return [];
|
|
2597
2471
|
const session = selectActiveSession(state);
|
|
2598
2472
|
if (!session || session.isGroup) return [];
|
|
2599
2473
|
const status = String(session?.status || "").toLowerCase();
|
|
@@ -2665,7 +2539,7 @@ export function selectLiveActivityLines(state, options = {}) {
|
|
|
2665
2539
|
// Keyed on identities rather than fields on purpose: selectActiveChat is NOT a
|
|
2666
2540
|
// pure function of the chat array — it also appends a pending-question message,
|
|
2667
2541
|
// an answered-question message and a session-error message, and branches on
|
|
2668
|
-
//
|
|
2542
|
+
// branding. Anything derived from `session` therefore has
|
|
2669
2543
|
// to invalidate when `session` does, which identity comparison gives for free.
|
|
2670
2544
|
//
|
|
2671
2545
|
// `byId` is in the key for two reasons found by adversarial review, both of
|
|
@@ -2695,7 +2569,6 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2695
2569
|
session: memoSessionId ? state?.sessions?.byId?.[memoSessionId] ?? null : null,
|
|
2696
2570
|
byId: state?.sessions?.byId ?? null,
|
|
2697
2571
|
chat: state?.history?.bySessionId?.get?.(memoSessionId)?.chat ?? null,
|
|
2698
|
-
viewMode: state?.ui?.chatViewMode ?? null,
|
|
2699
2572
|
branding: state?.branding ?? null,
|
|
2700
2573
|
principal: state?.auth?.principal ?? null,
|
|
2701
2574
|
maxWidth,
|
|
@@ -2754,6 +2627,10 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2754
2627
|
lines.push(buildRegenRefusedLine(message, maxWidth));
|
|
2755
2628
|
} else if (message?.kind === "regen-failed") {
|
|
2756
2629
|
lines.push(buildRegenFailedLine(message, maxWidth));
|
|
2630
|
+
} else if (message?.kind === "canvas-update") {
|
|
2631
|
+
lines.push(buildCanvasUpdateLine(message, message.sessionId || memoSessionId));
|
|
2632
|
+
} else if (message?.kind === "canvas-action") {
|
|
2633
|
+
lines.push(buildCanvasActionLine(message));
|
|
2757
2634
|
} else {
|
|
2758
2635
|
const messageLines = buildChatMessageLines(message, maxWidth, buildOptions);
|
|
2759
2636
|
appendChatBlockLines(lines, messageLines);
|
|
@@ -2770,6 +2647,92 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2770
2647
|
return memoize(lines.length > 0 ? lines : [{ text: "No messages yet.", color: "gray" }]);
|
|
2771
2648
|
}
|
|
2772
2649
|
|
|
2650
|
+
/**
|
|
2651
|
+
* The canvas-update transcript line — carried by BOTH hosts, rendered by one.
|
|
2652
|
+
*
|
|
2653
|
+
* The line is flagged `canvasUpdate: true`; the portal's chat renderer skips
|
|
2654
|
+
* flagged lines outright (there, the canvas pane updating is the whole
|
|
2655
|
+
* signal), while the TUI renders it as an ordinary artifact link, press-a
|
|
2656
|
+
* affordance and all — a host that cannot render the canvas needs the link.
|
|
2657
|
+
*/
|
|
2658
|
+
/**
|
|
2659
|
+
* Everything the Canvas toggle and pane need about the ACTIVE session's
|
|
2660
|
+
* canvas, in one read: whether one exists, its revision and caption, and
|
|
2661
|
+
* whether the yellow unseen-changes badge should light
|
|
2662
|
+
* (latestRev > lastViewedRev, and only while the canvas is not on screen).
|
|
2663
|
+
*/
|
|
2664
|
+
export function selectCanvasView(state) {
|
|
2665
|
+
const sessionId = state?.sessions?.activeSessionId || null;
|
|
2666
|
+
const entry = sessionId ? state?.canvas?.bySessionId?.[sessionId] : null;
|
|
2667
|
+
const prefs = sessionId ? state?.canvas?.prefs?.[sessionId] : null;
|
|
2668
|
+
const latestRev = entry?.latestRev || 0;
|
|
2669
|
+
const lastViewedRev = prefs?.lastViewedRev || 0;
|
|
2670
|
+
const latestDataRev = entry?.latestDataRev || 0;
|
|
2671
|
+
const lastViewedDataRev = prefs?.lastViewedDataRev || 0;
|
|
2672
|
+
const mode = state?.ui?.rightPaneMode === "canvas" ? "canvas" : "panes";
|
|
2673
|
+
// A cleared canvas (draw_canvas("") → sizeBytes 0) no longer exists for
|
|
2674
|
+
// affordance purposes: no phone tab, no badge, blank teaching state.
|
|
2675
|
+
// Unknown size (null — snapshot degrade) is presumed drawn.
|
|
2676
|
+
const exists = latestRev > 0 && entry?.sizeBytes !== 0;
|
|
2677
|
+
return {
|
|
2678
|
+
sessionId,
|
|
2679
|
+
mode,
|
|
2680
|
+
exists,
|
|
2681
|
+
latestRev,
|
|
2682
|
+
note: entry?.note || "",
|
|
2683
|
+
sizeBytes: entry?.sizeBytes ?? null,
|
|
2684
|
+
snapshotLoaded: Boolean(entry?.snapshotLoaded),
|
|
2685
|
+
optedOut: Boolean(prefs?.optedOut),
|
|
2686
|
+
lastViewedRev,
|
|
2687
|
+
flipSeq: state?.canvas?.flipSeq || 0,
|
|
2688
|
+
responseContract: entry?.responseContract || null,
|
|
2689
|
+
latestDataRev,
|
|
2690
|
+
lastViewedDataRev,
|
|
2691
|
+
dataPayload: entry?.dataPayload || null,
|
|
2692
|
+
unseen: exists && (latestRev > lastViewedRev || latestDataRev > lastViewedDataRev) && mode !== "canvas",
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
/**
|
|
2697
|
+
* A validated canvas response on its way to the agent. Flagged so the PORTAL
|
|
2698
|
+
* chat skips it (the canvas redraw is the visible half of the loop) while the
|
|
2699
|
+
* TUI — which renders no canvas and would otherwise show a silent gap in the
|
|
2700
|
+
* conversation — gets a compact dim line.
|
|
2701
|
+
*/
|
|
2702
|
+
function buildCanvasActionLine(message) {
|
|
2703
|
+
const dataKeys = message?.data && typeof message.data === "object" ? Object.keys(message.data) : [];
|
|
2704
|
+
const detail = dataKeys.length === 1 && typeof message.data[dataKeys[0]] === "string"
|
|
2705
|
+
? message.data[dataKeys[0]].slice(0, 120)
|
|
2706
|
+
: (dataKeys.length ? JSON.stringify(message.data).slice(0, 120) : "");
|
|
2707
|
+
return {
|
|
2708
|
+
runs: [
|
|
2709
|
+
{ text: "[canvas] ", color: "cyan", bold: true },
|
|
2710
|
+
{ text: `${message.action || "action"}`, color: "white" },
|
|
2711
|
+
...(detail ? [{ text: ` — ${detail}`, color: "gray" }] : []),
|
|
2712
|
+
],
|
|
2713
|
+
canvasAction: true,
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
function buildCanvasUpdateLine(message, sessionId) {
|
|
2718
|
+
const rev = Number(message?.rev) || 0;
|
|
2719
|
+
const note = typeof message?.note === "string" && message.note.trim() ? ` — ${message.note.trim()}` : "";
|
|
2720
|
+
const runs = [
|
|
2721
|
+
{ text: "[canvas] ", color: "cyan", bold: true },
|
|
2722
|
+
{ text: `rev ${rev}${note} `, color: "white" },
|
|
2723
|
+
];
|
|
2724
|
+
if (sessionId) {
|
|
2725
|
+
runs.push({
|
|
2726
|
+
text: "[artifact: canvas.html]",
|
|
2727
|
+
color: "cyan",
|
|
2728
|
+
underline: true,
|
|
2729
|
+
href: `artifact://${sessionId}/canvas.html`,
|
|
2730
|
+
});
|
|
2731
|
+
runs.push({ text: " (press a to download)", color: "gray" });
|
|
2732
|
+
}
|
|
2733
|
+
return { runs, canvasUpdate: true };
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2773
2736
|
function buildRuleLine(label, color, maxWidth) {
|
|
2774
2737
|
const safeWidth = Math.max(24, Number(maxWidth) || 80);
|
|
2775
2738
|
const room = safeWidth - label.length;
|
|
@@ -2825,6 +2788,14 @@ function buildRegenFailedLine(message, maxWidth) {
|
|
|
2825
2788
|
return buildRuleLine(label, "red", maxWidth);
|
|
2826
2789
|
}
|
|
2827
2790
|
|
|
2791
|
+
export function selectActiveOutboxMessages(state) {
|
|
2792
|
+
const sessionId = state.sessions.activeSessionId;
|
|
2793
|
+
if (!sessionId) return [];
|
|
2794
|
+
return Array.isArray(state.outbox?.bySessionId?.[sessionId])
|
|
2795
|
+
? state.outbox.bySessionId[sessionId].map((item) => buildPendingOutboxMessage(sessionId, item)).filter(Boolean)
|
|
2796
|
+
: [];
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2828
2799
|
export function selectOutboxOverlayLines(state, maxWidth = 80, options = {}) {
|
|
2829
2800
|
const messages = selectActiveOutboxMessages(state);
|
|
2830
2801
|
if (!messages || messages.length === 0) return [];
|
|
@@ -2956,9 +2927,7 @@ export function selectChatPaneChrome(state, options = {}) {
|
|
|
2956
2927
|
const outboxItems = Array.isArray(state.outbox?.bySessionId?.[session.sessionId])
|
|
2957
2928
|
? state.outbox.bySessionId[session.sessionId]
|
|
2958
2929
|
: [];
|
|
2959
|
-
const progress =
|
|
2960
|
-
? null
|
|
2961
|
-
: buildLiveProgressState(session, history, history?.chat || [], outboxItems);
|
|
2930
|
+
const progress = buildLiveProgressState(session, history, history?.chat || [], outboxItems);
|
|
2962
2931
|
|
|
2963
2932
|
// Border top-right: status · model · context (mirrors the session-row meta),
|
|
2964
2933
|
// with the live turn-progress label appended while a turn is running.
|
|
@@ -4266,11 +4235,9 @@ export function selectStatusBar(state) {
|
|
|
4266
4235
|
right: "up/down move · space toggle · ctrl-g group · c cancel · d complete · D hard delete · V/esc exit",
|
|
4267
4236
|
};
|
|
4268
4237
|
}
|
|
4269
|
-
const chatViewMode = state.ui?.chatViewMode === "summary" ? "summary" : "transcript";
|
|
4270
|
-
const chatViewHint = chatViewMode === "summary" ? "s transcript" : "s summary";
|
|
4271
4238
|
const hints = {
|
|
4272
4239
|
[FOCUS_REGIONS.SESSIONS]: `up/down switch · ctrl-u/ctrl-d page · ctrl-g move group · f filter · P pin · V select · d done · D delete · r refresh · t title · v visibility · S share · [/] resize pane · {/} columns · T themes · ? help · a linked items · drag copy · tab next pane · p prompt`,
|
|
4273
|
-
[FOCUS_REGIONS.CHAT]:
|
|
4240
|
+
[FOCUS_REGIONS.CHAT]: `j/k scroll · ctrl-u/ctrl-d page · e older history · g/G top/bottom · d done · ${fullscreenHint} · [/] resize pane · {/} columns · T themes · ? help · a linked items · drag copy · tab next pane · p prompt`,
|
|
4274
4241
|
[FOCUS_REGIONS.INSPECTOR]: state.ui.inspectorTab === "logs"
|
|
4275
4242
|
? `j/k scroll · ctrl-u/ctrl-d page · g/G top/bottom · d done · t tail · f filter · ${fullscreenHint} · left/right tab · [/] resize pane · {/} columns · T themes · ? help · a linked items · drag copy · tab next pane`
|
|
4276
4243
|
: state.ui.inspectorTab === "stats"
|
|
@@ -4855,13 +4822,8 @@ function buildSequenceStatsLines(state, session, maxWidth) {
|
|
|
4855
4822
|
if (!body) body = "orchestration stats unavailable";
|
|
4856
4823
|
}
|
|
4857
4824
|
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
{ text: "Stats ", color: "cyan", bold: true },
|
|
4861
|
-
{ text: body, color: "white" },
|
|
4862
|
-
], Math.max(18, maxWidth))];
|
|
4863
|
-
}
|
|
4864
|
-
|
|
4825
|
+
// Narrow panes used to degrade to a single truncated line; a box whose
|
|
4826
|
+
// content wraps reads better on a phone and costs two rows of chrome.
|
|
4865
4827
|
return buildMessageCardLines({
|
|
4866
4828
|
title: "Stats",
|
|
4867
4829
|
body,
|
|
@@ -5884,8 +5846,10 @@ export function selectSessionGroupNameModal(state, maxWidth = 76) {
|
|
|
5884
5846
|
const value = String(modal.value || "");
|
|
5885
5847
|
const sessionCount = Array.isArray(modal.sessionIds) ? modal.sessionIds.length : 0;
|
|
5886
5848
|
const previewTitle = value.trim() || "…";
|
|
5849
|
+
const renaming = modal.mode === "rename";
|
|
5887
5850
|
return {
|
|
5888
|
-
title: modal.title || "New Group",
|
|
5851
|
+
title: modal.title || (renaming ? "Rename Group" : "New Group"),
|
|
5852
|
+
mode: renaming ? "rename" : "create",
|
|
5889
5853
|
value,
|
|
5890
5854
|
cursorIndex: Math.max(0, Math.min(Number(modal.cursorIndex) || 0, value.length)),
|
|
5891
5855
|
placeholder: "Type a group name",
|
|
@@ -5893,15 +5857,17 @@ export function selectSessionGroupNameModal(state, maxWidth = 76) {
|
|
|
5893
5857
|
helpLines: [
|
|
5894
5858
|
[
|
|
5895
5859
|
{ text: "Enter", color: "cyan", bold: true },
|
|
5896
|
-
{ text: " create and move ", color: "gray" },
|
|
5860
|
+
{ text: renaming ? " rename " : " create and move ", color: "gray" },
|
|
5897
5861
|
{ text: "Esc", color: "cyan", bold: true },
|
|
5898
5862
|
{ text: " cancel", color: "gray" },
|
|
5899
5863
|
],
|
|
5900
5864
|
[{ text: "", color: "gray" }],
|
|
5901
|
-
|
|
5865
|
+
renaming
|
|
5866
|
+
? [{ text: "Only the group's name changes; its sessions stay put.", color: "gray" }]
|
|
5867
|
+
: [{ text: `The new group will contain ${sessionCount} selected session${sessionCount === 1 ? "" : "s"}.`, color: "gray" }],
|
|
5902
5868
|
],
|
|
5903
5869
|
detailsLines: [
|
|
5904
|
-
[{ text: "New group: ", color: "gray" }, { text: previewTitle, color: "white", bold: true }],
|
|
5870
|
+
[{ text: renaming ? "Rename to: " : "New group: ", color: "gray" }, { text: previewTitle, color: "white", bold: true }],
|
|
5905
5871
|
[{ text: "Groups are containers only; session actions remain per-session.", color: "gray" }],
|
|
5906
5872
|
],
|
|
5907
5873
|
idealWidth: Math.min(Math.max(56, displayLength(previewTitle) + 18), maxWidth),
|
|
@@ -6328,6 +6294,20 @@ function formatSessionAge(ts) {
|
|
|
6328
6294
|
return `${Math.floor(days / 7)}w`;
|
|
6329
6295
|
}
|
|
6330
6296
|
|
|
6297
|
+
// Card-row timestamp: month, day, clock — no year, no "at", no timezone.
|
|
6298
|
+
// Cards live inside fitted boxes on a ~44-column phone; the full form is 29
|
|
6299
|
+
// characters and its padStart alignment dragged EVERY row in the table past
|
|
6300
|
+
// the card interior, wrapping all of them. Recency is what these rows convey;
|
|
6301
|
+
// the year and zone are noise there (the full form remains on Created/Updated).
|
|
6302
|
+
function formatCardTimestamp(ts) {
|
|
6303
|
+
if (!ts) return "—";
|
|
6304
|
+
const date = ts instanceof Date ? ts : new Date(ts);
|
|
6305
|
+
if (Number.isNaN(date.getTime())) return "—";
|
|
6306
|
+
const day = date.toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
6307
|
+
const clock = date.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit", second: "2-digit" });
|
|
6308
|
+
return `${day}, ${clock}`;
|
|
6309
|
+
}
|
|
6310
|
+
|
|
6331
6311
|
function formatLocalTimestamp(ts) {
|
|
6332
6312
|
if (!ts) return "—";
|
|
6333
6313
|
const date = ts instanceof Date ? ts : new Date(ts);
|
|
@@ -6470,7 +6450,7 @@ function buildSessionStatsLines(state, session, maxWidth) {
|
|
|
6470
6450
|
["Cache Read", formatCompactNumber(summary.tokensCacheRead)],
|
|
6471
6451
|
["Cache Write", formatCompactNumber(summary.tokensCacheWrite)],
|
|
6472
6452
|
["Hit Ratio", hitRatioLabel],
|
|
6473
|
-
]),
|
|
6453
|
+
], { maxWidth: Math.max(12, w - 4) }),
|
|
6474
6454
|
width: w,
|
|
6475
6455
|
titleColor: "green",
|
|
6476
6456
|
borderColor: "gray",
|
|
@@ -6506,9 +6486,9 @@ function buildSessionStatsLines(state, session, maxWidth) {
|
|
|
6506
6486
|
["Dehydrations", String(summary.dehydrationCount)],
|
|
6507
6487
|
["Hydrations", String(summary.hydrationCount)],
|
|
6508
6488
|
["Lossy Handoffs", String(summary.lossyHandoffCount)],
|
|
6509
|
-
["Last Dehydrated", summary.lastDehydratedAt ?
|
|
6510
|
-
["Last Hydrated", summary.lastHydratedAt ?
|
|
6511
|
-
]),
|
|
6489
|
+
["Last Dehydrated", summary.lastDehydratedAt ? formatCardTimestamp(summary.lastDehydratedAt) : null],
|
|
6490
|
+
["Last Hydrated", summary.lastHydratedAt ? formatCardTimestamp(summary.lastHydratedAt) : null],
|
|
6491
|
+
], { maxWidth: Math.max(12, w - 4) }),
|
|
6512
6492
|
width: w,
|
|
6513
6493
|
titleColor: "yellow",
|
|
6514
6494
|
borderColor: "gray",
|
|
@@ -6636,8 +6616,17 @@ function formatKeyValueTable(pairs, options = {}) {
|
|
|
6636
6616
|
const labelWidth = rows.reduce((max, [label]) => Math.max(max, String(label).length), 0);
|
|
6637
6617
|
const valueWidth = rows.reduce((max, [, value]) => Math.max(max, String(value).length), 0);
|
|
6638
6618
|
const filler = " ".repeat(gap);
|
|
6619
|
+
// One oversized value must not wrap every row: the aligned grid pads each
|
|
6620
|
+
// row to labelWidth+gap+valueWidth, so a single 29-char timestamp used to
|
|
6621
|
+
// push ALL rows past a narrow card's interior. When the grid cannot fit,
|
|
6622
|
+
// keep label alignment but let each value sit flush after its label —
|
|
6623
|
+
// short rows stay whole and only the long row wraps.
|
|
6624
|
+
const maxWidth = Number(options.maxWidth);
|
|
6625
|
+
const aligned = !Number.isFinite(maxWidth) || labelWidth + gap + valueWidth <= maxWidth;
|
|
6639
6626
|
return rows
|
|
6640
|
-
.map(([label, value]) =>
|
|
6627
|
+
.map(([label, value]) => (aligned
|
|
6628
|
+
? `${String(label).padEnd(labelWidth)}${filler}${String(value).padStart(valueWidth)}`
|
|
6629
|
+
: `${String(label).padEnd(labelWidth)}${filler}${String(value)}`))
|
|
6641
6630
|
.join("\n");
|
|
6642
6631
|
}
|
|
6643
6632
|
|
package/ui/core/src/state.js
CHANGED
|
@@ -128,13 +128,48 @@ export function normalizeStoredCollapsedSessionIds(value) {
|
|
|
128
128
|
return out;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Right-column mode: the inspector/activity split, or the session canvas.
|
|
133
|
+
* Anything unrecognized falls back to panes — a stored value from a newer
|
|
134
|
+
* build must not strand an older portal in a mode it cannot render.
|
|
135
|
+
*/
|
|
136
|
+
export function normalizeStoredRightPaneMode(value) {
|
|
137
|
+
return value === "canvas" ? "canvas" : "panes";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Per-session canvas preferences: what this user has chosen and seen.
|
|
142
|
+
*
|
|
143
|
+
* `optedOut` — the user manually left canvas mode this session, so agent
|
|
144
|
+
* draws badge instead of flipping. `lastViewedRev` — the highest revision
|
|
145
|
+
* this user has had on screen; the yellow badge is latestRev > lastViewedRev.
|
|
146
|
+
* Persisted (like pinned ids) so the overnight case lights the badge on
|
|
147
|
+
* reopen with no extra query.
|
|
148
|
+
*/
|
|
149
|
+
export function normalizeStoredCanvasPrefs(value) {
|
|
150
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const [sessionId, entry] of Object.entries(value)) {
|
|
153
|
+
const id = String(sessionId || "").trim();
|
|
154
|
+
if (!id || !entry || typeof entry !== "object") continue;
|
|
155
|
+
const lastViewedRev = Number(entry.lastViewedRev);
|
|
156
|
+
const lastViewedDataRev = Number(entry.lastViewedDataRev);
|
|
157
|
+
out[id] = {
|
|
158
|
+
optedOut: entry.optedOut === true,
|
|
159
|
+
lastViewedRev: Number.isFinite(lastViewedRev) && lastViewedRev > 0 ? Math.floor(lastViewedRev) : 0,
|
|
160
|
+
lastViewedDataRev: Number.isFinite(lastViewedDataRev) && lastViewedDataRev > 0 ? Math.floor(lastViewedDataRev) : 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
131
166
|
export function normalizeStoredActiveSessionId(value) {
|
|
132
167
|
if (value == null) return null;
|
|
133
168
|
const id = String(value).trim();
|
|
134
169
|
return id ? id : null;
|
|
135
170
|
}
|
|
136
171
|
|
|
137
|
-
export function createInitialState({ mode = "local", branding = null, docs = null, themeId = null,
|
|
172
|
+
export function createInitialState({ mode = "local", branding = null, docs = null, themeId = null, sessionOwnerFilter = null, layoutAdjustments = null, pinnedSessionIds = null, collapsedSessionIds = null, activeSessionId = null, sessionOrder = null, rightPaneMode = null, canvasPrefs = null } = {}) {
|
|
138
173
|
const hasStoredSessionOwnerFilter = sessionOwnerFilter != null;
|
|
139
174
|
const hasStoredCollapsedSessionIds = collapsedSessionIds != null;
|
|
140
175
|
const initialLayoutAdjustments = normalizeStoredLayoutAdjustments(layoutAdjustments);
|
|
@@ -164,7 +199,9 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
|
|
|
164
199
|
sequenceSelectedTurn: null,
|
|
165
200
|
// "rich" was retired as a view mode; a persisted "rich" falls
|
|
166
201
|
// through to the transcript.
|
|
167
|
-
|
|
202
|
+
// Which face the right column wears: the inspector/activity panes
|
|
203
|
+
// or the session canvas. Persisted.
|
|
204
|
+
rightPaneMode: normalizeStoredRightPaneMode(rightPaneMode),
|
|
168
205
|
statsViewMode: "session",
|
|
169
206
|
prompt: "",
|
|
170
207
|
promptCursor: 0,
|
|
@@ -301,6 +338,18 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
|
|
|
301
338
|
query: "",
|
|
302
339
|
},
|
|
303
340
|
},
|
|
341
|
+
canvas: {
|
|
342
|
+
// Runtime knowledge of each session's canvas, fed by the
|
|
343
|
+
// selection-burst snapshot and then by canvas_updated events.
|
|
344
|
+
// { latestRev, note, sizeBytes, snapshotLoaded } per session.
|
|
345
|
+
bySessionId: {},
|
|
346
|
+
// Persisted per-user attention state — see normalizeStoredCanvasPrefs.
|
|
347
|
+
prefs: normalizeStoredCanvasPrefs(canvasPrefs),
|
|
348
|
+
// Ticks on every agent-driven flip. Hosts whose pane state lives
|
|
349
|
+
// outside ui.rightPaneMode (the phone's tab strip) watch this to
|
|
350
|
+
// bring their canvas surface forward.
|
|
351
|
+
flipSeq: 0,
|
|
352
|
+
},
|
|
304
353
|
logs: {
|
|
305
354
|
available: false,
|
|
306
355
|
availabilityReason: "Log tailing disabled: no K8S_CONTEXT configured in the env file.",
|
|
@@ -119,7 +119,12 @@ const duroxideTheme = createTheme({
|
|
|
119
119
|
brightWhite: "#fdf7f2",
|
|
120
120
|
},
|
|
121
121
|
tui: {
|
|
122
|
-
|
|
122
|
+
// 2.49:1 on the surface. The old #46403d was 1.48:1 — enough to
|
|
123
|
+
// suggest an edge on a flat panel, and nothing at all once the felt
|
|
124
|
+
// texture ran over it or it was drawn as a one-pixel seam. Every
|
|
125
|
+
// structural line in the theme reads from this: panel edges, the
|
|
126
|
+
// joined-workspace seams, and the table rules.
|
|
127
|
+
border: "#6a615b",
|
|
123
128
|
// The human speaks in the warning register, as in the Rust theme: the
|
|
124
129
|
// accent stays reserved for the machine and what it produces.
|
|
125
130
|
userChat: "#e0b070",
|
|
@@ -24,7 +24,10 @@ const win95Theme = createTheme({
|
|
|
24
24
|
overlayForeground: "#000000",
|
|
25
25
|
// The classic disabled/secondary grey; darker than the face so it
|
|
26
26
|
// stays legible on #c0c0c0 rather than vanishing into it.
|
|
27
|
-
|
|
27
|
+
// Darker than the authentic #808080 system grey, which sat at 2.2:1
|
|
28
|
+
// on the #c0c0c0 chrome. Even #5a5a5a only reached 3.79:1; this
|
|
29
|
+
// clears 4.5:1 so muted text is readable wherever the chrome shows.
|
|
30
|
+
hintColor: "#4a4a4a",
|
|
28
31
|
modalBackdrop: "rgba(0, 0, 0, 0.45)",
|
|
29
32
|
modalBackground: "#c0c0c0",
|
|
30
33
|
modalBorder: "#808080",
|
|
@@ -353,7 +353,6 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
353
353
|
branding: state.branding,
|
|
354
354
|
connectionError: state.connection.error,
|
|
355
355
|
connectionMode: state.connection.mode,
|
|
356
|
-
chatViewMode: state.ui.chatViewMode || "transcript",
|
|
357
356
|
chatScroll: state.ui.scroll.chat,
|
|
358
357
|
focused: state.ui.focusRegion === "chat",
|
|
359
358
|
};
|
|
@@ -371,9 +370,7 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
371
370
|
error: chatView.connectionError,
|
|
372
371
|
mode: chatView.connectionMode,
|
|
373
372
|
},
|
|
374
|
-
ui: {
|
|
375
|
-
chatViewMode: chatView.chatViewMode,
|
|
376
|
-
},
|
|
373
|
+
ui: {},
|
|
377
374
|
sessions: {
|
|
378
375
|
activeSessionId: chatView.activeSessionId,
|
|
379
376
|
byId: buildSingleSessionMap(chatView.activeSessionId, chatView.activeSession),
|
|
@@ -398,7 +395,6 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
398
395
|
chatView.activeOutbox,
|
|
399
396
|
chatView.authPrincipal,
|
|
400
397
|
chatView.branding,
|
|
401
|
-
chatView.chatViewMode,
|
|
402
398
|
chatView.connectionError,
|
|
403
399
|
chatView.connectionMode,
|
|
404
400
|
]);
|
|
@@ -966,7 +962,6 @@ const StatusBar = React.memo(function StatusBar({ controller }) {
|
|
|
966
962
|
workersOnline: state.connection.workersOnline,
|
|
967
963
|
focusRegion: state.ui.focusRegion,
|
|
968
964
|
inspectorTab: state.ui.inspectorTab,
|
|
969
|
-
chatViewMode: state.ui.chatViewMode || "transcript",
|
|
970
965
|
fullscreenPane: state.ui.fullscreenPane || null,
|
|
971
966
|
logsAvailable: state.logs.available,
|
|
972
967
|
logsTailing: state.logs.tailing,
|
|
@@ -988,7 +983,6 @@ const StatusBar = React.memo(function StatusBar({ controller }) {
|
|
|
988
983
|
ui: {
|
|
989
984
|
focusRegion: statusState.focusRegion,
|
|
990
985
|
inspectorTab: statusState.inspectorTab,
|
|
991
|
-
chatViewMode: statusState.chatViewMode,
|
|
992
986
|
fullscreenPane: statusState.fullscreenPane,
|
|
993
987
|
statusText: statusState.statusText,
|
|
994
988
|
modal: statusState.modal,
|