@agimon-ai/po-template-advanced 0.0.0-alpha.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.
@@ -0,0 +1,1271 @@
1
+ import { defineWebPlugin, defineWebTemplate } from "@agimon-ai/po-core/web";
2
+ import { DEFAULT_REPOSITORY_SETTINGS_SECTION, DEFAULT_SETTINGS_SECTION, DashboardRendererProvider, HOST_SLOTS, findTransientTab, pluginActivityGroups, refreshWebPluginCompositions, sessionsStore, setDockOpen, settingsSection, transientTabsStore, uiStore, updateWorkspaceAdmission, useActiveSession, useActiveSessionMeta, useDashboardRoute, useDashboardState, useNoSessions, usePluginSlotProps, useSessionMcp, useWebPluginRegistry, watchDashboardTranscript, webTabs, workspaceAdmissionStore, workspacesStore } from "@agimon-ai/po-web-dashboard";
3
+ import { ActivityDock, AppearanceSettings, CommandPalette, Composer, ContributedSettings, DialogOverlay, ImageSettings, NotificationSettings, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, RefusedCard, RemoteAccessDialog, RemoteControlSettings, RepositoryWorkspace, SelectionBar, SessionActivity, SessionMcp, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, ThreadView, Timeline, TopBar, WelcomePanel, useSessionRail } from "@agimon-ai/po-web-dashboard/views";
4
+ import { useStore } from "@tanstack/react-store";
5
+ import { useEffect, useRef, useState } from "react";
6
+ import { ActivityIcon, AlertIcon, Avatar, AvatarFallback, AvatarImage, BranchIcon, Button, ChevronUpIcon, CloseIcon, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Dot, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, FolderIcon, ForkIcon, GearIcon, Input, Kbd, KebabIcon, LoaderIcon, NavTab, OptionRow, PlusIcon, ShieldIcon, cn } from "@agimon-ai/po-web-components";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { Link, useNavigate } from "@tanstack/react-router";
9
+ //#region src/extensions/(frontend)/template/_components/AdvancedFolderBrowser.tsx
10
+ function folderName$1(path) {
11
+ return path.slice(path.lastIndexOf("/") + 1) || path;
12
+ }
13
+ /** Walks the server's folders inline: up, into a child, or pick the current folder. */
14
+ function AdvancedFolderBrowser({ start, listDirectory, onSelect }) {
15
+ const [target, setTarget] = useState(start);
16
+ const [listing, setListing] = useState(null);
17
+ const [error, setError] = useState("");
18
+ useEffect(() => {
19
+ let stale = false;
20
+ listDirectory(target).then((result) => {
21
+ if (stale) return;
22
+ if ("error" in result) setError(result.error);
23
+ else {
24
+ setError("");
25
+ setListing(result);
26
+ }
27
+ });
28
+ return () => {
29
+ stale = true;
30
+ };
31
+ }, [target, listDirectory]);
32
+ return /* @__PURE__ */ jsxs("div", {
33
+ "data-testid": "folder-browser",
34
+ className: "flex flex-col gap-1 rounded border border-doom-border bg-doom-deep p-1",
35
+ children: [
36
+ /* @__PURE__ */ jsxs("div", {
37
+ className: "flex items-center gap-1 px-1",
38
+ children: [
39
+ /* @__PURE__ */ jsx(Button, {
40
+ variant: "ghost",
41
+ size: "icon",
42
+ "data-testid": "folder-browser-up",
43
+ title: "parent folder",
44
+ "aria-label": "parent folder",
45
+ disabled: listing?.parent === void 0,
46
+ onClick: () => listing?.parent !== void 0 && setTarget(listing.parent),
47
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "h-3 w-3" })
48
+ }),
49
+ /* @__PURE__ */ jsx("span", {
50
+ className: "min-w-0 flex-1 truncate text-sm text-doom-dim",
51
+ title: listing?.path,
52
+ children: listing?.path ?? "loading…"
53
+ }),
54
+ /* @__PURE__ */ jsx(Button, {
55
+ variant: "outline",
56
+ size: "xs",
57
+ "data-testid": "folder-browser-select",
58
+ disabled: listing === null,
59
+ onClick: () => listing !== null && onSelect(listing.path),
60
+ children: "use this folder"
61
+ })
62
+ ]
63
+ }),
64
+ error ? /* @__PURE__ */ jsx("p", {
65
+ className: "px-2 py-1 text-sm text-doom-red",
66
+ children: error
67
+ }) : null,
68
+ /* @__PURE__ */ jsxs("div", {
69
+ role: "listbox",
70
+ "aria-label": "folders",
71
+ className: "max-h-40 overflow-y-auto",
72
+ children: [listing !== null && listing.directories.length === 0 ? /* @__PURE__ */ jsx("p", {
73
+ className: "px-2 py-1 text-sm text-doom-dim",
74
+ children: "no folders here"
75
+ }) : null, listing?.directories.map((directory) => /* @__PURE__ */ jsx(OptionRow, {
76
+ density: "compact",
77
+ "data-testid": "folder-browser-entry",
78
+ onClick: () => setTarget(directory),
79
+ className: "w-full px-2.5 py-1 ",
80
+ children: folderName$1(directory)
81
+ }, directory))]
82
+ })
83
+ ]
84
+ });
85
+ }
86
+ //#endregion
87
+ //#region src/extensions/(frontend)/template/_components/AdvancedAddWorkspaceDialog.tsx
88
+ const SUGGESTION_DEBOUNCE_MS = 120;
89
+ const NO_HIGHLIGHT = -1;
90
+ const TRAILING_SEPARATORS = /\/+$/u;
91
+ function folderName(path) {
92
+ const trimmed = path.trim().replace(TRAILING_SEPARATORS, "");
93
+ return trimmed.slice(trimmed.lastIndexOf("/") + 1);
94
+ }
95
+ /** Adds a workspace by name, by folder, or both; a name alone gets a new default folder. */
96
+ function AdvancedAddWorkspaceDialog({ suggestedPaths, actions }) {
97
+ const draft = useStore(workspaceAdmissionStore, (state) => state);
98
+ const { name, nameEdited, pending: busy, error } = draft;
99
+ const path = draft.path ?? suggestedPaths[0] ?? "";
100
+ const setName = (name) => updateWorkspaceAdmission({ name });
101
+ const setNameEdited = (nameEdited) => updateWorkspaceAdmission({ nameEdited });
102
+ const setPath = (path) => updateWorkspaceAdmission({ path });
103
+ const [browsing, setBrowsing] = useState(false);
104
+ const [suggestions, setSuggestions] = useState([]);
105
+ const [highlight, setHighlight] = useState(NO_HIGHLIGHT);
106
+ const pathInput = useRef(null);
107
+ const shownName = nameEdited ? name : folderName(path);
108
+ useEffect(() => {
109
+ const typed = path.trim();
110
+ if (typed === "") return;
111
+ let stale = false;
112
+ const timer = setTimeout(async () => {
113
+ const found = await actions.searchDirectories(typed);
114
+ if (stale) return;
115
+ setSuggestions(found.filter((directory) => directory !== typed));
116
+ setHighlight(NO_HIGHLIGHT);
117
+ }, SUGGESTION_DEBOUNCE_MS);
118
+ return () => {
119
+ stale = true;
120
+ clearTimeout(timer);
121
+ };
122
+ }, [path, actions]);
123
+ const changePath = (next) => {
124
+ setPath(next);
125
+ if (next.trim() === "") setSuggestions([]);
126
+ };
127
+ const pick = (directory) => {
128
+ changePath(`${directory}/`);
129
+ pathInput.current?.focus();
130
+ };
131
+ const trimmedPath = path.trim().replace(TRAILING_SEPARATORS, "") || (path.trim() === "" ? "" : "/");
132
+ const canSubmit = !busy && (trimmedPath !== "" || shownName.trim() !== "");
133
+ const submit = async () => {
134
+ if (!canSubmit) return;
135
+ await actions.addWorkspace({
136
+ name: shownName.trim(),
137
+ ...trimmedPath === "" ? {} : { path: trimmedPath }
138
+ });
139
+ };
140
+ return /* @__PURE__ */ jsx(Dialog, {
141
+ open: true,
142
+ onOpenChange: (next) => !next && !busy && actions.closeAddWorkspace(),
143
+ children: /* @__PURE__ */ jsxs(DialogContent, {
144
+ width: "md",
145
+ "data-testid": "add-workspace-dialog",
146
+ "aria-describedby": void 0,
147
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "add workspace" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
148
+ /* @__PURE__ */ jsxs("label", {
149
+ htmlFor: "add-workspace-name",
150
+ className: "flex flex-col gap-1",
151
+ children: [/* @__PURE__ */ jsx("span", {
152
+ className: "text-base font-medium text-doom-dim",
153
+ children: "name"
154
+ }), /* @__PURE__ */ jsx(Input, {
155
+ id: "add-workspace-name",
156
+ "data-testid": "add-workspace-name",
157
+ value: shownName,
158
+ autoFocus: true,
159
+ onChange: (event) => {
160
+ setNameEdited(true);
161
+ setName(event.target.value);
162
+ },
163
+ onKeyDown: (event) => {
164
+ if (event.key === "Enter") submit();
165
+ },
166
+ placeholder: "my-project"
167
+ })]
168
+ }),
169
+ /* @__PURE__ */ jsxs("label", {
170
+ htmlFor: "add-workspace-root",
171
+ className: "flex flex-col gap-1",
172
+ children: [/* @__PURE__ */ jsxs("span", {
173
+ className: "text-base font-medium text-doom-dim",
174
+ children: ["folder ", /* @__PURE__ */ jsx("span", {
175
+ className: "text-doom-dim",
176
+ children: "(optional; type a path or browse)"
177
+ })]
178
+ }), /* @__PURE__ */ jsxs("span", {
179
+ className: "flex items-center gap-1.5",
180
+ children: [/* @__PURE__ */ jsx(Input, {
181
+ id: "add-workspace-root",
182
+ ref: pathInput,
183
+ "data-testid": "add-workspace-root",
184
+ value: path,
185
+ onChange: (event) => changePath(event.target.value),
186
+ onKeyDown: (event) => {
187
+ if (event.key === "ArrowDown" && suggestions.length > 0) {
188
+ event.preventDefault();
189
+ setHighlight((current) => (current + 1) % suggestions.length);
190
+ } else if (event.key === "ArrowUp" && suggestions.length > 0) {
191
+ event.preventDefault();
192
+ setHighlight((current) => current <= 0 ? suggestions.length - 1 : current - 1);
193
+ } else if (event.key === "Enter") {
194
+ const chosen = highlight === NO_HIGHLIGHT ? void 0 : suggestions[highlight];
195
+ if (chosen === void 0) submit();
196
+ else pick(chosen);
197
+ }
198
+ },
199
+ placeholder: "~/workspace/my-project",
200
+ className: "min-w-0 flex-1"
201
+ }), /* @__PURE__ */ jsx(Button, {
202
+ variant: browsing ? "subtle" : "outline",
203
+ size: "md",
204
+ "data-testid": "add-workspace-browse",
205
+ "aria-pressed": browsing,
206
+ onClick: () => setBrowsing((open) => !open),
207
+ children: "browse"
208
+ })]
209
+ })]
210
+ }),
211
+ browsing ? /* @__PURE__ */ jsx(AdvancedFolderBrowser, {
212
+ ...trimmedPath.startsWith("/") ? { start: trimmedPath } : {},
213
+ listDirectory: actions.listDirectory,
214
+ onSelect: (selected) => {
215
+ changePath(selected);
216
+ setBrowsing(false);
217
+ }
218
+ }) : suggestions.length > 0 ? /* @__PURE__ */ jsx("div", {
219
+ role: "listbox",
220
+ "aria-label": "matching directories",
221
+ "data-testid": "add-workspace-suggestions",
222
+ className: "max-h-40 overflow-y-auto rounded border border-doom-border bg-doom-deep p-1",
223
+ children: suggestions.map((directory, index) => /* @__PURE__ */ jsx(OptionRow, {
224
+ density: "compact",
225
+ active: index === highlight,
226
+ "data-testid": "add-workspace-suggestion",
227
+ onMouseDown: (event) => event.preventDefault(),
228
+ onMouseEnter: () => setHighlight(index),
229
+ onClick: () => pick(directory),
230
+ className: "w-full px-2.5 py-1 ",
231
+ children: directory
232
+ }, directory))
233
+ }) : null,
234
+ suggestedPaths.length > 0 ? /* @__PURE__ */ jsx("div", {
235
+ className: "flex flex-wrap gap-1",
236
+ children: suggestedPaths.slice(0, 4).map((suggested) => /* @__PURE__ */ jsx(Button, {
237
+ variant: "outline",
238
+ size: "xs",
239
+ onClick: () => changePath(suggested),
240
+ children: suggested
241
+ }, suggested))
242
+ }) : null,
243
+ trimmedPath === "" && shownName.trim() !== "" ? /* @__PURE__ */ jsxs("p", {
244
+ "data-testid": "add-workspace-default-hint",
245
+ className: "text-sm text-doom-dim",
246
+ children: ["creates ~/.po/workspace/", shownName.trim()]
247
+ }) : null,
248
+ error ? /* @__PURE__ */ jsx("pre", {
249
+ className: "max-h-28 overflow-y-auto rounded border border-doom-edge-red bg-doom-tint-red/40 px-2.5 py-2 text-sm whitespace-pre-wrap text-doom-red",
250
+ children: error
251
+ }) : null,
252
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
253
+ variant: "outline",
254
+ onClick: () => actions.closeAddWorkspace(),
255
+ disabled: busy,
256
+ children: "cancel"
257
+ }), /* @__PURE__ */ jsx(Button, {
258
+ variant: "primary",
259
+ "data-testid": "add-workspace-confirm",
260
+ onClick: () => void submit(),
261
+ disabled: !canSubmit,
262
+ children: busy ? "adding…" : "add workspace"
263
+ })] })
264
+ ] })]
265
+ })
266
+ });
267
+ }
268
+ //#endregion
269
+ //#region src/extensions/(frontend)/template/_components/AdvancedPendingSetup.tsx
270
+ /** A pending reservation has no runtime and must not inherit live-session actions. */
271
+ function AdvancedPendingSetup({ sessionId, setup, actions }) {
272
+ const key = JSON.stringify([
273
+ "rail/setup",
274
+ sessionId,
275
+ setup.id
276
+ ]);
277
+ const [busy, setBusy, busyState] = useDashboardState(`${key}/busy`, false);
278
+ const [error, setError] = useDashboardState(`${key}/error`, "");
279
+ const remove = async () => {
280
+ if (busyState.state) return;
281
+ setBusy(true);
282
+ setError("");
283
+ const result = await actions.removePendingSetup(sessionId, setup.id);
284
+ setBusy(false);
285
+ if (result !== void 0) setError(result.error);
286
+ };
287
+ return /* @__PURE__ */ jsxs("div", {
288
+ "data-testid": `pending-session-${setup.id}`,
289
+ className: "ml-4 rounded-md border border-doom-border-soft bg-doom-panel p-3",
290
+ children: [
291
+ /* @__PURE__ */ jsxs("div", {
292
+ className: "flex items-center justify-between gap-2",
293
+ children: [/* @__PURE__ */ jsx("span", {
294
+ className: "truncate text-base font-semibold text-doom-hi",
295
+ children: setup.name
296
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
297
+ asChild: true,
298
+ children: /* @__PURE__ */ jsx(Button, {
299
+ variant: "ghost",
300
+ size: "icon",
301
+ title: "session setup actions",
302
+ disabled: busy,
303
+ "data-testid": `pending-session-menu-${setup.id}`,
304
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
305
+ })
306
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, { children: /* @__PURE__ */ jsx(DropdownMenuItem, {
307
+ variant: "destructive",
308
+ onSelect: () => void remove(),
309
+ children: "remove setup"
310
+ }) })] })]
311
+ }),
312
+ /* @__PURE__ */ jsx("p", {
313
+ className: setup.failed ? "text-sm text-doom-red" : "text-sm text-doom-dim",
314
+ children: setup.status
315
+ }),
316
+ setup.recovery ? /* @__PURE__ */ jsx("p", {
317
+ role: setup.failed ? "alert" : void 0,
318
+ className: "text-sm text-doom-dim",
319
+ children: setup.recovery
320
+ }) : null,
321
+ error ? /* @__PURE__ */ jsx("p", {
322
+ role: "alert",
323
+ className: "text-sm text-doom-red",
324
+ children: error
325
+ }) : null
326
+ ]
327
+ });
328
+ }
329
+ //#endregion
330
+ //#region src/extensions/(frontend)/template/_components/AdvancedSessionCard.tsx
331
+ function AdvancedSessionCard({ session, actions }) {
332
+ const key = `rail/session/${session.id}`;
333
+ const [mode, setMode] = useDashboardState(`${key}/mode`, "view");
334
+ const [draft, setDraft] = useDashboardState(`${key}/draft`, "");
335
+ const [removeError, setRemoveError] = useDashboardState(`${key}/error`, "");
336
+ const [, setRemoving, removingState] = useDashboardState(`${key}/removing`, false);
337
+ const modeRef = useRef(mode);
338
+ const enterMode = (next) => {
339
+ modeRef.current = next;
340
+ setMode(next);
341
+ };
342
+ const { active } = session;
343
+ const error = removeError || session.error;
344
+ const cardClass = active ? "rounded-lg border border-doom-edge-green bg-doom-tint-green px-3 py-3 hover:bg-doom-tint-green" : "rounded-lg border border-transparent px-3 py-3 hover:bg-doom-panel";
345
+ const commitRename = () => {
346
+ actions.renameSession(session.id, draft);
347
+ enterMode("view");
348
+ };
349
+ const remove = async () => {
350
+ if (removingState.state) return;
351
+ setRemoving(true);
352
+ enterMode("view");
353
+ setRemoveError("");
354
+ const result = await actions.removeSession(session.id);
355
+ setRemoving(false);
356
+ if (result !== void 0) setRemoveError(result.error);
357
+ };
358
+ const failed = session.status.startsWith("failed") || session.status.includes(" failed");
359
+ const running = session.status.startsWith("running");
360
+ const status = /* @__PURE__ */ jsxs("span", {
361
+ className: `flex min-w-0 items-start gap-1.5 text-sm leading-snug ${failed ? "text-doom-red" : running ? "text-doom-yellow" : active ? "text-doom-text" : "text-doom-dim"}`,
362
+ children: [session.awaitingInput ? /* @__PURE__ */ jsx(AlertIcon, {
363
+ "data-testid": "session-awaiting-input",
364
+ "aria-label": "Waiting for your input",
365
+ className: "mt-0.5 size-3 shrink-0 text-doom-magenta"
366
+ }) : running ? /* @__PURE__ */ jsx(LoaderIcon, {
367
+ "aria-hidden": true,
368
+ className: "mt-0.5 size-3 shrink-0 animate-spin"
369
+ }) : failed ? /* @__PURE__ */ jsx(AlertIcon, {
370
+ "aria-hidden": true,
371
+ className: "mt-0.5 size-3 shrink-0"
372
+ }) : null, /* @__PURE__ */ jsx("span", {
373
+ "data-testid": "session-status",
374
+ className: active ? "line-clamp-2" : "truncate",
375
+ children: session.status
376
+ })]
377
+ });
378
+ const heading = /* @__PURE__ */ jsxs("span", {
379
+ className: "flex min-w-0 items-start gap-2",
380
+ children: [
381
+ session.nested && session.provenance === "workflow-session" ? /* @__PURE__ */ jsx(ActivityIcon, {
382
+ "aria-label": "workflow session",
383
+ className: `mt-1 size-3 shrink-0 ${active ? "text-doom-green" : "text-doom-faint"}`
384
+ }) : session.nested && session.provenance ? /* @__PURE__ */ jsx(ForkIcon, {
385
+ "aria-label": session.provenance === "worktree" ? "automatic per-conversation worktree" : session.provenance,
386
+ className: `mt-1 size-3 shrink-0 ${active ? "text-doom-green" : "text-doom-faint"}`
387
+ }) : null,
388
+ /* @__PURE__ */ jsx("span", {
389
+ className: "min-w-0 flex-1 text-base font-semibold break-words text-doom-hi",
390
+ children: session.name || "Untitled"
391
+ }),
392
+ session.ordinal <= 9 ? /* @__PURE__ */ jsx("span", {
393
+ title: `press ${String(session.ordinal)} to focus`,
394
+ className: "grid size-5 shrink-0 place-items-center rounded-full bg-doom-panel text-2xs text-doom-dim transition-opacity group-focus-within:opacity-0 group-hover:opacity-0",
395
+ children: session.ordinal
396
+ }) : null
397
+ ]
398
+ });
399
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
400
+ /* @__PURE__ */ jsxs("span", {
401
+ className: "flex w-full min-w-0 items-start gap-2.5",
402
+ children: [session.profile ? /* @__PURE__ */ jsxs(Avatar, {
403
+ "data-testid": "session-avatar",
404
+ "aria-label": session.profile.label,
405
+ title: session.profile.label,
406
+ className: "size-8 shrink-0 rounded-full",
407
+ children: [session.profile.avatarUrl ? /* @__PURE__ */ jsx(AvatarImage, {
408
+ src: session.profile.avatarUrl,
409
+ alt: ""
410
+ }) : null, /* @__PURE__ */ jsx(AvatarFallback, {
411
+ className: "bg-doom-tint-green text-xs font-semibold text-doom-green",
412
+ children: session.profile.initials
413
+ })]
414
+ }) : null, /* @__PURE__ */ jsxs("span", {
415
+ className: "flex min-w-0 flex-1 flex-col gap-1",
416
+ children: [heading, status]
417
+ })]
418
+ }),
419
+ session.git ? /* @__PURE__ */ jsxs("span", {
420
+ "data-testid": "session-branch",
421
+ className: `flex min-w-0 items-center gap-1.5 text-sm text-doom-dim ${session.profile ? "pl-10.5" : ""}`,
422
+ children: [
423
+ /* @__PURE__ */ jsx(BranchIcon, { className: `size-3.5 shrink-0 ${active ? "text-doom-green" : ""}` }),
424
+ /* @__PURE__ */ jsx("span", {
425
+ className: "truncate font-mono",
426
+ children: session.git.branch
427
+ }),
428
+ session.git.dirty ? /* @__PURE__ */ jsx("span", {
429
+ className: "shrink-0 text-doom-yellow",
430
+ children: "modified"
431
+ }) : null
432
+ ]
433
+ }) : null,
434
+ error ? /* @__PURE__ */ jsx("span", {
435
+ "data-testid": "session-error",
436
+ className: "line-clamp-3 text-sm break-words text-doom-red",
437
+ children: error
438
+ }) : null
439
+ ] });
440
+ const menuOpen = mode === "menu";
441
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
442
+ className: cn("group relative", session.nested && "pl-5"),
443
+ "data-testid": `session-card-${session.id}`,
444
+ "data-active": active,
445
+ "data-nested": session.nested,
446
+ children: [
447
+ /* @__PURE__ */ jsx(Button, {
448
+ variant: "ghost",
449
+ size: "card",
450
+ "data-testid": `session-open-${session.id}`,
451
+ onClick: () => actions.openSession(session.id),
452
+ className: cardClass,
453
+ children: body
454
+ }),
455
+ mode === "view" || menuOpen ? /* @__PURE__ */ jsx("div", {
456
+ className: `absolute top-2 right-2 transition-opacity ${menuOpen ? "opacity-100" : "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"}`,
457
+ children: /* @__PURE__ */ jsxs(DropdownMenu, {
458
+ open: menuOpen,
459
+ onOpenChange: (next) => {
460
+ const current = modeRef.current;
461
+ enterMode(next ? "menu" : current === "menu" ? "view" : current);
462
+ },
463
+ children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
464
+ asChild: true,
465
+ children: /* @__PURE__ */ jsx(Button, {
466
+ variant: "ghost",
467
+ size: "icon",
468
+ "data-testid": `session-menu-${session.id}`,
469
+ title: "session actions",
470
+ className: active ? "text-doom-dim hover:bg-doom-tint-green hover:text-doom-hi data-[state=open]:bg-doom-tint-green data-[state=open]:text-doom-hi" : "text-doom-faint hover:bg-doom-deep hover:text-doom-hi data-[state=open]:bg-doom-deep data-[state=open]:text-doom-hi",
471
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
472
+ })
473
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
474
+ "data-testid": `session-menu-list-${session.id}`,
475
+ onCloseAutoFocus: (event) => {
476
+ if (modeRef.current !== "view") event.preventDefault();
477
+ },
478
+ children: [
479
+ actions.renderSessionMenuItems(session.id),
480
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
481
+ "data-testid": `session-rename-${session.id}`,
482
+ onSelect: () => {
483
+ setDraft(session.name);
484
+ enterMode("rename");
485
+ },
486
+ children: "rename"
487
+ }),
488
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
489
+ "data-testid": `session-resume-${session.id}`,
490
+ onSelect: () => actions.resumeSession(session.id),
491
+ children: "resume"
492
+ }),
493
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
494
+ "data-testid": `session-restart-${session.id}`,
495
+ disabled: session.restarting || session.restartBlocked === true,
496
+ onSelect: () => actions.restartSession(session.id),
497
+ children: "restart"
498
+ }),
499
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
500
+ variant: "destructive",
501
+ "data-testid": `session-stop-${session.id}`,
502
+ onSelect: () => enterMode("confirm"),
503
+ children: "remove"
504
+ })
505
+ ]
506
+ })]
507
+ })
508
+ }) : null,
509
+ /* @__PURE__ */ jsx(Dialog, {
510
+ open: mode === "rename",
511
+ onOpenChange: (next) => !next && enterMode("view"),
512
+ children: /* @__PURE__ */ jsxs(DialogContent, {
513
+ width: "sm",
514
+ "data-testid": `session-rename-dialog-${session.id}`,
515
+ "aria-describedby": void 0,
516
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "rename session" }) }), /* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("form", {
517
+ className: "flex flex-col gap-3",
518
+ onSubmit: (event) => {
519
+ event.preventDefault();
520
+ commitRename();
521
+ },
522
+ children: [/* @__PURE__ */ jsx(Input, {
523
+ "data-testid": `session-name-input-${session.id}`,
524
+ "aria-label": "session name",
525
+ value: draft,
526
+ autoFocus: true,
527
+ onChange: (event) => setDraft(event.target.value)
528
+ }), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
529
+ type: "button",
530
+ variant: "outline",
531
+ size: "md",
532
+ "data-testid": `session-rename-cancel-${session.id}`,
533
+ onClick: () => enterMode("view"),
534
+ children: "cancel"
535
+ }), /* @__PURE__ */ jsx(Button, {
536
+ type: "submit",
537
+ variant: "primary",
538
+ size: "md",
539
+ "data-testid": `session-rename-confirm-${session.id}`,
540
+ disabled: draft.trim() === "",
541
+ children: "rename"
542
+ })] })]
543
+ }) })]
544
+ })
545
+ }),
546
+ /* @__PURE__ */ jsx(Dialog, {
547
+ open: mode === "confirm",
548
+ onOpenChange: (next) => !next && enterMode("view"),
549
+ children: /* @__PURE__ */ jsxs(DialogContent, {
550
+ width: "sm",
551
+ "data-testid": `session-stop-dialog-${session.id}`,
552
+ "aria-describedby": void 0,
553
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "remove session" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [/* @__PURE__ */ jsxs(DialogDescription, { children: [
554
+ "this stops ",
555
+ /* @__PURE__ */ jsx("span", {
556
+ className: "font-semibold text-doom-hi",
557
+ children: session.name || "untitled"
558
+ }),
559
+ " and removes it from the rail. anything it is running ends now, but its saved history remains available to resume."
560
+ ] }), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
561
+ variant: "outline",
562
+ size: "md",
563
+ "data-testid": `session-stop-cancel-${session.id}`,
564
+ autoFocus: true,
565
+ onClick: () => enterMode("view"),
566
+ children: "cancel"
567
+ }), /* @__PURE__ */ jsx(Button, {
568
+ variant: "danger",
569
+ size: "md",
570
+ "data-testid": `session-stop-confirm-${session.id}`,
571
+ onClick: () => void remove(),
572
+ children: "remove"
573
+ })] })] })]
574
+ })
575
+ })
576
+ ]
577
+ }), session.pendingSetups.map((setup) => /* @__PURE__ */ jsx(AdvancedPendingSetup, {
578
+ sessionId: session.id,
579
+ setup,
580
+ actions
581
+ }, `setup:${setup.id}`))] });
582
+ }
583
+ //#endregion
584
+ //#region src/extensions/(frontend)/template/_components/AdvancedWorkspaceGroup.tsx
585
+ /** A folder heading over its sessions; only a hover tint marks the row. */
586
+ function AdvancedWorkspaceGroup({ workspace, actions }) {
587
+ const key = `rail/workspace/${workspace.id}`;
588
+ const [removeOpen, setRemoveOpen] = useDashboardState(`${key}/open`, false);
589
+ const [removeError, setRemoveError] = useDashboardState(`${key}/error`, "");
590
+ const [removing, setRemoving, removingState] = useDashboardState(`${key}/removing`, false);
591
+ const { id, name, root, available, legacy } = workspace;
592
+ const unregister = async () => {
593
+ if (removingState.state) return;
594
+ setRemoving(true);
595
+ setRemoveError("");
596
+ const result = await actions.deleteWorkspace(id);
597
+ setRemoving(false);
598
+ if (result !== void 0) setRemoveError(result.error);
599
+ else setRemoveOpen(false);
600
+ };
601
+ return /* @__PURE__ */ jsxs("section", {
602
+ "data-testid": `workspace-group-${id}`,
603
+ "data-available": available,
604
+ className: "flex flex-col gap-1.5",
605
+ children: [
606
+ /* @__PURE__ */ jsxs("div", {
607
+ className: "flex min-w-0 items-start gap-2.5 rounded-md px-2 py-1 transition-colors hover:bg-doom-panel/60",
608
+ children: [
609
+ /* @__PURE__ */ jsx(FolderIcon, {
610
+ "aria-hidden": true,
611
+ className: "mt-0.5 size-4 shrink-0 text-doom-dim"
612
+ }),
613
+ /* @__PURE__ */ jsxs("div", {
614
+ className: "flex min-w-0 flex-1 flex-col",
615
+ children: [/* @__PURE__ */ jsx("p", {
616
+ className: "truncate text-base font-semibold text-doom-hi",
617
+ children: name
618
+ }), /* @__PURE__ */ jsx("p", {
619
+ className: "truncate font-mono text-sm text-doom-dim",
620
+ title: root,
621
+ children: root
622
+ })]
623
+ }),
624
+ legacy ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
625
+ variant: "ghost",
626
+ size: "icon",
627
+ "data-testid": `workspace-new-session-${id}`,
628
+ title: "New session",
629
+ "aria-label": `New session in ${name}`,
630
+ disabled: !available,
631
+ onClick: () => actions.createSession(id),
632
+ className: "text-doom-faint hover:text-doom-hi",
633
+ children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
634
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
635
+ asChild: true,
636
+ children: /* @__PURE__ */ jsx(Button, {
637
+ variant: "ghost",
638
+ size: "icon",
639
+ "data-testid": `workspace-menu-${id}`,
640
+ title: "Workspace actions",
641
+ "aria-label": `${name} actions`,
642
+ className: "text-doom-faint hover:text-doom-hi",
643
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "size-3.5" })
644
+ })
645
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
646
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
647
+ disabled: !available,
648
+ onSelect: () => actions.resumeInWorkspace(id),
649
+ children: "resume session"
650
+ }),
651
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
652
+ disabled: !available,
653
+ onSelect: () => actions.openWorkspaceSettings(id),
654
+ children: "workspace settings"
655
+ }),
656
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
657
+ variant: "destructive",
658
+ "data-testid": `workspace-delete-${id}`,
659
+ onSelect: () => {
660
+ setRemoveError("");
661
+ setRemoveOpen(true);
662
+ },
663
+ children: "delete workspace"
664
+ })
665
+ ] })] })] })
666
+ ]
667
+ }),
668
+ !available ? /* @__PURE__ */ jsx("p", {
669
+ className: "px-9 text-sm text-doom-red",
670
+ children: "Workspace unavailable"
671
+ }) : null,
672
+ workspace.sessions.length > 0 ? /* @__PURE__ */ jsx("div", {
673
+ className: "flex flex-col gap-1",
674
+ children: workspace.sessions.map((session) => /* @__PURE__ */ jsx(AdvancedSessionCard, {
675
+ session,
676
+ actions
677
+ }, session.id))
678
+ }) : available ? /* @__PURE__ */ jsx("p", {
679
+ className: "px-9 pb-1 text-sm text-doom-dim",
680
+ children: "No sessions yet. Start or resume one."
681
+ }) : null,
682
+ /* @__PURE__ */ jsx(Dialog, {
683
+ open: removeOpen,
684
+ onOpenChange: (next) => !next && !removing && setRemoveOpen(false),
685
+ children: /* @__PURE__ */ jsxs(DialogContent, {
686
+ width: "sm",
687
+ "data-testid": `workspace-delete-dialog-${id}`,
688
+ "aria-describedby": void 0,
689
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "delete workspace" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
690
+ /* @__PURE__ */ jsxs(DialogDescription, { children: [
691
+ "this unregisters ",
692
+ /* @__PURE__ */ jsx("span", {
693
+ className: "font-semibold text-doom-hi",
694
+ children: name
695
+ }),
696
+ " from Po only. files at",
697
+ " ",
698
+ /* @__PURE__ */ jsx("code", { children: root }),
699
+ " stay on disk. its sessions must be removed first."
700
+ ] }),
701
+ removeError ? /* @__PURE__ */ jsx("p", {
702
+ "data-testid": `workspace-delete-error-${id}`,
703
+ className: "text-sm text-doom-red",
704
+ children: removeError
705
+ }) : null,
706
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
707
+ variant: "outline",
708
+ size: "md",
709
+ "data-testid": `workspace-delete-cancel-${id}`,
710
+ disabled: removing,
711
+ autoFocus: true,
712
+ onClick: () => setRemoveOpen(false),
713
+ children: "cancel"
714
+ }), /* @__PURE__ */ jsx(Button, {
715
+ variant: "danger",
716
+ size: "md",
717
+ "data-testid": `workspace-delete-confirm-${id}`,
718
+ disabled: removing,
719
+ onClick: () => void unregister(),
720
+ children: removing ? "deleting…" : "delete"
721
+ })] })
722
+ ] })]
723
+ })
724
+ })
725
+ ]
726
+ });
727
+ }
728
+ //#endregion
729
+ //#region src/extensions/(frontend)/template/_components/AdvancedRail.tsx
730
+ /** The persistent session rail: workspaces with their sessions, remote access, and settings. */
731
+ function AdvancedRail({ rail, onClose }) {
732
+ const { remote, actions } = rail;
733
+ const live = remote.status === "on" || remote.status === "starting";
734
+ const remoteDetail = remote.status === "on" ? `Remote access on, ${String(remote.deviceCount)} device${remote.deviceCount === 1 ? "" : "s"} paired` : void 0;
735
+ return /* @__PURE__ */ jsxs("div", {
736
+ className: "flex min-h-full flex-col",
737
+ children: [
738
+ /* @__PURE__ */ jsxs("div", {
739
+ "data-po-session-rail-header": true,
740
+ className: "flex min-h-16 items-center justify-between gap-2 px-5 py-3",
741
+ children: [/* @__PURE__ */ jsxs("span", {
742
+ className: "flex items-center gap-2.5",
743
+ "aria-label": "Po",
744
+ children: [/* @__PURE__ */ jsx("img", {
745
+ src: "/favicon.svg",
746
+ alt: "",
747
+ "aria-hidden": "true",
748
+ className: "size-9 rounded-lg"
749
+ }), /* @__PURE__ */ jsx("span", {
750
+ "aria-hidden": "true",
751
+ className: "text-2xl font-semibold text-doom-hi",
752
+ children: "Po"
753
+ })]
754
+ }), /* @__PURE__ */ jsxs("span", {
755
+ className: "flex items-center gap-1",
756
+ children: [/* @__PURE__ */ jsxs(Button, {
757
+ variant: remote.status === "failed" ? "danger-outline" : "outline",
758
+ size: "md",
759
+ "data-testid": "remote-access-open",
760
+ title: remoteDetail ?? "Remote access",
761
+ "aria-label": remoteDetail ?? `Remote access is ${remote.status}`,
762
+ onClick: actions.openRemoteAccess,
763
+ className: `gap-1.5 rounded-md ${remote.status === "failed" ? "" : "bg-doom-panel"}`,
764
+ children: [live ? /* @__PURE__ */ jsx(Dot, {
765
+ tone: "yellow",
766
+ pulse: true,
767
+ "data-testid": "remote-access-live"
768
+ }) : /* @__PURE__ */ jsx(ShieldIcon, { className: "size-4" }), "Remote access"]
769
+ }), /* @__PURE__ */ jsx(Button, {
770
+ variant: "ghost",
771
+ size: "icon",
772
+ "data-testid": "mobile-sessions-close",
773
+ title: "Hide sessions",
774
+ "aria-label": "Hide sessions",
775
+ onClick: onClose,
776
+ className: "text-doom-dim md:hidden",
777
+ children: /* @__PURE__ */ jsx(CloseIcon, { className: "size-3.5" })
778
+ })]
779
+ })]
780
+ }),
781
+ /* @__PURE__ */ jsxs("div", {
782
+ className: "flex items-center justify-between px-5 pt-4 pb-2",
783
+ children: [/* @__PURE__ */ jsx("h2", {
784
+ className: "text-lg font-semibold text-doom-hi",
785
+ children: "Workspaces"
786
+ }), /* @__PURE__ */ jsx(Button, {
787
+ variant: "ghost",
788
+ size: "icon",
789
+ "data-testid": "add-workspace-open",
790
+ title: "Add workspace",
791
+ "aria-label": "Add workspace",
792
+ onClick: actions.openAddWorkspace,
793
+ className: "text-doom-faint hover:text-doom-hi",
794
+ children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
795
+ })]
796
+ }),
797
+ /* @__PURE__ */ jsxs("div", {
798
+ className: "flex flex-col gap-4 px-3 pb-3",
799
+ children: [rail.workspaces.map((workspace) => /* @__PURE__ */ jsx(AdvancedWorkspaceGroup, {
800
+ workspace,
801
+ actions
802
+ }, workspace.id)), rail.workspaces.length === 0 ? /* @__PURE__ */ jsxs("div", {
803
+ className: "flex flex-col gap-3 rounded-lg border border-dashed border-doom-border px-4 py-5",
804
+ children: [/* @__PURE__ */ jsx("p", {
805
+ className: "text-sm text-doom-dim",
806
+ children: "Add a folder to start working with Po."
807
+ }), /* @__PURE__ */ jsxs(Button, {
808
+ variant: "outline",
809
+ size: "md",
810
+ "data-testid": "add-workspace-empty",
811
+ onClick: actions.openAddWorkspace,
812
+ className: "justify-start gap-2 bg-doom-panel",
813
+ children: [/* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }), "Add workspace"]
814
+ })]
815
+ }) : null]
816
+ }),
817
+ rail.pluginContent,
818
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
819
+ /* @__PURE__ */ jsxs("div", {
820
+ className: "sticky bottom-0 mt-auto shrink-0 bg-doom-rail",
821
+ children: [remote.status === "off" ? null : /* @__PURE__ */ jsxs("div", {
822
+ "data-testid": "remote-banner",
823
+ className: `flex flex-col gap-1.5 border-t border-doom-border-soft px-4 py-2 text-sm ${remote.status === "failed" ? "bg-doom-tint-red text-doom-red" : "bg-doom-tint-yellow text-doom-yellow"}`,
824
+ children: [/* @__PURE__ */ jsxs("output", {
825
+ className: "flex min-w-0 items-center gap-2",
826
+ children: [/* @__PURE__ */ jsx(Dot, { tone: remote.status === "failed" ? "red" : "yellow" }), /* @__PURE__ */ jsx("span", {
827
+ className: "truncate",
828
+ children: remote.status === "failed" ? `Remote access failed: ${remote.error ?? "the tunnel stopped"}` : `Remote access is ${remote.status === "starting" ? "starting" : "on"}${remote.host === void 0 ? "" : ` · ${remote.host}`} · ${String(remote.deviceCount)} device${remote.deviceCount === 1 ? "" : "s"} paired`
829
+ })]
830
+ }), /* @__PURE__ */ jsxs("span", {
831
+ className: "flex items-center justify-end gap-1",
832
+ children: [/* @__PURE__ */ jsx(Button, {
833
+ variant: "ghost",
834
+ size: "sm",
835
+ "data-testid": "remote-banner-open",
836
+ onClick: actions.openRemoteAccess,
837
+ children: "Manage"
838
+ }), /* @__PURE__ */ jsx(Button, {
839
+ variant: "ghost",
840
+ size: "sm",
841
+ "data-testid": "remote-banner-off",
842
+ onClick: actions.turnRemoteAccessOff,
843
+ children: "Turn off"
844
+ })]
845
+ })]
846
+ }), /* @__PURE__ */ jsxs("div", {
847
+ className: "flex flex-col gap-1 border-t border-doom-border-soft px-3 py-3",
848
+ children: [/* @__PURE__ */ jsxs(Button, {
849
+ variant: "ghost",
850
+ size: "lg",
851
+ "data-testid": "settings-open",
852
+ onClick: actions.openSettings,
853
+ className: "justify-start gap-2.5 px-2 text-doom-text",
854
+ children: [/* @__PURE__ */ jsx(GearIcon, { className: "size-5 text-doom-dim" }), "Settings"]
855
+ }), /* @__PURE__ */ jsxs("span", {
856
+ className: "px-2 text-sm text-doom-dim max-sm:hidden",
857
+ children: [
858
+ /* @__PURE__ */ jsx(Kbd, {
859
+ className: "bg-doom-panel",
860
+ children: "ctrl+k"
861
+ }),
862
+ " commands · ",
863
+ /* @__PURE__ */ jsx(Kbd, {
864
+ className: "bg-doom-panel",
865
+ children: "ctrl+t"
866
+ }),
867
+ " new session"
868
+ ]
869
+ })]
870
+ })]
871
+ })
872
+ ]
873
+ });
874
+ }
875
+ //#endregion
876
+ //#region src/extensions/(frontend)/template/_components/AdvancedLayout.tsx
877
+ /** This application's full cockpit frame, with each view mounted once. */
878
+ function AdvancedLayout({ view, slots, rail, navigationOpen, desktopActivityOpen, mobileActivityOpen, onNavigationOpenChange, onDesktopActivityOpenChange, onMobileActivityOpenChange }) {
879
+ const { scrollRef } = rail;
880
+ const conversation = view === "conversation" || view === "welcome";
881
+ const activityDockClass = mobileActivityOpen ? desktopActivityOpen ? "fixed inset-y-0 right-0 z-40 flex lg:static lg:z-auto" : "fixed inset-y-0 right-0 z-40 flex lg:hidden" : desktopActivityOpen ? "hidden lg:flex" : "hidden";
882
+ return /* @__PURE__ */ jsxs("div", {
883
+ className: "relative flex h-full min-w-0 overflow-hidden",
884
+ children: [
885
+ /* @__PURE__ */ jsx("aside", {
886
+ ref: scrollRef,
887
+ "data-testid": "session-rail-panel",
888
+ className: `fixed inset-y-0 left-0 z-40 flex w-[min(320px,calc(100vw-48px))] shrink-0 flex-col overflow-y-auto border-r border-doom-border-soft bg-doom-rail transition-transform md:visible md:static md:z-auto md:w-[320px] md:translate-x-0 ${navigationOpen ? "visible translate-x-0" : "invisible -translate-x-full"}`,
889
+ children: /* @__PURE__ */ jsx(AdvancedRail, {
890
+ rail,
891
+ onClose: () => onNavigationOpenChange(false)
892
+ })
893
+ }),
894
+ navigationOpen ? /* @__PURE__ */ jsx(Button, {
895
+ variant: "ghost",
896
+ "data-testid": "mobile-drawer-backdrop",
897
+ "aria-label": "Hide sessions",
898
+ className: "fixed inset-0 z-30 h-auto w-auto rounded-none bg-black/55 p-0 hover:bg-black/55 md:hidden",
899
+ onClick: () => onNavigationOpenChange(false)
900
+ }) : null,
901
+ /* @__PURE__ */ jsxs("main", {
902
+ className: "flex min-w-0 flex-1 flex-col overflow-hidden",
903
+ children: [
904
+ slots.notices,
905
+ slots.header({
906
+ navigationToggle: "mobile",
907
+ activityToggle: "mobile"
908
+ }),
909
+ /* @__PURE__ */ jsxs("div", {
910
+ "data-testid": "template-content",
911
+ className: conversation ? "mx-auto flex min-h-0 w-full max-w-[960px] flex-1 flex-col overflow-hidden px-2 sm:px-4" : "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
912
+ children: [
913
+ slots.content,
914
+ slots.composer,
915
+ slots.controls
916
+ ]
917
+ })
918
+ ]
919
+ }),
920
+ slots.activity == null ? null : desktopActivityOpen || mobileActivityOpen ? /* @__PURE__ */ jsx("div", {
921
+ className: activityDockClass,
922
+ children: slots.activity
923
+ }) : /* @__PURE__ */ jsx(Button, {
924
+ variant: "ghost",
925
+ "data-testid": "activity-show",
926
+ title: "Show activity",
927
+ onClick: () => onDesktopActivityOpenChange(true),
928
+ className: "hidden h-auto shrink-0 rounded-none border-l border-doom-border-soft bg-doom-rail px-2 py-3 font-semibold text-doom-dim hover:bg-doom-panel hover:text-doom-hi lg:flex",
929
+ style: { writingMode: "vertical-rl" },
930
+ children: "Activity"
931
+ }),
932
+ mobileActivityOpen ? /* @__PURE__ */ jsx(Button, {
933
+ variant: "ghost",
934
+ "data-testid": "mobile-activity-backdrop",
935
+ "aria-label": "Hide activity",
936
+ className: "fixed inset-0 z-30 h-auto w-auto rounded-none bg-black/55 p-0 hover:bg-black/55 lg:hidden",
937
+ onClick: () => onMobileActivityOpenChange(false)
938
+ }) : null,
939
+ rail.addWorkspace ? /* @__PURE__ */ jsx(AdvancedAddWorkspaceDialog, {
940
+ suggestedPaths: rail.addWorkspace.suggestedPaths,
941
+ actions: rail.actions
942
+ }) : null
943
+ ]
944
+ });
945
+ }
946
+ //#endregion
947
+ //#region src/extensions/(frontend)/template/_components/AdvancedConversation.tsx
948
+ function AdvancedConversation() {
949
+ useWebPluginRegistry();
950
+ const dockOpen = useStore(uiStore, (state) => state.dockOpen);
951
+ const [railOpen, setRailOpen] = useState(false);
952
+ const [mobileActivityOpen, setMobileActivityOpen] = useState(false);
953
+ const { sessionId, tabId } = useDashboardRoute();
954
+ const slotProps = usePluginSlotProps(sessionId ?? null);
955
+ const sessionWorkspaceId = useStore(sessionsStore, (state) => sessionId === void 0 ? void 0 : state.byId[sessionId]?.summary.workspaceId);
956
+ const selectedWorkspaceId = useStore(workspacesStore, (state) => state.selectedId);
957
+ const mcp = useSessionMcp(sessionId === void 0 ? selectedWorkspaceId ?? void 0 : sessionWorkspaceId, sessionId);
958
+ const mcpView = tabId === "mcp";
959
+ const mcpEnabled = mcp.snapshot?.enabled === true || mcpView;
960
+ const transferLabel = useStore(sessionsStore, (state) => {
961
+ if (state.transferringToId === null) return null;
962
+ return state.byId[state.transferringToId]?.summary.name ?? "destination session";
963
+ });
964
+ const noSessions = useNoSessions();
965
+ const activeMeta = useActiveSessionMeta();
966
+ const dormantMeta = activeMeta?.summary.dormant === true ? activeMeta : null;
967
+ const dialogId = useActiveSession((state) => state.dialog?.id ?? null);
968
+ const storedTransientTab = useStore(transientTabsStore, (state) => findTransientTab(state, sessionId, tabId));
969
+ const transientTab = storedTransientTab === void 0 ? void 0 : pluginActivityGroups().map((group) => group.transientTab?.()).find((candidate) => candidate?.id === storedTransientTab.id) ?? storedTransientTab;
970
+ const tab = (tabId === void 0 ? void 0 : webTabs().find((entry) => entry.id === tabId)) ?? transientTab;
971
+ const composerContext = tab === transientTab ? transientTab?.composerContext : void 0;
972
+ const routeKey = `${sessionId ?? ""}\u0000${tabId ?? ""}`;
973
+ const [lastRouteKey, setLastRouteKey] = useState(routeKey);
974
+ if (lastRouteKey !== routeKey) {
975
+ setLastRouteKey(routeKey);
976
+ setRailOpen(false);
977
+ setMobileActivityOpen(false);
978
+ }
979
+ const [lastDialogId, setLastDialogId] = useState(dialogId);
980
+ if (lastDialogId !== dialogId) {
981
+ setLastDialogId(dialogId);
982
+ if (dialogId !== null) {
983
+ setMobileActivityOpen(false);
984
+ setRailOpen(false);
985
+ }
986
+ }
987
+ const rail = useSessionRail({ onDismiss: () => setRailOpen(false) });
988
+ const closeActivity = () => {
989
+ if (mobileActivityOpen) setMobileActivityOpen(false);
990
+ else setDockOpen(false);
991
+ };
992
+ return /* @__PURE__ */ jsxs("div", {
993
+ "data-testid": "cockpit",
994
+ className: "h-full min-w-0 overflow-hidden",
995
+ children: [/* @__PURE__ */ jsx(AdvancedLayout, {
996
+ view: mcpView || tab ? "panel" : noSessions ? "welcome" : "conversation",
997
+ navigationOpen: railOpen,
998
+ desktopActivityOpen: dockOpen,
999
+ mobileActivityOpen,
1000
+ onNavigationOpenChange: setRailOpen,
1001
+ onDesktopActivityOpenChange: setDockOpen,
1002
+ onMobileActivityOpenChange: setMobileActivityOpen,
1003
+ rail,
1004
+ slots: {
1005
+ header: (options) => /* @__PURE__ */ jsx(TopBar, {
1006
+ ...options,
1007
+ view: mcpView ? "mcp" : tab?.id ?? "conversation",
1008
+ mcpEnabled,
1009
+ onShowSessions: () => {
1010
+ setMobileActivityOpen(false);
1011
+ setRailOpen(true);
1012
+ },
1013
+ onShowActivity: () => {
1014
+ setRailOpen(false);
1015
+ setMobileActivityOpen(true);
1016
+ }
1017
+ }),
1018
+ notices: transferLabel === null ? null : /* @__PURE__ */ jsxs("output", {
1019
+ "data-testid": "voice-transfer-transition",
1020
+ className: "border-b border-doom-cyan/30 bg-doom-cyan/10 px-4 py-2 text-center text-base font-medium text-doom-cyan",
1021
+ children: [
1022
+ "Transferring voice to ",
1023
+ transferLabel,
1024
+ "..."
1025
+ ]
1026
+ }),
1027
+ content: mcpView ? /* @__PURE__ */ jsx(SessionMcp, { state: mcp }, sessionId) : tab ? /* @__PURE__ */ jsx(tab.panel, { ...slotProps }) : noSessions ? /* @__PURE__ */ jsx(WelcomePanel, {}) : /* @__PURE__ */ jsx(Timeline, {}),
1028
+ composer: mcpView ? null : tab ? tab.retainComposer === true ? /* @__PURE__ */ jsx(Composer, { composerContext }) : null : noSessions ? null : /* @__PURE__ */ jsx(Composer, {}),
1029
+ controls: !mcpView && !tab && !noSessions && dormantMeta === null ? /* @__PURE__ */ jsx(SelectionBar, {}) : null,
1030
+ activity: /* @__PURE__ */ jsx(ActivityDock, {
1031
+ mcp,
1032
+ onClose: closeActivity,
1033
+ onOpenContent: () => setMobileActivityOpen(false)
1034
+ })
1035
+ }
1036
+ }), /* @__PURE__ */ jsx(SessionRailDialogs, {})]
1037
+ });
1038
+ }
1039
+ //#endregion
1040
+ //#region src/extensions/(frontend)/template/_components/AdvancedSettings.tsx
1041
+ /** Settings have their own page frame, independent of session navigation. */
1042
+ function AdvancedSettings() {
1043
+ useWebPluginRegistry();
1044
+ const { section, workspaceId } = useDashboardRoute();
1045
+ const navigate = useNavigate();
1046
+ const activeId = useStore(sessionsStore, (state) => state.activeId);
1047
+ const [loadError, setLoadError] = useState();
1048
+ const [repositoryReady, setRepositoryReady] = useState(false);
1049
+ const [templateTarget, setTemplateTarget] = useDashboardState("settings/template-target", "global");
1050
+ const current = settingsSection(section);
1051
+ const workspace = current?.workspace ?? (section?.startsWith("repository-") ? "repository" : "general");
1052
+ useEffect(() => {
1053
+ if (current) return;
1054
+ if (workspace === "repository" && !repositoryReady) return;
1055
+ let cancelled = false;
1056
+ refreshWebPluginCompositions().then(() => {
1057
+ if (cancelled || settingsSection(section)) return;
1058
+ navigate({
1059
+ to: "/settings/$section",
1060
+ params: { section: DEFAULT_SETTINGS_SECTION },
1061
+ search: { workspace: workspaceId },
1062
+ replace: true
1063
+ });
1064
+ }).catch((error) => {
1065
+ if (!cancelled) setLoadError(error instanceof Error ? error.message : String(error));
1066
+ });
1067
+ return () => {
1068
+ cancelled = true;
1069
+ };
1070
+ }, [
1071
+ current,
1072
+ section,
1073
+ navigate,
1074
+ workspace,
1075
+ workspaceId,
1076
+ repositoryReady
1077
+ ]);
1078
+ return /* @__PURE__ */ jsxs("div", {
1079
+ "data-testid": "settings",
1080
+ className: "flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-doom-bg",
1081
+ children: [/* @__PURE__ */ jsxs("header", {
1082
+ className: "grid shrink-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-2 gap-y-2 border-b border-doom-border px-3 py-2 sm:h-14 sm:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] sm:px-6 sm:py-0",
1083
+ children: [
1084
+ /* @__PURE__ */ jsx("h1", {
1085
+ className: "order-1 min-w-0 truncate text-xl font-semibold text-doom-hi sm:order-none",
1086
+ children: "Settings"
1087
+ }),
1088
+ /* @__PURE__ */ jsxs("nav", {
1089
+ className: "order-3 col-span-2 flex w-full items-center gap-1 rounded-lg bg-doom-panel p-1 sm:order-none sm:col-span-1 sm:w-auto",
1090
+ "aria-label": "settings workspace",
1091
+ children: [/* @__PURE__ */ jsx(NavTab, {
1092
+ asChild: true,
1093
+ active: workspace === "general",
1094
+ className: "min-h-8 flex-1 justify-center rounded-md px-4 focus-visible:ring-2 focus-visible:ring-doom-green sm:flex-none",
1095
+ "aria-current": workspace === "general" ? "true" : void 0,
1096
+ children: /* @__PURE__ */ jsx(Link, {
1097
+ to: "/settings/$section",
1098
+ params: { section: DEFAULT_SETTINGS_SECTION },
1099
+ search: { workspace: workspaceId },
1100
+ "data-testid": "settings-workspace-general",
1101
+ children: "General"
1102
+ })
1103
+ }), /* @__PURE__ */ jsx(NavTab, {
1104
+ asChild: true,
1105
+ active: workspace === "repository",
1106
+ className: "min-h-8 flex-1 justify-center rounded-md px-4 focus-visible:ring-2 focus-visible:ring-doom-green sm:flex-none",
1107
+ "aria-current": workspace === "repository" ? "true" : void 0,
1108
+ children: /* @__PURE__ */ jsx(Link, {
1109
+ to: "/settings/$section",
1110
+ params: { section: DEFAULT_REPOSITORY_SETTINGS_SECTION },
1111
+ search: { workspace: workspaceId },
1112
+ "data-testid": "settings-workspace-repository",
1113
+ children: "Workspaces"
1114
+ })
1115
+ })]
1116
+ }),
1117
+ /* @__PURE__ */ jsx(Button, {
1118
+ asChild: true,
1119
+ variant: "ghost",
1120
+ size: "sm",
1121
+ className: "order-2 sm:order-none sm:justify-self-end",
1122
+ children: activeId !== null ? /* @__PURE__ */ jsxs(Link, {
1123
+ to: "/session/$sessionId",
1124
+ params: { sessionId: activeId },
1125
+ "data-testid": "settings-close",
1126
+ children: [/* @__PURE__ */ jsx("span", {
1127
+ className: "sm:hidden",
1128
+ children: "Back"
1129
+ }), /* @__PURE__ */ jsx("span", {
1130
+ className: "hidden sm:inline",
1131
+ children: "Back to session"
1132
+ })]
1133
+ }) : /* @__PURE__ */ jsx(Link, {
1134
+ to: "/",
1135
+ "data-testid": "settings-close",
1136
+ children: "Back"
1137
+ })
1138
+ })
1139
+ ]
1140
+ }), /* @__PURE__ */ jsx("main", {
1141
+ className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
1142
+ children: workspace === "repository" ? /* @__PURE__ */ jsx(RepositoryWorkspace, {
1143
+ current: current ?? {
1144
+ id: section ?? "",
1145
+ label: "",
1146
+ detail: "",
1147
+ workspace: "repository"
1148
+ },
1149
+ initialRepositoryId: workspaceId,
1150
+ onRepositoryChange: (nextWorkspaceId) => {
1151
+ navigate({
1152
+ to: "/settings/$section",
1153
+ params: { section: current?.id ?? section ?? DEFAULT_REPOSITORY_SETTINGS_SECTION },
1154
+ search: { workspace: nextWorkspaceId },
1155
+ replace: true
1156
+ });
1157
+ },
1158
+ onReady: setRepositoryReady
1159
+ }, workspaceId ?? "remembered-workspace") : /* @__PURE__ */ jsxs("div", {
1160
+ className: "flex min-h-0 flex-1 flex-col lg:flex-row",
1161
+ children: [/* @__PURE__ */ jsx(SettingsMenu, {
1162
+ active: current?.id,
1163
+ workspace: "general",
1164
+ repositoryId: workspaceId
1165
+ }), /* @__PURE__ */ jsx("section", {
1166
+ "data-testid": "settings-content",
1167
+ className: "min-w-0 flex-1 overflow-y-auto px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10",
1168
+ children: /* @__PURE__ */ jsxs("div", {
1169
+ className: "mx-auto flex w-full max-w-[720px] flex-col gap-8",
1170
+ children: [
1171
+ !current && loadError ? /* @__PURE__ */ jsx("p", {
1172
+ role: "alert",
1173
+ children: loadError
1174
+ }) : null,
1175
+ current?.id === "providers" ? /* @__PURE__ */ jsx(ProviderSettings, {}) : null,
1176
+ current?.id === "appearance" ? /* @__PURE__ */ jsx(AppearanceSettings, {
1177
+ templateTarget,
1178
+ onTemplateTargetChange: setTemplateTarget
1179
+ }) : null,
1180
+ current?.id === "notifications" ? /* @__PURE__ */ jsx(NotificationSettings, {}) : null,
1181
+ current?.id === "images" ? /* @__PURE__ */ jsx(ImageSettings, {}) : null,
1182
+ current?.id === "remote" ? /* @__PURE__ */ jsx(RemoteControlSettings, {}) : null,
1183
+ current?.id === "plugins" ? /* @__PURE__ */ jsx(PluginSettings, {}) : null,
1184
+ current?.contribution === void 0 ? null : /* @__PURE__ */ jsx(ContributedSettings, {
1185
+ section: current.contribution,
1186
+ scope: "global"
1187
+ }),
1188
+ current?.panel === void 0 ? null : /* @__PURE__ */ jsx(SettingsPanelHost, { panel: current.panel })
1189
+ ]
1190
+ })
1191
+ })]
1192
+ })
1193
+ })]
1194
+ });
1195
+ }
1196
+ //#endregion
1197
+ //#region src/extensions/(frontend)/template/_components/AdvancedDashboard.tsx
1198
+ const renderers = {
1199
+ thread: (id, threadId, options) => /* @__PURE__ */ jsx(ThreadView, {
1200
+ sessionId: id,
1201
+ threadId,
1202
+ options
1203
+ }),
1204
+ activity: (id, open) => /* @__PURE__ */ jsx(SessionActivity, {
1205
+ sessionId: id,
1206
+ onOpenConversation: open
1207
+ }),
1208
+ transcript: (id, options) => /* @__PURE__ */ jsx(SessionTranscriptView, {
1209
+ sessionId: id,
1210
+ options,
1211
+ watch: watchDashboardTranscript
1212
+ })
1213
+ };
1214
+ function AdvancedDashboard() {
1215
+ const route = useDashboardRoute();
1216
+ const sessionId = useStore(sessionsStore, (state) => state.activeId);
1217
+ return /* @__PURE__ */ jsxs(DashboardRendererProvider, {
1218
+ value: renderers,
1219
+ children: [
1220
+ route.kind === "settings" ? /* @__PURE__ */ jsx(AdvancedSettings, {}) : /* @__PURE__ */ jsx(AdvancedConversation, {}),
1221
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1222
+ /* @__PURE__ */ jsx(RefusedCard, {}),
1223
+ /* @__PURE__ */ jsx(CommandPalette, {}),
1224
+ /* @__PURE__ */ jsx(PluginSurface, {
1225
+ slot: HOST_SLOTS.overlay,
1226
+ sessionId
1227
+ }),
1228
+ /* @__PURE__ */ jsx(RemoteAccessDialog, {}),
1229
+ /* @__PURE__ */ jsx(PairingApprovalDialog, {})
1230
+ ]
1231
+ });
1232
+ }
1233
+ //#endregion
1234
+ //#region src/extensions/(frontend)/template/_lib/definition.ts
1235
+ const advancedTemplate = {
1236
+ label: "Advanced",
1237
+ description: "The full cockpit with persistent session navigation and an activity dock.",
1238
+ contractVersion: 2,
1239
+ app: AdvancedDashboard
1240
+ };
1241
+ //#endregion
1242
+ //#region src/extensions/(frontend)/template/po-template-advanced.web.ts
1243
+ var po_template_advanced_web_default$2 = defineWebTemplate(advancedTemplate);
1244
+ //#endregion
1245
+ //#region src/extensions/workspaces/(frontend)/template/po-template-advanced.web.ts
1246
+ var po_template_advanced_web_default$1 = defineWebTemplate(advancedTemplate);
1247
+ //#endregion
1248
+ //#region src/extensions/workspaces/sessions/(frontend)/template/po-template-advanced.web.ts
1249
+ var po_template_advanced_web_default = defineWebTemplate(advancedTemplate);
1250
+ //#endregion
1251
+ //#region generated/web.ts
1252
+ const via = (identity, value) => value === void 0 ? void 0 : {
1253
+ ...identity,
1254
+ ...value
1255
+ };
1256
+ const webPlugin = defineWebPlugin({
1257
+ id: "template-advanced",
1258
+ global: { get templates() {
1259
+ return [via({ id: "po-template-advanced" }, po_template_advanced_web_default$2)];
1260
+ } },
1261
+ workspace: { get templates() {
1262
+ return [via({ id: "po-template-advanced" }, po_template_advanced_web_default$1)];
1263
+ } },
1264
+ session: { get templates() {
1265
+ return [via({ id: "po-template-advanced" }, po_template_advanced_web_default)];
1266
+ } }
1267
+ });
1268
+ //#endregion
1269
+ export { webPlugin };
1270
+
1271
+ //# sourceMappingURL=web.mjs.map