@agimon-ai/doompi-template-elegant 0.0.1-alpha.4 → 0.0.1-alpha.40
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 +1 -1
- package/dist/extensions/web.mjs +790 -5
- package/dist/extensions/web.mjs.map +1 -1
- package/package.json +9 -9
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
|
|
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
|
|
package/dist/extensions/web.mjs
CHANGED
|
@@ -1,9 +1,790 @@
|
|
|
1
1
|
import { defineWebPlugin, defineWebTemplate } from "@agimon-ai/doompi-core/web";
|
|
2
|
-
import { Drawer } from "@agimon-ai/doompi-web-components";
|
|
3
|
-
import {
|
|
2
|
+
import { ActivityIcon, 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, 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 === "workflow-session" ? /* @__PURE__ */ jsx(ActivityIcon, {
|
|
369
|
+
"aria-label": "workflow session",
|
|
370
|
+
className: `h-[11px] w-[11px] shrink-0 ${active ? "text-doom-on-selected" : "text-doom-faint"}`
|
|
371
|
+
}) : session.nested && session.provenance ? /* @__PURE__ */ jsx(ForkIcon, {
|
|
372
|
+
"aria-label": session.provenance === "worktree" ? "automatic per-conversation worktree" : session.provenance,
|
|
373
|
+
className: `h-[11px] w-[11px] shrink-0 ${active ? "text-doom-on-selected" : "text-doom-faint"}`
|
|
374
|
+
}) : null,
|
|
375
|
+
/* @__PURE__ */ jsx("span", {
|
|
376
|
+
className: `min-w-0 flex-1 truncate text-sm font-bold ${active ? "text-doom-on-selected" : "text-doom-hi"}`,
|
|
377
|
+
children: session.name || "untitled"
|
|
378
|
+
}),
|
|
379
|
+
session.ordinal <= 9 ? /* @__PURE__ */ jsx("span", {
|
|
380
|
+
title: `press ${String(session.ordinal)} to focus`,
|
|
381
|
+
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"}`,
|
|
382
|
+
children: session.ordinal
|
|
383
|
+
}) : null
|
|
384
|
+
]
|
|
385
|
+
}),
|
|
386
|
+
/* @__PURE__ */ jsxs("span", {
|
|
387
|
+
className: "flex items-start gap-1",
|
|
388
|
+
children: [session.awaitingInput ? /* @__PURE__ */ jsx(AlertIcon, {
|
|
389
|
+
"data-testid": "session-awaiting-input",
|
|
390
|
+
"aria-label": "waiting for your input",
|
|
391
|
+
className: "mt-[2px] h-[11px] w-[11px] shrink-0 text-doom-red"
|
|
392
|
+
}) : null, /* @__PURE__ */ jsx("span", {
|
|
393
|
+
"data-testid": "session-status",
|
|
394
|
+
className: `truncate text-xs ${session.awaitingInput ? "text-doom-red" : active ? "text-doom-on-selected/80" : "text-doom-dim"}`,
|
|
395
|
+
children: session.status
|
|
396
|
+
})]
|
|
397
|
+
}),
|
|
398
|
+
session.git ? /* @__PURE__ */ jsxs("span", {
|
|
399
|
+
"data-testid": "session-branch",
|
|
400
|
+
className: `flex items-center gap-1.5 text-2xs ${active ? "text-doom-on-selected/70" : "text-doom-faint"}`,
|
|
401
|
+
children: [/* @__PURE__ */ jsx(BranchIcon, { className: "h-[9px] w-[9px] shrink-0" }), /* @__PURE__ */ jsxs("span", {
|
|
402
|
+
className: "truncate",
|
|
403
|
+
children: [session.git.branch, session.git.dirty ? "*" : ""]
|
|
404
|
+
})]
|
|
405
|
+
}) : null,
|
|
406
|
+
error ? /* @__PURE__ */ jsx("span", {
|
|
407
|
+
"data-testid": "session-error",
|
|
408
|
+
className: "line-clamp-3 text-xs break-words text-doom-red",
|
|
409
|
+
children: error
|
|
410
|
+
}) : null
|
|
411
|
+
]
|
|
412
|
+
})]
|
|
413
|
+
});
|
|
414
|
+
const menuOpen = mode === "menu";
|
|
415
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
416
|
+
className: cn("group relative", session.nested && "pl-4"),
|
|
417
|
+
"data-testid": `session-card-${session.id}`,
|
|
418
|
+
"data-active": active,
|
|
419
|
+
"data-nested": session.nested,
|
|
420
|
+
children: [
|
|
421
|
+
/* @__PURE__ */ jsx(Button, {
|
|
422
|
+
variant: "ghost",
|
|
423
|
+
size: "card",
|
|
424
|
+
"data-testid": `session-open-${session.id}`,
|
|
425
|
+
onClick: () => actions.openSession(session.id),
|
|
426
|
+
className: tileClass,
|
|
427
|
+
children: body
|
|
428
|
+
}),
|
|
429
|
+
mode === "view" || menuOpen ? /* @__PURE__ */ jsx("div", {
|
|
430
|
+
className: `absolute top-2 right-2 transition-opacity ${menuOpen ? "opacity-100" : "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"}`,
|
|
431
|
+
children: /* @__PURE__ */ jsxs(DropdownMenu, {
|
|
432
|
+
open: menuOpen,
|
|
433
|
+
onOpenChange: (next) => {
|
|
434
|
+
const current = modeRef.current;
|
|
435
|
+
enterMode(next ? "menu" : current === "menu" ? "view" : current);
|
|
436
|
+
},
|
|
437
|
+
children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
438
|
+
asChild: true,
|
|
439
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
440
|
+
variant: "ghost",
|
|
441
|
+
size: "icon",
|
|
442
|
+
"data-testid": `session-menu-${session.id}`,
|
|
443
|
+
title: "session actions",
|
|
444
|
+
className: active ? "text-doom-on-selected/80" : "text-doom-faint hover:text-doom-hi",
|
|
445
|
+
children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
|
|
446
|
+
})
|
|
447
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
448
|
+
"data-testid": `session-menu-list-${session.id}`,
|
|
449
|
+
onCloseAutoFocus: (event) => {
|
|
450
|
+
if (modeRef.current !== "view") event.preventDefault();
|
|
451
|
+
},
|
|
452
|
+
children: [
|
|
453
|
+
actions.renderSessionMenuItems(session.id),
|
|
454
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
455
|
+
"data-testid": `session-rename-${session.id}`,
|
|
456
|
+
onSelect: () => {
|
|
457
|
+
setDraft(session.name);
|
|
458
|
+
enterMode("rename");
|
|
459
|
+
},
|
|
460
|
+
children: "rename"
|
|
461
|
+
}),
|
|
462
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
463
|
+
"data-testid": `session-resume-${session.id}`,
|
|
464
|
+
onSelect: () => actions.resumeSession(session.id),
|
|
465
|
+
children: "resume"
|
|
466
|
+
}),
|
|
467
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
468
|
+
"data-testid": `session-restart-${session.id}`,
|
|
469
|
+
disabled: session.restarting,
|
|
470
|
+
onSelect: () => actions.restartSession(session.id),
|
|
471
|
+
children: "restart"
|
|
472
|
+
}),
|
|
473
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
474
|
+
variant: "destructive",
|
|
475
|
+
"data-testid": `session-stop-${session.id}`,
|
|
476
|
+
onSelect: () => enterMode("confirm"),
|
|
477
|
+
children: "remove"
|
|
478
|
+
})
|
|
479
|
+
]
|
|
480
|
+
})]
|
|
481
|
+
})
|
|
482
|
+
}) : null,
|
|
483
|
+
/* @__PURE__ */ jsx(Dialog, {
|
|
484
|
+
open: mode === "rename",
|
|
485
|
+
onOpenChange: (next) => !next && enterMode("view"),
|
|
486
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
487
|
+
width: "sm",
|
|
488
|
+
"data-testid": `session-rename-dialog-${session.id}`,
|
|
489
|
+
"aria-describedby": void 0,
|
|
490
|
+
children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "rename session" }) }), /* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("form", {
|
|
491
|
+
className: "flex flex-col gap-3",
|
|
492
|
+
onSubmit: (event) => {
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
commitRename();
|
|
495
|
+
},
|
|
496
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
497
|
+
"data-testid": `session-name-input-${session.id}`,
|
|
498
|
+
"aria-label": "session name",
|
|
499
|
+
value: draft,
|
|
500
|
+
autoFocus: true,
|
|
501
|
+
onChange: (event) => setDraft(event.target.value)
|
|
502
|
+
}), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
503
|
+
type: "button",
|
|
504
|
+
variant: "outline",
|
|
505
|
+
size: "md",
|
|
506
|
+
"data-testid": `session-rename-cancel-${session.id}`,
|
|
507
|
+
onClick: () => enterMode("view"),
|
|
508
|
+
children: "cancel"
|
|
509
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
510
|
+
type: "submit",
|
|
511
|
+
variant: "primary",
|
|
512
|
+
size: "md",
|
|
513
|
+
"data-testid": `session-rename-confirm-${session.id}`,
|
|
514
|
+
disabled: draft.trim() === "",
|
|
515
|
+
children: "rename"
|
|
516
|
+
})] })]
|
|
517
|
+
}) })]
|
|
518
|
+
})
|
|
519
|
+
}),
|
|
520
|
+
/* @__PURE__ */ jsx(Dialog, {
|
|
521
|
+
open: mode === "confirm",
|
|
522
|
+
onOpenChange: (next) => !next && enterMode("view"),
|
|
523
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
524
|
+
width: "sm",
|
|
525
|
+
"data-testid": `session-stop-dialog-${session.id}`,
|
|
526
|
+
"aria-describedby": void 0,
|
|
527
|
+
children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "remove session" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [/* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
528
|
+
"this stops ",
|
|
529
|
+
/* @__PURE__ */ jsx("span", {
|
|
530
|
+
className: "font-bold text-doom-hi",
|
|
531
|
+
children: session.name || "untitled"
|
|
532
|
+
}),
|
|
533
|
+
" and removes it from the list. anything it is running ends now, but its saved history remains available to resume."
|
|
534
|
+
] }), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
535
|
+
variant: "outline",
|
|
536
|
+
size: "md",
|
|
537
|
+
"data-testid": `session-stop-cancel-${session.id}`,
|
|
538
|
+
autoFocus: true,
|
|
539
|
+
onClick: () => enterMode("view"),
|
|
540
|
+
children: "cancel"
|
|
541
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
542
|
+
variant: "danger",
|
|
543
|
+
size: "md",
|
|
544
|
+
"data-testid": `session-stop-confirm-${session.id}`,
|
|
545
|
+
onClick: () => void remove(),
|
|
546
|
+
children: "remove"
|
|
547
|
+
})] })] })]
|
|
548
|
+
})
|
|
549
|
+
})
|
|
550
|
+
]
|
|
551
|
+
}), session.pendingSetups.map((setup) => /* @__PURE__ */ jsx(ElegantPendingSetup, {
|
|
552
|
+
sessionId: session.id,
|
|
553
|
+
setup,
|
|
554
|
+
actions
|
|
555
|
+
}, `setup:${setup.id}`))] });
|
|
556
|
+
}
|
|
557
|
+
//#endregion
|
|
558
|
+
//#region src/extensions/(frontend)/template/_components/ElegantWorkspaceGroup.tsx
|
|
559
|
+
/** A small-caps workspace label; its path lives in the tooltip and its actions appear on hover. */
|
|
560
|
+
function ElegantWorkspaceGroup({ workspace, actions }) {
|
|
561
|
+
const [removeOpen, setRemoveOpen] = useState(false);
|
|
562
|
+
const [removeError, setRemoveError] = useState("");
|
|
563
|
+
const [removing, setRemoving] = useState(false);
|
|
564
|
+
const { id, name, root, available, legacy } = workspace;
|
|
565
|
+
const unregister = async () => {
|
|
566
|
+
setRemoving(true);
|
|
567
|
+
setRemoveError("");
|
|
568
|
+
const result = await actions.deleteWorkspace(id);
|
|
569
|
+
setRemoving(false);
|
|
570
|
+
if (result !== void 0) setRemoveError(result.error);
|
|
571
|
+
else setRemoveOpen(false);
|
|
572
|
+
};
|
|
573
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
574
|
+
"data-testid": `workspace-group-${id}`,
|
|
575
|
+
"data-available": available,
|
|
576
|
+
className: "flex flex-col gap-1.5 pt-4",
|
|
577
|
+
children: [
|
|
578
|
+
/* @__PURE__ */ jsxs("div", {
|
|
579
|
+
className: "group/workspace flex min-w-0 items-center gap-1 px-1",
|
|
580
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
581
|
+
className: "min-w-0 flex-1 truncate text-2xs font-bold tracking-widest text-doom-faint uppercase",
|
|
582
|
+
title: root,
|
|
583
|
+
children: name
|
|
584
|
+
}), legacy ? null : /* @__PURE__ */ jsxs("span", {
|
|
585
|
+
className: "flex items-center gap-0.5 opacity-0 transition-opacity group-focus-within/workspace:opacity-100 group-hover/workspace:opacity-100",
|
|
586
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
587
|
+
variant: "ghost",
|
|
588
|
+
size: "icon",
|
|
589
|
+
"data-testid": `workspace-new-session-${id}`,
|
|
590
|
+
title: "new session",
|
|
591
|
+
"aria-label": `new session in ${name}`,
|
|
592
|
+
disabled: !available,
|
|
593
|
+
onClick: () => actions.createSession(id),
|
|
594
|
+
className: "text-doom-faint hover:text-doom-hi",
|
|
595
|
+
children: /* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" })
|
|
596
|
+
}), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
597
|
+
asChild: true,
|
|
598
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
599
|
+
variant: "ghost",
|
|
600
|
+
size: "icon",
|
|
601
|
+
"data-testid": `workspace-menu-${id}`,
|
|
602
|
+
title: "workspace actions",
|
|
603
|
+
"aria-label": `${name} actions`,
|
|
604
|
+
className: "text-doom-faint hover:text-doom-hi",
|
|
605
|
+
children: /* @__PURE__ */ jsx(KebabIcon, { className: "h-3 w-3" })
|
|
606
|
+
})
|
|
607
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
|
|
608
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
609
|
+
disabled: !available,
|
|
610
|
+
onSelect: () => actions.resumeInWorkspace(id),
|
|
611
|
+
children: "resume session"
|
|
612
|
+
}),
|
|
613
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
614
|
+
disabled: !available,
|
|
615
|
+
onSelect: () => actions.openWorkspaceSettings(id),
|
|
616
|
+
children: "workspace settings"
|
|
617
|
+
}),
|
|
618
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
619
|
+
variant: "destructive",
|
|
620
|
+
"data-testid": `workspace-delete-${id}`,
|
|
621
|
+
onSelect: () => {
|
|
622
|
+
setRemoveError("");
|
|
623
|
+
setRemoveOpen(true);
|
|
624
|
+
},
|
|
625
|
+
children: "delete workspace"
|
|
626
|
+
})
|
|
627
|
+
] })] })]
|
|
628
|
+
})]
|
|
629
|
+
}),
|
|
630
|
+
!available ? /* @__PURE__ */ jsx("p", {
|
|
631
|
+
className: "px-1 text-2xs text-doom-red",
|
|
632
|
+
children: "workspace unavailable"
|
|
633
|
+
}) : null,
|
|
634
|
+
workspace.sessions.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
635
|
+
className: "flex flex-col gap-1.5",
|
|
636
|
+
children: workspace.sessions.map((session) => /* @__PURE__ */ jsx(ElegantSessionCard, {
|
|
637
|
+
session,
|
|
638
|
+
actions
|
|
639
|
+
}, session.id))
|
|
640
|
+
}) : available ? /* @__PURE__ */ jsx("p", {
|
|
641
|
+
className: "px-1 text-2xs text-doom-faint",
|
|
642
|
+
children: "no sessions yet"
|
|
643
|
+
}) : null,
|
|
644
|
+
/* @__PURE__ */ jsx(Dialog, {
|
|
645
|
+
open: removeOpen,
|
|
646
|
+
onOpenChange: (next) => !next && !removing && setRemoveOpen(false),
|
|
647
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
648
|
+
width: "sm",
|
|
649
|
+
"data-testid": `workspace-delete-dialog-${id}`,
|
|
650
|
+
"aria-describedby": void 0,
|
|
651
|
+
children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "delete workspace" }) }), /* @__PURE__ */ jsxs(DialogBody, { children: [
|
|
652
|
+
/* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
653
|
+
"this unregisters ",
|
|
654
|
+
/* @__PURE__ */ jsx("span", {
|
|
655
|
+
className: "font-bold text-doom-hi",
|
|
656
|
+
children: name
|
|
657
|
+
}),
|
|
658
|
+
" from DoomPi only. files at",
|
|
659
|
+
" ",
|
|
660
|
+
/* @__PURE__ */ jsx("code", { children: root }),
|
|
661
|
+
" stay on disk. its sessions must be removed first."
|
|
662
|
+
] }),
|
|
663
|
+
removeError ? /* @__PURE__ */ jsx("p", {
|
|
664
|
+
"data-testid": `workspace-delete-error-${id}`,
|
|
665
|
+
className: "text-xs text-doom-red",
|
|
666
|
+
children: removeError
|
|
667
|
+
}) : null,
|
|
668
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
669
|
+
variant: "outline",
|
|
670
|
+
size: "md",
|
|
671
|
+
"data-testid": `workspace-delete-cancel-${id}`,
|
|
672
|
+
disabled: removing,
|
|
673
|
+
autoFocus: true,
|
|
674
|
+
onClick: () => setRemoveOpen(false),
|
|
675
|
+
children: "cancel"
|
|
676
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
677
|
+
variant: "danger",
|
|
678
|
+
size: "md",
|
|
679
|
+
"data-testid": `workspace-delete-confirm-${id}`,
|
|
680
|
+
disabled: removing,
|
|
681
|
+
onClick: () => void unregister(),
|
|
682
|
+
children: removing ? "deleting…" : "delete"
|
|
683
|
+
})] })
|
|
684
|
+
] })]
|
|
685
|
+
})
|
|
686
|
+
})
|
|
687
|
+
]
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/extensions/(frontend)/template/_components/ElegantRail.tsx
|
|
692
|
+
/** The drawer's session list: quiet labels, soft tiles, and a low-key footer. */
|
|
693
|
+
function ElegantRail({ rail }) {
|
|
694
|
+
const { remote, actions } = rail;
|
|
695
|
+
const live = remote.status === "on" || remote.status === "starting";
|
|
696
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
697
|
+
className: "flex h-full flex-col gap-2 px-3 pb-3",
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ jsxs("div", {
|
|
700
|
+
className: "flex items-center justify-between gap-2 pt-1",
|
|
701
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
702
|
+
variant: "subtle",
|
|
703
|
+
size: "sm",
|
|
704
|
+
"data-testid": "add-workspace-open",
|
|
705
|
+
"aria-label": "add workspace",
|
|
706
|
+
onClick: actions.openAddWorkspace,
|
|
707
|
+
className: "gap-1.5",
|
|
708
|
+
children: [/* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" }), "workspace"]
|
|
709
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
710
|
+
variant: live ? "primary" : remote.status === "failed" ? "danger-outline" : "ghost",
|
|
711
|
+
size: "sm",
|
|
712
|
+
"data-testid": "remote-access-open",
|
|
713
|
+
"aria-label": `remote access is ${remote.status}`,
|
|
714
|
+
onClick: actions.openRemoteAccess,
|
|
715
|
+
className: "gap-1.5",
|
|
716
|
+
children: [live ? /* @__PURE__ */ jsx(Dot, {
|
|
717
|
+
tone: "yellow",
|
|
718
|
+
pulse: true,
|
|
719
|
+
"data-testid": "remote-access-live"
|
|
720
|
+
}) : /* @__PURE__ */ jsx(ShieldIcon, { className: "h-3 w-3" }), "remote"]
|
|
721
|
+
})]
|
|
722
|
+
}),
|
|
723
|
+
/* @__PURE__ */ jsxs("div", {
|
|
724
|
+
className: "min-h-0 flex-1 overflow-y-auto",
|
|
725
|
+
children: [
|
|
726
|
+
rail.workspaces.map((workspace) => /* @__PURE__ */ jsx(ElegantWorkspaceGroup, {
|
|
727
|
+
workspace,
|
|
728
|
+
actions
|
|
729
|
+
}, workspace.id)),
|
|
730
|
+
rail.workspaces.length === 0 ? /* @__PURE__ */ jsxs(Button, {
|
|
731
|
+
variant: "outline",
|
|
732
|
+
size: "lg",
|
|
733
|
+
"data-testid": "add-workspace-empty",
|
|
734
|
+
onClick: actions.openAddWorkspace,
|
|
735
|
+
className: "mt-4 w-full justify-start px-3 text-sm",
|
|
736
|
+
children: [/* @__PURE__ */ jsx(PlusIcon, { className: "h-3 w-3" }), "add your first workspace"]
|
|
737
|
+
}) : null,
|
|
738
|
+
rail.pluginContent
|
|
739
|
+
]
|
|
740
|
+
}),
|
|
741
|
+
remote.status === "off" ? null : /* @__PURE__ */ jsxs("div", {
|
|
742
|
+
"data-testid": "remote-banner",
|
|
743
|
+
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"}`,
|
|
744
|
+
children: [
|
|
745
|
+
/* @__PURE__ */ jsx(Dot, { tone: remote.status === "failed" ? "red" : "yellow" }),
|
|
746
|
+
/* @__PURE__ */ jsx("span", {
|
|
747
|
+
className: "min-w-0 flex-1 truncate",
|
|
748
|
+
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"}`
|
|
749
|
+
}),
|
|
750
|
+
/* @__PURE__ */ jsx(Button, {
|
|
751
|
+
variant: "ghost",
|
|
752
|
+
size: "xs",
|
|
753
|
+
"data-testid": "remote-banner-open",
|
|
754
|
+
onClick: actions.openRemoteAccess,
|
|
755
|
+
children: "manage"
|
|
756
|
+
}),
|
|
757
|
+
/* @__PURE__ */ jsx(Button, {
|
|
758
|
+
variant: "ghost",
|
|
759
|
+
size: "xs",
|
|
760
|
+
"data-testid": "remote-banner-off",
|
|
761
|
+
onClick: actions.turnRemoteAccessOff,
|
|
762
|
+
children: "off"
|
|
763
|
+
})
|
|
764
|
+
]
|
|
765
|
+
}),
|
|
766
|
+
/* @__PURE__ */ jsxs("div", {
|
|
767
|
+
className: "flex items-center justify-between border-t border-doom-border-soft pt-2",
|
|
768
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
769
|
+
variant: "ghost",
|
|
770
|
+
size: "sm",
|
|
771
|
+
"data-testid": "settings-open",
|
|
772
|
+
"aria-label": "settings",
|
|
773
|
+
onClick: actions.openSettings,
|
|
774
|
+
className: "gap-1.5 text-doom-faint",
|
|
775
|
+
children: [/* @__PURE__ */ jsx(GearIcon, { className: "h-3 w-3" }), "settings"]
|
|
776
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
777
|
+
className: "text-2xs text-doom-faint max-sm:hidden",
|
|
778
|
+
children: "ctrl+t new session"
|
|
779
|
+
})]
|
|
780
|
+
})
|
|
781
|
+
]
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
//#endregion
|
|
4
785
|
//#region src/extensions/(frontend)/template/_components/ElegantLayout.tsx
|
|
5
786
|
/** A conversation-first frame, with the same host capabilities available on demand. */
|
|
6
|
-
function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavigationOpenChange, onMobileActivityOpenChange }) {
|
|
787
|
+
function ElegantLayout({ view, slots, rail, navigationOpen, mobileActivityOpen, onNavigationOpenChange, onMobileActivityOpenChange }) {
|
|
7
788
|
const conversation = view === "conversation" || view === "welcome";
|
|
8
789
|
return /* @__PURE__ */ jsxs("div", {
|
|
9
790
|
className: "relative flex h-full min-w-0 flex-col overflow-hidden bg-doom-bg",
|
|
@@ -15,7 +796,7 @@ function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavi
|
|
|
15
796
|
title: "Sessions",
|
|
16
797
|
"data-testid": "session-rail-panel",
|
|
17
798
|
backdropTestId: "mobile-drawer-backdrop",
|
|
18
|
-
children:
|
|
799
|
+
children: /* @__PURE__ */ jsx(ElegantRail, { rail })
|
|
19
800
|
}),
|
|
20
801
|
slots.notices,
|
|
21
802
|
slots.header({
|
|
@@ -53,7 +834,11 @@ function ElegantLayout({ view, slots, navigationOpen, mobileActivityOpen, onNavi
|
|
|
53
834
|
backdropTestId: "mobile-activity-backdrop",
|
|
54
835
|
className: "w-auto max-w-[calc(100vw-48px)]",
|
|
55
836
|
children: slots.activity
|
|
56
|
-
})
|
|
837
|
+
}),
|
|
838
|
+
rail.addWorkspace ? /* @__PURE__ */ jsx(ElegantAddWorkspaceDialog, {
|
|
839
|
+
suggestedPaths: rail.addWorkspace.suggestedPaths,
|
|
840
|
+
actions: rail.actions
|
|
841
|
+
}) : null
|
|
57
842
|
]
|
|
58
843
|
});
|
|
59
844
|
}
|
|
@@ -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 =>\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":";;;;;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,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,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 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 ActivityIcon,\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 === 'workflow-session' ? (\n <ActivityIcon\n aria-label=\"workflow session\"\n className={`h-[11px] w-[11px] shrink-0 ${active ? 'text-doom-on-selected' : 'text-doom-faint'}`}\n />\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 <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 {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 <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 {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 rename\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 === 'rename'} onOpenChange={(next) => !next && enterMode('view')}>\n <DialogContent width=\"sm\" data-testid={`session-rename-dialog-${session.id}`} aria-describedby={undefined}>\n <DialogHeader>\n <DialogTitle>rename session</DialogTitle>\n </DialogHeader>\n <DialogBody>\n <form\n className=\"flex flex-col gap-3\"\n onSubmit={(event) => {\n event.preventDefault();\n commitRename();\n }}\n >\n <Input\n data-testid={`session-name-input-${session.id}`}\n aria-label=\"session name\"\n value={draft}\n autoFocus\n onChange={(event) => setDraft(event.target.value)}\n />\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"md\"\n data-testid={`session-rename-cancel-${session.id}`}\n onClick={() => enterMode('view')}\n >\n cancel\n </Button>\n <Button\n type=\"submit\"\n variant=\"primary\"\n size=\"md\"\n data-testid={`session-rename-confirm-${session.id}`}\n disabled={draft.trim() === ''}\n >\n rename\n </Button>\n </DialogFooter>\n </form>\n </DialogBody>\n </DialogContent>\n </Dialog>\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.
|
|
3
|
+
"version": "0.0.1-alpha.40",
|
|
4
4
|
"description": "Elegant chat-first template for the DoomPi web client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -51,24 +51,24 @@
|
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@agimon-ai/doompi-core": "0.0.1-alpha.
|
|
55
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.
|
|
54
|
+
"@agimon-ai/doompi-core": "0.0.1-alpha.120",
|
|
55
|
+
"@agimon-ai/doompi-web-components": "0.0.1-alpha.78"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@agimon-ai/doompi-build": "0.0.1-alpha.
|
|
59
|
-
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.
|
|
60
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
61
|
-
"@types/node": "26.6.
|
|
58
|
+
"@agimon-ai/doompi-build": "0.0.1-alpha.35",
|
|
59
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.102",
|
|
60
|
+
"@earendil-works/pi-coding-agent": "1.0.4",
|
|
61
|
+
"@types/node": "26.6.4",
|
|
62
62
|
"@types/react": "19.3.0",
|
|
63
63
|
"@types/react-dom": "19.3.0",
|
|
64
64
|
"react": "19.3.0",
|
|
65
65
|
"react-dom": "19.3.0",
|
|
66
66
|
"tsdown": "0.23.0",
|
|
67
67
|
"typescript": "7.0.2",
|
|
68
|
-
"vitest": "5.0.
|
|
68
|
+
"vitest": "5.0.3"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
71
|
+
"@earendil-works/pi-coding-agent": "1.0.4",
|
|
72
72
|
"react": "^19.0.0",
|
|
73
73
|
"react-dom": "^19.0.0"
|
|
74
74
|
},
|