@agimon-ai/doompi-template-elegant 0.0.1-alpha.2 → 0.0.1-alpha.20

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 CHANGED
@@ -15,7 +15,7 @@ web:
15
15
 
16
16
  Global defaults live in `~/.pi/.doom/config.yaml`. Workspace defaults live in `.doom/config.yaml` and override the global value. Clearing a workspace default restores inheritance.
17
17
 
18
- A template changes presentation, not the active tools, session runtime, or color theme. The host supplies navigation, header, notices, content, composer, session controls, and activity slots. Approval and security overlays stay outside the template.
18
+ A template changes presentation, not the active tools, session runtime, or color theme. The host supplies header, notices, content, composer, session controls, and activity slots, plus the session rail as data and actions (`rail`). The template renders the rail itself: workspaces, session cards, and the add-workspace dialog (render that dialog at the layout root so it opens while a drawer is closed). The new-session, resume, and remote-access dialogs, the keyboard shortcuts, approval and security overlays stay with the host.
19
19
 
20
20
  ## Package contract
21
21
 
@@ -1,9 +1,767 @@
1
1
  import { defineWebPlugin, defineWebTemplate } from "@agimon-ai/doompi-core/web";
2
- import { Drawer } from "@agimon-ai/doompi-web-components";
3
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { AlertIcon, Avatar, AvatarFallback, AvatarImage, BranchIcon, Button, ChevronUpIcon, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Dot, Drawer, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, ForkIcon, GearIcon, Input, KebabIcon, OptionRow, PlusIcon, ShieldIcon, buttonVariants, cn } from "@agimon-ai/doompi-web-components";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/extensions/(frontend)/template/_components/ElegantFolderBrowser.tsx
6
+ function folderName$1(path) {
7
+ return path.slice(path.lastIndexOf("/") + 1) || path;
8
+ }
9
+ /** Walks the server's folders inline: up, into a child, or pick the current folder. */
10
+ function ElegantFolderBrowser({ start, listDirectory, onSelect }) {
11
+ const [target, setTarget] = useState(start);
12
+ const [listing, setListing] = useState(null);
13
+ const [error, setError] = useState("");
14
+ useEffect(() => {
15
+ let stale = false;
16
+ listDirectory(target).then((result) => {
17
+ if (stale) return;
18
+ if ("error" in result) setError(result.error);
19
+ else {
20
+ setError("");
21
+ setListing(result);
22
+ }
23
+ });
24
+ return () => {
25
+ stale = true;
26
+ };
27
+ }, [target, listDirectory]);
28
+ return /* @__PURE__ */ jsxs("div", {
29
+ "data-testid": "folder-browser",
30
+ className: "flex flex-col gap-1 rounded-lg border border-doom-border-soft bg-doom-panel/40 p-1.5",
31
+ children: [
32
+ /* @__PURE__ */ jsxs("div", {
33
+ className: "flex items-center gap-1 px-1",
34
+ children: [
35
+ /* @__PURE__ */ jsx(Button, {
36
+ variant: "ghost",
37
+ size: "icon",
38
+ "data-testid": "folder-browser-up",
39
+ title: "parent folder",
40
+ "aria-label": "parent folder",
41
+ disabled: listing?.parent === void 0,
42
+ onClick: () => listing?.parent !== void 0 && setTarget(listing.parent),
43
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "h-3 w-3" })
44
+ }),
45
+ /* @__PURE__ */ jsx("span", {
46
+ className: "min-w-0 flex-1 truncate text-xs text-doom-dim",
47
+ title: listing?.path,
48
+ children: listing?.path ?? "loading…"
49
+ }),
50
+ /* @__PURE__ */ jsx(Button, {
51
+ variant: "outline",
52
+ size: "xs",
53
+ "data-testid": "folder-browser-select",
54
+ disabled: listing === null,
55
+ onClick: () => listing !== null && onSelect(listing.path),
56
+ children: "use this folder"
57
+ })
58
+ ]
59
+ }),
60
+ error ? /* @__PURE__ */ jsx("p", {
61
+ className: "px-2 py-1 text-xs text-doom-red",
62
+ children: error
63
+ }) : null,
64
+ /* @__PURE__ */ jsxs("div", {
65
+ role: "listbox",
66
+ "aria-label": "folders",
67
+ className: "max-h-40 overflow-y-auto",
68
+ children: [listing !== null && listing.directories.length === 0 ? /* @__PURE__ */ jsx("p", {
69
+ className: "px-2 py-1 text-2xs text-doom-faint",
70
+ children: "no folders here"
71
+ }) : null, listing?.directories.map((directory) => /* @__PURE__ */ jsx(OptionRow, {
72
+ density: "compact",
73
+ "data-testid": "folder-browser-entry",
74
+ onClick: () => setTarget(directory),
75
+ className: "w-full px-2.5 py-1 text-sm",
76
+ children: folderName$1(directory)
77
+ }, directory))]
78
+ })
79
+ ]
80
+ });
81
+ }
82
+ //#endregion
83
+ //#region src/extensions/(frontend)/template/_components/ElegantAddWorkspaceDialog.tsx
84
+ const SUGGESTION_DEBOUNCE_MS = 120;
85
+ const NO_HIGHLIGHT = -1;
86
+ const TRAILING_SEPARATORS = /\/+$/u;
87
+ function folderName(path) {
88
+ const trimmed = path.trim().replace(TRAILING_SEPARATORS, "");
89
+ return trimmed.slice(trimmed.lastIndexOf("/") + 1);
90
+ }
91
+ /** Adds a workspace by name, by folder, or both; a name alone gets a new default folder. */
92
+ function ElegantAddWorkspaceDialog({ suggestedPaths, actions }) {
93
+ const [name, setName] = useState("");
94
+ const [nameEdited, setNameEdited] = useState(false);
95
+ const [path, setPath] = useState(suggestedPaths[0] ?? "");
96
+ const [browsing, setBrowsing] = useState(false);
97
+ const [suggestions, setSuggestions] = useState([]);
98
+ const [highlight, setHighlight] = useState(NO_HIGHLIGHT);
99
+ const [error, setError] = useState("");
100
+ const [busy, setBusy] = useState(false);
101
+ const pathInput = useRef(null);
102
+ const shownName = nameEdited ? name : folderName(path);
103
+ useEffect(() => {
104
+ const typed = path.trim();
105
+ if (typed === "") return;
106
+ let stale = false;
107
+ const timer = setTimeout(async () => {
108
+ const found = await actions.searchDirectories(typed);
109
+ if (stale) return;
110
+ setSuggestions(found.filter((directory) => directory !== typed));
111
+ setHighlight(NO_HIGHLIGHT);
112
+ }, SUGGESTION_DEBOUNCE_MS);
113
+ return () => {
114
+ stale = true;
115
+ clearTimeout(timer);
116
+ };
117
+ }, [path, actions]);
118
+ const changePath = (next) => {
119
+ setPath(next);
120
+ if (next.trim() === "") setSuggestions([]);
121
+ };
122
+ const pick = (directory) => {
123
+ changePath(`${directory}/`);
124
+ pathInput.current?.focus();
125
+ };
126
+ const trimmedPath = path.trim().replace(TRAILING_SEPARATORS, "") || (path.trim() === "" ? "" : "/");
127
+ const canSubmit = !busy && (trimmedPath !== "" || shownName.trim() !== "");
128
+ const submit = async () => {
129
+ if (!canSubmit) return;
130
+ setBusy(true);
131
+ setError("");
132
+ const result = await actions.addWorkspace({
133
+ name: shownName.trim(),
134
+ ...trimmedPath === "" ? {} : { path: trimmedPath }
135
+ });
136
+ setBusy(false);
137
+ if (result !== void 0) setError(result.error);
138
+ };
139
+ return /* @__PURE__ */ jsx(Dialog, {
140
+ open: true,
141
+ onOpenChange: (next) => !next && !busy && actions.closeAddWorkspace(),
142
+ children: /* @__PURE__ */ jsxs(DialogContent, {
143
+ width: "md",
144
+ "data-testid": "add-workspace-dialog",
145
+ "aria-describedby": void 0,
146
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "add workspace" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
147
+ /* @__PURE__ */ jsxs("label", {
148
+ htmlFor: "add-workspace-name",
149
+ className: "flex flex-col gap-1",
150
+ children: [/* @__PURE__ */ jsx("span", {
151
+ className: "text-xs text-doom-faint",
152
+ children: "name"
153
+ }), /* @__PURE__ */ jsx(Input, {
154
+ id: "add-workspace-name",
155
+ "data-testid": "add-workspace-name",
156
+ value: shownName,
157
+ autoFocus: true,
158
+ onChange: (event) => {
159
+ setNameEdited(true);
160
+ setName(event.target.value);
161
+ },
162
+ onKeyDown: (event) => {
163
+ if (event.key === "Enter") submit();
164
+ },
165
+ placeholder: "my-project"
166
+ })]
167
+ }),
168
+ /* @__PURE__ */ jsxs("label", {
169
+ htmlFor: "add-workspace-root",
170
+ className: "flex flex-col gap-1",
171
+ children: [/* @__PURE__ */ jsxs("span", {
172
+ className: "text-xs text-doom-faint",
173
+ children: ["folder ", /* @__PURE__ */ jsx("span", {
174
+ className: "text-doom-faint",
175
+ children: "(optional; type a path or browse)"
176
+ })]
177
+ }), /* @__PURE__ */ jsxs("span", {
178
+ className: "flex items-center gap-1.5",
179
+ children: [/* @__PURE__ */ jsx(Input, {
180
+ id: "add-workspace-root",
181
+ ref: pathInput,
182
+ "data-testid": "add-workspace-root",
183
+ value: path,
184
+ onChange: (event) => changePath(event.target.value),
185
+ onKeyDown: (event) => {
186
+ if (event.key === "ArrowDown" && suggestions.length > 0) {
187
+ event.preventDefault();
188
+ setHighlight((current) => (current + 1) % suggestions.length);
189
+ } else if (event.key === "ArrowUp" && suggestions.length > 0) {
190
+ event.preventDefault();
191
+ setHighlight((current) => current <= 0 ? suggestions.length - 1 : current - 1);
192
+ } else if (event.key === "Enter") {
193
+ const chosen = highlight === NO_HIGHLIGHT ? void 0 : suggestions[highlight];
194
+ if (chosen === void 0) submit();
195
+ else pick(chosen);
196
+ }
197
+ },
198
+ placeholder: "~/workspace/my-project",
199
+ className: "min-w-0 flex-1"
200
+ }), /* @__PURE__ */ jsx(Button, {
201
+ variant: browsing ? "subtle" : "outline",
202
+ size: "md",
203
+ "data-testid": "add-workspace-browse",
204
+ "aria-pressed": browsing,
205
+ onClick: () => setBrowsing((open) => !open),
206
+ children: "browse"
207
+ })]
208
+ })]
209
+ }),
210
+ browsing ? /* @__PURE__ */ jsx(ElegantFolderBrowser, {
211
+ ...trimmedPath.startsWith("/") ? { start: trimmedPath } : {},
212
+ listDirectory: actions.listDirectory,
213
+ onSelect: (selected) => {
214
+ changePath(selected);
215
+ setBrowsing(false);
216
+ }
217
+ }) : suggestions.length > 0 ? /* @__PURE__ */ jsx("div", {
218
+ role: "listbox",
219
+ "aria-label": "matching directories",
220
+ "data-testid": "add-workspace-suggestions",
221
+ className: "max-h-40 overflow-y-auto rounded-lg border border-doom-border-soft bg-doom-panel/40 p-1.5",
222
+ children: suggestions.map((directory, index) => /* @__PURE__ */ jsx(OptionRow, {
223
+ density: "compact",
224
+ active: index === highlight,
225
+ "data-testid": "add-workspace-suggestion",
226
+ onMouseDown: (event) => event.preventDefault(),
227
+ onMouseEnter: () => setHighlight(index),
228
+ onClick: () => pick(directory),
229
+ className: "w-full px-2.5 py-1 text-sm",
230
+ children: directory
231
+ }, directory))
232
+ }) : null,
233
+ suggestedPaths.length > 0 ? /* @__PURE__ */ jsx("div", {
234
+ className: "flex flex-wrap gap-1",
235
+ children: suggestedPaths.slice(0, 4).map((suggested) => /* @__PURE__ */ jsx(Button, {
236
+ variant: "outline",
237
+ size: "xs",
238
+ onClick: () => changePath(suggested),
239
+ children: suggested
240
+ }, suggested))
241
+ }) : null,
242
+ trimmedPath === "" && shownName.trim() !== "" ? /* @__PURE__ */ jsxs("p", {
243
+ "data-testid": "add-workspace-default-hint",
244
+ className: "text-xs text-doom-faint",
245
+ children: ["creates ~/.pi/.doom/workspace/", shownName.trim()]
246
+ }) : null,
247
+ error ? /* @__PURE__ */ jsx("pre", {
248
+ className: "max-h-28 overflow-y-auto rounded border border-doom-edge-red bg-doom-tint-red/40 px-2.5 py-2 text-xs whitespace-pre-wrap text-doom-red",
249
+ children: error
250
+ }) : null,
251
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
252
+ variant: "outline",
253
+ onClick: () => actions.closeAddWorkspace(),
254
+ disabled: busy,
255
+ children: "cancel"
256
+ }), /* @__PURE__ */ jsx(Button, {
257
+ variant: "primary",
258
+ "data-testid": "add-workspace-confirm",
259
+ onClick: () => void submit(),
260
+ disabled: !canSubmit,
261
+ children: busy ? "adding…" : "add workspace"
262
+ })] })
263
+ ] })]
264
+ })
265
+ });
266
+ }
267
+ //#endregion
268
+ //#region src/extensions/(frontend)/template/_components/ElegantPendingSetup.tsx
269
+ /** A setup-only reservation: no runtime, so it offers removal and nothing else. */
270
+ function ElegantPendingSetup({ sessionId, setup, actions }) {
271
+ const [busy, setBusy] = useState(false);
272
+ const [error, setError] = useState("");
273
+ const remove = async () => {
274
+ if (busy) return;
275
+ setBusy(true);
276
+ setError("");
277
+ const result = await actions.removePendingSetup(sessionId, setup.id);
278
+ setBusy(false);
279
+ if (result !== void 0) setError(result.error);
280
+ };
281
+ return /* @__PURE__ */ jsxs("div", {
282
+ "data-testid": `pending-session-${setup.id}`,
283
+ className: "ml-9 flex flex-col gap-0.5 rounded-lg border border-dashed border-doom-border-soft px-3 py-2",
284
+ children: [
285
+ /* @__PURE__ */ jsxs("div", {
286
+ className: "flex items-center justify-between gap-2",
287
+ children: [/* @__PURE__ */ jsx("span", {
288
+ className: "truncate text-xs font-bold text-doom-hi",
289
+ children: setup.name
290
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
291
+ asChild: true,
292
+ children: /* @__PURE__ */ jsx(Button, {
293
+ variant: "ghost",
294
+ size: "icon",
295
+ title: "session setup actions",
296
+ disabled: busy,
297
+ "data-testid": `pending-session-menu-${setup.id}`,
298
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
299
+ })
300
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, { children: /* @__PURE__ */ jsx(DropdownMenuItem, {
301
+ variant: "destructive",
302
+ onSelect: () => void remove(),
303
+ children: "remove setup"
304
+ }) })] })]
305
+ }),
306
+ /* @__PURE__ */ jsx("p", {
307
+ className: setup.failed ? "text-2xs text-doom-red" : "text-2xs text-doom-faint",
308
+ children: setup.status
309
+ }),
310
+ setup.recovery ? /* @__PURE__ */ jsx("p", {
311
+ role: setup.failed ? "alert" : void 0,
312
+ className: "text-2xs text-doom-faint",
313
+ children: setup.recovery
314
+ }) : null,
315
+ error ? /* @__PURE__ */ jsx("p", {
316
+ role: "alert",
317
+ className: "text-2xs text-doom-red",
318
+ children: error
319
+ }) : null
320
+ ]
321
+ });
322
+ }
323
+ //#endregion
324
+ //#region src/extensions/(frontend)/template/_components/ElegantSessionCard.tsx
325
+ /** A soft tile led by the persona avatar, with the branch as a quiet caption. */
326
+ function ElegantSessionCard({ session, actions }) {
327
+ const [mode, setMode] = useState("view");
328
+ const [draft, setDraft] = useState("");
329
+ const [removeError, setRemoveError] = useState("");
330
+ const modeRef = useRef(mode);
331
+ const enterMode = (next) => {
332
+ modeRef.current = next;
333
+ setMode(next);
334
+ };
335
+ const { active } = session;
336
+ const error = removeError || session.error;
337
+ const tileClass = active ? "rounded-lg bg-doom-selected hover:bg-doom-selected" : "rounded-lg bg-doom-panel/40 hover:bg-doom-panel";
338
+ const commitRename = () => {
339
+ actions.renameSession(session.id, draft);
340
+ enterMode("view");
341
+ };
342
+ const remove = async () => {
343
+ enterMode("view");
344
+ setRemoveError("");
345
+ const result = await actions.removeSession(session.id);
346
+ if (result !== void 0) setRemoveError(result.error);
347
+ };
348
+ const body = /* @__PURE__ */ jsxs("div", {
349
+ className: "flex items-start gap-2.5",
350
+ children: [/* @__PURE__ */ jsxs(Avatar, {
351
+ "data-testid": session.profile ? "session-avatar" : void 0,
352
+ "aria-label": session.profile?.label ?? "DoomPi",
353
+ title: session.profile?.label,
354
+ className: "mt-0.5 h-7 w-7 shrink-0 border border-doom-border-soft bg-doom-deep",
355
+ children: [session.profile?.avatarUrl ? /* @__PURE__ */ jsx(AvatarImage, {
356
+ src: session.profile.avatarUrl,
357
+ alt: ""
358
+ }) : null, /* @__PURE__ */ jsx(AvatarFallback, {
359
+ className: "text-2xs text-doom-magenta",
360
+ children: session.profile?.initials ?? "DP"
361
+ })]
362
+ }), /* @__PURE__ */ jsxs("div", {
363
+ className: "flex min-w-0 flex-1 flex-col gap-0.5",
364
+ children: [
365
+ /* @__PURE__ */ jsxs("div", {
366
+ className: "flex items-center gap-1.5",
367
+ children: [
368
+ session.nested && session.provenance ? /* @__PURE__ */ jsx(ForkIcon, {
369
+ "aria-label": session.provenance === "worktree" ? "automatic per-conversation worktree" : session.provenance,
370
+ className: `h-[11px] w-[11px] shrink-0 ${active ? "text-doom-on-selected" : "text-doom-faint"}`
371
+ }) : null,
372
+ mode === "rename" ? /* @__PURE__ */ jsx(Input, {
373
+ "data-testid": `session-name-input-${session.id}`,
374
+ value: draft,
375
+ autoFocus: true,
376
+ onChange: (event) => setDraft(event.target.value),
377
+ onKeyDown: (event) => {
378
+ if (event.key === "Enter") commitRename();
379
+ if (event.key === "Escape") enterMode("view");
380
+ },
381
+ onBlur: () => enterMode("view"),
382
+ className: "border-doom-blue/60 px-1.5 py-0.5 text-sm font-bold"
383
+ }) : /* @__PURE__ */ jsx("span", {
384
+ className: `min-w-0 flex-1 truncate text-sm font-bold ${active ? "text-doom-on-selected" : "text-doom-hi"}`,
385
+ children: session.name || "untitled"
386
+ }),
387
+ mode !== "rename" && session.ordinal <= 9 ? /* @__PURE__ */ jsx("span", {
388
+ title: `press ${String(session.ordinal)} to focus`,
389
+ className: `shrink-0 text-2xs transition-opacity group-focus-within:opacity-0 group-hover:opacity-0 ${active ? "text-doom-on-selected/70" : "text-doom-faint"}`,
390
+ children: session.ordinal
391
+ }) : null
392
+ ]
393
+ }),
394
+ /* @__PURE__ */ jsxs("span", {
395
+ className: "flex items-start gap-1",
396
+ children: [session.awaitingInput ? /* @__PURE__ */ jsx(AlertIcon, {
397
+ "data-testid": "session-awaiting-input",
398
+ "aria-label": "waiting for your input",
399
+ className: "mt-[2px] h-[11px] w-[11px] shrink-0 text-doom-red"
400
+ }) : null, /* @__PURE__ */ jsx("span", {
401
+ "data-testid": "session-status",
402
+ className: `truncate text-xs ${session.awaitingInput ? "text-doom-red" : active ? "text-doom-on-selected/80" : "text-doom-dim"}`,
403
+ children: session.status
404
+ })]
405
+ }),
406
+ session.git ? /* @__PURE__ */ jsxs("span", {
407
+ "data-testid": "session-branch",
408
+ className: `flex items-center gap-1.5 text-2xs ${active ? "text-doom-on-selected/70" : "text-doom-faint"}`,
409
+ children: [/* @__PURE__ */ jsx(BranchIcon, { className: "h-[9px] w-[9px] shrink-0" }), /* @__PURE__ */ jsxs("span", {
410
+ className: "truncate",
411
+ children: [session.git.branch, session.git.dirty ? "*" : ""]
412
+ })]
413
+ }) : null,
414
+ error ? /* @__PURE__ */ jsx("span", {
415
+ "data-testid": "session-error",
416
+ className: "line-clamp-3 text-xs break-words text-doom-red",
417
+ children: error
418
+ }) : null
419
+ ]
420
+ })]
421
+ });
422
+ const menuOpen = mode === "menu";
423
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
424
+ className: cn("group relative", session.nested && "pl-4"),
425
+ "data-testid": `session-card-${session.id}`,
426
+ "data-active": active,
427
+ "data-nested": session.nested,
428
+ children: [
429
+ mode === "rename" ? /* @__PURE__ */ jsx("div", {
430
+ className: cn(buttonVariants({
431
+ variant: "ghost",
432
+ size: "card"
433
+ }), tileClass, "cursor-default"),
434
+ children: body
435
+ }) : /* @__PURE__ */ jsx(Button, {
436
+ variant: "ghost",
437
+ size: "card",
438
+ "data-testid": `session-open-${session.id}`,
439
+ onClick: () => actions.openSession(session.id),
440
+ className: tileClass,
441
+ children: body
442
+ }),
443
+ mode === "view" || menuOpen ? /* @__PURE__ */ jsx("div", {
444
+ className: `absolute top-2 right-2 transition-opacity ${menuOpen ? "opacity-100" : "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"}`,
445
+ children: /* @__PURE__ */ jsxs(DropdownMenu, {
446
+ open: menuOpen,
447
+ onOpenChange: (next) => {
448
+ const current = modeRef.current;
449
+ enterMode(next ? "menu" : current === "menu" ? "view" : current);
450
+ },
451
+ children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
452
+ asChild: true,
453
+ children: /* @__PURE__ */ jsx(Button, {
454
+ variant: "ghost",
455
+ size: "icon",
456
+ "data-testid": `session-menu-${session.id}`,
457
+ title: "session actions",
458
+ className: active ? "text-doom-on-selected/80" : "text-doom-faint hover:text-doom-hi",
459
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
460
+ })
461
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
462
+ "data-testid": `session-menu-list-${session.id}`,
463
+ onCloseAutoFocus: (event) => {
464
+ if (modeRef.current !== "view") event.preventDefault();
465
+ },
466
+ children: [
467
+ actions.renderSessionMenuItems(session.id),
468
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
469
+ "data-testid": `session-rename-${session.id}`,
470
+ onSelect: () => {
471
+ setDraft(session.name);
472
+ enterMode("rename");
473
+ },
474
+ children: "edit"
475
+ }),
476
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
477
+ "data-testid": `session-resume-${session.id}`,
478
+ onSelect: () => actions.resumeSession(session.id),
479
+ children: "resume"
480
+ }),
481
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
482
+ "data-testid": `session-restart-${session.id}`,
483
+ disabled: session.restarting,
484
+ onSelect: () => actions.restartSession(session.id),
485
+ children: "restart"
486
+ }),
487
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
488
+ variant: "destructive",
489
+ "data-testid": `session-stop-${session.id}`,
490
+ onSelect: () => enterMode("confirm"),
491
+ children: "remove"
492
+ })
493
+ ]
494
+ })]
495
+ })
496
+ }) : null,
497
+ /* @__PURE__ */ jsx(Dialog, {
498
+ open: mode === "confirm",
499
+ onOpenChange: (next) => !next && enterMode("view"),
500
+ children: /* @__PURE__ */ jsxs(DialogContent, {
501
+ width: "sm",
502
+ "data-testid": `session-stop-dialog-${session.id}`,
503
+ "aria-describedby": void 0,
504
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "remove session" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [/* @__PURE__ */ jsxs(DialogDescription, { children: [
505
+ "this stops ",
506
+ /* @__PURE__ */ jsx("span", {
507
+ className: "font-bold text-doom-hi",
508
+ children: session.name || "untitled"
509
+ }),
510
+ " and removes it from the list. anything it is running ends now, but its saved history remains available to resume."
511
+ ] }), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
512
+ variant: "outline",
513
+ size: "md",
514
+ "data-testid": `session-stop-cancel-${session.id}`,
515
+ autoFocus: true,
516
+ onClick: () => enterMode("view"),
517
+ children: "cancel"
518
+ }), /* @__PURE__ */ jsx(Button, {
519
+ variant: "danger",
520
+ size: "md",
521
+ "data-testid": `session-stop-confirm-${session.id}`,
522
+ onClick: () => void remove(),
523
+ children: "remove"
524
+ })] })] })]
525
+ })
526
+ })
527
+ ]
528
+ }), session.pendingSetups.map((setup) => /* @__PURE__ */ jsx(ElegantPendingSetup, {
529
+ sessionId: session.id,
530
+ setup,
531
+ actions
532
+ }, `setup:${setup.id}`))] });
533
+ }
534
+ //#endregion
535
+ //#region src/extensions/(frontend)/template/_components/ElegantWorkspaceGroup.tsx
536
+ /** A small-caps workspace label; its path lives in the tooltip and its actions appear on hover. */
537
+ function ElegantWorkspaceGroup({ workspace, actions }) {
538
+ const [removeOpen, setRemoveOpen] = useState(false);
539
+ const [removeError, setRemoveError] = useState("");
540
+ const [removing, setRemoving] = useState(false);
541
+ const { id, name, root, available, legacy } = workspace;
542
+ const unregister = async () => {
543
+ setRemoving(true);
544
+ setRemoveError("");
545
+ const result = await actions.deleteWorkspace(id);
546
+ setRemoving(false);
547
+ if (result !== void 0) setRemoveError(result.error);
548
+ else setRemoveOpen(false);
549
+ };
550
+ return /* @__PURE__ */ jsxs("section", {
551
+ "data-testid": `workspace-group-${id}`,
552
+ "data-available": available,
553
+ className: "flex flex-col gap-1.5 pt-4",
554
+ children: [
555
+ /* @__PURE__ */ jsxs("div", {
556
+ className: "group/workspace flex min-w-0 items-center gap-1 px-1",
557
+ children: [/* @__PURE__ */ jsx("p", {
558
+ className: "min-w-0 flex-1 truncate text-2xs font-bold tracking-widest text-doom-faint uppercase",
559
+ title: root,
560
+ children: name
561
+ }), legacy ? null : /* @__PURE__ */ jsxs("span", {
562
+ className: "flex items-center gap-0.5 opacity-0 transition-opacity group-focus-within/workspace:opacity-100 group-hover/workspace:opacity-100",
563
+ children: [/* @__PURE__ */ jsx(Button, {
564
+ variant: "ghost",
565
+ size: "icon",
566
+ "data-testid": `workspace-new-session-${id}`,
567
+ title: "new session",
568
+ "aria-label": `new session in ${name}`,
569
+ disabled: !available,
570
+ onClick: () => actions.createSession(id),
571
+ className: "text-doom-faint hover:text-doom-hi",
572
+ children: /* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" })
573
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
574
+ asChild: true,
575
+ children: /* @__PURE__ */ jsx(Button, {
576
+ variant: "ghost",
577
+ size: "icon",
578
+ "data-testid": `workspace-menu-${id}`,
579
+ title: "workspace actions",
580
+ "aria-label": `${name} actions`,
581
+ className: "text-doom-faint hover:text-doom-hi",
582
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
583
+ })
584
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
585
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
586
+ disabled: !available,
587
+ onSelect: () => actions.resumeInWorkspace(id),
588
+ children: "resume session"
589
+ }),
590
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
591
+ disabled: !available,
592
+ onSelect: () => actions.openWorkspaceSettings(id),
593
+ children: "workspace settings"
594
+ }),
595
+ /* @__PURE__ */ jsx(DropdownMenuItem, {
596
+ variant: "destructive",
597
+ "data-testid": `workspace-delete-${id}`,
598
+ onSelect: () => {
599
+ setRemoveError("");
600
+ setRemoveOpen(true);
601
+ },
602
+ children: "delete workspace"
603
+ })
604
+ ] })] })]
605
+ })]
606
+ }),
607
+ !available ? /* @__PURE__ */ jsx("p", {
608
+ className: "px-1 text-2xs text-doom-red",
609
+ children: "workspace unavailable"
610
+ }) : null,
611
+ workspace.sessions.length > 0 ? /* @__PURE__ */ jsx("div", {
612
+ className: "flex flex-col gap-1.5",
613
+ children: workspace.sessions.map((session) => /* @__PURE__ */ jsx(ElegantSessionCard, {
614
+ session,
615
+ actions
616
+ }, session.id))
617
+ }) : available ? /* @__PURE__ */ jsx("p", {
618
+ className: "px-1 text-2xs text-doom-faint",
619
+ children: "no sessions yet"
620
+ }) : null,
621
+ /* @__PURE__ */ jsx(Dialog, {
622
+ open: removeOpen,
623
+ onOpenChange: (next) => !next && !removing && setRemoveOpen(false),
624
+ children: /* @__PURE__ */ jsxs(DialogContent, {
625
+ width: "sm",
626
+ "data-testid": `workspace-delete-dialog-${id}`,
627
+ "aria-describedby": void 0,
628
+ children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "delete workspace" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
629
+ /* @__PURE__ */ jsxs(DialogDescription, { children: [
630
+ "this unregisters ",
631
+ /* @__PURE__ */ jsx("span", {
632
+ className: "font-bold text-doom-hi",
633
+ children: name
634
+ }),
635
+ " from DoomPi only. files at",
636
+ " ",
637
+ /* @__PURE__ */ jsx("code", { children: root }),
638
+ " stay on disk. its sessions must be removed first."
639
+ ] }),
640
+ removeError ? /* @__PURE__ */ jsx("p", {
641
+ "data-testid": `workspace-delete-error-${id}`,
642
+ className: "text-xs text-doom-red",
643
+ children: removeError
644
+ }) : null,
645
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
646
+ variant: "outline",
647
+ size: "md",
648
+ "data-testid": `workspace-delete-cancel-${id}`,
649
+ disabled: removing,
650
+ autoFocus: true,
651
+ onClick: () => setRemoveOpen(false),
652
+ children: "cancel"
653
+ }), /* @__PURE__ */ jsx(Button, {
654
+ variant: "danger",
655
+ size: "md",
656
+ "data-testid": `workspace-delete-confirm-${id}`,
657
+ disabled: removing,
658
+ onClick: () => void unregister(),
659
+ children: removing ? "deleting…" : "delete"
660
+ })] })
661
+ ] })]
662
+ })
663
+ })
664
+ ]
665
+ });
666
+ }
667
+ //#endregion
668
+ //#region src/extensions/(frontend)/template/_components/ElegantRail.tsx
669
+ /** The drawer's session list: quiet labels, soft tiles, and a low-key footer. */
670
+ function ElegantRail({ rail }) {
671
+ const { remote, actions } = rail;
672
+ const live = remote.status === "on" || remote.status === "starting";
673
+ return /* @__PURE__ */ jsxs("div", {
674
+ className: "flex h-full flex-col gap-2 px-3 pb-3",
675
+ children: [
676
+ /* @__PURE__ */ jsxs("div", {
677
+ className: "flex items-center justify-between gap-2 pt-1",
678
+ children: [/* @__PURE__ */ jsxs(Button, {
679
+ variant: "subtle",
680
+ size: "sm",
681
+ "data-testid": "add-workspace-open",
682
+ "aria-label": "add workspace",
683
+ onClick: actions.openAddWorkspace,
684
+ className: "gap-1.5",
685
+ children: [/* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" }), "workspace"]
686
+ }), /* @__PURE__ */ jsxs(Button, {
687
+ variant: live ? "primary" : remote.status === "failed" ? "danger-outline" : "ghost",
688
+ size: "sm",
689
+ "data-testid": "remote-access-open",
690
+ "aria-label": `remote access is ${remote.status}`,
691
+ onClick: actions.openRemoteAccess,
692
+ className: "gap-1.5",
693
+ children: [live ? /* @__PURE__ */ jsx(Dot, {
694
+ tone: "yellow",
695
+ pulse: true,
696
+ "data-testid": "remote-access-live"
697
+ }) : /* @__PURE__ */ jsx(ShieldIcon, { className: "h-3 w-3" }), "remote"]
698
+ })]
699
+ }),
700
+ /* @__PURE__ */ jsxs("div", {
701
+ className: "min-h-0 flex-1 overflow-y-auto",
702
+ children: [
703
+ rail.workspaces.map((workspace) => /* @__PURE__ */ jsx(ElegantWorkspaceGroup, {
704
+ workspace,
705
+ actions
706
+ }, workspace.id)),
707
+ rail.workspaces.length === 0 ? /* @__PURE__ */ jsxs(Button, {
708
+ variant: "outline",
709
+ size: "lg",
710
+ "data-testid": "add-workspace-empty",
711
+ onClick: actions.openAddWorkspace,
712
+ className: "mt-4 w-full justify-start px-3 text-sm",
713
+ children: [/* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" }), "add your first workspace"]
714
+ }) : null,
715
+ rail.pluginContent
716
+ ]
717
+ }),
718
+ remote.status === "off" ? null : /* @__PURE__ */ jsxs("div", {
719
+ "data-testid": "remote-banner",
720
+ className: `flex items-center gap-2 rounded-lg px-3 py-2 text-xs ${remote.status === "failed" ? "bg-doom-tint-red text-doom-red" : "bg-doom-tint-yellow text-doom-yellow"}`,
721
+ children: [
722
+ /* @__PURE__ */ jsx(Dot, { tone: remote.status === "failed" ? "red" : "yellow" }),
723
+ /* @__PURE__ */ jsx("span", {
724
+ className: "min-w-0 flex-1 truncate",
725
+ children: remote.status === "failed" ? `remote access failed: ${remote.error ?? "the tunnel stopped"}` : `remote ${remote.status === "starting" ? "starting" : "on"}${remote.host === void 0 ? "" : ` · ${remote.host}`} · ${String(remote.deviceCount)} device${remote.deviceCount === 1 ? "" : "s"}`
726
+ }),
727
+ /* @__PURE__ */ jsx(Button, {
728
+ variant: "ghost",
729
+ size: "xs",
730
+ "data-testid": "remote-banner-open",
731
+ onClick: actions.openRemoteAccess,
732
+ children: "manage"
733
+ }),
734
+ /* @__PURE__ */ jsx(Button, {
735
+ variant: "ghost",
736
+ size: "xs",
737
+ "data-testid": "remote-banner-off",
738
+ onClick: actions.turnRemoteAccessOff,
739
+ children: "off"
740
+ })
741
+ ]
742
+ }),
743
+ /* @__PURE__ */ jsxs("div", {
744
+ className: "flex items-center justify-between border-t border-doom-border-soft pt-2",
745
+ children: [/* @__PURE__ */ jsxs(Button, {
746
+ variant: "ghost",
747
+ size: "sm",
748
+ "data-testid": "settings-open",
749
+ "aria-label": "settings",
750
+ onClick: actions.openSettings,
751
+ className: "gap-1.5 text-doom-faint",
752
+ children: [/* @__PURE__ */ jsx(GearIcon, { className: "h-3 w-3" }), "settings"]
753
+ }), /* @__PURE__ */ jsx("span", {
754
+ className: "text-2xs text-doom-faint max-sm:hidden",
755
+ children: "ctrl+t new session"
756
+ })]
757
+ })
758
+ ]
759
+ });
760
+ }
761
+ //#endregion
4
762
  //#region src/extensions/(frontend)/template/_components/ElegantLayout.tsx
5
763
  /** A conversation-first frame, with the same host capabilities available on demand. */
6
- function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavigationOpenChange, onMobileActivityOpenChange }) {
764
+ function ElegantLayout({ view, slots, rail, navigationOpen, mobileActivityOpen, onNavigationOpenChange, onMobileActivityOpenChange }) {
7
765
  const conversation = view === "conversation" || view === "welcome";
8
766
  return /* @__PURE__ */ jsxs("div", {
9
767
  className: "relative flex h-full min-w-0 flex-col overflow-hidden bg-doom-bg",
@@ -15,7 +773,7 @@ function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavi
15
773
  title: "Sessions",
16
774
  "data-testid": "session-rail-panel",
17
775
  backdropTestId: "mobile-drawer-backdrop",
18
- children: slots.navigation
776
+ children: /* @__PURE__ */ jsx(ElegantRail, { rail })
19
777
  }),
20
778
  slots.notices,
21
779
  slots.header({
@@ -53,7 +811,11 @@ function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavi
53
811
  backdropTestId: "mobile-activity-backdrop",
54
812
  className: "w-auto max-w-[calc(100vw-48px)]",
55
813
  children: slots.activity
56
- })
814
+ }),
815
+ rail.addWorkspace ? /* @__PURE__ */ jsx(ElegantAddWorkspaceDialog, {
816
+ suggestedPaths: rail.addWorkspace.suggestedPaths,
817
+ actions: rail.actions
818
+ }) : null
57
819
  ]
58
820
  });
59
821
  }
@@ -76,10 +838,10 @@ var doompi_template_elegant_web_default$1 = defineWebTemplate(elegantTemplate);
76
838
  var doompi_template_elegant_web_default = defineWebTemplate(elegantTemplate);
77
839
  //#endregion
78
840
  //#region generated/web.ts
79
- const via = (identity, value) => ({
841
+ const via = (identity, value) => value === void 0 ? void 0 : {
80
842
  ...identity,
81
843
  ...value
82
- });
844
+ };
83
845
  const webPlugin = defineWebPlugin({
84
846
  id: "template-elegant",
85
847
  global: { get templates() {
@@ -1 +1 @@
1
- {"version":3,"file":"web.mjs","names":["templateDoompiTemplateElegant","templateDoompiTemplateElegant2","templateDoompiTemplateElegant3"],"sources":["../../src/extensions/(frontend)/template/_components/ElegantLayout.tsx","../../src/extensions/(frontend)/template/_lib/definition.ts","../../src/extensions/(frontend)/template/doompi-template-elegant.web.ts","../../src/extensions/workspaces/(frontend)/template/doompi-template-elegant.web.ts","../../src/extensions/workspaces/sessions/(frontend)/template/doompi-template-elegant.web.ts","../../generated/web.ts"],"sourcesContent":["import type { WebTemplateProps } from '@agimon-ai/doompi-core/web';\nimport { Drawer } from '@agimon-ai/doompi-web-components';\n\n/** A conversation-first frame, with the same host capabilities available on demand. */\nexport function ElegantLayout({\n view,\n slots,\n navigationOpen,\n mobileActivityOpen,\n onNavigationOpenChange,\n onMobileActivityOpenChange,\n}: WebTemplateProps) {\n const conversation = view === 'conversation' || view === 'welcome';\n return (\n <div className=\"relative flex h-full min-w-0 flex-col overflow-hidden bg-doom-bg\">\n <Drawer\n open={navigationOpen}\n onOpenChange={onNavigationOpenChange}\n side=\"left\"\n title=\"Sessions\"\n data-testid=\"session-rail-panel\"\n backdropTestId=\"mobile-drawer-backdrop\"\n >\n {slots.navigation}\n </Drawer>\n {slots.notices}\n {slots.header({ navigationToggle: 'always', activityToggle: 'always' })}\n <main className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <div\n data-testid=\"template-content\"\n className={\n conversation\n ? 'mx-auto flex min-h-0 w-full max-w-[960px] flex-1 flex-col overflow-hidden px-2 sm:px-6'\n : 'flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden'\n }\n >\n {slots.content}\n {slots.composer == null ? null : (\n <div className=\"mx-2 mb-3 shrink-0 overflow-hidden rounded-lg border border-doom-border bg-doom-panel sm:mx-4 sm:mb-5\">\n {slots.composer}\n </div>\n )}\n {slots.controls == null ? null : (\n <details className=\"shrink-0 px-3 pb-3 text-sm text-doom-dim\">\n <summary\n data-testid=\"template-controls-toggle\"\n className=\"cursor-pointer rounded-sm px-1 py-1 focus-visible:outline focus-visible:outline-doom-blue\"\n >\n Session controls\n </summary>\n {slots.controls}\n </details>\n )}\n </div>\n </main>\n {slots.activity == null ? null : (\n <Drawer\n open={mobileActivityOpen}\n onOpenChange={onMobileActivityOpenChange}\n side=\"right\"\n title=\"Activity\"\n showHeader={false}\n backdropTestId=\"mobile-activity-backdrop\"\n className=\"w-auto max-w-[calc(100vw-48px)]\"\n >\n {slots.activity}\n </Drawer>\n )}\n </div>\n );\n}\n","import type { WebTemplateFile } from '@agimon-ai/doompi-core/web';\n\nimport { ElegantLayout } from '../_components/ElegantLayout';\n\nexport const elegantTemplate = {\n label: 'Elegant',\n description: 'A focused conversation with collapsible navigation, session controls, and activity.',\n contractVersion: 1,\n layout: ElegantLayout,\n} satisfies WebTemplateFile;\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from './_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from '../../../(frontend)/template/_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from '../../../../(frontend)/template/_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport templateDoompiTemplateElegant from '../src/extensions/(frontend)/template/doompi-template-elegant.web';\nimport templateDoompiTemplateElegant2 from '../src/extensions/workspaces/(frontend)/template/doompi-template-elegant.web';\nimport templateDoompiTemplateElegant3 from '../src/extensions/workspaces/sessions/(frontend)/template/doompi-template-elegant.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'template-elegant',\n global: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant)]; },\n },\n workspace: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant2)]; },\n },\n session: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant3)]; },\n },\n});\n"],"mappings":";;;;;AAIA,SAAgB,cAAc,EAC5B,MACA,OACA,gBACA,oBACA,wBACA,8BACmB;CACnB,MAAM,eAAe,SAAS,kBAAkB,SAAS;CACzD,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,oBAAC,QAAD;IACE,MAAM;IACN,cAAc;IACd,MAAK;IACL,OAAM;IACN,eAAY;IACZ,gBAAe;IAEd,UAAA,MAAM;GACD,CAAA;GACP,MAAM;GACN,MAAM,OAAO;IAAE,kBAAkB;IAAU,gBAAgB;GAAS,CAAC;GACtE,oBAAC,QAAD;IAAM,WAAU;IACd,UAAA,qBAAC,OAAD;KACE,eAAY;KACZ,WACE,eACI,2FACA;KALR,UAAA;MAQG,MAAM;MACN,MAAM,YAAY,OAAO,OACxB,oBAAC,OAAD;OAAK,WAAU;OACZ,UAAA,MAAM;MACJ,CAAA;MAEN,MAAM,YAAY,OAAO,OACxB,qBAAC,WAAD;OAAS,WAAU;OAAnB,UAAA,CACE,oBAAC,WAAD;QACE,eAAY;QACZ,WAAU;QACX,UAAA;OAEQ,CAAA,GACR,MAAM,QACA;;KAER;;GACD,CAAA;GACL,MAAM,YAAY,OAAO,OACxB,oBAAC,QAAD;IACE,MAAM;IACN,cAAc;IACd,MAAK;IACL,OAAM;IACN,YAAY;IACZ,gBAAe;IACf,WAAU;IAET,UAAA,MAAM;GACD,CAAA;EAEP;;AAET;;;AClEA,MAAa,kBAAkB;CAC7B,OAAO;CACP,aAAa;CACb,iBAAiB;CACjB,QAAQ;AACV;;;ACLA,IAAA,wCAAe,kBAAkB,eAAe;;;ACAhD,IAAA,wCAAe,kBAAkB,eAAe;;;ACAhD,IAAA,sCAAe,kBAAkB,eAAe;;;ACGhD,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,YAAY,gBAAgB;CACvC,IAAI;CACJ,QAAQ,EACN,IAAI,YAAiD;EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,0BAA0B,GAAGA,qCAA6B,CAAC;CAAG,EACzI;CACA,WAAW,EACT,IAAI,YAAiD;EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,0BAA0B,GAAGC,qCAA8B,CAAC;CAAG,EAC1I;CACA,SAAS,EACP,IAAI,YAAiD;EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,0BAA0B,GAAGC,mCAA8B,CAAC;CAAG,EAC1I;AACF,CAAC"}
1
+ {"version":3,"file":"web.mjs","names":["folderName","templateDoompiTemplateElegant","templateDoompiTemplateElegant2","templateDoompiTemplateElegant3"],"sources":["../../src/extensions/(frontend)/template/_components/ElegantFolderBrowser.tsx","../../src/extensions/(frontend)/template/_components/ElegantAddWorkspaceDialog.tsx","../../src/extensions/(frontend)/template/_components/ElegantPendingSetup.tsx","../../src/extensions/(frontend)/template/_components/ElegantSessionCard.tsx","../../src/extensions/(frontend)/template/_components/ElegantWorkspaceGroup.tsx","../../src/extensions/(frontend)/template/_components/ElegantRail.tsx","../../src/extensions/(frontend)/template/_components/ElegantLayout.tsx","../../src/extensions/(frontend)/template/_lib/definition.ts","../../src/extensions/(frontend)/template/doompi-template-elegant.web.ts","../../src/extensions/workspaces/(frontend)/template/doompi-template-elegant.web.ts","../../src/extensions/workspaces/sessions/(frontend)/template/doompi-template-elegant.web.ts","../../generated/web.ts"],"sourcesContent":["import type { WebTemplateRailActions, WebTemplateRailDirectoryListing } from '@agimon-ai/doompi-core/web';\nimport { Button, ChevronUpIcon, OptionRow } from '@agimon-ai/doompi-web-components';\nimport { useEffect, useState } from 'react';\n\nfunction folderName(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1) || path;\n}\n\n/** Walks the server's folders inline: up, into a child, or pick the current folder. */\nexport function ElegantFolderBrowser({\n start,\n listDirectory,\n onSelect,\n}: {\n /** Folder to open first; home when absent. */\n start?: string;\n listDirectory: WebTemplateRailActions['listDirectory'];\n onSelect: (path: string) => void;\n}) {\n const [target, setTarget] = useState(start);\n const [listing, setListing] = useState<WebTemplateRailDirectoryListing | null>(null);\n const [error, setError] = useState('');\n\n useEffect(() => {\n let stale = false;\n void listDirectory(target).then((result) => {\n if (stale) return;\n if ('error' in result) setError(result.error);\n else {\n setError('');\n setListing(result);\n }\n });\n return () => {\n stale = true;\n };\n }, [target, listDirectory]);\n\n return (\n <div\n data-testid=\"folder-browser\"\n className=\"flex flex-col gap-1 rounded-lg border border-doom-border-soft bg-doom-panel/40 p-1.5\"\n >\n <div className=\"flex items-center gap-1 px-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid=\"folder-browser-up\"\n title=\"parent folder\"\n aria-label=\"parent folder\"\n disabled={listing?.parent === undefined}\n onClick={() => listing?.parent !== undefined && setTarget(listing.parent)}\n >\n <ChevronUpIcon className=\"h-3 w-3\" />\n </Button>\n <span className=\"min-w-0 flex-1 truncate text-xs text-doom-dim\" title={listing?.path}>\n {listing?.path ?? 'loading…'}\n </span>\n <Button\n variant=\"outline\"\n size=\"xs\"\n data-testid=\"folder-browser-select\"\n disabled={listing === null}\n onClick={() => listing !== null && onSelect(listing.path)}\n >\n use this folder\n </Button>\n </div>\n {error ? <p className=\"px-2 py-1 text-xs text-doom-red\">{error}</p> : null}\n <div role=\"listbox\" aria-label=\"folders\" className=\"max-h-40 overflow-y-auto\">\n {listing !== null && listing.directories.length === 0 ? (\n <p className=\"px-2 py-1 text-2xs text-doom-faint\">no folders here</p>\n ) : null}\n {listing?.directories.map((directory) => (\n <OptionRow\n key={directory}\n density=\"compact\"\n data-testid=\"folder-browser-entry\"\n onClick={() => setTarget(directory)}\n className=\"w-full px-2.5 py-1 text-sm\"\n >\n {folderName(directory)}\n </OptionRow>\n ))}\n </div>\n </div>\n );\n}\n","import type { WebTemplateRailActions } from '@agimon-ai/doompi-core/web';\nimport {\n Button,\n Dialog,\n DialogBody,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n OptionRow,\n} from '@agimon-ai/doompi-web-components';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { ElegantFolderBrowser } from './ElegantFolderBrowser';\n\nconst SUGGESTION_DEBOUNCE_MS = 120;\nconst NO_HIGHLIGHT = -1;\nconst TRAILING_SEPARATORS = /\\/+$/u;\n\nfunction folderName(path: string): string {\n const trimmed = path.trim().replace(TRAILING_SEPARATORS, '');\n return trimmed.slice(trimmed.lastIndexOf('/') + 1);\n}\n\n/** Adds a workspace by name, by folder, or both; a name alone gets a new default folder. */\nexport function ElegantAddWorkspaceDialog({\n suggestedPaths,\n actions,\n}: {\n suggestedPaths: readonly string[];\n actions: WebTemplateRailActions;\n}) {\n const [name, setName] = useState('');\n // The name follows the folder until the user types their own.\n const [nameEdited, setNameEdited] = useState(false);\n const [path, setPath] = useState(suggestedPaths[0] ?? '');\n const [browsing, setBrowsing] = useState(false);\n const [suggestions, setSuggestions] = useState<readonly string[]>([]);\n const [highlight, setHighlight] = useState(NO_HIGHLIGHT);\n const [error, setError] = useState('');\n const [busy, setBusy] = useState(false);\n const pathInput = useRef<HTMLInputElement>(null);\n const shownName = nameEdited ? name : folderName(path);\n\n useEffect(() => {\n const typed = path.trim();\n if (typed === '') return;\n let stale = false;\n const timer = setTimeout(async () => {\n const found = await actions.searchDirectories(typed);\n if (stale) return;\n setSuggestions(found.filter((directory) => directory !== typed));\n setHighlight(NO_HIGHLIGHT);\n }, SUGGESTION_DEBOUNCE_MS);\n return () => {\n stale = true;\n clearTimeout(timer);\n };\n }, [path, actions]);\n\n const changePath = (next: string): void => {\n setPath(next);\n if (next.trim() === '') setSuggestions([]);\n };\n const pick = (directory: string): void => {\n changePath(`${directory}/`);\n pathInput.current?.focus();\n };\n const trimmedPath = path.trim().replace(TRAILING_SEPARATORS, '') || (path.trim() === '' ? '' : '/');\n const canSubmit = !busy && (trimmedPath !== '' || shownName.trim() !== '');\n const submit = async (): Promise<void> => {\n if (!canSubmit) return;\n setBusy(true);\n setError('');\n const result = await actions.addWorkspace({\n name: shownName.trim(),\n ...(trimmedPath === '' ? {} : { path: trimmedPath }),\n });\n setBusy(false);\n if (result !== undefined) setError(result.error);\n };\n\n return (\n <Dialog open onOpenChange={(next) => !next && !busy && actions.closeAddWorkspace()}>\n <DialogContent width=\"md\" data-testid=\"add-workspace-dialog\" aria-describedby={undefined}>\n <DialogHeader>\n <DialogTitle>add workspace</DialogTitle>\n </DialogHeader>\n <DialogBody>\n <label htmlFor=\"add-workspace-name\" className=\"flex flex-col gap-1\">\n <span className=\"text-xs text-doom-faint\">name</span>\n <Input\n id=\"add-workspace-name\"\n data-testid=\"add-workspace-name\"\n value={shownName}\n autoFocus\n onChange={(event) => {\n setNameEdited(true);\n setName(event.target.value);\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void submit();\n }}\n placeholder=\"my-project\"\n />\n </label>\n <label htmlFor=\"add-workspace-root\" className=\"flex flex-col gap-1\">\n <span className=\"text-xs text-doom-faint\">\n folder <span className=\"text-doom-faint\">(optional; type a path or browse)</span>\n </span>\n <span className=\"flex items-center gap-1.5\">\n <Input\n id=\"add-workspace-root\"\n ref={pathInput}\n data-testid=\"add-workspace-root\"\n value={path}\n onChange={(event) => changePath(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'ArrowDown' && suggestions.length > 0) {\n event.preventDefault();\n setHighlight((current) => (current + 1) % suggestions.length);\n } else if (event.key === 'ArrowUp' && suggestions.length > 0) {\n event.preventDefault();\n setHighlight((current) => (current <= 0 ? suggestions.length - 1 : current - 1));\n } else if (event.key === 'Enter') {\n const chosen = highlight === NO_HIGHLIGHT ? undefined : suggestions[highlight];\n if (chosen === undefined) void submit();\n else pick(chosen);\n }\n }}\n placeholder=\"~/workspace/my-project\"\n className=\"min-w-0 flex-1\"\n />\n <Button\n variant={browsing ? 'subtle' : 'outline'}\n size=\"md\"\n data-testid=\"add-workspace-browse\"\n aria-pressed={browsing}\n onClick={() => setBrowsing((open) => !open)}\n >\n browse\n </Button>\n </span>\n </label>\n {browsing ? (\n <ElegantFolderBrowser\n {...(trimmedPath.startsWith('/') ? { start: trimmedPath } : {})}\n listDirectory={actions.listDirectory}\n onSelect={(selected) => {\n changePath(selected);\n setBrowsing(false);\n }}\n />\n ) : suggestions.length > 0 ? (\n <div\n role=\"listbox\"\n aria-label=\"matching directories\"\n data-testid=\"add-workspace-suggestions\"\n className=\"max-h-40 overflow-y-auto rounded-lg border border-doom-border-soft bg-doom-panel/40 p-1.5\"\n >\n {suggestions.map((directory, index) => (\n <OptionRow\n key={directory}\n density=\"compact\"\n active={index === highlight}\n data-testid=\"add-workspace-suggestion\"\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setHighlight(index)}\n onClick={() => pick(directory)}\n className=\"w-full px-2.5 py-1 text-sm\"\n >\n {directory}\n </OptionRow>\n ))}\n </div>\n ) : null}\n {suggestedPaths.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {suggestedPaths.slice(0, 4).map((suggested) => (\n <Button key={suggested} variant=\"outline\" size=\"xs\" onClick={() => changePath(suggested)}>\n {suggested}\n </Button>\n ))}\n </div>\n ) : null}\n {trimmedPath === '' && shownName.trim() !== '' ? (\n <p data-testid=\"add-workspace-default-hint\" className=\"text-xs text-doom-faint\">\n creates ~/.pi/.doom/workspace/{shownName.trim()}\n </p>\n ) : null}\n {error ? (\n <pre className=\"max-h-28 overflow-y-auto rounded border border-doom-edge-red bg-doom-tint-red/40 px-2.5 py-2 text-xs whitespace-pre-wrap text-doom-red\">\n {error}\n </pre>\n ) : null}\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => actions.closeAddWorkspace()} disabled={busy}>\n cancel\n </Button>\n <Button\n variant=\"primary\"\n data-testid=\"add-workspace-confirm\"\n onClick={() => void submit()}\n disabled={!canSubmit}\n >\n {busy ? 'adding…' : 'add workspace'}\n </Button>\n </DialogFooter>\n </DialogBody>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { WebTemplateRailActions, WebTemplateRailPendingSetup } from '@agimon-ai/doompi-core/web';\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n KebabIcon,\n} from '@agimon-ai/doompi-web-components';\nimport { useState } from 'react';\n\n/** A setup-only reservation: no runtime, so it offers removal and nothing else. */\nexport function ElegantPendingSetup({\n sessionId,\n setup,\n actions,\n}: {\n sessionId: string;\n setup: WebTemplateRailPendingSetup;\n actions: WebTemplateRailActions;\n}) {\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState('');\n const remove = async (): Promise<void> => {\n if (busy) return;\n setBusy(true);\n setError('');\n const result = await actions.removePendingSetup(sessionId, setup.id);\n setBusy(false);\n if (result !== undefined) setError(result.error);\n };\n return (\n <div\n data-testid={`pending-session-${setup.id}`}\n className=\"ml-9 flex flex-col gap-0.5 rounded-lg border border-dashed border-doom-border-soft px-3 py-2\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-xs font-bold text-doom-hi\">{setup.name}</span>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title=\"session setup actions\"\n disabled={busy}\n data-testid={`pending-session-menu-${setup.id}`}\n >\n <KebabIcon className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem variant=\"destructive\" onSelect={() => void remove()}>\n remove setup\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n <p className={setup.failed ? 'text-2xs text-doom-red' : 'text-2xs text-doom-faint'}>{setup.status}</p>\n {setup.recovery ? (\n <p role={setup.failed ? 'alert' : undefined} className=\"text-2xs text-doom-faint\">\n {setup.recovery}\n </p>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"text-2xs text-doom-red\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n","import type { WebTemplateRailActions, WebTemplateRailSession } from '@agimon-ai/doompi-core/web';\nimport {\n AlertIcon,\n Avatar,\n AvatarFallback,\n AvatarImage,\n BranchIcon,\n Button,\n buttonVariants,\n cn,\n Dialog,\n DialogBody,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n ForkIcon,\n Input,\n KebabIcon,\n} from '@agimon-ai/doompi-web-components';\nimport { useRef, useState } from 'react';\n\nimport { ElegantPendingSetup } from './ElegantPendingSetup';\n\ntype CardMode = 'view' | 'menu' | 'rename' | 'confirm';\n\n/** A soft tile led by the persona avatar, with the branch as a quiet caption. */\nexport function ElegantSessionCard({\n session,\n actions,\n}: {\n session: WebTemplateRailSession;\n actions: WebTemplateRailActions;\n}) {\n const [mode, setMode] = useState<CardMode>('view');\n const [draft, setDraft] = useState('');\n const [removeError, setRemoveError] = useState('');\n // Written by the event that changes the mode, so the menu's close-focus handler\n // sees the new mode before React re-renders the card.\n const modeRef = useRef(mode);\n const enterMode = (next: CardMode): void => {\n modeRef.current = next;\n setMode(next);\n };\n const { active } = session;\n const error = removeError || session.error;\n const tileClass = active\n ? 'rounded-lg bg-doom-selected hover:bg-doom-selected'\n : 'rounded-lg bg-doom-panel/40 hover:bg-doom-panel';\n const commitRename = (): void => {\n actions.renameSession(session.id, draft);\n enterMode('view');\n };\n const remove = async (): Promise<void> => {\n enterMode('view');\n setRemoveError('');\n const result = await actions.removeSession(session.id);\n if (result !== undefined) setRemoveError(result.error);\n };\n\n const body = (\n <div className=\"flex items-start gap-2.5\">\n <Avatar\n data-testid={session.profile ? 'session-avatar' : undefined}\n aria-label={session.profile?.label ?? 'DoomPi'}\n title={session.profile?.label}\n className=\"mt-0.5 h-7 w-7 shrink-0 border border-doom-border-soft bg-doom-deep\"\n >\n {session.profile?.avatarUrl ? <AvatarImage src={session.profile.avatarUrl} alt=\"\" /> : null}\n <AvatarFallback className=\"text-2xs text-doom-magenta\">{session.profile?.initials ?? 'DP'}</AvatarFallback>\n </Avatar>\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <div className=\"flex items-center gap-1.5\">\n {session.nested && session.provenance ? (\n <ForkIcon\n aria-label={\n session.provenance === 'worktree' ? 'automatic per-conversation worktree' : session.provenance\n }\n className={`h-[11px] w-[11px] shrink-0 ${active ? 'text-doom-on-selected' : 'text-doom-faint'}`}\n />\n ) : null}\n {mode === 'rename' ? (\n <Input\n data-testid={`session-name-input-${session.id}`}\n value={draft}\n autoFocus\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') enterMode('view');\n }}\n onBlur={() => enterMode('view')}\n className=\"border-doom-blue/60 px-1.5 py-0.5 text-sm font-bold\"\n />\n ) : (\n <span\n className={`min-w-0 flex-1 truncate text-sm font-bold ${active ? 'text-doom-on-selected' : 'text-doom-hi'}`}\n >\n {session.name || 'untitled'}\n </span>\n )}\n {mode !== 'rename' && session.ordinal <= 9 ? (\n <span\n title={`press ${String(session.ordinal)} to focus`}\n className={`shrink-0 text-2xs transition-opacity group-focus-within:opacity-0 group-hover:opacity-0 ${\n active ? 'text-doom-on-selected/70' : 'text-doom-faint'\n }`}\n >\n {session.ordinal}\n </span>\n ) : null}\n </div>\n <span className=\"flex items-start gap-1\">\n {session.awaitingInput ? (\n <AlertIcon\n data-testid=\"session-awaiting-input\"\n aria-label=\"waiting for your input\"\n className=\"mt-[2px] h-[11px] w-[11px] shrink-0 text-doom-red\"\n />\n ) : null}\n <span\n data-testid=\"session-status\"\n className={`truncate text-xs ${session.awaitingInput ? 'text-doom-red' : active ? 'text-doom-on-selected/80' : 'text-doom-dim'}`}\n >\n {session.status}\n </span>\n </span>\n {session.git ? (\n <span\n data-testid=\"session-branch\"\n className={`flex items-center gap-1.5 text-2xs ${active ? 'text-doom-on-selected/70' : 'text-doom-faint'}`}\n >\n <BranchIcon className=\"h-[9px] w-[9px] shrink-0\" />\n <span className=\"truncate\">\n {session.git.branch}\n {session.git.dirty ? '*' : ''}\n </span>\n </span>\n ) : null}\n {error ? (\n <span data-testid=\"session-error\" className=\"line-clamp-3 text-xs break-words text-doom-red\">\n {error}\n </span>\n ) : null}\n </div>\n </div>\n );\n\n const menuOpen = mode === 'menu';\n return (\n <>\n <div\n className={cn('group relative', session.nested && 'pl-4')}\n data-testid={`session-card-${session.id}`}\n data-active={active}\n data-nested={session.nested}\n >\n {mode === 'rename' ? (\n <div className={cn(buttonVariants({ variant: 'ghost', size: 'card' }), tileClass, 'cursor-default')}>\n {body}\n </div>\n ) : (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid={`session-open-${session.id}`}\n onClick={() => actions.openSession(session.id)}\n className={tileClass}\n >\n {body}\n </Button>\n )}\n {mode === 'view' || menuOpen ? (\n <div\n className={`absolute top-2 right-2 transition-opacity ${\n menuOpen ? 'opacity-100' : 'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100'\n }`}\n >\n <DropdownMenu\n open={menuOpen}\n onOpenChange={(next) => {\n const current = modeRef.current;\n enterMode(next ? 'menu' : current === 'menu' ? 'view' : current);\n }}\n >\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`session-menu-${session.id}`}\n title=\"session actions\"\n className={active ? 'text-doom-on-selected/80' : 'text-doom-faint hover:text-doom-hi'}\n >\n <KebabIcon className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n data-testid={`session-menu-list-${session.id}`}\n onCloseAutoFocus={(event) => {\n if (modeRef.current !== 'view') event.preventDefault();\n }}\n >\n {actions.renderSessionMenuItems(session.id)}\n <DropdownMenuItem\n data-testid={`session-rename-${session.id}`}\n onSelect={() => {\n setDraft(session.name);\n enterMode('rename');\n }}\n >\n edit\n </DropdownMenuItem>\n <DropdownMenuItem\n data-testid={`session-resume-${session.id}`}\n onSelect={() => actions.resumeSession(session.id)}\n >\n resume\n </DropdownMenuItem>\n <DropdownMenuItem\n data-testid={`session-restart-${session.id}`}\n disabled={session.restarting}\n onSelect={() => actions.restartSession(session.id)}\n >\n restart\n </DropdownMenuItem>\n <DropdownMenuItem\n variant=\"destructive\"\n data-testid={`session-stop-${session.id}`}\n onSelect={() => enterMode('confirm')}\n >\n remove\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n ) : null}\n <Dialog open={mode === 'confirm'} onOpenChange={(next) => !next && enterMode('view')}>\n <DialogContent width=\"sm\" data-testid={`session-stop-dialog-${session.id}`} aria-describedby={undefined}>\n <DialogHeader>\n <DialogTitle>remove session</DialogTitle>\n </DialogHeader>\n <DialogBody>\n <DialogDescription>\n this stops <span className=\"font-bold text-doom-hi\">{session.name || 'untitled'}</span> and removes it\n from the list. anything it is running ends now, but its saved history remains available to resume.\n </DialogDescription>\n <DialogFooter>\n <Button\n variant=\"outline\"\n size=\"md\"\n data-testid={`session-stop-cancel-${session.id}`}\n autoFocus\n onClick={() => enterMode('view')}\n >\n cancel\n </Button>\n <Button\n variant=\"danger\"\n size=\"md\"\n data-testid={`session-stop-confirm-${session.id}`}\n onClick={() => void remove()}\n >\n remove\n </Button>\n </DialogFooter>\n </DialogBody>\n </DialogContent>\n </Dialog>\n </div>\n {session.pendingSetups.map((setup) => (\n <ElegantPendingSetup key={`setup:${setup.id}`} sessionId={session.id} setup={setup} actions={actions} />\n ))}\n </>\n );\n}\n","import type { WebTemplateRailActions, WebTemplateRailWorkspace } from '@agimon-ai/doompi-core/web';\nimport {\n Button,\n Dialog,\n DialogBody,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n KebabIcon,\n PlusIcon,\n} from '@agimon-ai/doompi-web-components';\nimport { useState } from 'react';\n\nimport { ElegantSessionCard } from './ElegantSessionCard';\n\n/** A small-caps workspace label; its path lives in the tooltip and its actions appear on hover. */\nexport function ElegantWorkspaceGroup({\n workspace,\n actions,\n}: {\n workspace: WebTemplateRailWorkspace;\n actions: WebTemplateRailActions;\n}) {\n const [removeOpen, setRemoveOpen] = useState(false);\n const [removeError, setRemoveError] = useState('');\n const [removing, setRemoving] = useState(false);\n const { id, name, root, available, legacy } = workspace;\n const unregister = async (): Promise<void> => {\n setRemoving(true);\n setRemoveError('');\n const result = await actions.deleteWorkspace(id);\n setRemoving(false);\n if (result !== undefined) setRemoveError(result.error);\n else setRemoveOpen(false);\n };\n return (\n <section data-testid={`workspace-group-${id}`} data-available={available} className=\"flex flex-col gap-1.5 pt-4\">\n <div className=\"group/workspace flex min-w-0 items-center gap-1 px-1\">\n <p\n className=\"min-w-0 flex-1 truncate text-2xs font-bold tracking-widest text-doom-faint uppercase\"\n title={root}\n >\n {name}\n </p>\n {legacy ? null : (\n <span className=\"flex items-center gap-0.5 opacity-0 transition-opacity group-focus-within/workspace:opacity-100 group-hover/workspace:opacity-100\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`workspace-new-session-${id}`}\n title=\"new session\"\n aria-label={`new session in ${name}`}\n disabled={!available}\n onClick={() => actions.createSession(id)}\n className=\"text-doom-faint hover:text-doom-hi\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n </Button>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`workspace-menu-${id}`}\n title=\"workspace actions\"\n aria-label={`${name} actions`}\n className=\"text-doom-faint hover:text-doom-hi\"\n >\n <KebabIcon className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem disabled={!available} onSelect={() => actions.resumeInWorkspace(id)}>\n resume session\n </DropdownMenuItem>\n <DropdownMenuItem disabled={!available} onSelect={() => actions.openWorkspaceSettings(id)}>\n workspace settings\n </DropdownMenuItem>\n <DropdownMenuItem\n variant=\"destructive\"\n data-testid={`workspace-delete-${id}`}\n onSelect={() => {\n setRemoveError('');\n setRemoveOpen(true);\n }}\n >\n delete workspace\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </span>\n )}\n </div>\n {!available ? <p className=\"px-1 text-2xs text-doom-red\">workspace unavailable</p> : null}\n {workspace.sessions.length > 0 ? (\n <div className=\"flex flex-col gap-1.5\">\n {workspace.sessions.map((session) => (\n <ElegantSessionCard key={session.id} session={session} actions={actions} />\n ))}\n </div>\n ) : available ? (\n <p className=\"px-1 text-2xs text-doom-faint\">no sessions yet</p>\n ) : null}\n <Dialog open={removeOpen} onOpenChange={(next) => !next && !removing && setRemoveOpen(false)}>\n <DialogContent width=\"sm\" data-testid={`workspace-delete-dialog-${id}`} aria-describedby={undefined}>\n <DialogHeader>\n <DialogTitle>delete workspace</DialogTitle>\n </DialogHeader>\n <DialogBody>\n <DialogDescription>\n this unregisters <span className=\"font-bold text-doom-hi\">{name}</span> from DoomPi only. files at{' '}\n <code>{root}</code> stay on disk. its sessions must be removed first.\n </DialogDescription>\n {removeError ? (\n <p data-testid={`workspace-delete-error-${id}`} className=\"text-xs text-doom-red\">\n {removeError}\n </p>\n ) : null}\n <DialogFooter>\n <Button\n variant=\"outline\"\n size=\"md\"\n data-testid={`workspace-delete-cancel-${id}`}\n disabled={removing}\n autoFocus\n onClick={() => setRemoveOpen(false)}\n >\n cancel\n </Button>\n <Button\n variant=\"danger\"\n size=\"md\"\n data-testid={`workspace-delete-confirm-${id}`}\n disabled={removing}\n onClick={() => void unregister()}\n >\n {removing ? 'deleting…' : 'delete'}\n </Button>\n </DialogFooter>\n </DialogBody>\n </DialogContent>\n </Dialog>\n </section>\n );\n}\n","import type { WebTemplateRail } from '@agimon-ai/doompi-core/web';\nimport { Button, Dot, GearIcon, PlusIcon, ShieldIcon } from '@agimon-ai/doompi-web-components';\n\nimport { ElegantWorkspaceGroup } from './ElegantWorkspaceGroup';\n\n/** The drawer's session list: quiet labels, soft tiles, and a low-key footer. */\nexport function ElegantRail({ rail }: { rail: WebTemplateRail }) {\n const { remote, actions } = rail;\n const live = remote.status === 'on' || remote.status === 'starting';\n return (\n <div className=\"flex h-full flex-col gap-2 px-3 pb-3\">\n <div className=\"flex items-center justify-between gap-2 pt-1\">\n <Button\n variant=\"subtle\"\n size=\"sm\"\n data-testid=\"add-workspace-open\"\n aria-label=\"add workspace\"\n onClick={actions.openAddWorkspace}\n className=\"gap-1.5\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n workspace\n </Button>\n <Button\n variant={live ? 'primary' : remote.status === 'failed' ? 'danger-outline' : 'ghost'}\n size=\"sm\"\n data-testid=\"remote-access-open\"\n aria-label={`remote access is ${remote.status}`}\n onClick={actions.openRemoteAccess}\n className=\"gap-1.5\"\n >\n {live ? <Dot tone=\"yellow\" pulse data-testid=\"remote-access-live\" /> : <ShieldIcon className=\"h-3 w-3\" />}\n remote\n </Button>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {rail.workspaces.map((workspace) => (\n <ElegantWorkspaceGroup key={workspace.id} workspace={workspace} actions={actions} />\n ))}\n {rail.workspaces.length === 0 ? (\n <Button\n variant=\"outline\"\n size=\"lg\"\n data-testid=\"add-workspace-empty\"\n onClick={actions.openAddWorkspace}\n className=\"mt-4 w-full justify-start px-3 text-sm\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n add your first workspace\n </Button>\n ) : null}\n {rail.pluginContent}\n </div>\n\n {remote.status === 'off' ? null : (\n <div\n data-testid=\"remote-banner\"\n className={`flex items-center gap-2 rounded-lg px-3 py-2 text-xs ${\n remote.status === 'failed' ? 'bg-doom-tint-red text-doom-red' : 'bg-doom-tint-yellow text-doom-yellow'\n }`}\n >\n <Dot tone={remote.status === 'failed' ? 'red' : 'yellow'} />\n <span className=\"min-w-0 flex-1 truncate\">\n {remote.status === 'failed'\n ? `remote access failed: ${remote.error ?? 'the tunnel stopped'}`\n : `remote ${remote.status === 'starting' ? 'starting' : 'on'}${\n remote.host === undefined ? '' : ` · ${remote.host}`\n } · ${String(remote.deviceCount)} device${remote.deviceCount === 1 ? '' : 's'}`}\n </span>\n <Button variant=\"ghost\" size=\"xs\" data-testid=\"remote-banner-open\" onClick={actions.openRemoteAccess}>\n manage\n </Button>\n <Button variant=\"ghost\" size=\"xs\" data-testid=\"remote-banner-off\" onClick={actions.turnRemoteAccessOff}>\n off\n </Button>\n </div>\n )}\n <div className=\"flex items-center justify-between border-t border-doom-border-soft pt-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n data-testid=\"settings-open\"\n aria-label=\"settings\"\n onClick={actions.openSettings}\n className=\"gap-1.5 text-doom-faint\"\n >\n <GearIcon className=\"h-3 w-3\" />\n settings\n </Button>\n <span className=\"text-2xs text-doom-faint max-sm:hidden\">ctrl+t new session</span>\n </div>\n </div>\n );\n}\n","import type { WebTemplateProps } from '@agimon-ai/doompi-core/web';\nimport { Drawer } from '@agimon-ai/doompi-web-components';\n\nimport { ElegantAddWorkspaceDialog } from './ElegantAddWorkspaceDialog';\nimport { ElegantRail } from './ElegantRail';\n\n/** A conversation-first frame, with the same host capabilities available on demand. */\nexport function ElegantLayout({\n view,\n slots,\n rail,\n navigationOpen,\n mobileActivityOpen,\n onNavigationOpenChange,\n onMobileActivityOpenChange,\n}: WebTemplateProps) {\n const conversation = view === 'conversation' || view === 'welcome';\n return (\n <div className=\"relative flex h-full min-w-0 flex-col overflow-hidden bg-doom-bg\">\n <Drawer\n open={navigationOpen}\n onOpenChange={onNavigationOpenChange}\n side=\"left\"\n title=\"Sessions\"\n data-testid=\"session-rail-panel\"\n backdropTestId=\"mobile-drawer-backdrop\"\n >\n <ElegantRail rail={rail} />\n </Drawer>\n {slots.notices}\n {slots.header({ navigationToggle: 'always', activityToggle: 'always' })}\n <main className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <div\n data-testid=\"template-content\"\n className={\n conversation\n ? 'mx-auto flex min-h-0 w-full max-w-[960px] flex-1 flex-col overflow-hidden px-2 sm:px-6'\n : 'flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden'\n }\n >\n {slots.content}\n {slots.composer == null ? null : (\n <div className=\"mx-2 mb-3 shrink-0 overflow-hidden rounded-lg border border-doom-border bg-doom-panel sm:mx-4 sm:mb-5\">\n {slots.composer}\n </div>\n )}\n {slots.controls == null ? null : (\n <details className=\"shrink-0 px-3 pb-3 text-sm text-doom-dim\">\n <summary\n data-testid=\"template-controls-toggle\"\n className=\"cursor-pointer rounded-sm px-1 py-1 focus-visible:outline focus-visible:outline-doom-blue\"\n >\n Session controls\n </summary>\n {slots.controls}\n </details>\n )}\n </div>\n </main>\n {slots.activity == null ? null : (\n <Drawer\n open={mobileActivityOpen}\n onOpenChange={onMobileActivityOpenChange}\n side=\"right\"\n title=\"Activity\"\n showHeader={false}\n backdropTestId=\"mobile-activity-backdrop\"\n className=\"w-auto max-w-[calc(100vw-48px)]\"\n >\n {slots.activity}\n </Drawer>\n )}\n {rail.addWorkspace ? (\n <ElegantAddWorkspaceDialog suggestedPaths={rail.addWorkspace.suggestedPaths} actions={rail.actions} />\n ) : null}\n </div>\n );\n}\n","import type { WebTemplateFile } from '@agimon-ai/doompi-core/web';\n\nimport { ElegantLayout } from '../_components/ElegantLayout';\n\nexport const elegantTemplate = {\n label: 'Elegant',\n description: 'A focused conversation with collapsible navigation, session controls, and activity.',\n contractVersion: 1,\n layout: ElegantLayout,\n} satisfies WebTemplateFile;\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from './_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from '../../../(frontend)/template/_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","import { defineWebTemplate } from '@agimon-ai/doompi-core/web';\n\nimport { elegantTemplate } from '../../../../(frontend)/template/_lib/definition';\n\nexport default defineWebTemplate(elegantTemplate);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport templateDoompiTemplateElegant from '../src/extensions/(frontend)/template/doompi-template-elegant.web';\nimport templateDoompiTemplateElegant2 from '../src/extensions/workspaces/(frontend)/template/doompi-template-elegant.web';\nimport templateDoompiTemplateElegant3 from '../src/extensions/workspaces/sessions/(frontend)/template/doompi-template-elegant.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'template-elegant',\n global: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant)]; },\n },\n workspace: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant2)]; },\n },\n session: {\n get templates(): WebPluginContributions['templates'] { return [via({ id: 'doompi-template-elegant' }, templateDoompiTemplateElegant3)]; },\n },\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-template-elegant",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.0.1-alpha.20",
4
4
  "description": "Elegant chat-first template for the DoomPi web client.",
5
5
  "keywords": [
6
6
  "ai",
@@ -51,14 +51,14 @@
51
51
  "access": "public"
52
52
  },
53
53
  "dependencies": {
54
- "@agimon-ai/doompi-core": "0.0.1-alpha.82",
55
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.40"
54
+ "@agimon-ai/doompi-core": "0.0.1-alpha.100",
55
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.58"
56
56
  },
57
57
  "devDependencies": {
58
- "@agimon-ai/doompi-build": "0.0.1-alpha.11",
59
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.77",
60
- "@earendil-works/pi-coding-agent": "0.87.1",
61
- "@types/node": "26.6.1",
58
+ "@agimon-ai/doompi-build": "0.0.1-alpha.22",
59
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.88",
60
+ "@earendil-works/pi-coding-agent": "0.99.1",
61
+ "@types/node": "26.6.2",
62
62
  "@types/react": "19.3.0",
63
63
  "@types/react-dom": "19.3.0",
64
64
  "react": "19.3.0",
@@ -68,7 +68,7 @@
68
68
  "vitest": "5.0.1"
69
69
  },
70
70
  "peerDependencies": {
71
- "@earendil-works/pi-coding-agent": "0.87.1",
71
+ "@earendil-works/pi-coding-agent": "0.99.1",
72
72
  "react": "^19.0.0",
73
73
  "react-dom": "^19.0.0"
74
74
  },