pilotswarm 0.5.57 → 0.5.59
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/mcp/dist/src/context.d.ts +2 -0
- package/mcp/dist/src/context.d.ts.map +1 -1
- package/mcp/dist/src/context.js +3 -1
- package/mcp/dist/src/context.js.map +1 -1
- package/mcp/dist/src/tools/agent-packages.js +1 -1
- package/mcp/dist/src/tools/agent-packages.js.map +1 -1
- package/mcp/dist/src/tools/capabilities.d.ts +4 -0
- package/mcp/dist/src/tools/capabilities.d.ts.map +1 -1
- package/mcp/dist/src/tools/capabilities.js +4 -0
- package/mcp/dist/src/tools/capabilities.js.map +1 -1
- package/package.json +2 -2
- package/tui/src/node-sdk-transport.js +5 -0
- package/ui/core/src/controller.js +136 -11
- package/ui/core/src/formatting.js +18 -9
- package/ui/core/src/history.js +162 -9
- package/ui/core/src/reducer.js +32 -5
- package/ui/core/src/selectors.js +160 -16
- package/ui/core/src/session-errors.js +16 -11
- package/ui/react/src/components.js +1 -0
- package/ui/react/src/web-app.js +230 -80
- package/web/api/live-plane.js +193 -0
- package/web/api/router.js +1 -1
- package/web/api/ws.js +160 -4
- package/web/authz.js +6 -1
- package/web/dist/assets/{abnfDiagram-VRR7QNED-CJOR2hPi.js → abnfDiagram-VRR7QNED-D9Rv2VpV.js} +1 -1
- package/web/dist/assets/{arc-Yh3kc_KB.js → arc-BATiucnq.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-DLRlUeyk.js → architectureDiagram-ZJ3FMSHR-CSTMt0si.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-x_TGH_fp.js → blockDiagram-677ZJIJ3-C_RGqfeY.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-C-j_bxeF.js → c4Diagram-LMCZKHZV-DH__s__0.js} +1 -1
- package/web/dist/assets/channel-CqgY9oZh.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-CaI5Xqwo.js → chunk-2Q5K7J3B-C197fD3B.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-Bh_Crac6.js → chunk-32BRIVSS-BjpuBroU.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-XM-3ajKG.js → chunk-5VM5RSS4-W_jL8euj.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-ZwkulfFp.js → chunk-EX3LRPZG-a9h0dtlp.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-CT3Q7T2A.js → chunk-JWPE2WC7-CQN0qE7F.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-_Uc0ksg3.js → chunk-MOJQB5TN-D-Y6KQ7t.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-DosoId7u.js → chunk-RYQCIY6F-jPwseC5f.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-KRjq1cwM.js → chunk-V7JOEXUC-DRNCEcQh.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-B6Ul1z3S.js → chunk-VR4S4FIN-DzhUcoZ7.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-AikdVXm4.js → chunk-XXDRQBXY-ldJfyPXK.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-Uz4DYZs2.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-Uz4DYZs2.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-DTYwgEAa.js → cose-bilkent-JH36ORCC-BBVo21wR.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-C_8v2wIz.js → cynefin-VYW2F7L2-CgRQfdW1.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-1-nw1zbQ.js → cynefinDiagram-TSTJHNR4-Ckz2X0t3.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-CAjwRpvT.js → dagre-VKFMJZFB-C1SanJiY.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-Bg41nQw_.js → diagram-FQU43EPY-dBdEifVy.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-Cp6P-qMn.js → diagram-G47NLZAW-Byqc1VCB.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-DNdFuedg.js → diagram-NH7WQ7WH-9aeTaCsX.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-DcHpELG2.js → diagram-OA4YK3LP-CL0a0o0k.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-B2_kyMw3.js → diagram-WEI45ONY-YlMQOJfY.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-DztuAo3o.js → ebnfDiagram-CCIWWBDH-qMZQEnws.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-DNjKTOzp.js → erDiagram-Q63AITRT-C3CWBCtq.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-DWjfeUM1.js → flowDiagram-23GEKE2U-BN79sybL.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-Y0MgGVmy.js → ganttDiagram-NO4QXBWP-BOWhiAgB.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX--lDKAKVz.js → gitGraphDiagram-IHSO6WYX-CKX9OACp.js} +1 -1
- package/web/dist/assets/index-B4s2lTsK.js +24 -0
- package/web/dist/assets/{index-BxPfp0AO.css → index-DhGdE2Nd.css} +1 -1
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-CKmH8_YO.js → infoDiagram-FWYZ7A6U-QQEunPNs.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-D88R7zUc.js → ishikawaDiagram-FXEZZL3T-CvMwrnLQ.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-Ck7EMrsY.js → journeyDiagram-5HDEW3XC-giNpEVpj.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-hc-mOfSG.js → kanban-definition-HUTT4EX6-CUPPpo27.js} +1 -1
- package/web/dist/assets/{linear-BVhU4csP.js → linear-AXqadfOR.js} +1 -1
- package/web/dist/assets/{mermaid.core-C9u4FOCX.js → mermaid.core-C78wdk8Y.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-DatQu9Xp.js → mindmap-definition-LN4V7U3C-BGUmh9bK.js} +1 -1
- package/web/dist/assets/{papaparse.min-BdnoXc8M.js → papaparse.min-Bh8RLCk_.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-Cr_nZ_jR.js → pegDiagram-2B236MQR-BHsKXKhK.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-CcAbSIcY.js → pieDiagram-ENE6RG2P-D3EvWTdH.js} +1 -1
- package/web/dist/assets/pilotswarm-UV6T1_QI.js +98 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-CgJw5laM.js → quadrantDiagram-ABIIQ3AL-CbkxfLkV.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-CHeun9SN.js → railroadDiagram-RFXS5EU6-H4M6JSDW.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-D1kpDj8W.js → requirementDiagram-TGXJPOKE-BJo7JW1v.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-C5vUSUaX.js → sankeyDiagram-HTMAVEWB-DL19MeOP.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-T1aPSxk1.js → sequenceDiagram-DBY2YBRQ-DyVFc5pQ.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-j-wh2LVX.js → sizeCapture-X5ZJPWSS-CKbcKIEQ.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-DfUZZR8N.js → stateDiagram-2N3HPSRC-BSukSXzS.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-BhKG3pJB.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-OpASJiRw.js → swimlanes-5IMT3BWC-BUd7UhsF.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-CgoFkXJE.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-BBnc_z1U.js → timeline-definition-FHXFAJF6-Dt1gludn.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-BlPQe9p4.js → vennDiagram-L72KCM5P-BHxt00GV.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-DBcBFUx2.js → wardleyDiagram-EHGQE667-BT3fIDbe.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-BQUSGL6g.js → xychartDiagram-FW5EYKEG-ChoYDS2b.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/runtime.js +89 -44
- package/web/server.js +9 -0
- package/web/dist/assets/channel-BIn92AzB.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-B7ktW6XM.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-B7ktW6XM.js +0 -1
- package/web/dist/assets/index-rPji8Uud.js +0 -24
- package/web/dist/assets/pilotswarm--kvfB_6q.js +0 -99
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-ilKtTdHG.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-C1PthnB4.js +0 -8
package/ui/react/src/web-app.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
// createPortal is only invoked
|
|
3
|
-
//
|
|
4
|
-
// dependency wherever this file loads, so it is safe in the
|
|
2
|
+
// createPortal is only invoked by browser-only surfaces (tooltips, toolbar
|
|
3
|
+
// slots, and viewport-level dialogs); the import itself is side-effect-free
|
|
4
|
+
// and react-dom is a dependency wherever this file loads, so it is safe in the
|
|
5
|
+
// shared module.
|
|
5
6
|
import { createPortal } from "react-dom";
|
|
6
7
|
import { appendAnimatedDotsToRuns, useAnimatedDots, useSpinnerFrame } from "./chat-status.js";
|
|
7
8
|
import {
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
isThemeLight,
|
|
26
27
|
normalizeStoredCanvasPrefs,
|
|
27
28
|
parseTerminalMarkupRuns,
|
|
29
|
+
parseMarkdownLines,
|
|
28
30
|
PilotSwarmUiController,
|
|
29
31
|
tokenizeInlineMarkdown,
|
|
30
32
|
selectActivityPane,
|
|
@@ -818,6 +820,12 @@ function normalizeLines(lines) {
|
|
|
818
820
|
};
|
|
819
821
|
|
|
820
822
|
for (const line of lines || []) {
|
|
823
|
+
if (line?.kind === "assistantPreview") {
|
|
824
|
+
// Preserve selector-cache identity so another stream's ticks don't
|
|
825
|
+
// rerender every completed response or recreate its scroll observer.
|
|
826
|
+
normalized.push(line);
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
821
829
|
if (line?.kind === "markup") {
|
|
822
830
|
// Markup lines carry preformatted art (splash screens). Tag them
|
|
823
831
|
// so the chat renderer keeps each line intact: the pane's
|
|
@@ -1255,7 +1263,7 @@ function Runs({ runs, theme }) {
|
|
|
1255
1263
|
|
|
1256
1264
|
if (artifactRef) {
|
|
1257
1265
|
return React.createElement(ArtifactLink, {
|
|
1258
|
-
key:
|
|
1266
|
+
key: index,
|
|
1259
1267
|
artifactRef,
|
|
1260
1268
|
style,
|
|
1261
1269
|
});
|
|
@@ -1268,7 +1276,7 @@ function Runs({ runs, theme }) {
|
|
|
1268
1276
|
|
|
1269
1277
|
return isExternalHref
|
|
1270
1278
|
? React.createElement("a", {
|
|
1271
|
-
key:
|
|
1279
|
+
key: index,
|
|
1272
1280
|
className: "ps-md-link",
|
|
1273
1281
|
href,
|
|
1274
1282
|
target: "_blank",
|
|
@@ -1276,7 +1284,8 @@ function Runs({ runs, theme }) {
|
|
|
1276
1284
|
style,
|
|
1277
1285
|
}, text)
|
|
1278
1286
|
: React.createElement("span", {
|
|
1279
|
-
key:
|
|
1287
|
+
key: index,
|
|
1288
|
+
className: run?.role === "streamingCaret" ? "ps-streaming-caret" : undefined,
|
|
1280
1289
|
style,
|
|
1281
1290
|
}, text);
|
|
1282
1291
|
}),
|
|
@@ -1284,14 +1293,16 @@ function Runs({ runs, theme }) {
|
|
|
1284
1293
|
}
|
|
1285
1294
|
|
|
1286
1295
|
function SystemNoticeLine({ line, theme }) {
|
|
1296
|
+
const [open, setOpen] = React.useState(false);
|
|
1287
1297
|
const body = String(line?.body || "").trim();
|
|
1288
|
-
|
|
1298
|
+
const className = `ps-system-notice${line?.liveReasoning || line?.streamReasoning ? " is-live-reasoning" : ""}`;
|
|
1299
|
+
return React.createElement("details", { className, onToggle: (event) => setOpen(event.currentTarget.open) },
|
|
1289
1300
|
React.createElement("summary", {
|
|
1290
1301
|
className: "ps-system-notice-summary",
|
|
1291
1302
|
style: { color: resolveColor(theme, line?.color) || "var(--ps-muted)" },
|
|
1292
1303
|
},
|
|
1293
1304
|
React.createElement("span", { className: "ps-system-notice-summary-text" }, line?.text || "System notice")),
|
|
1294
|
-
body
|
|
1305
|
+
body && open
|
|
1295
1306
|
? React.createElement("div", { className: "ps-system-notice-body" },
|
|
1296
1307
|
React.createElement(MarkdownPreviewContent, { content: body, theme }))
|
|
1297
1308
|
: null);
|
|
@@ -3185,6 +3196,12 @@ function parseStructuredChatBlocks(lines = []) {
|
|
|
3185
3196
|
for (let index = 0; index < lines.length;) {
|
|
3186
3197
|
const currentLine = lines[index];
|
|
3187
3198
|
|
|
3199
|
+
if (currentLine?.kind === "assistantPreview") {
|
|
3200
|
+
blocks.push({ type: "assistantPreview", line: currentLine });
|
|
3201
|
+
index += 1;
|
|
3202
|
+
continue;
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3188
3205
|
// Preformatted markup lines (splash art) bypass box/table/card
|
|
3189
3206
|
// detection entirely and render as one preserve block, so no line
|
|
3190
3207
|
// ever rewraps or gets reinterpreted as a structured element.
|
|
@@ -3216,6 +3233,7 @@ function parseStructuredChatBlocks(lines = []) {
|
|
|
3216
3233
|
}
|
|
3217
3234
|
blocks.push({
|
|
3218
3235
|
type: "card",
|
|
3236
|
+
cardKey: currentLine.cardKey || null,
|
|
3219
3237
|
headerRuns: Array.isArray(currentLine.runs) ? currentLine.runs : [],
|
|
3220
3238
|
borderColor: currentLine.borderColor || "gray",
|
|
3221
3239
|
blocks: parseStructuredChatBlocks(innerLines),
|
|
@@ -3489,12 +3507,129 @@ function ArtifactImageStrip({ controller, sessionId, attachments }) {
|
|
|
3489
3507
|
}));
|
|
3490
3508
|
}
|
|
3491
3509
|
|
|
3510
|
+
const PreviewMessageContent = React.memo(function PreviewMessageContent({ content, width, theme, controller }) {
|
|
3511
|
+
const lines = React.useMemo(
|
|
3512
|
+
() => normalizeLines(parseMarkdownLines(content, { width, tableMode: "sentinel" })),
|
|
3513
|
+
[content, width],
|
|
3514
|
+
);
|
|
3515
|
+
return React.createElement(StructuredChatBlocks, { lines, theme, controller });
|
|
3516
|
+
});
|
|
3517
|
+
|
|
3518
|
+
const AssistantPreviewCard = React.memo(function AssistantPreviewCard({ line, theme, controller }) {
|
|
3519
|
+
const [open, setOpen] = React.useState(false);
|
|
3520
|
+
const [revealed, setRevealed] = React.useState(
|
|
3521
|
+
!line.liveStartedAt || Date.now() - line.liveStartedAt >= 200,
|
|
3522
|
+
);
|
|
3523
|
+
const viewportRef = React.useRef(null);
|
|
3524
|
+
const contentRef = React.useRef(null);
|
|
3525
|
+
const followRef = React.useRef(true);
|
|
3526
|
+
const snapshotRef = React.useRef(null);
|
|
3527
|
+
const expanded = open || line.final;
|
|
3528
|
+
// Keep already-mounted markdown/disclosures when closed, but don't parse
|
|
3529
|
+
// new hidden deltas. Reopening catches up in a single render.
|
|
3530
|
+
if (expanded) snapshotRef.current = line;
|
|
3531
|
+
const snapshot = snapshotRef.current;
|
|
3532
|
+
|
|
3533
|
+
React.useEffect(() => {
|
|
3534
|
+
if (line.final || revealed) return undefined;
|
|
3535
|
+
const timer = setTimeout(() => setRevealed(true), Math.max(0, Math.min(200, 200 - (Date.now() - line.liveStartedAt))));
|
|
3536
|
+
return () => clearTimeout(timer);
|
|
3537
|
+
}, [line.final, line.liveStartedAt, revealed]);
|
|
3538
|
+
|
|
3539
|
+
React.useLayoutEffect(() => {
|
|
3540
|
+
if (!expanded || line.final) return undefined;
|
|
3541
|
+
const follow = () => {
|
|
3542
|
+
const viewport = viewportRef.current;
|
|
3543
|
+
if (viewport && line.isLive && followRef.current) viewport.scrollTop = viewport.scrollHeight;
|
|
3544
|
+
};
|
|
3545
|
+
follow();
|
|
3546
|
+
// Images, tables and an opened reasoning disclosure may resize after
|
|
3547
|
+
// the delta render. Only the inner viewport follows their growth.
|
|
3548
|
+
const observer = new ResizeObserver(follow);
|
|
3549
|
+
if (contentRef.current) observer.observe(contentRef.current);
|
|
3550
|
+
return () => observer.disconnect();
|
|
3551
|
+
}, [expanded, line.final, line.isLive, snapshot]);
|
|
3552
|
+
|
|
3553
|
+
// A fast durable answer can beat the preview reveal too. Never flash a
|
|
3554
|
+
// one-frame "Saved" card between its last delta and turn completion.
|
|
3555
|
+
if (!line.final && !revealed) return null;
|
|
3556
|
+
|
|
3557
|
+
return React.createElement("details", {
|
|
3558
|
+
className: `ps-system-notice ps-assistant-preview${line.final ? " is-final" : ""}`,
|
|
3559
|
+
open: expanded,
|
|
3560
|
+
onToggle: (event) => {
|
|
3561
|
+
if (event.target === event.currentTarget && !line.final) setOpen(event.currentTarget.open);
|
|
3562
|
+
},
|
|
3563
|
+
},
|
|
3564
|
+
React.createElement("summary", {
|
|
3565
|
+
className: "ps-system-notice-summary ps-canvas-action-summary",
|
|
3566
|
+
// The completed answer is ordinary transcript, not a collapsible
|
|
3567
|
+
// preview. Keep this same shell/body mounted through promotion.
|
|
3568
|
+
tabIndex: line.final ? -1 : undefined,
|
|
3569
|
+
onClick: (event) => { if (line.final) event.preventDefault(); },
|
|
3570
|
+
}, line.final
|
|
3571
|
+
? React.createElement(Runs, { runs: line.headerRuns, theme })
|
|
3572
|
+
: React.createElement(React.Fragment, null,
|
|
3573
|
+
React.createElement("span", { className: "ps-canvas-action-tag" }, "Agent"),
|
|
3574
|
+
React.createElement("span", { className: "ps-system-notice-summary-text" }, "Message preview"),
|
|
3575
|
+
React.createElement("span", { className: "ps-preview-status" },
|
|
3576
|
+
line.isLive ? (line.body ? "Responding" : "Thinking") : "Saved"))),
|
|
3577
|
+
React.createElement("div", {
|
|
3578
|
+
ref: viewportRef,
|
|
3579
|
+
className: "ps-assistant-preview-viewport",
|
|
3580
|
+
tabIndex: expanded && !line.final ? 0 : undefined,
|
|
3581
|
+
role: line.final ? undefined : "region",
|
|
3582
|
+
"aria-label": line.final ? undefined : "Agent message preview",
|
|
3583
|
+
onScroll: (event) => {
|
|
3584
|
+
if (!line.final) {
|
|
3585
|
+
event.stopPropagation();
|
|
3586
|
+
followRef.current = getScrollDistanceToBottom(event.currentTarget) <= 24;
|
|
3587
|
+
}
|
|
3588
|
+
},
|
|
3589
|
+
onWheel: (event) => {
|
|
3590
|
+
if (!line.final) {
|
|
3591
|
+
event.stopPropagation();
|
|
3592
|
+
if (event.deltaY < 0) followRef.current = false;
|
|
3593
|
+
}
|
|
3594
|
+
},
|
|
3595
|
+
onTouchStart: (event) => {
|
|
3596
|
+
if (!line.final) { event.stopPropagation(); followRef.current = false; }
|
|
3597
|
+
},
|
|
3598
|
+
onTouchMove: (event) => { if (!line.final) event.stopPropagation(); },
|
|
3599
|
+
onTouchEnd: (event) => { if (!line.final) event.stopPropagation(); },
|
|
3600
|
+
onKeyDown: (event) => {
|
|
3601
|
+
if (!line.final && ["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(event.key)) {
|
|
3602
|
+
event.stopPropagation();
|
|
3603
|
+
if (["ArrowUp", "PageUp", "Home"].includes(event.key)) followRef.current = false;
|
|
3604
|
+
}
|
|
3605
|
+
},
|
|
3606
|
+
}, React.createElement("div", { ref: contentRef, className: "ps-assistant-preview-content" },
|
|
3607
|
+
snapshot?.reasoningText ? React.createElement(SystemNoticeLine, {
|
|
3608
|
+
key: "reasoning",
|
|
3609
|
+
line: { text: line.isLive ? "Thinking" : "Thought", body: snapshot.reasoningText, streamReasoning: true },
|
|
3610
|
+
theme,
|
|
3611
|
+
}) : null,
|
|
3612
|
+
snapshot?.body ? React.createElement(PreviewMessageContent, {
|
|
3613
|
+
key: "message", content: snapshot.body, width: snapshot.width, theme, controller,
|
|
3614
|
+
}) : null,
|
|
3615
|
+
snapshot?.truncated && line.isLive
|
|
3616
|
+
? React.createElement("div", { className: "ps-preview-status" }, "Preview paused — the full answer will appear when complete.")
|
|
3617
|
+
: null)));
|
|
3618
|
+
});
|
|
3492
3619
|
// Renders parsed chat blocks; sentinel card blocks recurse through this list
|
|
3493
3620
|
// so structured content (box/markdown tables, code fences) inside a card
|
|
3494
3621
|
// renders exactly the same as it does at top level.
|
|
3495
3622
|
function StructuredBlockList({ blocks, theme, controller = null }) {
|
|
3496
3623
|
return React.createElement(React.Fragment, null,
|
|
3497
3624
|
(blocks || []).map((block, index) => {
|
|
3625
|
+
if (block.type === "assistantPreview") {
|
|
3626
|
+
return React.createElement(AssistantPreviewCard, {
|
|
3627
|
+
key: block.line.previewKey,
|
|
3628
|
+
line: block.line,
|
|
3629
|
+
theme,
|
|
3630
|
+
controller,
|
|
3631
|
+
});
|
|
3632
|
+
}
|
|
3498
3633
|
if (block.type === "imageAttachments") {
|
|
3499
3634
|
return React.createElement(ArtifactImageStrip, {
|
|
3500
3635
|
key: `imageAttachments:${index}`,
|
|
@@ -3538,7 +3673,7 @@ function StructuredBlockList({ blocks, theme, controller = null }) {
|
|
|
3538
3673
|
|
|
3539
3674
|
if (block.type === "card") {
|
|
3540
3675
|
return React.createElement("section", {
|
|
3541
|
-
key: `card:${index}`,
|
|
3676
|
+
key: `card:${block.cardKey || index}`,
|
|
3542
3677
|
className: "ps-chat-card",
|
|
3543
3678
|
style: { "--ps-chat-card-accent": resolveColor(theme, block.borderColor) || "var(--ps-border)" },
|
|
3544
3679
|
},
|
|
@@ -3812,7 +3947,7 @@ function focusRegionForPaneKey(paneKey, override = null) {
|
|
|
3812
3947
|
return PANE_KEY_FOCUS_REGIONS[String(paneKey || "")] || null;
|
|
3813
3948
|
}
|
|
3814
3949
|
|
|
3815
|
-
function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, lines, stickyLines = [], bottomStickyLines = [], scrollOffset = 0, scrollMode = "top", paneKey, controller, className = "", panelClassName = "", topContent = null, bottomContent = null, structuredBlocks = false, stickyBottom = false, renderBody = null, focusRegion = null, panelRef = null }) {
|
|
3950
|
+
function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, lines, stickyLines = [], bottomStickyLines = [], scrollOffset = 0, scrollMode = "top", paneKey, controller, className = "", panelClassName = "", topContent = null, bottomContent = null, structuredBlocks = false, stickyBottom = false, renderBody = null, focusRegion = null, panelRef = null, ariaLive = null }) {
|
|
3816
3951
|
const themeId = useControllerSelector(controller, (state) => state.ui.themeId);
|
|
3817
3952
|
const theme = getTheme(themeId);
|
|
3818
3953
|
const ref = React.useRef(null);
|
|
@@ -3924,7 +4059,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
|
|
|
3924
4059
|
normalizedSticky.map((line, index) => React.createElement(Line, { key: `sticky:${index}`, line, theme })),
|
|
3925
4060
|
)
|
|
3926
4061
|
: null,
|
|
3927
|
-
React.createElement("div", { ref: setPanelNode, className: `ps-scroll-panel ${className}${scrollShadow.down ? " is-scrolled-down" : ""}${scrollShadow.up ? " is-scrolled-up" : ""}`.trim(), "data-session-scroll": focusRegion === "sessions" ? "1" : undefined, onScroll: handleBodyScroll, onMouseDown: claimFocus, onTouchStart, onWheel, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd },
|
|
4062
|
+
React.createElement("div", { ref: setPanelNode, className: `ps-scroll-panel ${className}${scrollShadow.down ? " is-scrolled-down" : ""}${scrollShadow.up ? " is-scrolled-up" : ""}`.trim(), "data-session-scroll": focusRegion === "sessions" ? "1" : undefined, "aria-live": ariaLive || undefined, "aria-atomic": ariaLive ? "false" : undefined, onScroll: handleBodyScroll, onMouseDown: claimFocus, onTouchStart, onWheel, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd },
|
|
3928
4063
|
typeof renderBody === "function"
|
|
3929
4064
|
? renderBody(normalizedLines, theme)
|
|
3930
4065
|
: structuredBlocks
|
|
@@ -4422,13 +4557,48 @@ function StatusDot({ color, node = false }) {
|
|
|
4422
4557
|
* person cannot look like two different people in two panes.
|
|
4423
4558
|
*/
|
|
4424
4559
|
function OwnerAvatar({ badge, size = "sm" }) {
|
|
4560
|
+
// The full name on hover, drawn the way the toolbar tooltips are (a
|
|
4561
|
+
// fixed-position label portalled to the body) rather than the browser's
|
|
4562
|
+
// native title: that one is slow to appear, easy to miss, and styled by
|
|
4563
|
+
// the OS rather than the app.
|
|
4564
|
+
const [tip, setTip] = React.useState(null);
|
|
4565
|
+
const ref = React.useRef(null);
|
|
4566
|
+
const tipRef = React.useRef(null);
|
|
4567
|
+
React.useLayoutEffect(() => {
|
|
4568
|
+
if (!tip || !tipRef.current || typeof window === "undefined") return;
|
|
4569
|
+
const half = tipRef.current.offsetWidth / 2;
|
|
4570
|
+
const margin = 6;
|
|
4571
|
+
tipRef.current.style.left = `${Math.max(half + margin, Math.min(tip.x, window.innerWidth - half - margin))}px`;
|
|
4572
|
+
}, [tip]);
|
|
4425
4573
|
if (!badge) return null;
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4574
|
+
const label = badge.isMine ? `${badge.name} (you)` : badge.name;
|
|
4575
|
+
const show = () => {
|
|
4576
|
+
const el = ref.current;
|
|
4577
|
+
if (!el || typeof window === "undefined") return;
|
|
4578
|
+
const r = el.getBoundingClientRect();
|
|
4579
|
+
const below = r.bottom + 44 < window.innerHeight;
|
|
4580
|
+
setTip({ x: r.left + r.width / 2, y: below ? r.bottom + 6 : r.top - 6, placement: below ? "below" : "above" });
|
|
4581
|
+
};
|
|
4582
|
+
const hide = () => setTip(null);
|
|
4583
|
+
return React.createElement(React.Fragment, null,
|
|
4584
|
+
React.createElement("span", {
|
|
4585
|
+
ref,
|
|
4586
|
+
className: `ps-owner-avatar is-${size}${badge.isMine ? " is-mine" : ""}`,
|
|
4587
|
+
"data-owner-hue": String(badge.hue ?? 0),
|
|
4588
|
+
"aria-label": label,
|
|
4589
|
+
onMouseEnter: show,
|
|
4590
|
+
onMouseLeave: hide,
|
|
4591
|
+
onFocus: show,
|
|
4592
|
+
onBlur: hide,
|
|
4593
|
+
}, badge.initials),
|
|
4594
|
+
tip && typeof document !== "undefined"
|
|
4595
|
+
? createPortal(React.createElement("span", {
|
|
4596
|
+
ref: tipRef,
|
|
4597
|
+
className: `ps-icon-tooltip is-${tip.placement}`,
|
|
4598
|
+
role: "tooltip",
|
|
4599
|
+
style: { top: `${tip.y}px` },
|
|
4600
|
+
}, label), document.body)
|
|
4601
|
+
: null);
|
|
4432
4602
|
}
|
|
4433
4603
|
|
|
4434
4604
|
/** Drop the TUI-only text chip; the portal draws OwnerAvatar instead. */
|
|
@@ -4550,17 +4720,14 @@ function useStableValue(value) {
|
|
|
4550
4720
|
* vote on the diagonal. Vertical stays the default: sideways has to be asked
|
|
4551
4721
|
* for, by a drag that beats vertical by PAN_HORIZONTAL_RATIO.
|
|
4552
4722
|
*
|
|
4553
|
-
*
|
|
4554
|
-
*
|
|
4555
|
-
*
|
|
4723
|
+
* The list tracks the finger and stops on release: no inertial fling. Chat
|
|
4724
|
+
* and canvas scrolling are independent and keep their native behavior.
|
|
4725
|
+
* CSS keeps `touch-action: pan-y` as a floor: if these
|
|
4556
4726
|
* listeners ever fail to attach, the list still scrolls vertically the native
|
|
4557
4727
|
* way rather than becoming inert.
|
|
4558
4728
|
*/
|
|
4559
4729
|
const PAN_COMMIT_PX = 10; // ignore the first few px — every swipe starts noisy
|
|
4560
4730
|
const PAN_HORIZONTAL_RATIO = 1.6; // |dx| must beat |dy| by this much to go sideways
|
|
4561
|
-
const FLING_MIN_VELOCITY = 0.05; // px/ms below which a lift is a stop, not a flick
|
|
4562
|
-
const FLING_FRICTION = 0.95; // per frame; ~0.35s of glide from a brisk flick
|
|
4563
|
-
const FLING_MAX_VELOCITY = 4; // px/ms ceiling, so a fast flick stays catchable
|
|
4564
4731
|
|
|
4565
4732
|
/**
|
|
4566
4733
|
* Which axis does this gesture own? Pure, and exported so the rule can be
|
|
@@ -4594,38 +4761,20 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
4594
4761
|
let startY = 0;
|
|
4595
4762
|
let lastX = 0;
|
|
4596
4763
|
let lastY = 0;
|
|
4597
|
-
let lastT = 0;
|
|
4598
|
-
let velocity = 0; // px/ms along the committed axis, sign follows the finger
|
|
4599
4764
|
let axis = null; // null = undecided, "x" | "y" = committed, "done" = not ours
|
|
4600
|
-
let flingFrame = null;
|
|
4601
4765
|
let swallowClick = false;
|
|
4602
|
-
|
|
4603
|
-
const stopFling = () => {
|
|
4604
|
-
if (flingFrame !== null) {
|
|
4605
|
-
cancelAnimationFrame(flingFrame);
|
|
4606
|
-
flingFrame = null;
|
|
4607
|
-
}
|
|
4608
|
-
};
|
|
4609
|
-
|
|
4610
|
-
const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
|
|
4611
4766
|
const canScroll = (which) => (which === "x"
|
|
4612
4767
|
? el.scrollWidth > el.clientWidth + 1
|
|
4613
4768
|
: el.scrollHeight > el.clientHeight + 1);
|
|
4614
4769
|
|
|
4615
4770
|
const onTouchStart = (event) => {
|
|
4616
|
-
|
|
4617
|
-
// "open whatever is passing under my finger". Native momentum never
|
|
4618
|
-
// opened a row on the arresting tap; ours must not either.
|
|
4619
|
-
swallowClick = flingFrame !== null;
|
|
4620
|
-
stopFling();
|
|
4771
|
+
swallowClick = false;
|
|
4621
4772
|
// targetTouches, not touches: a thumb resting elsewhere on the page
|
|
4622
4773
|
// is not part of this gesture. Two fingers HERE is a pinch.
|
|
4623
4774
|
if (event.targetTouches.length !== 1) { axis = "done"; return; }
|
|
4624
4775
|
const touch = event.targetTouches[0];
|
|
4625
4776
|
startX = lastX = touch.clientX;
|
|
4626
4777
|
startY = lastY = touch.clientY;
|
|
4627
|
-
lastT = now();
|
|
4628
|
-
velocity = 0;
|
|
4629
4778
|
axis = null;
|
|
4630
4779
|
};
|
|
4631
4780
|
|
|
@@ -4668,7 +4817,8 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
4668
4817
|
// Measure from here, so the slop we ignored is not also applied.
|
|
4669
4818
|
lastX = touch.clientX;
|
|
4670
4819
|
lastY = touch.clientY;
|
|
4671
|
-
|
|
4820
|
+
// A drag must not select whichever row ends up under the thumb.
|
|
4821
|
+
swallowClick = true;
|
|
4672
4822
|
}
|
|
4673
4823
|
|
|
4674
4824
|
// Ours: stop the browser doing it as well. A repeat of the
|
|
@@ -4678,39 +4828,12 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
4678
4828
|
|
|
4679
4829
|
const stepX = touch.clientX - lastX;
|
|
4680
4830
|
const stepY = touch.clientY - lastY;
|
|
4681
|
-
const t = now();
|
|
4682
|
-
const elapsed = Math.max(1, t - lastT);
|
|
4683
|
-
const step = axis === "x" ? stepX : stepY;
|
|
4684
4831
|
|
|
4685
4832
|
if (axis === "x") el.scrollLeft -= stepX;
|
|
4686
4833
|
else el.scrollTop -= stepY;
|
|
4687
4834
|
|
|
4688
|
-
// Exponential smoothing: one jittery sample should not decide the
|
|
4689
|
-
// whole flick, and a finger that stops before lifting should decay
|
|
4690
|
-
// to zero rather than fling on a stale reading.
|
|
4691
|
-
const sample = Math.max(-FLING_MAX_VELOCITY, Math.min(FLING_MAX_VELOCITY, step / elapsed));
|
|
4692
|
-
velocity = velocity * 0.7 + sample * 0.3;
|
|
4693
|
-
|
|
4694
4835
|
lastX = touch.clientX;
|
|
4695
4836
|
lastY = touch.clientY;
|
|
4696
|
-
lastT = t;
|
|
4697
|
-
};
|
|
4698
|
-
|
|
4699
|
-
const releaseFling = (committed) => {
|
|
4700
|
-
if (Math.abs(velocity) < FLING_MIN_VELOCITY) return;
|
|
4701
|
-
let v = velocity;
|
|
4702
|
-
const tick = () => {
|
|
4703
|
-
v *= FLING_FRICTION;
|
|
4704
|
-
if (Math.abs(v) < FLING_MIN_VELOCITY) { flingFrame = null; return; }
|
|
4705
|
-
const before = committed === "x" ? el.scrollLeft : el.scrollTop;
|
|
4706
|
-
if (committed === "x") el.scrollLeft -= v * 16;
|
|
4707
|
-
else el.scrollTop -= v * 16;
|
|
4708
|
-
const after = committed === "x" ? el.scrollLeft : el.scrollTop;
|
|
4709
|
-
// Hit an end stop — no point animating against it.
|
|
4710
|
-
if (after === before) { flingFrame = null; return; }
|
|
4711
|
-
flingFrame = requestAnimationFrame(tick);
|
|
4712
|
-
};
|
|
4713
|
-
flingFrame = requestAnimationFrame(tick);
|
|
4714
4837
|
};
|
|
4715
4838
|
|
|
4716
4839
|
const onTouchEnd = (event) => {
|
|
@@ -4718,16 +4841,13 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
4718
4841
|
// its start point belongs to a different gesture, and the commit
|
|
4719
4842
|
// would land on a large bogus delta. Stay out until all lift.
|
|
4720
4843
|
if (event.targetTouches.length > 0) { axis = "done"; return; }
|
|
4721
|
-
const committed = axis;
|
|
4722
4844
|
axis = null;
|
|
4723
|
-
if (committed === "x" || committed === "y") releaseFling(committed);
|
|
4724
|
-
velocity = 0;
|
|
4725
4845
|
};
|
|
4726
4846
|
|
|
4727
|
-
const onTouchCancel = () => { axis =
|
|
4847
|
+
const onTouchCancel = () => { axis = "done"; };
|
|
4728
4848
|
|
|
4729
4849
|
const onClickCapture = (event) => {
|
|
4730
|
-
if (!swallowClick) return;
|
|
4850
|
+
if (!swallowClick || event.detail === 0) return;
|
|
4731
4851
|
swallowClick = false;
|
|
4732
4852
|
event.preventDefault();
|
|
4733
4853
|
event.stopPropagation();
|
|
@@ -4739,7 +4859,6 @@ function useAxisLockedPan(ref, enabled = true) {
|
|
|
4739
4859
|
el.addEventListener("touchcancel", onTouchCancel, { passive: true });
|
|
4740
4860
|
el.addEventListener("click", onClickCapture, true);
|
|
4741
4861
|
return () => {
|
|
4742
|
-
stopFling();
|
|
4743
4862
|
el.removeEventListener("touchstart", onTouchStart);
|
|
4744
4863
|
el.removeEventListener("touchmove", onTouchMove);
|
|
4745
4864
|
el.removeEventListener("touchend", onTouchEnd);
|
|
@@ -4886,6 +5005,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
4886
5005
|
const authPrincipal = viewState.auth?.principal || null;
|
|
4887
5006
|
const viewerRole = viewState.auth?.authorization?.role;
|
|
4888
5007
|
const isAdminViewer = viewerRole === "admin" || viewerRole === "anonymous";
|
|
5008
|
+
const resourceAdminViewer = isAdminViewer && viewState.auth?.adminScope !== "cluster";
|
|
4889
5009
|
const ownsActiveSession = Boolean(
|
|
4890
5010
|
activeSession?.owner
|
|
4891
5011
|
&& authPrincipal
|
|
@@ -4906,7 +5026,7 @@ function SessionPane({ controller, actions = null, panelClassName = "", structur
|
|
|
4906
5026
|
activeSession && !activeSession.isGroup
|
|
4907
5027
|
&& (activeSession.isSystem
|
|
4908
5028
|
? isAdminViewer
|
|
4909
|
-
: (
|
|
5029
|
+
: (resourceAdminViewer || ownsActiveSession)),
|
|
4910
5030
|
);
|
|
4911
5031
|
const selectedCount = Array.isArray(viewState.selectedIds) ? viewState.selectedIds.length : 0;
|
|
4912
5032
|
const isBulkSelection = selectedCount > 1;
|
|
@@ -6398,6 +6518,7 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
|
|
|
6398
6518
|
scrollOffset: viewState.scroll,
|
|
6399
6519
|
scrollMode: "bottom",
|
|
6400
6520
|
paneKey: "chat",
|
|
6521
|
+
ariaLive: "polite",
|
|
6401
6522
|
className: "is-wrapped",
|
|
6402
6523
|
panelClassName: "ps-chat-panel",
|
|
6403
6524
|
panelRef,
|
|
@@ -6877,8 +6998,19 @@ function CanvasShareDialog({ controller, sessionId, slot, onClose }) {
|
|
|
6877
6998
|
// the policy, so the control is disabled for everyone else rather than
|
|
6878
6999
|
// letting them pick a value and then showing a refusal.
|
|
6879
7000
|
const [kvMe, setKvMe] = React.useState(null);
|
|
7001
|
+
// Off by default: every link this dialog has ever produced opened the full
|
|
7002
|
+
// workspace, and a share option that silently changed that for existing
|
|
7003
|
+
// habits would be a surprise. Checked, the link carries `show_chrome=false`
|
|
7004
|
+
// and lands on the canvas alone.
|
|
7005
|
+
//
|
|
7006
|
+
// Presentation only. It hides the portal header; it does NOT make the
|
|
7007
|
+
// canvas read-only, because a query parameter is not a permission — the
|
|
7008
|
+
// recipient can simply delete it. What a viewer may change inside the app
|
|
7009
|
+
// stays governed by the KV policy below, and the strip is worded so it
|
|
7010
|
+
// never implies otherwise.
|
|
7011
|
+
const [hideChrome, setHideChrome] = React.useState(false);
|
|
6880
7012
|
const transport = controller.transport;
|
|
6881
|
-
const sessionLink = `${window.location.origin}/?session=${encodeURIComponent(sessionId)}&view=canvas&slot=${slot}&max=1`;
|
|
7013
|
+
const sessionLink = `${window.location.origin}/?session=${encodeURIComponent(sessionId)}&view=canvas&slot=${slot}&max=1${hideChrome ? "&show_chrome=false" : ""}`;
|
|
6882
7014
|
// The token variants all resolve the same hashed row: one token, N doors,
|
|
6883
7015
|
// single-switch revocation whichever door the bearer uses.
|
|
6884
7016
|
const linkRows = (url, keyPrefix) => React.createElement(MultiOriginLinkRows, {
|
|
@@ -6933,7 +7065,7 @@ function CanvasShareDialog({ controller, sessionId, slot, onClose }) {
|
|
|
6933
7065
|
.finally(() => setBusy(false));
|
|
6934
7066
|
};
|
|
6935
7067
|
|
|
6936
|
-
|
|
7068
|
+
const overlay = React.createElement("div", { className: "ps-canvas-share-overlay", onClick: onClose },
|
|
6937
7069
|
React.createElement("div", { className: "ps-canvas-share-dialog", onClick: (e) => e.stopPropagation(), role: "dialog", "aria-label": "Share canvas" },
|
|
6938
7070
|
React.createElement("div", { className: "ps-canvas-share-title" },
|
|
6939
7071
|
"Share this canvas",
|
|
@@ -6965,6 +7097,13 @@ function CanvasShareDialog({ controller, sessionId, slot, onClose }) {
|
|
|
6965
7097
|
},
|
|
6966
7098
|
React.createElement("div", { className: "ps-canvas-share-sub" }, "Opens the portal signed in, canvas full screen. Session visibility rules apply."),
|
|
6967
7099
|
linkRows(sessionLink, "session"),
|
|
7100
|
+
React.createElement("label", { className: "ps-canvas-share-check" },
|
|
7101
|
+
React.createElement("input", {
|
|
7102
|
+
type: "checkbox",
|
|
7103
|
+
checked: hideChrome,
|
|
7104
|
+
onChange: (event) => setHideChrome(event.target.checked),
|
|
7105
|
+
}),
|
|
7106
|
+
"Hide page chrome"),
|
|
6968
7107
|
// Who may WRITE the app's shared state. Stated in words:
|
|
6969
7108
|
// "readers" reaches everyone the session is read-shared
|
|
6970
7109
|
// with, which a dialog that only says "link" hides.
|
|
@@ -7016,6 +7155,16 @@ function CanvasShareDialog({ controller, sessionId, slot, onClose }) {
|
|
|
7016
7155
|
? React.createElement("button", { type: "button", className: "ps-mini-button", disabled: busy, onClick: remove }, "Remove link")
|
|
7017
7156
|
: null))),
|
|
7018
7157
|
error ? React.createElement("div", { className: "ps-canvas-share-error" }, error) : null));
|
|
7158
|
+
|
|
7159
|
+
// CanvasPane lives inside `.ps-canvas-layer`, which is a z-indexed
|
|
7160
|
+
// stacking context below the pane resizers. A fixed overlay left inside
|
|
7161
|
+
// that tree cannot out-rank a sibling resizer regardless of its own
|
|
7162
|
+
// z-index, so the divider used to paint straight through this dialog.
|
|
7163
|
+
// Put viewport chrome at the document root; React keeps context and event
|
|
7164
|
+
// bubbling intact across the portal.
|
|
7165
|
+
return typeof document !== "undefined" && document.body
|
|
7166
|
+
? createPortal(overlay, document.body)
|
|
7167
|
+
: overlay;
|
|
7019
7168
|
}
|
|
7020
7169
|
|
|
7021
7170
|
/**
|
|
@@ -12888,7 +13037,8 @@ function AdminConsolePanel({ controller, mobile = false }) {
|
|
|
12888
13037
|
// the way out is the toolbar's Mode cluster (Workspace) \u2014 a \u2715 here was a
|
|
12889
13038
|
// second, unlabelled way to do the same thing.
|
|
12890
13039
|
const header = React.createElement("header", { className: "ps-admin-console__header" },
|
|
12891
|
-
React.createElement("h2", null, view.isAdmin ? "Admin Console" : "Settings")
|
|
13040
|
+
React.createElement("h2", null, view.isAdmin ? "Admin Console" : "Settings"),
|
|
13041
|
+
view.isAdmin ? React.createElement("small", null, view.adminPolicyLabel) : null);
|
|
12892
13042
|
|
|
12893
13043
|
const providerSection = React.createElement(AdminModelProvidersSection, {
|
|
12894
13044
|
controller,
|