pilotswarm-web 0.1.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/README.md +144 -0
- package/auth/authz/engine.js +139 -0
- package/auth/config.js +110 -0
- package/auth/index.js +153 -0
- package/auth/normalize/entra.js +22 -0
- package/auth/providers/entra.js +76 -0
- package/auth/providers/none.js +24 -0
- package/auth.js +10 -0
- package/bin/serve.js +53 -0
- package/config.js +20 -0
- package/dist/app.js +469 -0
- package/dist/assets/index-BSVg-lGb.css +1 -0
- package/dist/assets/index-BXD5YP7A.js +24 -0
- package/dist/assets/msal-CytV9RFv.js +7 -0
- package/dist/assets/pilotswarm-WX3NED6m.js +40 -0
- package/dist/assets/react-jg0oazEi.js +1 -0
- package/dist/index.html +16 -0
- package/node_modules/pilotswarm-ui-core/README.md +6 -0
- package/node_modules/pilotswarm-ui-core/package.json +32 -0
- package/node_modules/pilotswarm-ui-core/src/commands.js +72 -0
- package/node_modules/pilotswarm-ui-core/src/context-usage.js +212 -0
- package/node_modules/pilotswarm-ui-core/src/controller.js +3613 -0
- package/node_modules/pilotswarm-ui-core/src/formatting.js +872 -0
- package/node_modules/pilotswarm-ui-core/src/history.js +571 -0
- package/node_modules/pilotswarm-ui-core/src/index.js +13 -0
- package/node_modules/pilotswarm-ui-core/src/layout.js +196 -0
- package/node_modules/pilotswarm-ui-core/src/reducer.js +1027 -0
- package/node_modules/pilotswarm-ui-core/src/selectors.js +2786 -0
- package/node_modules/pilotswarm-ui-core/src/session-tree.js +109 -0
- package/node_modules/pilotswarm-ui-core/src/state.js +80 -0
- package/node_modules/pilotswarm-ui-core/src/store.js +23 -0
- package/node_modules/pilotswarm-ui-core/src/system-titles.js +24 -0
- package/node_modules/pilotswarm-ui-core/src/themes/catppuccin-mocha.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/cobalt2.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/dark-high-contrast.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/dracula.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/github-dark.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/gruvbox-dark.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/hacker-x-matrix.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/hacker-x-orion-prime.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/helpers.js +77 -0
- package/node_modules/pilotswarm-ui-core/src/themes/index.js +42 -0
- package/node_modules/pilotswarm-ui-core/src/themes/noctis-viola.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/noctis.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/nord.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/solarized-dark.js +56 -0
- package/node_modules/pilotswarm-ui-core/src/themes/tokyo-night.js +56 -0
- package/node_modules/pilotswarm-ui-react/README.md +5 -0
- package/node_modules/pilotswarm-ui-react/package.json +36 -0
- package/node_modules/pilotswarm-ui-react/src/components.js +1316 -0
- package/node_modules/pilotswarm-ui-react/src/index.js +4 -0
- package/node_modules/pilotswarm-ui-react/src/platform.js +15 -0
- package/node_modules/pilotswarm-ui-react/src/use-controller-state.js +38 -0
- package/node_modules/pilotswarm-ui-react/src/web-app.js +2661 -0
- package/package.json +64 -0
- package/runtime.js +146 -0
- package/server.js +311 -0
|
@@ -0,0 +1,1316 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
applyActiveHighlightRuns,
|
|
4
|
+
computeLegacyLayout,
|
|
5
|
+
getPromptInputRows,
|
|
6
|
+
selectActiveSession,
|
|
7
|
+
selectChatPaneChrome,
|
|
8
|
+
selectChatLines,
|
|
9
|
+
selectActivityPane,
|
|
10
|
+
selectArtifactUploadModal,
|
|
11
|
+
selectArtifactPickerModal,
|
|
12
|
+
selectFilesFilterModal,
|
|
13
|
+
selectFilesView,
|
|
14
|
+
selectHistoryFormatModal,
|
|
15
|
+
INSPECTOR_TABS,
|
|
16
|
+
selectInspector,
|
|
17
|
+
selectLogFilterModal,
|
|
18
|
+
selectModelPickerModal,
|
|
19
|
+
selectRenameSessionModal,
|
|
20
|
+
selectSessionAgentPickerModal,
|
|
21
|
+
selectStatusBar,
|
|
22
|
+
selectThemePickerModal,
|
|
23
|
+
selectVisibleSessionRows,
|
|
24
|
+
} from "pilotswarm-ui-core";
|
|
25
|
+
import { useUiPlatform } from "./platform.js";
|
|
26
|
+
import { useControllerSelector } from "./use-controller-state.js";
|
|
27
|
+
|
|
28
|
+
const PANE_GAP_X = 0;
|
|
29
|
+
const PANE_GAP_Y = 0;
|
|
30
|
+
|
|
31
|
+
function shallowEqualObject(left, right) {
|
|
32
|
+
if (Object.is(left, right)) return true;
|
|
33
|
+
if (!left || !right || typeof left !== "object" || typeof right !== "object") return false;
|
|
34
|
+
const leftKeys = Object.keys(left);
|
|
35
|
+
const rightKeys = Object.keys(right);
|
|
36
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
37
|
+
for (const key of leftKeys) {
|
|
38
|
+
if (!Object.prototype.hasOwnProperty.call(right, key)) return false;
|
|
39
|
+
if (!Object.is(left[key], right[key])) return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function shallowEqualArray(left, right, itemEqual = Object.is) {
|
|
45
|
+
if (Object.is(left, right)) return true;
|
|
46
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
47
|
+
if (left.length !== right.length) return false;
|
|
48
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
49
|
+
if (!itemEqual(left[index], right[index])) return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function shallowEqualChatChrome(left, right) {
|
|
55
|
+
if (Object.is(left, right)) return true;
|
|
56
|
+
if (!left || !right) return false;
|
|
57
|
+
return Object.is(left.color, right.color)
|
|
58
|
+
&& shallowEqualArray(left.title, right.title, shallowEqualObject);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readProcessRssBytes() {
|
|
62
|
+
try {
|
|
63
|
+
const runtimeProcess = globalThis?.process;
|
|
64
|
+
const memoryUsage = runtimeProcess?.memoryUsage;
|
|
65
|
+
if (typeof memoryUsage?.rss === "function") {
|
|
66
|
+
const rss = Number(memoryUsage.rss());
|
|
67
|
+
return Number.isFinite(rss) && rss > 0 ? rss : null;
|
|
68
|
+
}
|
|
69
|
+
if (typeof memoryUsage === "function") {
|
|
70
|
+
const rss = Number(memoryUsage.call(runtimeProcess)?.rss);
|
|
71
|
+
return Number.isFinite(rss) && rss > 0 ? rss : null;
|
|
72
|
+
}
|
|
73
|
+
} catch {}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatProcessRssTitleRuns(rssBytes) {
|
|
78
|
+
if (!Number.isFinite(rssBytes) || rssBytes <= 0) return null;
|
|
79
|
+
const rssMb = Math.round(rssBytes / (1024 * 1024));
|
|
80
|
+
return [
|
|
81
|
+
{ text: "rss ", color: "gray" },
|
|
82
|
+
{ text: `${rssMb}M`, color: "white", bold: true },
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function useProcessRssTitleRuns(sampleIntervalMs = 2000) {
|
|
87
|
+
const [rssBytes, setRssBytes] = React.useState(() => readProcessRssBytes());
|
|
88
|
+
|
|
89
|
+
React.useEffect(() => {
|
|
90
|
+
if (readProcessRssBytes() == null) return undefined;
|
|
91
|
+
const update = () => {
|
|
92
|
+
const nextRssBytes = readProcessRssBytes();
|
|
93
|
+
if (nextRssBytes == null) return;
|
|
94
|
+
setRssBytes((currentRssBytes) => (currentRssBytes === nextRssBytes ? currentRssBytes : nextRssBytes));
|
|
95
|
+
};
|
|
96
|
+
update();
|
|
97
|
+
const timer = setInterval(update, sampleIntervalMs);
|
|
98
|
+
return () => clearInterval(timer);
|
|
99
|
+
}, [sampleIntervalMs]);
|
|
100
|
+
|
|
101
|
+
return React.useMemo(() => formatProcessRssTitleRuns(rssBytes), [rssBytes]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildSingleSessionMap(sessionId, session) {
|
|
105
|
+
return sessionId && session ? { [sessionId]: session } : {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function normalizeInspectorLine(line) {
|
|
109
|
+
return typeof line === "string"
|
|
110
|
+
? { text: line, color: "white" }
|
|
111
|
+
: line;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function renderInspectorPanel(platform, inspector, meta, width, height, frame) {
|
|
115
|
+
const tabLine = (inspector.tabs || INSPECTOR_TABS).map((tab) => ({
|
|
116
|
+
text: tab === inspector.activeTab ? `[${tab}] ` : `${tab} `,
|
|
117
|
+
color: tab === inspector.activeTab ? "magenta" : "gray",
|
|
118
|
+
bold: tab === inspector.activeTab,
|
|
119
|
+
}));
|
|
120
|
+
const normalizedLines = (inspector.lines || []).map(normalizeInspectorLine);
|
|
121
|
+
const normalizedStickyLines = (inspector.stickyLines || []).map(normalizeInspectorLine);
|
|
122
|
+
const stickyLines = inspector.activeTab === "sequence"
|
|
123
|
+
? [tabLine, ...normalizedStickyLines]
|
|
124
|
+
: [];
|
|
125
|
+
const lines = inspector.activeTab === "sequence"
|
|
126
|
+
? normalizedLines
|
|
127
|
+
: [tabLine, ...normalizedLines];
|
|
128
|
+
|
|
129
|
+
return React.createElement(platform.Panel, {
|
|
130
|
+
title: inspector.title,
|
|
131
|
+
color: "magenta",
|
|
132
|
+
focused: meta.focused,
|
|
133
|
+
width,
|
|
134
|
+
height,
|
|
135
|
+
stickyLines,
|
|
136
|
+
marginBottom: PANE_GAP_Y,
|
|
137
|
+
lines,
|
|
138
|
+
scrollOffset: meta.inspectorScroll,
|
|
139
|
+
scrollMode: inspector.activeTab === "logs" || inspector.activeTab === "sequence" ? "bottom" : "top",
|
|
140
|
+
paneId: "inspector",
|
|
141
|
+
paneLabel: inspector.activeTab === "sequence" ? "Sequence" : "Inspector",
|
|
142
|
+
frame,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function fitText(value, maxWidth) {
|
|
147
|
+
const text = String(value || "");
|
|
148
|
+
if (maxWidth <= 0) return "";
|
|
149
|
+
if (text.length <= maxWidth) return text;
|
|
150
|
+
if (maxWidth === 1) return text.slice(0, 1);
|
|
151
|
+
return `${text.slice(0, maxWidth - 1)}...`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildWorkspacePaneFrames(layout) {
|
|
155
|
+
const leftX = 0;
|
|
156
|
+
const rightX = layout.leftHidden ? 0 : layout.leftWidth + (layout.rightHidden ? 0 : PANE_GAP_X);
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
sessions: layout.leftHidden ? null : {
|
|
160
|
+
x: leftX,
|
|
161
|
+
y: 0,
|
|
162
|
+
width: layout.leftWidth,
|
|
163
|
+
height: layout.sessionPaneHeight,
|
|
164
|
+
},
|
|
165
|
+
chat: layout.leftHidden ? null : {
|
|
166
|
+
x: leftX,
|
|
167
|
+
y: layout.sessionPaneHeight + PANE_GAP_Y,
|
|
168
|
+
width: layout.leftWidth,
|
|
169
|
+
height: layout.chatPaneHeight,
|
|
170
|
+
},
|
|
171
|
+
inspector: layout.rightHidden ? null : {
|
|
172
|
+
x: rightX,
|
|
173
|
+
y: 0,
|
|
174
|
+
width: layout.rightWidth,
|
|
175
|
+
height: layout.inspectorPaneHeight,
|
|
176
|
+
},
|
|
177
|
+
activity: layout.rightHidden ? null : {
|
|
178
|
+
x: rightX,
|
|
179
|
+
y: layout.inspectorPaneHeight + PANE_GAP_Y,
|
|
180
|
+
width: layout.rightWidth,
|
|
181
|
+
height: layout.activityPaneHeight,
|
|
182
|
+
},
|
|
183
|
+
fullscreenFiles: {
|
|
184
|
+
x: 0,
|
|
185
|
+
y: 0,
|
|
186
|
+
width: layout.totalWidth,
|
|
187
|
+
height: layout.bodyHeight,
|
|
188
|
+
},
|
|
189
|
+
fullscreenPane: {
|
|
190
|
+
x: 0,
|
|
191
|
+
y: 0,
|
|
192
|
+
width: layout.totalWidth,
|
|
193
|
+
height: layout.bodyHeight,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const SessionList = React.memo(function SessionList({ controller, maxRows, width, height, frame }) {
|
|
199
|
+
const platform = useUiPlatform();
|
|
200
|
+
const rssTitleRuns = useProcessRssTitleRuns();
|
|
201
|
+
const sessionView = useControllerSelector(controller, (state) => ({
|
|
202
|
+
sessions: state.sessions,
|
|
203
|
+
mode: state.connection?.mode || "local",
|
|
204
|
+
brandingTitle: state.branding?.title || "PilotSwarm",
|
|
205
|
+
focused: state.ui.focusRegion === "sessions",
|
|
206
|
+
}), shallowEqualObject);
|
|
207
|
+
const selectorState = React.useMemo(() => ({
|
|
208
|
+
sessions: sessionView.sessions,
|
|
209
|
+
connection: { mode: sessionView.mode },
|
|
210
|
+
branding: { title: sessionView.brandingTitle },
|
|
211
|
+
}), [sessionView.brandingTitle, sessionView.mode, sessionView.sessions]);
|
|
212
|
+
const rows = React.useMemo(
|
|
213
|
+
() => selectVisibleSessionRows(selectorState, maxRows),
|
|
214
|
+
[selectorState, maxRows],
|
|
215
|
+
);
|
|
216
|
+
const lines = React.useMemo(() => (
|
|
217
|
+
rows.length === 0
|
|
218
|
+
? [{ text: "No sessions yet. Press n to create one.", color: "gray" }]
|
|
219
|
+
: rows.map((row) => (row.active
|
|
220
|
+
? applyActiveHighlightRuns(row.runs, { preserveColors: true })
|
|
221
|
+
: row.runs))
|
|
222
|
+
), [rows]);
|
|
223
|
+
|
|
224
|
+
return React.createElement(platform.Panel, {
|
|
225
|
+
title: "Sessions",
|
|
226
|
+
titleRight: rssTitleRuns,
|
|
227
|
+
color: "yellow",
|
|
228
|
+
focused: sessionView.focused,
|
|
229
|
+
width,
|
|
230
|
+
height,
|
|
231
|
+
marginBottom: PANE_GAP_Y,
|
|
232
|
+
lines,
|
|
233
|
+
paneId: "sessions",
|
|
234
|
+
paneLabel: "Sessions",
|
|
235
|
+
frame,
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const ChatPane = React.memo(function ChatPane({ controller, width, height, frame }) {
|
|
240
|
+
const platform = useUiPlatform();
|
|
241
|
+
const chrome = useControllerSelector(controller, selectChatPaneChrome, shallowEqualChatChrome);
|
|
242
|
+
const chatView = useControllerSelector(controller, (state) => {
|
|
243
|
+
const activeSessionId = state.sessions.activeSessionId;
|
|
244
|
+
return {
|
|
245
|
+
activeSessionId,
|
|
246
|
+
activeSession: activeSessionId ? state.sessions.byId[activeSessionId] || null : null,
|
|
247
|
+
activeHistory: activeSessionId ? state.history.bySessionId.get(activeSessionId) || null : null,
|
|
248
|
+
branding: state.branding,
|
|
249
|
+
connectionError: state.connection.error,
|
|
250
|
+
connectionMode: state.connection.mode,
|
|
251
|
+
chatScroll: state.ui.scroll.chat,
|
|
252
|
+
focused: state.ui.focusRegion === "chat",
|
|
253
|
+
};
|
|
254
|
+
}, shallowEqualObject);
|
|
255
|
+
const contentWidth = Math.max(20, width - 4);
|
|
256
|
+
const selectorState = React.useMemo(() => {
|
|
257
|
+
const historyMap = new Map();
|
|
258
|
+
if (chatView.activeSessionId && chatView.activeHistory) {
|
|
259
|
+
historyMap.set(chatView.activeSessionId, chatView.activeHistory);
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
branding: chatView.branding,
|
|
263
|
+
connection: {
|
|
264
|
+
error: chatView.connectionError,
|
|
265
|
+
mode: chatView.connectionMode,
|
|
266
|
+
},
|
|
267
|
+
sessions: {
|
|
268
|
+
activeSessionId: chatView.activeSessionId,
|
|
269
|
+
byId: buildSingleSessionMap(chatView.activeSessionId, chatView.activeSession),
|
|
270
|
+
},
|
|
271
|
+
history: {
|
|
272
|
+
bySessionId: historyMap,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}, [
|
|
276
|
+
chatView.activeHistory,
|
|
277
|
+
chatView.activeSessionId,
|
|
278
|
+
chatView.activeSession,
|
|
279
|
+
chatView.branding,
|
|
280
|
+
chatView.connectionError,
|
|
281
|
+
chatView.connectionMode,
|
|
282
|
+
]);
|
|
283
|
+
const startupError = !chatView.activeSessionId && chatView.connectionError;
|
|
284
|
+
const elements = React.useMemo(() => (startupError
|
|
285
|
+
? [
|
|
286
|
+
{ kind: "markup", value: chatView.branding.splash },
|
|
287
|
+
{ text: "", color: "gray" },
|
|
288
|
+
{ text: "Startup failed", color: "red", bold: true },
|
|
289
|
+
{ text: chatView.connectionError, color: "white" },
|
|
290
|
+
{ text: "", color: "gray" },
|
|
291
|
+
{ text: "Check env credentials and model provider config, then relaunch.", color: "yellow" },
|
|
292
|
+
]
|
|
293
|
+
: selectChatLines(selectorState, contentWidth)), [chatView.branding.splash, chatView.connectionError, contentWidth, selectorState, startupError]);
|
|
294
|
+
|
|
295
|
+
return React.createElement(platform.Panel, {
|
|
296
|
+
title: chrome.title,
|
|
297
|
+
color: chrome.color,
|
|
298
|
+
focused: chatView.focused,
|
|
299
|
+
width,
|
|
300
|
+
height,
|
|
301
|
+
lines: elements,
|
|
302
|
+
scrollOffset: chatView.chatScroll,
|
|
303
|
+
scrollMode: "bottom",
|
|
304
|
+
paneId: "chat",
|
|
305
|
+
paneLabel: "Chat",
|
|
306
|
+
frame,
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const FilesBrowser = React.memo(function FilesBrowser({ controller, width, height, shellTitle, focused = false, frame, showFullscreenTitle = false }) {
|
|
311
|
+
const platform = useUiPlatform();
|
|
312
|
+
const filesState = useControllerSelector(controller, (state) => ({
|
|
313
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
314
|
+
activeSession: state.sessions.activeSessionId ? state.sessions.byId[state.sessions.activeSessionId] || null : null,
|
|
315
|
+
sessionsFlat: state.sessions.flat,
|
|
316
|
+
filesBySessionId: state.files.bySessionId,
|
|
317
|
+
filesFullscreen: Boolean(state.files.fullscreen),
|
|
318
|
+
selectedArtifactId: state.files.selectedArtifactId,
|
|
319
|
+
filesFilter: state.files.filter,
|
|
320
|
+
filePreviewScroll: state.ui.scroll.filePreview,
|
|
321
|
+
}), shallowEqualObject);
|
|
322
|
+
const contentWidth = Math.max(20, width - 4);
|
|
323
|
+
const selectorState = React.useMemo(() => ({
|
|
324
|
+
sessions: {
|
|
325
|
+
activeSessionId: filesState.activeSessionId,
|
|
326
|
+
byId: filesState.activeSessionId && filesState.activeSession
|
|
327
|
+
? { [filesState.activeSessionId]: filesState.activeSession }
|
|
328
|
+
: {},
|
|
329
|
+
flat: filesState.sessionsFlat,
|
|
330
|
+
},
|
|
331
|
+
files: {
|
|
332
|
+
bySessionId: filesState.filesBySessionId,
|
|
333
|
+
fullscreen: filesState.filesFullscreen,
|
|
334
|
+
selectedArtifactId: filesState.selectedArtifactId,
|
|
335
|
+
filter: filesState.filesFilter,
|
|
336
|
+
},
|
|
337
|
+
ui: {
|
|
338
|
+
scroll: {
|
|
339
|
+
filePreview: filesState.filePreviewScroll,
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
}), [filesState]);
|
|
343
|
+
const filesView = React.useMemo(() => selectFilesView(selectorState, {
|
|
344
|
+
listWidth: Math.max(8, contentWidth - 4),
|
|
345
|
+
previewWidth: Math.max(8, contentWidth - 4),
|
|
346
|
+
}), [contentWidth, selectorState]);
|
|
347
|
+
const title = shellTitle
|
|
348
|
+
|| (showFullscreenTitle ? filesView.fullscreenTitle : filesView.panelTitle || filesView.listTitle)
|
|
349
|
+
|| [{ text: "Files", color: "magenta", bold: true }];
|
|
350
|
+
const availablePanelsHeight = Math.max(9, height - 4);
|
|
351
|
+
const maxListPanelHeight = Math.max(5, Math.min(10, Math.floor(availablePanelsHeight * 0.35)));
|
|
352
|
+
let listPanelHeight = Math.max(5, Math.min(maxListPanelHeight, (filesView.listBodyLines || []).length + 2));
|
|
353
|
+
let previewPanelHeight = Math.max(5, availablePanelsHeight - listPanelHeight - 1);
|
|
354
|
+
const minPreviewPanelHeight = 8;
|
|
355
|
+
if (previewPanelHeight < minPreviewPanelHeight) {
|
|
356
|
+
const deficit = minPreviewPanelHeight - previewPanelHeight;
|
|
357
|
+
listPanelHeight = Math.max(5, listPanelHeight - deficit);
|
|
358
|
+
previewPanelHeight = Math.max(5, availablePanelsHeight - listPanelHeight - 1);
|
|
359
|
+
}
|
|
360
|
+
const listContentRows = Math.max(1, listPanelHeight - 2);
|
|
361
|
+
const listScrollOffset = Math.max(0, filesView.selectedIndex - Math.floor(listContentRows / 2));
|
|
362
|
+
const listFrame = frame
|
|
363
|
+
? {
|
|
364
|
+
x: frame.x + 2,
|
|
365
|
+
y: frame.y + 2,
|
|
366
|
+
width: contentWidth,
|
|
367
|
+
height: listPanelHeight,
|
|
368
|
+
}
|
|
369
|
+
: null;
|
|
370
|
+
const previewFrame = frame
|
|
371
|
+
? {
|
|
372
|
+
x: frame.x + 2,
|
|
373
|
+
y: frame.y + 2 + listPanelHeight + 1,
|
|
374
|
+
width: contentWidth,
|
|
375
|
+
height: previewPanelHeight,
|
|
376
|
+
}
|
|
377
|
+
: null;
|
|
378
|
+
|
|
379
|
+
const tabLine = INSPECTOR_TABS.map((tab) => ({
|
|
380
|
+
text: tab === "files" ? `[${tab}] ` : `${tab} `,
|
|
381
|
+
color: tab === "files" ? "magenta" : "gray",
|
|
382
|
+
bold: tab === "files",
|
|
383
|
+
}));
|
|
384
|
+
|
|
385
|
+
return React.createElement(platform.Panel, {
|
|
386
|
+
title,
|
|
387
|
+
color: "magenta",
|
|
388
|
+
focused,
|
|
389
|
+
width,
|
|
390
|
+
height,
|
|
391
|
+
},
|
|
392
|
+
React.createElement(platform.Column, { width: contentWidth },
|
|
393
|
+
React.createElement(platform.Lines, {
|
|
394
|
+
lines: [tabLine],
|
|
395
|
+
}),
|
|
396
|
+
React.createElement(platform.Panel, {
|
|
397
|
+
title: filesView.listTitle,
|
|
398
|
+
color: "gray",
|
|
399
|
+
focused: false,
|
|
400
|
+
width: contentWidth,
|
|
401
|
+
height: listPanelHeight,
|
|
402
|
+
lines: filesView.listBodyLines || filesView.listLines.slice(1),
|
|
403
|
+
scrollOffset: listScrollOffset,
|
|
404
|
+
scrollMode: "top",
|
|
405
|
+
marginBottom: 1,
|
|
406
|
+
paneId: "files:list",
|
|
407
|
+
paneLabel: "Files list",
|
|
408
|
+
frame: listFrame,
|
|
409
|
+
}),
|
|
410
|
+
React.createElement(platform.Panel, {
|
|
411
|
+
title: filesView.previewTitle,
|
|
412
|
+
color: "gray",
|
|
413
|
+
focused: false,
|
|
414
|
+
width: contentWidth,
|
|
415
|
+
height: previewPanelHeight,
|
|
416
|
+
lines: filesView.previewLines,
|
|
417
|
+
scrollOffset: filesView.previewScrollOffset,
|
|
418
|
+
scrollMode: "top",
|
|
419
|
+
paneId: "files:preview",
|
|
420
|
+
paneLabel: "File preview",
|
|
421
|
+
frame: previewFrame,
|
|
422
|
+
}),
|
|
423
|
+
));
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const InspectorSequencePane = React.memo(function InspectorSequencePane({ controller, width, height, frame, meta }) {
|
|
427
|
+
const platform = useUiPlatform();
|
|
428
|
+
const contentWidth = Math.max(20, width - 4);
|
|
429
|
+
const sequenceState = useControllerSelector(controller, (state) => ({
|
|
430
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
431
|
+
activeSession: state.sessions.activeSessionId ? state.sessions.byId[state.sessions.activeSessionId] || null : null,
|
|
432
|
+
activeOrchestration: state.sessions.activeSessionId
|
|
433
|
+
? state.orchestration.bySessionId?.[state.sessions.activeSessionId] || null
|
|
434
|
+
: null,
|
|
435
|
+
histories: state.history.bySessionId,
|
|
436
|
+
}), shallowEqualObject);
|
|
437
|
+
const selectorState = React.useMemo(() => ({
|
|
438
|
+
sessions: {
|
|
439
|
+
activeSessionId: sequenceState.activeSessionId,
|
|
440
|
+
byId: buildSingleSessionMap(sequenceState.activeSessionId, sequenceState.activeSession),
|
|
441
|
+
},
|
|
442
|
+
history: {
|
|
443
|
+
bySessionId: sequenceState.histories,
|
|
444
|
+
},
|
|
445
|
+
orchestration: {
|
|
446
|
+
bySessionId: sequenceState.activeSessionId && sequenceState.activeOrchestration
|
|
447
|
+
? { [sequenceState.activeSessionId]: sequenceState.activeOrchestration }
|
|
448
|
+
: {},
|
|
449
|
+
},
|
|
450
|
+
ui: {
|
|
451
|
+
inspectorTab: "sequence",
|
|
452
|
+
},
|
|
453
|
+
}), [
|
|
454
|
+
sequenceState.activeOrchestration,
|
|
455
|
+
sequenceState.activeSession,
|
|
456
|
+
sequenceState.activeSessionId,
|
|
457
|
+
sequenceState.histories,
|
|
458
|
+
]);
|
|
459
|
+
const inspector = React.useMemo(
|
|
460
|
+
() => selectInspector(selectorState, { width: contentWidth }),
|
|
461
|
+
[contentWidth, selectorState],
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
return renderInspectorPanel(platform, inspector, meta, width, height, frame);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const InspectorLogsPane = React.memo(function InspectorLogsPane({ controller, width, height, frame, meta }) {
|
|
468
|
+
const platform = useUiPlatform();
|
|
469
|
+
const contentWidth = Math.max(20, width - 4);
|
|
470
|
+
const logsState = useControllerSelector(controller, (state) => ({
|
|
471
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
472
|
+
activeSession: state.sessions.activeSessionId ? state.sessions.byId[state.sessions.activeSessionId] || null : null,
|
|
473
|
+
logs: state.logs,
|
|
474
|
+
}), shallowEqualObject);
|
|
475
|
+
const selectorState = React.useMemo(() => ({
|
|
476
|
+
sessions: {
|
|
477
|
+
activeSessionId: logsState.activeSessionId,
|
|
478
|
+
byId: buildSingleSessionMap(logsState.activeSessionId, logsState.activeSession),
|
|
479
|
+
},
|
|
480
|
+
logs: logsState.logs,
|
|
481
|
+
ui: {
|
|
482
|
+
inspectorTab: "logs",
|
|
483
|
+
},
|
|
484
|
+
}), [logsState.activeSession, logsState.activeSessionId, logsState.logs]);
|
|
485
|
+
const inspector = React.useMemo(
|
|
486
|
+
() => selectInspector(selectorState, { width: contentWidth }),
|
|
487
|
+
[contentWidth, selectorState],
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
return renderInspectorPanel(platform, inspector, meta, width, height, frame);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const InspectorHistoryPane = React.memo(function InspectorHistoryPane({ controller, width, height, frame, meta }) {
|
|
494
|
+
const platform = useUiPlatform();
|
|
495
|
+
const contentWidth = Math.max(20, width - 4);
|
|
496
|
+
const historyState = useControllerSelector(controller, (state) => ({
|
|
497
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
498
|
+
activeSession: state.sessions.activeSessionId ? state.sessions.byId[state.sessions.activeSessionId] || null : null,
|
|
499
|
+
executionHistory: state.executionHistory,
|
|
500
|
+
}), shallowEqualObject);
|
|
501
|
+
const selectorState = React.useMemo(() => ({
|
|
502
|
+
sessions: {
|
|
503
|
+
activeSessionId: historyState.activeSessionId,
|
|
504
|
+
byId: buildSingleSessionMap(historyState.activeSessionId, historyState.activeSession),
|
|
505
|
+
},
|
|
506
|
+
executionHistory: historyState.executionHistory,
|
|
507
|
+
ui: {
|
|
508
|
+
inspectorTab: "history",
|
|
509
|
+
},
|
|
510
|
+
}), [historyState.activeSession, historyState.activeSessionId, historyState.executionHistory]);
|
|
511
|
+
const inspector = React.useMemo(
|
|
512
|
+
() => selectInspector(selectorState, { width: contentWidth }),
|
|
513
|
+
[contentWidth, selectorState],
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
return renderInspectorPanel(platform, inspector, meta, width, height, frame);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const InspectorNodesPane = React.memo(function InspectorNodesPane({ controller, width, height, frame, meta }) {
|
|
520
|
+
const platform = useUiPlatform();
|
|
521
|
+
const contentWidth = Math.max(20, width - 4);
|
|
522
|
+
const nodesState = useControllerSelector(controller, (state) => ({
|
|
523
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
524
|
+
sessionsById: state.sessions.byId,
|
|
525
|
+
sessionsFlat: state.sessions.flat,
|
|
526
|
+
histories: state.history.bySessionId,
|
|
527
|
+
}), shallowEqualObject);
|
|
528
|
+
const selectorState = React.useMemo(() => ({
|
|
529
|
+
sessions: {
|
|
530
|
+
activeSessionId: nodesState.activeSessionId,
|
|
531
|
+
byId: nodesState.sessionsById,
|
|
532
|
+
flat: nodesState.sessionsFlat,
|
|
533
|
+
},
|
|
534
|
+
history: {
|
|
535
|
+
bySessionId: nodesState.histories,
|
|
536
|
+
},
|
|
537
|
+
ui: {
|
|
538
|
+
inspectorTab: "nodes",
|
|
539
|
+
},
|
|
540
|
+
}), [
|
|
541
|
+
nodesState.activeSessionId,
|
|
542
|
+
nodesState.histories,
|
|
543
|
+
nodesState.sessionsById,
|
|
544
|
+
nodesState.sessionsFlat,
|
|
545
|
+
]);
|
|
546
|
+
const inspector = React.useMemo(
|
|
547
|
+
() => selectInspector(selectorState, { width: contentWidth }),
|
|
548
|
+
[contentWidth, selectorState],
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
return renderInspectorPanel(platform, inspector, meta, width, height, frame);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
const InspectorPane = React.memo(function InspectorPane({ controller, width, height, frame }) {
|
|
555
|
+
const inspectorMeta = useControllerSelector(controller, (state) => ({
|
|
556
|
+
inspectorTab: state.ui.inspectorTab,
|
|
557
|
+
inspectorScroll: state.ui.scroll.inspector,
|
|
558
|
+
focused: state.ui.focusRegion === "inspector",
|
|
559
|
+
}), shallowEqualObject);
|
|
560
|
+
if (inspectorMeta.inspectorTab === "files") {
|
|
561
|
+
return React.createElement(FilesBrowser, {
|
|
562
|
+
controller,
|
|
563
|
+
width,
|
|
564
|
+
height,
|
|
565
|
+
focused: inspectorMeta.focused,
|
|
566
|
+
frame,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
if (inspectorMeta.inspectorTab === "sequence") {
|
|
570
|
+
return React.createElement(InspectorSequencePane, {
|
|
571
|
+
controller,
|
|
572
|
+
width,
|
|
573
|
+
height,
|
|
574
|
+
frame,
|
|
575
|
+
meta: inspectorMeta,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
if (inspectorMeta.inspectorTab === "logs") {
|
|
579
|
+
return React.createElement(InspectorLogsPane, {
|
|
580
|
+
controller,
|
|
581
|
+
width,
|
|
582
|
+
height,
|
|
583
|
+
frame,
|
|
584
|
+
meta: inspectorMeta,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
if (inspectorMeta.inspectorTab === "history") {
|
|
588
|
+
return React.createElement(InspectorHistoryPane, {
|
|
589
|
+
controller,
|
|
590
|
+
width,
|
|
591
|
+
height,
|
|
592
|
+
frame,
|
|
593
|
+
meta: inspectorMeta,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return React.createElement(InspectorNodesPane, {
|
|
597
|
+
controller,
|
|
598
|
+
width,
|
|
599
|
+
height,
|
|
600
|
+
frame,
|
|
601
|
+
meta: inspectorMeta,
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const ActivityPane = React.memo(function ActivityPane({ controller, width, height, maxLines, frame }) {
|
|
606
|
+
const platform = useUiPlatform();
|
|
607
|
+
const activityState = useControllerSelector(controller, (state) => {
|
|
608
|
+
const activeSessionId = state.sessions.activeSessionId;
|
|
609
|
+
return {
|
|
610
|
+
sessionsById: state.sessions.byId,
|
|
611
|
+
activeSessionId,
|
|
612
|
+
activeSession: activeSessionId ? state.sessions.byId[activeSessionId] || null : null,
|
|
613
|
+
activeHistory: activeSessionId ? state.history.bySessionId.get(activeSessionId) || null : null,
|
|
614
|
+
scroll: state.ui.scroll.activity,
|
|
615
|
+
focused: state.ui.focusRegion === "activity",
|
|
616
|
+
};
|
|
617
|
+
}, shallowEqualObject);
|
|
618
|
+
const selectorState = React.useMemo(() => {
|
|
619
|
+
const historyMap = new Map();
|
|
620
|
+
if (activityState.activeSessionId && activityState.activeHistory) {
|
|
621
|
+
historyMap.set(activityState.activeSessionId, activityState.activeHistory);
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
sessions: {
|
|
625
|
+
activeSessionId: activityState.activeSessionId,
|
|
626
|
+
byId: activityState.sessionsById,
|
|
627
|
+
},
|
|
628
|
+
history: {
|
|
629
|
+
bySessionId: historyMap,
|
|
630
|
+
},
|
|
631
|
+
};
|
|
632
|
+
}, [activityState.activeHistory, activityState.activeSessionId, activityState.sessionsById]);
|
|
633
|
+
const activity = React.useMemo(() => selectActivityPane(selectorState, maxLines), [maxLines, selectorState]);
|
|
634
|
+
|
|
635
|
+
return React.createElement(platform.Panel, {
|
|
636
|
+
title: activity.title,
|
|
637
|
+
color: "gray",
|
|
638
|
+
focused: activityState.focused,
|
|
639
|
+
width,
|
|
640
|
+
height,
|
|
641
|
+
lines: activity.lines,
|
|
642
|
+
scrollOffset: activityState.scroll,
|
|
643
|
+
scrollMode: "bottom",
|
|
644
|
+
paneId: "activity",
|
|
645
|
+
paneLabel: "Activity",
|
|
646
|
+
frame,
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
const PromptBar = React.memo(function PromptBar({ controller, rows }) {
|
|
651
|
+
const platform = useUiPlatform();
|
|
652
|
+
const promptState = useControllerSelector(controller, (state) => {
|
|
653
|
+
const activeSessionId = state.sessions.activeSessionId;
|
|
654
|
+
const activeSession = activeSessionId ? state.sessions.byId[activeSessionId] || null : null;
|
|
655
|
+
return {
|
|
656
|
+
prompt: state.ui.prompt,
|
|
657
|
+
promptCursor: state.ui.promptCursor,
|
|
658
|
+
focused: state.ui.focusRegion === "prompt",
|
|
659
|
+
answeringQuestion: Boolean(activeSession?.pendingQuestion?.question),
|
|
660
|
+
};
|
|
661
|
+
}, shallowEqualObject);
|
|
662
|
+
return React.createElement(platform.Input, {
|
|
663
|
+
label: promptState.answeringQuestion ? "answer" : "you",
|
|
664
|
+
value: promptState.prompt,
|
|
665
|
+
cursorIndex: promptState.promptCursor,
|
|
666
|
+
focused: promptState.focused,
|
|
667
|
+
placeholder: promptState.answeringQuestion
|
|
668
|
+
? "Type an answer and press Enter"
|
|
669
|
+
: "Type a message and press Enter",
|
|
670
|
+
rows,
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const StatusBar = React.memo(function StatusBar({ controller }) {
|
|
675
|
+
const platform = useUiPlatform();
|
|
676
|
+
const statusState = useControllerSelector(controller, (state) => ({
|
|
677
|
+
connected: state.connection.connected,
|
|
678
|
+
workersOnline: state.connection.workersOnline,
|
|
679
|
+
focusRegion: state.ui.focusRegion,
|
|
680
|
+
inspectorTab: state.ui.inspectorTab,
|
|
681
|
+
fullscreenPane: state.ui.fullscreenPane || null,
|
|
682
|
+
logsAvailable: state.logs.available,
|
|
683
|
+
logsTailing: state.logs.tailing,
|
|
684
|
+
filesFullscreen: Boolean(state.files.fullscreen),
|
|
685
|
+
mode: state.connection.mode,
|
|
686
|
+
statusText: state.ui.statusText,
|
|
687
|
+
modal: state.ui.modal,
|
|
688
|
+
activeSessionId: state.sessions.activeSessionId,
|
|
689
|
+
activeSession: state.sessions.activeSessionId ? state.sessions.byId[state.sessions.activeSessionId] || null : null,
|
|
690
|
+
}), shallowEqualObject);
|
|
691
|
+
const selectorState = React.useMemo(() => ({
|
|
692
|
+
connection: {
|
|
693
|
+
connected: statusState.connected,
|
|
694
|
+
workersOnline: statusState.workersOnline,
|
|
695
|
+
mode: statusState.mode,
|
|
696
|
+
},
|
|
697
|
+
ui: {
|
|
698
|
+
focusRegion: statusState.focusRegion,
|
|
699
|
+
inspectorTab: statusState.inspectorTab,
|
|
700
|
+
fullscreenPane: statusState.fullscreenPane,
|
|
701
|
+
statusText: statusState.statusText,
|
|
702
|
+
modal: statusState.modal,
|
|
703
|
+
},
|
|
704
|
+
logs: {
|
|
705
|
+
available: statusState.logsAvailable,
|
|
706
|
+
tailing: statusState.logsTailing,
|
|
707
|
+
},
|
|
708
|
+
files: {
|
|
709
|
+
fullscreen: statusState.filesFullscreen,
|
|
710
|
+
},
|
|
711
|
+
sessions: {
|
|
712
|
+
activeSessionId: statusState.activeSessionId,
|
|
713
|
+
byId: statusState.activeSessionId && statusState.activeSession
|
|
714
|
+
? { [statusState.activeSessionId]: statusState.activeSession }
|
|
715
|
+
: {},
|
|
716
|
+
},
|
|
717
|
+
}), [statusState]);
|
|
718
|
+
const status = React.useMemo(() => selectStatusBar(selectorState), [selectorState]);
|
|
719
|
+
const viewport = typeof platform.getViewport === "function"
|
|
720
|
+
? platform.getViewport()
|
|
721
|
+
: { width: 120 };
|
|
722
|
+
const innerWidth = Math.max(20, (viewport.width || 120) - 4);
|
|
723
|
+
const rightMax = Math.min(Math.max(18, Math.floor(innerWidth * 0.45)), innerWidth - 8);
|
|
724
|
+
const leftMax = Math.max(8, innerWidth - rightMax - 3);
|
|
725
|
+
|
|
726
|
+
return React.createElement(platform.StatusLine, {
|
|
727
|
+
left: fitText(status.left, leftMax),
|
|
728
|
+
right: fitText(status.right, rightMax),
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
function ModelPickerModal({ state }) {
|
|
733
|
+
const platform = useUiPlatform();
|
|
734
|
+
const modal = selectModelPickerModal(state);
|
|
735
|
+
if (!modal) return null;
|
|
736
|
+
|
|
737
|
+
const viewport = typeof platform.getViewport === "function"
|
|
738
|
+
? platform.getViewport()
|
|
739
|
+
: { width: 120, height: 40 };
|
|
740
|
+
const width = Math.max(46, Math.min(modal.idealWidth || 68, (viewport.width || 120) - 16));
|
|
741
|
+
const listHeight = Math.max(8, Math.min(modal.rows.length + 2, 14, (viewport.height || 40) - 16));
|
|
742
|
+
const detailsHeight = Math.max(6, Math.min(8, (viewport.height || 40) - listHeight - 10));
|
|
743
|
+
const lines = modal.rows.length > 0
|
|
744
|
+
? modal.rows
|
|
745
|
+
: [{ text: "No models available.", color: "gray" }];
|
|
746
|
+
const contentRows = Math.max(1, listHeight - 2);
|
|
747
|
+
const scrollOffset = Math.max(0, modal.selectedRowIndex - Math.floor(contentRows / 2));
|
|
748
|
+
|
|
749
|
+
return React.createElement(platform.Overlay, null,
|
|
750
|
+
React.createElement(platform.Column, { width },
|
|
751
|
+
React.createElement(platform.Panel, {
|
|
752
|
+
title: modal.title,
|
|
753
|
+
color: "cyan",
|
|
754
|
+
focused: false,
|
|
755
|
+
width,
|
|
756
|
+
height: listHeight,
|
|
757
|
+
lines,
|
|
758
|
+
scrollOffset,
|
|
759
|
+
scrollMode: "top",
|
|
760
|
+
marginBottom: 1,
|
|
761
|
+
fillColor: "surface",
|
|
762
|
+
}),
|
|
763
|
+
React.createElement(platform.Panel, {
|
|
764
|
+
title: modal.detailsTitle || "Model Details",
|
|
765
|
+
color: "cyan",
|
|
766
|
+
focused: false,
|
|
767
|
+
width,
|
|
768
|
+
height: detailsHeight,
|
|
769
|
+
lines: modal.detailsLines,
|
|
770
|
+
scrollOffset: 0,
|
|
771
|
+
scrollMode: "top",
|
|
772
|
+
fillColor: "surface",
|
|
773
|
+
}),
|
|
774
|
+
));
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function ModelPickerModalContainer({ controller }) {
|
|
778
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
779
|
+
ui: {
|
|
780
|
+
modal: rootState.ui.modal,
|
|
781
|
+
},
|
|
782
|
+
}), shallowEqualObject);
|
|
783
|
+
return React.createElement(ModelPickerModal, { state });
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function SessionAgentPickerModal({ state }) {
|
|
787
|
+
const platform = useUiPlatform();
|
|
788
|
+
const modal = selectSessionAgentPickerModal(state);
|
|
789
|
+
if (!modal) return null;
|
|
790
|
+
|
|
791
|
+
const viewport = typeof platform.getViewport === "function"
|
|
792
|
+
? platform.getViewport()
|
|
793
|
+
: { width: 120, height: 40 };
|
|
794
|
+
const width = Math.max(50, Math.min(modal.idealWidth || 72, (viewport.width || 120) - 16));
|
|
795
|
+
const listHeight = Math.max(8, Math.min(modal.rows.length + 2, 14, (viewport.height || 40) - 16));
|
|
796
|
+
const detailsHeight = Math.max(7, Math.min(9, (viewport.height || 40) - listHeight - 10));
|
|
797
|
+
const lines = modal.rows.length > 0
|
|
798
|
+
? modal.rows
|
|
799
|
+
: [{ text: "No agents available.", color: "gray" }];
|
|
800
|
+
const contentRows = Math.max(1, listHeight - 2);
|
|
801
|
+
const scrollOffset = Math.max(0, modal.selectedRowIndex - Math.floor(contentRows / 2));
|
|
802
|
+
|
|
803
|
+
return React.createElement(platform.Overlay, null,
|
|
804
|
+
React.createElement(platform.Column, { width },
|
|
805
|
+
React.createElement(platform.Panel, {
|
|
806
|
+
title: modal.title,
|
|
807
|
+
color: "cyan",
|
|
808
|
+
focused: false,
|
|
809
|
+
width,
|
|
810
|
+
height: listHeight,
|
|
811
|
+
lines,
|
|
812
|
+
scrollOffset,
|
|
813
|
+
scrollMode: "top",
|
|
814
|
+
marginBottom: 1,
|
|
815
|
+
fillColor: "surface",
|
|
816
|
+
}),
|
|
817
|
+
React.createElement(platform.Panel, {
|
|
818
|
+
title: modal.detailsTitle || "Agent Details",
|
|
819
|
+
color: "cyan",
|
|
820
|
+
focused: false,
|
|
821
|
+
width,
|
|
822
|
+
height: detailsHeight,
|
|
823
|
+
lines: modal.detailsLines,
|
|
824
|
+
scrollOffset: 0,
|
|
825
|
+
scrollMode: "top",
|
|
826
|
+
fillColor: "surface",
|
|
827
|
+
}),
|
|
828
|
+
));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function SessionAgentPickerModalContainer({ controller }) {
|
|
832
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
833
|
+
ui: {
|
|
834
|
+
modal: rootState.ui.modal,
|
|
835
|
+
},
|
|
836
|
+
}), shallowEqualObject);
|
|
837
|
+
return React.createElement(SessionAgentPickerModal, { state });
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function RenameSessionModal({ state }) {
|
|
841
|
+
const platform = useUiPlatform();
|
|
842
|
+
const modal = selectRenameSessionModal(state);
|
|
843
|
+
if (!modal) return null;
|
|
844
|
+
|
|
845
|
+
const viewport = typeof platform.getViewport === "function"
|
|
846
|
+
? platform.getViewport()
|
|
847
|
+
: { width: 120, height: 40 };
|
|
848
|
+
const width = Math.max(56, Math.min(modal.idealWidth || 72, (viewport.width || 120) - 12));
|
|
849
|
+
const detailsHeight = Math.max(6, Math.min(7, (modal.detailsLines?.length || 0) + 2, (viewport.height || 40) - 14));
|
|
850
|
+
const helpHeight = Math.max(6, Math.min(7, (viewport.height || 40) - detailsHeight - 8));
|
|
851
|
+
|
|
852
|
+
return React.createElement(platform.Overlay, null,
|
|
853
|
+
React.createElement(platform.Column, { width },
|
|
854
|
+
React.createElement(platform.Panel, {
|
|
855
|
+
title: modal.title,
|
|
856
|
+
color: "cyan",
|
|
857
|
+
focused: false,
|
|
858
|
+
width,
|
|
859
|
+
height: detailsHeight,
|
|
860
|
+
lines: modal.detailsLines,
|
|
861
|
+
scrollOffset: 0,
|
|
862
|
+
scrollMode: "top",
|
|
863
|
+
marginBottom: 1,
|
|
864
|
+
fillColor: "surface",
|
|
865
|
+
}),
|
|
866
|
+
React.createElement(platform.Input, {
|
|
867
|
+
label: "title",
|
|
868
|
+
value: modal.value,
|
|
869
|
+
cursorIndex: modal.cursorIndex,
|
|
870
|
+
focused: true,
|
|
871
|
+
placeholder: modal.placeholder,
|
|
872
|
+
rows: 1,
|
|
873
|
+
}),
|
|
874
|
+
React.createElement(platform.Panel, {
|
|
875
|
+
title: modal.helpTitle || "Rename Rules",
|
|
876
|
+
color: "cyan",
|
|
877
|
+
focused: false,
|
|
878
|
+
width,
|
|
879
|
+
height: helpHeight,
|
|
880
|
+
lines: modal.helpLines,
|
|
881
|
+
scrollOffset: 0,
|
|
882
|
+
scrollMode: "top",
|
|
883
|
+
fillColor: "surface",
|
|
884
|
+
}),
|
|
885
|
+
));
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function RenameSessionModalContainer({ controller }) {
|
|
889
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
890
|
+
ui: {
|
|
891
|
+
modal: rootState.ui.modal,
|
|
892
|
+
},
|
|
893
|
+
}), shallowEqualObject);
|
|
894
|
+
return React.createElement(RenameSessionModal, { state });
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function ArtifactUploadModal({ state }) {
|
|
898
|
+
const platform = useUiPlatform();
|
|
899
|
+
const modal = selectArtifactUploadModal(state);
|
|
900
|
+
if (!modal) return null;
|
|
901
|
+
|
|
902
|
+
const viewport = typeof platform.getViewport === "function"
|
|
903
|
+
? platform.getViewport()
|
|
904
|
+
: { width: 120, height: 40 };
|
|
905
|
+
const width = Math.max(58, Math.min(modal.idealWidth || 74, (viewport.width || 120) - 12));
|
|
906
|
+
const detailsHeight = Math.max(6, Math.min(7, (modal.detailsLines?.length || 0) + 2, (viewport.height || 40) - 14));
|
|
907
|
+
const helpHeight = Math.max(7, Math.min(8, (viewport.height || 40) - detailsHeight - 8));
|
|
908
|
+
|
|
909
|
+
return React.createElement(platform.Overlay, null,
|
|
910
|
+
React.createElement(platform.Column, { width },
|
|
911
|
+
React.createElement(platform.Panel, {
|
|
912
|
+
title: modal.title,
|
|
913
|
+
color: "cyan",
|
|
914
|
+
focused: false,
|
|
915
|
+
width,
|
|
916
|
+
height: detailsHeight,
|
|
917
|
+
lines: modal.detailsLines,
|
|
918
|
+
scrollOffset: 0,
|
|
919
|
+
scrollMode: "top",
|
|
920
|
+
marginBottom: 1,
|
|
921
|
+
fillColor: "surface",
|
|
922
|
+
}),
|
|
923
|
+
React.createElement(platform.Input, {
|
|
924
|
+
label: "path",
|
|
925
|
+
value: modal.value,
|
|
926
|
+
cursorIndex: modal.cursorIndex,
|
|
927
|
+
focused: true,
|
|
928
|
+
placeholder: modal.placeholder,
|
|
929
|
+
rows: 1,
|
|
930
|
+
}),
|
|
931
|
+
React.createElement(platform.Panel, {
|
|
932
|
+
title: modal.helpTitle || "Attach Rules",
|
|
933
|
+
color: "cyan",
|
|
934
|
+
focused: false,
|
|
935
|
+
width,
|
|
936
|
+
height: helpHeight,
|
|
937
|
+
lines: modal.helpLines,
|
|
938
|
+
scrollOffset: 0,
|
|
939
|
+
scrollMode: "top",
|
|
940
|
+
fillColor: "surface",
|
|
941
|
+
}),
|
|
942
|
+
));
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function ArtifactUploadModalContainer({ controller }) {
|
|
946
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
947
|
+
branding: rootState.branding,
|
|
948
|
+
sessions: {
|
|
949
|
+
activeSessionId: rootState.sessions.activeSessionId,
|
|
950
|
+
byId: rootState.sessions.byId,
|
|
951
|
+
},
|
|
952
|
+
ui: {
|
|
953
|
+
modal: rootState.ui.modal,
|
|
954
|
+
promptAttachments: rootState.ui.promptAttachments,
|
|
955
|
+
},
|
|
956
|
+
}), shallowEqualObject);
|
|
957
|
+
return React.createElement(ArtifactUploadModal, { state });
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function ArtifactPickerModal({ state }) {
|
|
961
|
+
const platform = useUiPlatform();
|
|
962
|
+
const modal = selectArtifactPickerModal(state);
|
|
963
|
+
if (!modal) return null;
|
|
964
|
+
|
|
965
|
+
const viewport = typeof platform.getViewport === "function"
|
|
966
|
+
? platform.getViewport()
|
|
967
|
+
: { width: 120, height: 40 };
|
|
968
|
+
const width = Math.max(54, Math.min(modal.idealWidth || 72, (viewport.width || 120) - 16));
|
|
969
|
+
const listHeight = Math.max(8, Math.min(modal.rows.length + 2, 14, (viewport.height || 40) - 16));
|
|
970
|
+
const detailsHeight = Math.max(7, Math.min(9, (viewport.height || 40) - listHeight - 10));
|
|
971
|
+
const lines = modal.rows.length > 0
|
|
972
|
+
? modal.rows
|
|
973
|
+
: [{ text: "No artifacts available.", color: "gray" }];
|
|
974
|
+
const contentRows = Math.max(1, listHeight - 2);
|
|
975
|
+
const scrollOffset = Math.max(0, modal.selectedRowIndex - Math.floor(contentRows / 2));
|
|
976
|
+
|
|
977
|
+
return React.createElement(platform.Overlay, null,
|
|
978
|
+
React.createElement(platform.Column, { width },
|
|
979
|
+
React.createElement(platform.Panel, {
|
|
980
|
+
title: modal.title,
|
|
981
|
+
color: "cyan",
|
|
982
|
+
focused: false,
|
|
983
|
+
width,
|
|
984
|
+
height: listHeight,
|
|
985
|
+
lines,
|
|
986
|
+
scrollOffset,
|
|
987
|
+
scrollMode: "top",
|
|
988
|
+
marginBottom: 1,
|
|
989
|
+
fillColor: "surface",
|
|
990
|
+
}),
|
|
991
|
+
React.createElement(platform.Panel, {
|
|
992
|
+
title: modal.detailsTitle || "Artifact Details",
|
|
993
|
+
color: "cyan",
|
|
994
|
+
focused: false,
|
|
995
|
+
width,
|
|
996
|
+
height: detailsHeight,
|
|
997
|
+
lines: modal.detailsLines,
|
|
998
|
+
scrollOffset: 0,
|
|
999
|
+
scrollMode: "top",
|
|
1000
|
+
fillColor: "surface",
|
|
1001
|
+
}),
|
|
1002
|
+
));
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function ThemePickerModal({ state }) {
|
|
1006
|
+
const platform = useUiPlatform();
|
|
1007
|
+
const modal = selectThemePickerModal(state);
|
|
1008
|
+
if (!modal) return null;
|
|
1009
|
+
|
|
1010
|
+
const viewport = typeof platform.getViewport === "function"
|
|
1011
|
+
? platform.getViewport()
|
|
1012
|
+
: { width: 120, height: 40 };
|
|
1013
|
+
const width = Math.max(54, Math.min(modal.idealWidth || 76, (viewport.width || 120) - 16));
|
|
1014
|
+
const listHeight = Math.max(8, Math.min(modal.rows.length + 2, 14, (viewport.height || 40) - 16));
|
|
1015
|
+
const detailsHeight = Math.max(8, Math.min(10, (viewport.height || 40) - listHeight - 10));
|
|
1016
|
+
const lines = modal.rows.length > 0
|
|
1017
|
+
? modal.rows
|
|
1018
|
+
: [{ text: "No themes available.", color: "gray" }];
|
|
1019
|
+
const contentRows = Math.max(1, listHeight - 2);
|
|
1020
|
+
const scrollOffset = Math.max(0, modal.selectedRowIndex - Math.floor(contentRows / 2));
|
|
1021
|
+
|
|
1022
|
+
return React.createElement(platform.Overlay, null,
|
|
1023
|
+
React.createElement(platform.Column, { width },
|
|
1024
|
+
React.createElement(platform.Panel, {
|
|
1025
|
+
title: modal.title,
|
|
1026
|
+
color: "cyan",
|
|
1027
|
+
focused: false,
|
|
1028
|
+
width,
|
|
1029
|
+
height: listHeight,
|
|
1030
|
+
lines,
|
|
1031
|
+
scrollOffset,
|
|
1032
|
+
scrollMode: "top",
|
|
1033
|
+
marginBottom: 1,
|
|
1034
|
+
fillColor: "surface",
|
|
1035
|
+
}),
|
|
1036
|
+
React.createElement(platform.Panel, {
|
|
1037
|
+
title: modal.detailsTitle || "Theme Details",
|
|
1038
|
+
color: "cyan",
|
|
1039
|
+
focused: false,
|
|
1040
|
+
width,
|
|
1041
|
+
height: detailsHeight,
|
|
1042
|
+
lines: modal.detailsLines,
|
|
1043
|
+
scrollOffset: 0,
|
|
1044
|
+
scrollMode: "top",
|
|
1045
|
+
fillColor: "surface",
|
|
1046
|
+
}),
|
|
1047
|
+
));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function ThemePickerModalContainer({ controller }) {
|
|
1051
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
1052
|
+
ui: {
|
|
1053
|
+
modal: rootState.ui.modal,
|
|
1054
|
+
themeId: rootState.ui.themeId,
|
|
1055
|
+
},
|
|
1056
|
+
}), shallowEqualObject);
|
|
1057
|
+
return React.createElement(ThemePickerModal, { state });
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function ArtifactPickerModalContainer({ controller }) {
|
|
1061
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
1062
|
+
ui: {
|
|
1063
|
+
modal: rootState.ui.modal,
|
|
1064
|
+
},
|
|
1065
|
+
files: {
|
|
1066
|
+
bySessionId: rootState.files.bySessionId,
|
|
1067
|
+
},
|
|
1068
|
+
}), shallowEqualObject);
|
|
1069
|
+
return React.createElement(ArtifactPickerModal, { state });
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function renderFilterModal(platform, modal) {
|
|
1073
|
+
if (!modal) return null;
|
|
1074
|
+
const viewport = typeof platform.getViewport === "function"
|
|
1075
|
+
? platform.getViewport()
|
|
1076
|
+
: { width: 120, height: 40 };
|
|
1077
|
+
const paneGap = 1;
|
|
1078
|
+
const rawPaneWidths = modal.panes.map((pane) => Math.max(20, Math.min(pane.idealWidth || 24, 36)));
|
|
1079
|
+
const idealContentWidth = rawPaneWidths.reduce((sum, value) => sum + value, 0) + Math.max(0, rawPaneWidths.length - 1) * paneGap;
|
|
1080
|
+
const width = Math.max(76, Math.min(modal.idealWidth || (idealContentWidth + 4), (viewport.width || 120) - 6));
|
|
1081
|
+
const availablePaneWidth = Math.max(18, width - 4 - (Math.max(0, rawPaneWidths.length - 1) * paneGap));
|
|
1082
|
+
const paneWidths = [...rawPaneWidths];
|
|
1083
|
+
let totalPaneWidth = paneWidths.reduce((sum, value) => sum + value, 0);
|
|
1084
|
+
while (totalPaneWidth > availablePaneWidth) {
|
|
1085
|
+
let shrank = false;
|
|
1086
|
+
for (let index = 0; index < paneWidths.length && totalPaneWidth > availablePaneWidth; index += 1) {
|
|
1087
|
+
if (paneWidths[index] <= 18) continue;
|
|
1088
|
+
paneWidths[index] -= 1;
|
|
1089
|
+
totalPaneWidth -= 1;
|
|
1090
|
+
shrank = true;
|
|
1091
|
+
}
|
|
1092
|
+
if (!shrank) break;
|
|
1093
|
+
}
|
|
1094
|
+
const paneHeight = Math.max(8, Math.min(12, (viewport.height || 40) - 18));
|
|
1095
|
+
const helpHeight = Math.max(5, Math.min(7, (viewport.height || 40) - paneHeight - 10));
|
|
1096
|
+
const modalHeight = paneHeight + helpHeight + 5;
|
|
1097
|
+
const contentWidth = width - 4;
|
|
1098
|
+
|
|
1099
|
+
return React.createElement(platform.Overlay, null,
|
|
1100
|
+
React.createElement(platform.Panel, {
|
|
1101
|
+
title: modal.title,
|
|
1102
|
+
color: "cyan",
|
|
1103
|
+
focused: false,
|
|
1104
|
+
width,
|
|
1105
|
+
height: modalHeight,
|
|
1106
|
+
fillColor: "surface",
|
|
1107
|
+
},
|
|
1108
|
+
React.createElement(platform.Column, { width: contentWidth },
|
|
1109
|
+
React.createElement(platform.Row, { marginBottom: 1 },
|
|
1110
|
+
modal.panes.map((pane, index) => React.createElement(platform.Panel, {
|
|
1111
|
+
key: pane.id || index,
|
|
1112
|
+
title: pane.title,
|
|
1113
|
+
color: "cyan",
|
|
1114
|
+
focused: Boolean(pane.focused),
|
|
1115
|
+
width: paneWidths[index],
|
|
1116
|
+
height: paneHeight,
|
|
1117
|
+
lines: pane.lines,
|
|
1118
|
+
scrollOffset: 0,
|
|
1119
|
+
scrollMode: "top",
|
|
1120
|
+
marginRight: index === modal.panes.length - 1 ? 0 : paneGap,
|
|
1121
|
+
fillColor: "surface",
|
|
1122
|
+
})),
|
|
1123
|
+
),
|
|
1124
|
+
React.createElement(platform.Panel, {
|
|
1125
|
+
title: modal.helpTitle || "Help",
|
|
1126
|
+
color: "cyan",
|
|
1127
|
+
focused: false,
|
|
1128
|
+
width: contentWidth,
|
|
1129
|
+
height: helpHeight,
|
|
1130
|
+
lines: modal.helpLines || [modal.footerRuns],
|
|
1131
|
+
scrollOffset: 0,
|
|
1132
|
+
scrollMode: "top",
|
|
1133
|
+
fillColor: "surface",
|
|
1134
|
+
}),
|
|
1135
|
+
)));
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function LogFilterModal({ state }) {
|
|
1139
|
+
const platform = useUiPlatform();
|
|
1140
|
+
const modal = selectLogFilterModal(state);
|
|
1141
|
+
return renderFilterModal(platform, modal);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function LogFilterModalContainer({ controller }) {
|
|
1145
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
1146
|
+
ui: {
|
|
1147
|
+
modal: rootState.ui.modal,
|
|
1148
|
+
},
|
|
1149
|
+
logs: {
|
|
1150
|
+
filter: rootState.logs.filter,
|
|
1151
|
+
},
|
|
1152
|
+
}), shallowEqualObject);
|
|
1153
|
+
return React.createElement(LogFilterModal, { state });
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function FilesFilterModal({ state }) {
|
|
1157
|
+
const platform = useUiPlatform();
|
|
1158
|
+
const modal = selectFilesFilterModal(state);
|
|
1159
|
+
return renderFilterModal(platform, modal);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function FilesFilterModalContainer({ controller }) {
|
|
1163
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
1164
|
+
ui: {
|
|
1165
|
+
modal: rootState.ui.modal,
|
|
1166
|
+
},
|
|
1167
|
+
files: {
|
|
1168
|
+
filter: rootState.files.filter,
|
|
1169
|
+
},
|
|
1170
|
+
}), shallowEqualObject);
|
|
1171
|
+
return React.createElement(FilesFilterModal, { state });
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function HistoryFormatModal({ state }) {
|
|
1175
|
+
const platform = useUiPlatform();
|
|
1176
|
+
const modal = selectHistoryFormatModal(state);
|
|
1177
|
+
return renderFilterModal(platform, modal);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function HistoryFormatModalContainer({ controller }) {
|
|
1181
|
+
const state = useControllerSelector(controller, (rootState) => ({
|
|
1182
|
+
ui: {
|
|
1183
|
+
modal: rootState.ui.modal,
|
|
1184
|
+
},
|
|
1185
|
+
executionHistory: {
|
|
1186
|
+
format: rootState.executionHistory?.format || "pretty",
|
|
1187
|
+
},
|
|
1188
|
+
}), shallowEqualObject);
|
|
1189
|
+
return React.createElement(HistoryFormatModal, { state });
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
export function SharedPilotSwarmApp({ controller }) {
|
|
1193
|
+
const platform = useUiPlatform();
|
|
1194
|
+
const layoutState = useControllerSelector(controller, (state) => ({
|
|
1195
|
+
paneAdjust: state.ui.layout?.paneAdjust ?? 0,
|
|
1196
|
+
promptRows: getPromptInputRows(state.ui.prompt),
|
|
1197
|
+
inspectorTab: state.ui.inspectorTab,
|
|
1198
|
+
filesFullscreen: Boolean(state.files?.fullscreen),
|
|
1199
|
+
fullscreenPane: state.ui.fullscreenPane || null,
|
|
1200
|
+
themeId: state.ui.themeId,
|
|
1201
|
+
viewportWidth: state.ui.layout?.viewportWidth ?? 120,
|
|
1202
|
+
viewportHeight: state.ui.layout?.viewportHeight ?? 40,
|
|
1203
|
+
}), shallowEqualObject);
|
|
1204
|
+
const viewportWidth = layoutState.viewportWidth;
|
|
1205
|
+
const viewportHeight = layoutState.viewportHeight;
|
|
1206
|
+
const layout = React.useMemo(
|
|
1207
|
+
() => computeLegacyLayout({ width: viewportWidth, height: viewportHeight }, layoutState.paneAdjust, layoutState.promptRows, 0, layoutState.fullscreenPane),
|
|
1208
|
+
[layoutState.fullscreenPane, layoutState.paneAdjust, layoutState.promptRows, viewportHeight, viewportWidth],
|
|
1209
|
+
);
|
|
1210
|
+
const frames = buildWorkspacePaneFrames(layout);
|
|
1211
|
+
const sessionRows = Math.max(3, (layout.fullscreenPane === "sessions" ? layout.bodyHeight : layout.sessionPaneHeight) - 2);
|
|
1212
|
+
const activityRows = Math.max(3, (layout.fullscreenPane === "activity" ? layout.bodyHeight : layout.activityPaneHeight) - 2);
|
|
1213
|
+
const filesFullscreenActive = layoutState.inspectorTab === "files" && layoutState.filesFullscreen;
|
|
1214
|
+
const fullscreenPaneActive = filesFullscreenActive ? null : layoutState.fullscreenPane;
|
|
1215
|
+
const workspaceHeight = Math.max(10, layout.bodyHeight);
|
|
1216
|
+
|
|
1217
|
+
React.useEffect(() => {
|
|
1218
|
+
if (typeof controller.setViewport === "function") {
|
|
1219
|
+
controller.setViewport({ width: viewportWidth, height: viewportHeight });
|
|
1220
|
+
}
|
|
1221
|
+
}, [controller, viewportHeight, viewportWidth]);
|
|
1222
|
+
|
|
1223
|
+
React.useEffect(() => {
|
|
1224
|
+
if (typeof platform.setTheme === "function") {
|
|
1225
|
+
platform.setTheme(layoutState.themeId);
|
|
1226
|
+
}
|
|
1227
|
+
}, [layoutState.themeId, platform]);
|
|
1228
|
+
|
|
1229
|
+
platform.clearSelectablePanes?.();
|
|
1230
|
+
|
|
1231
|
+
return React.createElement(platform.Root, null,
|
|
1232
|
+
React.createElement(platform.Row, { flexGrow: 1 },
|
|
1233
|
+
filesFullscreenActive
|
|
1234
|
+
? React.createElement(FilesBrowser, {
|
|
1235
|
+
controller,
|
|
1236
|
+
width: layout.totalWidth,
|
|
1237
|
+
height: workspaceHeight,
|
|
1238
|
+
frame: frames.fullscreenFiles,
|
|
1239
|
+
showFullscreenTitle: true,
|
|
1240
|
+
})
|
|
1241
|
+
: fullscreenPaneActive === "sessions"
|
|
1242
|
+
? React.createElement(SessionList, {
|
|
1243
|
+
controller,
|
|
1244
|
+
width: layout.totalWidth,
|
|
1245
|
+
height: workspaceHeight,
|
|
1246
|
+
maxRows: sessionRows,
|
|
1247
|
+
frame: frames.fullscreenPane,
|
|
1248
|
+
})
|
|
1249
|
+
: fullscreenPaneActive === "chat"
|
|
1250
|
+
? React.createElement(ChatPane, {
|
|
1251
|
+
controller,
|
|
1252
|
+
width: layout.totalWidth,
|
|
1253
|
+
height: workspaceHeight,
|
|
1254
|
+
frame: frames.fullscreenPane,
|
|
1255
|
+
})
|
|
1256
|
+
: fullscreenPaneActive === "inspector"
|
|
1257
|
+
? React.createElement(InspectorPane, {
|
|
1258
|
+
controller,
|
|
1259
|
+
width: layout.totalWidth,
|
|
1260
|
+
height: workspaceHeight,
|
|
1261
|
+
frame: frames.fullscreenPane,
|
|
1262
|
+
})
|
|
1263
|
+
: fullscreenPaneActive === "activity"
|
|
1264
|
+
? React.createElement(ActivityPane, {
|
|
1265
|
+
controller,
|
|
1266
|
+
width: layout.totalWidth,
|
|
1267
|
+
height: workspaceHeight,
|
|
1268
|
+
maxLines: activityRows,
|
|
1269
|
+
frame: frames.fullscreenPane,
|
|
1270
|
+
})
|
|
1271
|
+
: [
|
|
1272
|
+
!layout.leftHidden && React.createElement(platform.Column, { key: "left", width: layout.leftWidth, marginRight: layout.rightHidden ? 0 : PANE_GAP_X, flexGrow: 0 },
|
|
1273
|
+
React.createElement(SessionList, {
|
|
1274
|
+
controller,
|
|
1275
|
+
width: layout.leftWidth,
|
|
1276
|
+
height: layout.sessionPaneHeight,
|
|
1277
|
+
maxRows: sessionRows,
|
|
1278
|
+
frame: frames.sessions,
|
|
1279
|
+
}),
|
|
1280
|
+
React.createElement(ChatPane, {
|
|
1281
|
+
controller,
|
|
1282
|
+
width: layout.leftWidth,
|
|
1283
|
+
height: layout.chatPaneHeight,
|
|
1284
|
+
frame: frames.chat,
|
|
1285
|
+
}),
|
|
1286
|
+
),
|
|
1287
|
+
!layout.rightHidden && React.createElement(platform.Column, { key: "right", width: layout.rightWidth, flexGrow: 0 },
|
|
1288
|
+
React.createElement(InspectorPane, {
|
|
1289
|
+
controller,
|
|
1290
|
+
width: layout.rightWidth,
|
|
1291
|
+
height: layout.inspectorPaneHeight,
|
|
1292
|
+
frame: frames.inspector,
|
|
1293
|
+
}),
|
|
1294
|
+
React.createElement(ActivityPane, {
|
|
1295
|
+
controller,
|
|
1296
|
+
width: layout.rightWidth,
|
|
1297
|
+
height: layout.activityPaneHeight,
|
|
1298
|
+
maxLines: activityRows,
|
|
1299
|
+
frame: frames.activity,
|
|
1300
|
+
}),
|
|
1301
|
+
),
|
|
1302
|
+
],
|
|
1303
|
+
),
|
|
1304
|
+
React.createElement(StatusBar, { controller }),
|
|
1305
|
+
React.createElement(PromptBar, { controller, rows: layoutState.promptRows }),
|
|
1306
|
+
React.createElement(RenameSessionModalContainer, { controller }),
|
|
1307
|
+
React.createElement(ArtifactUploadModalContainer, { controller }),
|
|
1308
|
+
React.createElement(ArtifactPickerModalContainer, { controller }),
|
|
1309
|
+
React.createElement(ModelPickerModalContainer, { controller }),
|
|
1310
|
+
React.createElement(ThemePickerModalContainer, { controller }),
|
|
1311
|
+
React.createElement(SessionAgentPickerModalContainer, { controller }),
|
|
1312
|
+
React.createElement(LogFilterModalContainer, { controller }),
|
|
1313
|
+
React.createElement(FilesFilterModalContainer, { controller }),
|
|
1314
|
+
React.createElement(HistoryFormatModalContainer, { controller }),
|
|
1315
|
+
);
|
|
1316
|
+
}
|