@agimon-ai/po-artifact 0.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,507 @@
1
+ import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, defineSessionStore, defineWebPlugin, sessionApiAddress } from "@agimon-ai/po-core/web";
2
+ import { Breadcrumb, Button, CodeEditor, EmptyState, FileIcon, Markdown, MediaPreview } from "@agimon-ai/po-web-components";
3
+ import { useStore } from "@tanstack/react-store";
4
+ import { useCallback, useEffect, useState } from "react";
5
+ import { sealedTransport } from "@agimon-ai/po-web-security/browser";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ const api = createApiClient({
8
+ list: {
9
+ method: "GET",
10
+ path: "/",
11
+ response: void 0
12
+ },
13
+ preview: {
14
+ method: "GET",
15
+ path: "/preview",
16
+ query: ["path"],
17
+ response: void 0
18
+ },
19
+ content: {
20
+ method: "GET",
21
+ path: "/content",
22
+ query: ["path"],
23
+ raw: true
24
+ }
25
+ }, {
26
+ scopes: ["session"],
27
+ basePath: "artifacts",
28
+ transport: (input, init) => sealedTransport.fetch(input, init),
29
+ sessionAddress: sessionApiAddress
30
+ });
31
+ //#endregion
32
+ //#region src/types/artifacts.ts
33
+ const artifactsChannelType = "artifacts";
34
+ //#endregion
35
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts
36
+ function sortedArtifacts(items) {
37
+ return [...items].sort((left, right) => right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
38
+ }
39
+ function artifactTabId(sessionId, path) {
40
+ return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;
41
+ }
42
+ /** Resolves document assets within the artifact folder, never into another filesystem root. */
43
+ function artifactAssetPath(documentPath, imagePath) {
44
+ if (!imagePath || imagePath.startsWith("/") || imagePath.includes("\\") || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath)) throw new Error("Artifact images must use a relative path.");
45
+ const segments = documentPath.split("/").slice(0, -1);
46
+ for (const segment of imagePath.split("/")) {
47
+ if (segment === "" || segment === ".") continue;
48
+ if (segment === "..") {
49
+ if (segments.length === 0) throw new Error("The image is outside this artifact folder.");
50
+ segments.pop();
51
+ } else segments.push(segment);
52
+ }
53
+ if (segments.length === 0) throw new Error("The image path is empty.");
54
+ return segments.join("/");
55
+ }
56
+ function artifactPreviewMode(path) {
57
+ if (/\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return "media";
58
+ if (/\.(?:md|markdown)$/iu.test(path)) return "markdown";
59
+ if (/\.html?$/iu.test(path)) return "html";
60
+ if (/\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path)) return "code";
61
+ return "text";
62
+ }
63
+ /** A malformed channel frame must not replace the current session's known inventory. */
64
+ function parseArtifactSnapshot(input) {
65
+ if (typeof input !== "object" || input === null || !("items" in input) || !Array.isArray(input.items)) return null;
66
+ if ("error" in input && input.error !== void 0 && typeof input.error !== "string") return null;
67
+ const items = [];
68
+ for (const item of input.items) {
69
+ if (typeof item !== "object" || item === null || typeof item.path !== "string" || !item.path || item.path.startsWith("/") || item.path.includes("\\") || item.path.split("/").some((part) => part === ".." || part === ".") || typeof item.name !== "string" || !item.name || typeof item.size !== "number" || !Number.isFinite(item.size) || item.size < 0 || typeof item.modifiedAt !== "number" || !Number.isFinite(item.modifiedAt) || item.modifiedAt < 0) return null;
70
+ items.push({
71
+ path: item.path,
72
+ name: item.name,
73
+ size: item.size,
74
+ modifiedAt: item.modifiedAt
75
+ });
76
+ }
77
+ return {
78
+ items: sortedArtifacts(items),
79
+ ..."error" in input && typeof input.error === "string" ? { error: input.error } : {}
80
+ };
81
+ }
82
+ //#endregion
83
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts
84
+ const artifacts = defineSessionStore({
85
+ items: [],
86
+ loaded: false
87
+ });
88
+ const artifactsChannel = artifacts.channel({
89
+ channel: artifactsChannelType,
90
+ parse: parseArtifactSnapshot,
91
+ reduce: (_current, snapshot) => ({
92
+ ...snapshot,
93
+ loaded: true
94
+ })
95
+ });
96
+ /** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */
97
+ const artifactActivitySource = {
98
+ subscribe(listener) {
99
+ const subscription = artifacts.store.subscribe(listener);
100
+ return () => subscription.unsubscribe();
101
+ },
102
+ isActive: () => false
103
+ };
104
+ //#endregion
105
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts
106
+ async function refreshArtifacts(sessionId, signal) {
107
+ const before = artifacts.select(artifacts.store.state, sessionId);
108
+ try {
109
+ const result = await api.session(sessionId).list({ signal });
110
+ if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;
111
+ const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;
112
+ artifacts.update(sessionId, () => snapshot ? {
113
+ ...snapshot,
114
+ loaded: true
115
+ } : {
116
+ ...before,
117
+ loaded: true,
118
+ error: result.ok ? "The session answered with no artifact list." : result.error || "Artifacts could not be loaded."
119
+ });
120
+ } catch (error) {
121
+ if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before) artifacts.update(sessionId, () => ({
122
+ ...before,
123
+ loaded: true,
124
+ error: error instanceof Error ? error.message : String(error)
125
+ }));
126
+ }
127
+ }
128
+ async function fetchArtifactPreview(sessionId, path, signal) {
129
+ const result = await api.session(sessionId).preview({
130
+ query: { path },
131
+ signal
132
+ });
133
+ if (!result.ok) throw new Error(result.error || "This artifact could not be read.");
134
+ const preview = result.data;
135
+ if (!preview || typeof preview.path !== "string" || typeof preview.content !== "string" || typeof preview.mimeType !== "string") throw new Error("The session answered with no artifact.");
136
+ return preview;
137
+ }
138
+ /** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */
139
+ async function artifactBlob(sessionId, path, signal) {
140
+ const result = await api.session(sessionId).content({
141
+ query: { path },
142
+ ...signal ? { signal } : {}
143
+ });
144
+ if (!result.ok) throw new Error(result.error || "This artifact could not be loaded.");
145
+ return result.response.blob();
146
+ }
147
+ async function loadArtifactImage(sessionId, documentPath, imagePath) {
148
+ const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));
149
+ if (!blob.type.startsWith("image/")) throw new Error("This artifact asset is not an image.");
150
+ const url = URL.createObjectURL(blob);
151
+ return {
152
+ url,
153
+ dispose: () => URL.revokeObjectURL(url)
154
+ };
155
+ }
156
+ //#endregion
157
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx
158
+ function ArtifactPreviewView({ path, preview, error, media, loadImage }) {
159
+ const mode = artifactPreviewMode(path);
160
+ return /* @__PURE__ */ jsxs("div", {
161
+ "data-testid": "artifact-preview",
162
+ "data-artifact-path": path,
163
+ className: "flex min-h-0 flex-1 flex-col bg-doom-bg text-doom-text",
164
+ children: [/* @__PURE__ */ jsxs("header", {
165
+ className: "flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-3",
166
+ children: [/* @__PURE__ */ jsx(Breadcrumb, {
167
+ path,
168
+ className: "min-w-0 flex-1"
169
+ }), /* @__PURE__ */ jsx("span", {
170
+ className: "text-sm text-doom-dim",
171
+ children: "Artifact"
172
+ })]
173
+ }), /* @__PURE__ */ jsx("div", {
174
+ className: "min-h-0 flex-1 overflow-auto p-4",
175
+ children: error ? /* @__PURE__ */ jsx("p", {
176
+ role: "alert",
177
+ className: "text-sm text-doom-red",
178
+ children: error
179
+ }) : !preview ? /* @__PURE__ */ jsx("p", {
180
+ role: "status",
181
+ className: "text-sm text-doom-dim",
182
+ children: "Reading this artifact..."
183
+ }) : mode === "media" ? media : preview.unavailable ? /* @__PURE__ */ jsx("p", {
184
+ className: "text-sm text-doom-dim",
185
+ children: preview.reason ?? "This artifact cannot be previewed."
186
+ }) : mode === "markdown" ? /* @__PURE__ */ jsx(Markdown, {
187
+ text: preview.content,
188
+ loadImage
189
+ }) : mode === "html" ? /* @__PURE__ */ jsx("iframe", {
190
+ title: path,
191
+ sandbox: "",
192
+ srcDoc: preview.content,
193
+ className: "h-96 w-full rounded-md border border-doom-border bg-doom-deep"
194
+ }) : mode === "code" ? /* @__PURE__ */ jsx(CodeEditor, {
195
+ value: preview.content,
196
+ path,
197
+ readOnly: true,
198
+ className: "min-h-64 rounded-md border border-doom-border"
199
+ }) : /* @__PURE__ */ jsx("pre", {
200
+ className: "whitespace-pre-wrap break-words font-mono text-sm text-doom-text",
201
+ children: preview.content
202
+ })
203
+ })]
204
+ });
205
+ }
206
+ //#endregion
207
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx
208
+ function ArtifactMedia({ sessionId, path, revision }) {
209
+ const [loaded, setLoaded] = useState();
210
+ const source = `${sessionId}:${path}:${revision}`;
211
+ useEffect(() => {
212
+ const controller = new AbortController();
213
+ let url;
214
+ artifactBlob(sessionId, path, controller.signal).then((blob) => {
215
+ if (controller.signal.aborted) return;
216
+ url = URL.createObjectURL(blob);
217
+ setLoaded({
218
+ source,
219
+ url,
220
+ mimeType: blob.type
221
+ });
222
+ }).catch((error) => {
223
+ if (!controller.signal.aborted) setLoaded({
224
+ source,
225
+ error: error instanceof Error ? error.message : String(error)
226
+ });
227
+ });
228
+ return () => {
229
+ controller.abort();
230
+ if (url !== void 0) URL.revokeObjectURL(url);
231
+ };
232
+ }, [
233
+ sessionId,
234
+ path,
235
+ source
236
+ ]);
237
+ const current = loaded?.source === source ? loaded : void 0;
238
+ return current?.url ? /* @__PURE__ */ jsx(MediaPreview, {
239
+ src: current.url,
240
+ path,
241
+ contentType: current.mimeType
242
+ }) : /* @__PURE__ */ jsx("p", {
243
+ role: current?.error ? "alert" : "status",
244
+ className: current?.error ? "text-sm text-doom-red" : "text-sm text-doom-dim",
245
+ children: current?.error ?? "Loading artifact media..."
246
+ });
247
+ }
248
+ function ArtifactPreviewPanel({ path, sessionId }) {
249
+ const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));
250
+ const item = snapshot.items.find((candidate) => candidate.path === path);
251
+ const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? "missing" : "initial";
252
+ const [loaded, setLoaded] = useState();
253
+ useEffect(() => {
254
+ if (sessionId === null || revision === "missing") return;
255
+ const controller = new AbortController();
256
+ fetchArtifactPreview(sessionId, path, controller.signal).then((preview) => {
257
+ if (!controller.signal.aborted) setLoaded({
258
+ sessionId,
259
+ path,
260
+ revision,
261
+ preview
262
+ });
263
+ }).catch((error) => {
264
+ if (!controller.signal.aborted) setLoaded({
265
+ sessionId,
266
+ path,
267
+ revision,
268
+ error: error instanceof Error ? error.message : String(error)
269
+ });
270
+ });
271
+ return () => controller.abort();
272
+ }, [
273
+ sessionId,
274
+ path,
275
+ revision
276
+ ]);
277
+ const current = loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : void 0;
278
+ const loadImage = useCallback((imagePath) => {
279
+ if (sessionId === null) return Promise.reject(/* @__PURE__ */ new Error("The session is unavailable."));
280
+ return loadArtifactImage(sessionId, path, imagePath);
281
+ }, [sessionId, path]);
282
+ return /* @__PURE__ */ jsx(ArtifactPreviewView, {
283
+ path,
284
+ preview: current?.preview,
285
+ error: sessionId === null ? "The session is unavailable." : revision === "missing" ? "This artifact no longer exists." : current?.error,
286
+ loadImage,
287
+ media: sessionId === null ? void 0 : /* @__PURE__ */ jsx(ArtifactMedia, {
288
+ sessionId,
289
+ path,
290
+ revision
291
+ })
292
+ });
293
+ }
294
+ function artifactTab(sessionId, path) {
295
+ return {
296
+ id: artifactTabId(sessionId, path),
297
+ label: path.split("/").at(-1) ?? path,
298
+ panel: (props) => /* @__PURE__ */ jsx(ArtifactPreviewPanel, {
299
+ ...props,
300
+ sessionId,
301
+ path
302
+ })
303
+ };
304
+ }
305
+ //#endregion
306
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx
307
+ function sizeLabel(size) {
308
+ return size < 1024 ? `${size} B` : size < 1048576 ? `${Math.ceil(size / 1024)} KB` : `${(size / 1048576).toFixed(1)} MB`;
309
+ }
310
+ function RecentArtifactsView({ items, loading, error, onOpen }) {
311
+ return /* @__PURE__ */ jsxs("div", {
312
+ "data-testid": "artifacts-recent-items",
313
+ className: "flex flex-col gap-1",
314
+ children: [
315
+ error ? /* @__PURE__ */ jsx("p", {
316
+ role: "alert",
317
+ className: "text-sm text-doom-red",
318
+ children: error
319
+ }) : null,
320
+ loading ? /* @__PURE__ */ jsx("p", {
321
+ role: "status",
322
+ className: "text-sm text-doom-dim",
323
+ children: "Loading artifacts..."
324
+ }) : null,
325
+ !loading && !error && items.length === 0 ? /* @__PURE__ */ jsx("p", {
326
+ className: "text-sm text-doom-dim",
327
+ children: "No artifacts yet"
328
+ }) : null,
329
+ sortedArtifacts(items).slice(0, 3).map((item) => /* @__PURE__ */ jsxs(Button, {
330
+ variant: "ghost",
331
+ size: "card",
332
+ "data-artifact-path": item.path,
333
+ onClick: () => onOpen(item),
334
+ className: "gap-0.5 px-2 py-1.5",
335
+ children: [/* @__PURE__ */ jsxs("span", {
336
+ className: "flex min-w-0 items-center gap-2 text-base text-doom-text",
337
+ children: [/* @__PURE__ */ jsx(FileIcon, {
338
+ "aria-hidden": true,
339
+ className: "size-3.5 text-doom-dim"
340
+ }), /* @__PURE__ */ jsx("span", {
341
+ className: "truncate",
342
+ children: item.name
343
+ })]
344
+ }), /* @__PURE__ */ jsx("span", {
345
+ className: "truncate font-mono text-sm text-doom-dim",
346
+ title: item.path,
347
+ children: item.path
348
+ })]
349
+ }, item.path))
350
+ ]
351
+ });
352
+ }
353
+ function ArtifactGridView({ items, loading, error, onOpen }) {
354
+ return /* @__PURE__ */ jsxs("div", {
355
+ className: "flex min-h-0 flex-1 flex-col overflow-auto bg-doom-bg text-doom-text",
356
+ children: [
357
+ /* @__PURE__ */ jsxs("header", {
358
+ className: "flex shrink-0 items-center gap-3 border-b border-doom-border px-4 py-3",
359
+ children: [/* @__PURE__ */ jsx("h1", {
360
+ className: "text-lg font-semibold text-doom-hi",
361
+ children: "Artifacts"
362
+ }), /* @__PURE__ */ jsxs("span", {
363
+ "data-testid": "artifacts-count",
364
+ className: "text-sm text-doom-dim",
365
+ children: [
366
+ items.length,
367
+ " ",
368
+ items.length === 1 ? "artifact" : "artifacts"
369
+ ]
370
+ })]
371
+ }),
372
+ error ? /* @__PURE__ */ jsx("p", {
373
+ role: "alert",
374
+ className: "px-4 py-3 text-sm text-doom-red",
375
+ children: error
376
+ }) : null,
377
+ loading ? /* @__PURE__ */ jsx("p", {
378
+ role: "status",
379
+ className: "px-4 py-3 text-sm text-doom-dim",
380
+ children: "Loading artifacts..."
381
+ }) : null,
382
+ !loading && !error && items.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
383
+ title: "No artifacts yet",
384
+ description: "Documents Po prepares for this session will appear here.",
385
+ className: "px-4 py-8"
386
+ }) : null,
387
+ /* @__PURE__ */ jsx("div", {
388
+ "data-testid": "artifacts-grid",
389
+ className: "grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3",
390
+ children: sortedArtifacts(items).map((item) => /* @__PURE__ */ jsxs(Button, {
391
+ variant: "outline",
392
+ size: "card",
393
+ "data-artifact-path": item.path,
394
+ onClick: () => onOpen(item),
395
+ className: "min-w-0 gap-2 p-4",
396
+ children: [
397
+ /* @__PURE__ */ jsx(FileIcon, {
398
+ "aria-hidden": true,
399
+ className: "size-5 text-doom-green"
400
+ }),
401
+ /* @__PURE__ */ jsx("span", {
402
+ className: "truncate text-base font-semibold text-doom-hi",
403
+ title: item.name,
404
+ children: item.name
405
+ }),
406
+ /* @__PURE__ */ jsx("span", {
407
+ className: "truncate font-mono text-sm text-doom-dim",
408
+ title: item.path,
409
+ children: item.path
410
+ }),
411
+ /* @__PURE__ */ jsx("span", {
412
+ className: "text-sm text-doom-dim",
413
+ children: sizeLabel(item.size)
414
+ })
415
+ ]
416
+ }, item.path))
417
+ })
418
+ ]
419
+ });
420
+ }
421
+ //#endregion
422
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx
423
+ function artifactsTab() {
424
+ return {
425
+ id: "artifacts-grid",
426
+ label: "Artifacts",
427
+ panel: ArtifactsPanel
428
+ };
429
+ }
430
+ function ArtifactsActivitySection({ sessionId, openTransientTab }) {
431
+ const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));
432
+ useEffect(() => {
433
+ if (sessionId === null || snapshot.loaded) return;
434
+ const controller = new AbortController();
435
+ refreshArtifacts(sessionId, controller.signal);
436
+ return () => controller.abort();
437
+ }, [sessionId, snapshot.loaded]);
438
+ return /* @__PURE__ */ jsx(RecentArtifactsView, {
439
+ items: snapshot.items,
440
+ loading: !snapshot.loaded,
441
+ error: snapshot.error,
442
+ onOpen: (item) => {
443
+ if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));
444
+ }
445
+ });
446
+ }
447
+ function ArtifactsPanel({ sessionId, openTransientTab }) {
448
+ const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));
449
+ useEffect(() => {
450
+ if (sessionId === null) return;
451
+ const controller = new AbortController();
452
+ refreshArtifacts(sessionId, controller.signal);
453
+ return () => controller.abort();
454
+ }, [sessionId]);
455
+ return /* @__PURE__ */ jsx(ArtifactGridView, {
456
+ items: snapshot.items,
457
+ loading: !snapshot.loaded && sessionId !== null,
458
+ error: sessionId === null ? "The session is unavailable." : snapshot.error,
459
+ onOpen: (item) => {
460
+ if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));
461
+ }
462
+ });
463
+ }
464
+ //#endregion
465
+ //#region src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts
466
+ var artifacts_web_default$1 = defineActivityGroup({
467
+ icon: FileIcon,
468
+ keys: "",
469
+ statusKey: "doom-artifacts",
470
+ activeSource: artifactActivitySource,
471
+ transientTab: artifactsTab,
472
+ marksBackgroundWork: false,
473
+ order: 15
474
+ });
475
+ //#endregion
476
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts
477
+ var artifacts_web_default = defineChannelFile(artifactsChannel);
478
+ //#endregion
479
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx
480
+ var Artifacts_activity_artifacts_web_default = defineFill({ component: ArtifactsActivitySection });
481
+ //#endregion
482
+ //#region generated/web.ts
483
+ const via = (identity, value) => value === void 0 ? void 0 : {
484
+ ...identity,
485
+ ...value
486
+ };
487
+ const webPlugin = defineWebPlugin({
488
+ id: "artifacts",
489
+ session: {
490
+ get activityGroups() {
491
+ return [via({ name: "artifacts" }, artifacts_web_default$1)];
492
+ },
493
+ get channels() {
494
+ return [via({ channel: "artifacts" }, artifacts_web_default)];
495
+ },
496
+ get fills() {
497
+ return [via({
498
+ slot: "activity.artifacts",
499
+ id: "artifacts"
500
+ }, Artifacts_activity_artifacts_web_default)];
501
+ }
502
+ }
503
+ });
504
+ //#endregion
505
+ export { webPlugin };
506
+
507
+ //# sourceMappingURL=web.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.mjs","names":["activityGroupArtifacts","channelArtifacts","fillArtifacts"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/types/artifacts.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx","../../generated/web.ts"],"sourcesContent":["import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'artifacts' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'artifacts',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import type { ArtifactItem, ArtifactSnapshot } from '../../../../../types/artifacts';\n\nexport function sortedArtifacts(items: readonly ArtifactItem[]): ArtifactItem[] {\n return [...items].sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n}\n\nexport function artifactTabId(sessionId: string, path: string): string {\n return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;\n}\n\nexport function artifactsTabId(sessionId: string): string {\n return `artifacts-${encodeURIComponent(sessionId)}`;\n}\n\n/** Resolves document assets within the artifact folder, never into another filesystem root. */\nexport function artifactAssetPath(documentPath: string, imagePath: string): string {\n if (!imagePath || imagePath.startsWith('/') || imagePath.includes('\\\\') || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath))\n throw new Error('Artifact images must use a relative path.');\n const segments = documentPath.split('/').slice(0, -1);\n for (const segment of imagePath.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error('The image is outside this artifact folder.');\n segments.pop();\n } else segments.push(segment);\n }\n if (segments.length === 0) throw new Error('The image path is empty.');\n return segments.join('/');\n}\n\nexport function artifactPreviewMode(path: string): 'markdown' | 'html' | 'media' | 'code' | 'text' {\n if (/\\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return 'media';\n if (/\\.(?:md|markdown)$/iu.test(path)) return 'markdown';\n if (/\\.html?$/iu.test(path)) return 'html';\n if (/\\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path))\n return 'code';\n return 'text';\n}\n\n/** A malformed channel frame must not replace the current session's known inventory. */\nexport function parseArtifactSnapshot(input: unknown): ArtifactSnapshot | null {\n if (typeof input !== 'object' || input === null || !('items' in input) || !Array.isArray(input.items)) return null;\n if ('error' in input && input.error !== undefined && typeof input.error !== 'string') return null;\n const items: ArtifactItem[] = [];\n for (const item of input.items) {\n if (\n typeof item !== 'object' ||\n item === null ||\n typeof item.path !== 'string' ||\n !item.path ||\n item.path.startsWith('/') ||\n item.path.includes('\\\\') ||\n item.path.split('/').some((part: string) => part === '..' || part === '.') ||\n typeof item.name !== 'string' ||\n !item.name ||\n typeof item.size !== 'number' ||\n !Number.isFinite(item.size) ||\n item.size < 0 ||\n typeof item.modifiedAt !== 'number' ||\n !Number.isFinite(item.modifiedAt) ||\n item.modifiedAt < 0\n )\n return null;\n items.push({ path: item.path, name: item.name, size: item.size, modifiedAt: item.modifiedAt });\n }\n return {\n items: sortedArtifacts(items),\n ...('error' in input && typeof input.error === 'string' ? { error: input.error } : {}),\n };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../../../../types/artifacts';\nimport { parseArtifactSnapshot } from './artifactView';\n\nexport const artifacts = defineSessionStore<ArtifactSnapshot & { loaded: boolean }>({ items: [], loaded: false });\n\nexport const artifactsChannel = artifacts.channel<ArtifactSnapshot>({\n channel: artifactsChannelType,\n parse: parseArtifactSnapshot,\n reduce: (_current, snapshot) => ({ ...snapshot, loaded: true }),\n});\n\n/** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */\nexport const artifactActivitySource = {\n subscribe(listener: () => void) {\n const subscription = artifacts.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive: () => false,\n};\n","import { api } from '../../../../../../generated/client';\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifacts } from './artifactsStore';\nimport { artifactAssetPath, parseArtifactSnapshot } from './artifactView';\n\nexport async function refreshArtifacts(sessionId: string, signal: AbortSignal): Promise<void> {\n const before = artifacts.select(artifacts.store.state, sessionId);\n try {\n const result = await api.session(sessionId).list({ signal });\n if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;\n const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;\n artifacts.update(sessionId, () =>\n snapshot\n ? { ...snapshot, loaded: true }\n : {\n ...before,\n loaded: true,\n error: result.ok\n ? 'The session answered with no artifact list.'\n : result.error || 'Artifacts could not be loaded.',\n },\n );\n } catch (error) {\n if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before)\n artifacts.update(sessionId, () => ({\n ...before,\n loaded: true,\n error: error instanceof Error ? error.message : String(error),\n }));\n }\n}\n\nexport async function fetchArtifactPreview(\n sessionId: string,\n path: string,\n signal: AbortSignal,\n): Promise<ArtifactPreview> {\n const result = await api.session(sessionId).preview({ query: { path }, signal });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be read.');\n const preview = result.data;\n if (\n !preview ||\n typeof preview.path !== 'string' ||\n typeof preview.content !== 'string' ||\n typeof preview.mimeType !== 'string'\n )\n throw new Error('The session answered with no artifact.');\n return preview;\n}\n\n/** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */\nexport async function artifactBlob(sessionId: string, path: string, signal?: AbortSignal): Promise<Blob> {\n const result = await api.session(sessionId).content({ query: { path }, ...(signal ? { signal } : {}) });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be loaded.');\n return result.response.blob();\n}\n\nexport async function loadArtifactImage(sessionId: string, documentPath: string, imagePath: string) {\n const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));\n if (!blob.type.startsWith('image/')) throw new Error('This artifact asset is not an image.');\n const url = URL.createObjectURL(blob);\n return { url, dispose: () => URL.revokeObjectURL(url) };\n}\n","import { Breadcrumb, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactPreviewMode } from '../_lib/artifactView';\n\ninterface ArtifactPreviewViewProps {\n path: string;\n preview?: ArtifactPreview;\n error?: string;\n media?: ReactNode;\n loadImage?: (path: string) => Promise<{ url: string; dispose: () => void }>;\n}\n\nexport function ArtifactPreviewView({ path, preview, error, media, loadImage }: ArtifactPreviewViewProps) {\n const mode = artifactPreviewMode(path);\n return (\n <div\n data-testid=\"artifact-preview\"\n data-artifact-path={path}\n className=\"flex min-h-0 flex-1 flex-col bg-doom-bg text-doom-text\"\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-3\">\n <Breadcrumb path={path} className=\"min-w-0 flex-1\" />\n <span className=\"text-sm text-doom-dim\">Artifact</span>\n </header>\n <div className=\"min-h-0 flex-1 overflow-auto p-4\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : !preview ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Reading this artifact...\n </p>\n ) : mode === 'media' ? (\n media\n ) : preview.unavailable ? (\n <p className=\"text-sm text-doom-dim\">{preview.reason ?? 'This artifact cannot be previewed.'}</p>\n ) : mode === 'markdown' ? (\n <Markdown text={preview.content} loadImage={loadImage} />\n ) : mode === 'html' ? (\n <iframe\n title={path}\n sandbox=\"\"\n srcDoc={preview.content}\n className=\"h-96 w-full rounded-md border border-doom-border bg-doom-deep\"\n />\n ) : mode === 'code' ? (\n <CodeEditor\n value={preview.content}\n path={path}\n readOnly\n className=\"min-h-64 rounded-md border border-doom-border\"\n />\n ) : (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-sm text-doom-text\">{preview.content}</pre>\n )}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { MediaPreview } from '@agimon-ai/po-web-components';\nimport { useStore } from '@tanstack/react-store';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactBlob, fetchArtifactPreview, loadArtifactImage } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTabId } from '../_lib/artifactView';\nimport { ArtifactPreviewView } from './ArtifactPreviewView';\n\nfunction ArtifactMedia({ sessionId, path, revision }: { sessionId: string; path: string; revision: string }) {\n const [loaded, setLoaded] = useState<{ source: string; url?: string; mimeType?: string; error?: string }>();\n const source = `${sessionId}:${path}:${revision}`;\n useEffect(() => {\n const controller = new AbortController();\n let url: string | undefined;\n void artifactBlob(sessionId, path, controller.signal)\n .then((blob) => {\n if (controller.signal.aborted) return;\n url = URL.createObjectURL(blob);\n setLoaded({ source, url, mimeType: blob.type });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ source, error: error instanceof Error ? error.message : String(error) });\n });\n return () => {\n controller.abort();\n if (url !== undefined) URL.revokeObjectURL(url);\n };\n }, [sessionId, path, source]);\n const current = loaded?.source === source ? loaded : undefined;\n return current?.url ? (\n <MediaPreview src={current.url} path={path} contentType={current.mimeType} />\n ) : (\n <p\n role={current?.error ? 'alert' : 'status'}\n className={current?.error ? 'text-sm text-doom-red' : 'text-sm text-doom-dim'}\n >\n {current?.error ?? 'Loading artifact media...'}\n </p>\n );\n}\n\nexport function ArtifactPreviewPanel({ path, sessionId }: WebPluginSlotProps & { path: string }) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n const item = snapshot.items.find((candidate) => candidate.path === path);\n const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? 'missing' : 'initial';\n const [loaded, setLoaded] = useState<{\n sessionId: string;\n path: string;\n revision: string;\n preview?: ArtifactPreview;\n error?: string;\n }>();\n useEffect(() => {\n if (sessionId === null || revision === 'missing') return;\n const controller = new AbortController();\n void fetchArtifactPreview(sessionId, path, controller.signal)\n .then((preview) => {\n if (!controller.signal.aborted) setLoaded({ sessionId, path, revision, preview });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ sessionId, path, revision, error: error instanceof Error ? error.message : String(error) });\n });\n return () => controller.abort();\n }, [sessionId, path, revision]);\n const current =\n loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : undefined;\n const loadImage = useCallback(\n (imagePath: string) => {\n if (sessionId === null) return Promise.reject(new Error('The session is unavailable.'));\n return loadArtifactImage(sessionId, path, imagePath);\n },\n [sessionId, path],\n );\n return (\n <ArtifactPreviewView\n path={path}\n preview={current?.preview}\n error={\n sessionId === null\n ? 'The session is unavailable.'\n : revision === 'missing'\n ? 'This artifact no longer exists.'\n : current?.error\n }\n loadImage={loadImage}\n media={sessionId === null ? undefined : <ArtifactMedia sessionId={sessionId} path={path} revision={revision} />}\n />\n );\n}\n\nexport function artifactTab(sessionId: string, path: string): TransientTab {\n return {\n id: artifactTabId(sessionId, path),\n label: path.split('/').at(-1) ?? path,\n panel: (props) => <ArtifactPreviewPanel {...props} sessionId={sessionId} path={path} />,\n };\n}\n","import { Button, EmptyState, FileIcon } from '@agimon-ai/po-web-components';\n\nimport type { ArtifactItem } from '../../../../../types/artifacts';\nimport { sortedArtifacts } from '../_lib/artifactView';\n\ninterface ArtifactsViewProps {\n items: readonly ArtifactItem[];\n loading?: boolean;\n error?: string;\n onOpen: (item: ArtifactItem) => void;\n}\n\nfunction sizeLabel(size: number): string {\n return size < 1024\n ? `${size} B`\n : size < 1024 * 1024\n ? `${Math.ceil(size / 1024)} KB`\n : `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function RecentArtifactsView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div data-testid=\"artifacts-recent-items\" className=\"flex flex-col gap-1\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? <p className=\"text-sm text-doom-dim\">No artifacts yet</p> : null}\n {sortedArtifacts(items)\n .slice(0, 3)\n .map((item) => (\n <Button\n key={item.path}\n variant=\"ghost\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"gap-0.5 px-2 py-1.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-base text-doom-text\">\n <FileIcon aria-hidden className=\"size-3.5 text-doom-dim\" />\n <span className=\"truncate\">{item.name}</span>\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n </Button>\n ))}\n </div>\n );\n}\n\nexport function ArtifactGridView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-auto bg-doom-bg text-doom-text\">\n <header className=\"flex shrink-0 items-center gap-3 border-b border-doom-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold text-doom-hi\">Artifacts</h1>\n <span data-testid=\"artifacts-count\" className=\"text-sm text-doom-dim\">\n {items.length} {items.length === 1 ? 'artifact' : 'artifacts'}\n </span>\n </header>\n {error ? (\n <p role=\"alert\" className=\"px-4 py-3 text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"px-4 py-3 text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? (\n <EmptyState\n title=\"No artifacts yet\"\n description=\"Documents Po prepares for this session will appear here.\"\n className=\"px-4 py-8\"\n />\n ) : null}\n <div data-testid=\"artifacts-grid\" className=\"grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedArtifacts(items).map((item) => (\n <Button\n key={item.path}\n variant=\"outline\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"min-w-0 gap-2 p-4\"\n >\n <FileIcon aria-hidden className=\"size-5 text-doom-green\" />\n <span className=\"truncate text-base font-semibold text-doom-hi\" title={item.name}>\n {item.name}\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n <span className=\"text-sm text-doom-dim\">{sizeLabel(item.size)}</span>\n </Button>\n ))}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useStore } from '@tanstack/react-store';\nimport { useEffect } from 'react';\n\nimport { refreshArtifacts } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTab } from './ArtifactPreviewPanel';\nimport { ArtifactGridView, RecentArtifactsView } from './ArtifactsView';\n\nexport function artifactsTab(): TransientTab {\n // The host stores transient tabs per session, including this heading factory's fixed id.\n return { id: 'artifacts-grid', label: 'Artifacts', panel: ArtifactsPanel };\n}\n\nexport function ArtifactsActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null || snapshot.loaded) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId, snapshot.loaded]);\n return (\n <RecentArtifactsView\n items={snapshot.items}\n loading={!snapshot.loaded}\n error={snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n\nexport function ArtifactsPanel({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId]);\n return (\n <ArtifactGridView\n items={snapshot.items}\n loading={!snapshot.loaded && sessionId !== null}\n error={sessionId === null ? 'The session is unavailable.' : snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\nimport { FileIcon } from '@agimon-ai/po-web-components';\n\nimport { artifactsTab } from '../_components/ArtifactsPanel';\nimport { artifactActivitySource } from '../_lib/artifactsStore';\nexport default defineActivityGroup({\n icon: FileIcon,\n keys: '',\n statusKey: 'doom-artifacts',\n activeSource: artifactActivitySource,\n transientTab: artifactsTab,\n marksBackgroundWork: false,\n order: 15,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannel } from '../_lib/artifactsStore';\nexport default defineChannelFile(artifactsChannel);\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ArtifactsActivitySection } from '../_components/ArtifactsPanel';\nexport default defineFill({ component: ArtifactsActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupArtifacts from '../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web';\nimport channelArtifacts from '../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web';\nimport fillArtifacts from '../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.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: 'artifacts',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'artifacts' }, activityGroupArtifacts)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'artifacts' }, channelArtifacts)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.artifacts', id: 'artifacts' }, fillArtifacts)]; },\n },\n});\n"],"mappings":"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"}
package/dist/index.cjs ADDED
File without changes
@@ -0,0 +1,22 @@
1
+ //#region src/types/artifacts.d.ts
2
+ interface ArtifactItem {
3
+ /** Relative to the current session's artifact directory. */
4
+ path: string;
5
+ name: string;
6
+ size: number;
7
+ modifiedAt: number;
8
+ }
9
+ interface ArtifactSnapshot {
10
+ items: ArtifactItem[];
11
+ error?: string;
12
+ }
13
+ interface ArtifactPreview {
14
+ path: string;
15
+ content: string;
16
+ mimeType: string;
17
+ unavailable?: boolean;
18
+ reason?: string;
19
+ }
20
+ //#endregion
21
+ export type { ArtifactItem, ArtifactPreview, ArtifactSnapshot };
22
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/artifacts.ts"],"mappings":";UAAiB;;EAEf;EACA;EACA;EACA;;UAGe;EACf,OAAO;EACP;;UAGe;EACf;EACA;EACA;EACA;EACA"}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export {};