@agimon-ai/po-image-gen 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.
- package/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/apiContracts.cjs +53 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +52 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +48 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/extensions/server.cjs +119 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +114 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +501 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/imagegenTool-DDb0F5QH.cjs +287 -0
- package/dist/imagegenTool-DDb0F5QH.cjs.map +1 -0
- package/dist/imagegenTool-DMrMZjfO.mjs +258 -0
- package/dist/imagegenTool-DMrMZjfO.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1 -0
- package/llms.txt +7 -0
- package/package.json +110 -0
- package/src/prompts/po-use-image-gen/SKILL.md +14 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { apiResponse, createApiClient, defineActivityGroup, defineApiRoutes, defineFill, defineToolRenderer, defineWebPlugin, sessionApiAddress } from "@agimon-ai/po-core/web";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { sealedTransport } from "@agimon-ai/po-web-security/browser";
|
|
4
|
+
import { Button, MediaPreview, MessageItem, MessageItemBody, MessageItemHeader, MessageItemStatus, Skeleton, resultTextLines, toolTone } from "@agimon-ai/po-web-components";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/types/apiRoutes.ts
|
|
7
|
+
const routes = defineApiRoutes({
|
|
8
|
+
listImages: {
|
|
9
|
+
method: "GET",
|
|
10
|
+
path: "/artifacts",
|
|
11
|
+
query: ["limit", "cursor"],
|
|
12
|
+
response: apiResponse()
|
|
13
|
+
},
|
|
14
|
+
readImage: {
|
|
15
|
+
method: "GET",
|
|
16
|
+
path: "/artifact",
|
|
17
|
+
query: ["id", "toolCallId"],
|
|
18
|
+
response: apiResponse()
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const api = createApiClient(routes, {
|
|
22
|
+
scopes: ["session"],
|
|
23
|
+
basePath: "images",
|
|
24
|
+
transport: (input, init) => sealedTransport.fetch(input, init),
|
|
25
|
+
sessionAddress: sessionApiAddress
|
|
26
|
+
});
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/imageFeedback.ts
|
|
29
|
+
const imageFeedbackSlot = {
|
|
30
|
+
slot: "author.image-feedback",
|
|
31
|
+
parse(input) {
|
|
32
|
+
if (input === null || typeof input !== "object") return null;
|
|
33
|
+
const value = input;
|
|
34
|
+
return value.version === 1 && typeof value.panel === "function" ? value : null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/** Inline save failures are preview bytes, never filesystem authority. */
|
|
38
|
+
function inlineImageData(src) {
|
|
39
|
+
if (src === void 0 || !src.startsWith("data:image/png;base64,")) return void 0;
|
|
40
|
+
const data = src.slice(22);
|
|
41
|
+
if (data.length === 0 || data.length > Math.ceil(20971520 / 3) * 4 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data)) return void 0;
|
|
42
|
+
try {
|
|
43
|
+
const bytes = atob(data);
|
|
44
|
+
return bytes.length <= 20971520 && bytes.startsWith("PNG\r\n\n") ? data : void 0;
|
|
45
|
+
} catch {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/sessionImagesApi.ts
|
|
51
|
+
/** Resolve transcript identity at the session boundary, not from result metadata paths. */
|
|
52
|
+
async function resolveTranscriptImage(sessionId, toolCallId, src, signal, inlineOnly = false) {
|
|
53
|
+
if (inlineOnly) {
|
|
54
|
+
signal.throwIfAborted();
|
|
55
|
+
const inlineData = inlineImageData(src);
|
|
56
|
+
if (!inlineData) throw new Error("The inline image is not a valid bounded PNG.");
|
|
57
|
+
return {
|
|
58
|
+
id: `inline-${toolCallId}`,
|
|
59
|
+
inlineData
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const response = await api.session(sessionId).readImage({
|
|
63
|
+
query: { toolCallId },
|
|
64
|
+
signal
|
|
65
|
+
});
|
|
66
|
+
signal.throwIfAborted();
|
|
67
|
+
if (response.ok) {
|
|
68
|
+
if (response.data.sessionId !== sessionId) throw new Error("Image belongs to another session.");
|
|
69
|
+
return {
|
|
70
|
+
id: response.data.id,
|
|
71
|
+
artifact: response.data
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (response.status !== 404) throw new Error(response.error || "This image could not be loaded.");
|
|
75
|
+
const inlineData = inlineImageData(src);
|
|
76
|
+
if (!inlineData) throw new Error("The inline image is not a valid bounded PNG.");
|
|
77
|
+
return {
|
|
78
|
+
id: `inline-${toolCallId}`,
|
|
79
|
+
inlineData
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_components/SessionImagesView.tsx
|
|
84
|
+
/** Presentation only. The session container owns metadata and verified image URLs. */
|
|
85
|
+
function ImagesGridView({ images, state, error, onOpen, variant, hasMore, onMore }) {
|
|
86
|
+
if (state === "error") return /* @__PURE__ */ jsx("p", {
|
|
87
|
+
role: "alert",
|
|
88
|
+
className: "p-3 text-sm text-doom-red",
|
|
89
|
+
children: error ?? "Images could not be loaded."
|
|
90
|
+
});
|
|
91
|
+
if (state === "ready" && images.length === 0) return /* @__PURE__ */ jsx("p", {
|
|
92
|
+
className: "p-3 text-sm text-doom-dim",
|
|
93
|
+
children: "Images generated in this session will appear here."
|
|
94
|
+
});
|
|
95
|
+
const visible = variant === "activity" ? images.slice(0, 6) : images;
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
97
|
+
className: "flex flex-col gap-3 p-3",
|
|
98
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
99
|
+
className: "grid grid-cols-3 gap-2",
|
|
100
|
+
children: state === "loading" && visible.length === 0 ? Array.from({ length: 6 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "aspect-square rounded-md" }, index)) : visible.map((image) => /* @__PURE__ */ jsxs("button", {
|
|
101
|
+
"data-testid": `image-thumbnail-${image.id}`,
|
|
102
|
+
type: "button",
|
|
103
|
+
onClick: () => onOpen(image.id),
|
|
104
|
+
title: image.label,
|
|
105
|
+
"aria-label": `Open ${image.label}`,
|
|
106
|
+
className: "group min-w-0 overflow-hidden rounded-md border border-doom-border bg-doom-deep focus-visible:outline-2 focus-visible:outline-doom-selected",
|
|
107
|
+
children: [image.src ? /* @__PURE__ */ jsx("img", {
|
|
108
|
+
src: image.src,
|
|
109
|
+
alt: image.label,
|
|
110
|
+
className: "aspect-square w-full object-cover transition-opacity group-hover:opacity-80"
|
|
111
|
+
}) : /* @__PURE__ */ jsx(Skeleton, { className: "aspect-square w-full" }), variant === "gallery" ? /* @__PURE__ */ jsx("span", {
|
|
112
|
+
className: "block truncate px-2 py-2 text-sm text-doom-text",
|
|
113
|
+
children: image.label
|
|
114
|
+
}) : null]
|
|
115
|
+
}, image.id))
|
|
116
|
+
}), hasMore ? /* @__PURE__ */ jsx(Button, {
|
|
117
|
+
variant: "outline",
|
|
118
|
+
onClick: onMore,
|
|
119
|
+
disabled: state === "loading",
|
|
120
|
+
children: "Load more images"
|
|
121
|
+
}) : null]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function ImageViewerView({ image, state, error, feedback }) {
|
|
125
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
126
|
+
className: "flex h-full min-h-0 flex-col gap-3 overflow-y-auto p-4",
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ jsx("h2", {
|
|
129
|
+
className: "truncate text-lg font-semibold text-doom-hi",
|
|
130
|
+
title: image.label,
|
|
131
|
+
children: image.label
|
|
132
|
+
}),
|
|
133
|
+
feedback ? feedback : state === "error" ? /* @__PURE__ */ jsx("p", {
|
|
134
|
+
role: "alert",
|
|
135
|
+
className: "text-sm text-doom-red",
|
|
136
|
+
children: error ?? "This image could not be loaded."
|
|
137
|
+
}) : state === "loading" || !image.src ? /* @__PURE__ */ jsx(Skeleton, { className: "h-64 w-full rounded-md" }) : /* @__PURE__ */ jsx(MediaPreview, {
|
|
138
|
+
src: image.src,
|
|
139
|
+
path: `${image.label}.png`,
|
|
140
|
+
kind: "image",
|
|
141
|
+
contentType: "image/png",
|
|
142
|
+
className: "min-h-0 flex-1 object-contain"
|
|
143
|
+
}),
|
|
144
|
+
!feedback ? /* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "text-sm text-doom-dim",
|
|
146
|
+
children: "Original image, read only."
|
|
147
|
+
}) : null
|
|
148
|
+
]
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_components/SessionImages.tsx
|
|
153
|
+
function imagesGalleryTab() {
|
|
154
|
+
return {
|
|
155
|
+
id: "image-gen-gallery",
|
|
156
|
+
label: "Images",
|
|
157
|
+
panel: ImagesGallery
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function pngUrl(data) {
|
|
161
|
+
const bytes = Uint8Array.from(atob(data), (character) => character.charCodeAt(0));
|
|
162
|
+
return URL.createObjectURL(new Blob([bytes], { type: "image/png" }));
|
|
163
|
+
}
|
|
164
|
+
function imageViewerTab(sessionId, image) {
|
|
165
|
+
return {
|
|
166
|
+
id: `${image.inlineData === void 0 ? "image-gen-image" : "image-gen-inline"}-${encodeURIComponent(image.id)}`,
|
|
167
|
+
label: image.label ?? `Image ${image.id}`,
|
|
168
|
+
panel: (props) => props.sessionId === sessionId ? /* @__PURE__ */ jsx(SessionImageViewer, {
|
|
169
|
+
...props,
|
|
170
|
+
image
|
|
171
|
+
}) : null
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** Inline bytes are a fallback only when no durable artifact exists. */
|
|
175
|
+
async function transcriptImageTab(sessionId, toolCallId, src, label, signal, inlineOnly = false) {
|
|
176
|
+
return imageViewerTab(sessionId, {
|
|
177
|
+
...await resolveTranscriptImage(sessionId, toolCallId, src, signal, inlineOnly),
|
|
178
|
+
label
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function ImagesActivity(props) {
|
|
182
|
+
return /* @__PURE__ */ jsx("div", {
|
|
183
|
+
"data-testid": "session-images-activity",
|
|
184
|
+
children: /* @__PURE__ */ jsx(SessionImages, {
|
|
185
|
+
...props,
|
|
186
|
+
variant: "activity"
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function ImagesGallery(props) {
|
|
191
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
192
|
+
"data-testid": "session-images-gallery",
|
|
193
|
+
className: "h-full min-h-0 overflow-y-auto p-4",
|
|
194
|
+
"aria-label": "Session image gallery",
|
|
195
|
+
children: [
|
|
196
|
+
/* @__PURE__ */ jsx("h2", {
|
|
197
|
+
className: "text-lg font-semibold text-doom-hi",
|
|
198
|
+
children: "Images"
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ jsx("p", {
|
|
201
|
+
className: "text-sm text-doom-dim",
|
|
202
|
+
children: "Generated in this session, newest first."
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ jsx(SessionImages, {
|
|
205
|
+
...props,
|
|
206
|
+
variant: "gallery"
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function SessionImages(props) {
|
|
212
|
+
if (props.sessionId === null) return null;
|
|
213
|
+
return /* @__PURE__ */ jsx(ImagesPage, {
|
|
214
|
+
...props,
|
|
215
|
+
sessionId: props.sessionId
|
|
216
|
+
}, `${props.sessionId}:${props.statuses["image-gen.images"] ?? ""}`);
|
|
217
|
+
}
|
|
218
|
+
function ImagesPage(props) {
|
|
219
|
+
const [images, setImages] = useState([]);
|
|
220
|
+
const [cursor, setCursor] = useState("0");
|
|
221
|
+
const [nextCursor, setNextCursor] = useState(null);
|
|
222
|
+
const [state, setState] = useState("loading");
|
|
223
|
+
const [error, setError] = useState("");
|
|
224
|
+
const urls = useRef([]);
|
|
225
|
+
useEffect(() => () => {
|
|
226
|
+
for (const url of urls.current) URL.revokeObjectURL(url);
|
|
227
|
+
}, []);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
const controller = new AbortController();
|
|
230
|
+
setState("loading");
|
|
231
|
+
(async () => {
|
|
232
|
+
const response = await api.session(props.sessionId).listImages({
|
|
233
|
+
query: {
|
|
234
|
+
limit: "6",
|
|
235
|
+
cursor
|
|
236
|
+
},
|
|
237
|
+
signal: controller.signal
|
|
238
|
+
});
|
|
239
|
+
if (!response.ok) throw new Error(response.error || "Images could not be loaded.");
|
|
240
|
+
if (response.data.sessionId !== props.sessionId) throw new Error("Image history belongs to another session.");
|
|
241
|
+
const artifacts = await Promise.all(response.data.items.map(async ({ id }) => {
|
|
242
|
+
const result = await api.session(props.sessionId).readImage({
|
|
243
|
+
query: { id },
|
|
244
|
+
signal: controller.signal
|
|
245
|
+
});
|
|
246
|
+
if (!result.ok) {
|
|
247
|
+
if (result.status === 404) return null;
|
|
248
|
+
throw new Error(result.error || "An image could not be loaded.");
|
|
249
|
+
}
|
|
250
|
+
if (result.data.sessionId !== props.sessionId) throw new Error("Image belongs to another session.");
|
|
251
|
+
return result.data;
|
|
252
|
+
}));
|
|
253
|
+
controller.signal.throwIfAborted();
|
|
254
|
+
const tiles = artifacts.flatMap((artifact) => {
|
|
255
|
+
if (!artifact) return [];
|
|
256
|
+
const src = pngUrl(artifact.data);
|
|
257
|
+
urls.current.push(src);
|
|
258
|
+
return [{
|
|
259
|
+
id: artifact.id,
|
|
260
|
+
label: `Image ${artifact.id}`,
|
|
261
|
+
src
|
|
262
|
+
}];
|
|
263
|
+
});
|
|
264
|
+
setImages((previous) => cursor === "0" ? tiles : [...previous, ...tiles.filter((tile) => !previous.some(({ id }) => id === tile.id))]);
|
|
265
|
+
setNextCursor(response.data.nextCursor);
|
|
266
|
+
setState("ready");
|
|
267
|
+
})().catch((reason) => {
|
|
268
|
+
if (controller.signal.aborted) return;
|
|
269
|
+
setError(reason instanceof Error ? reason.message : "Images could not be loaded.");
|
|
270
|
+
setState("error");
|
|
271
|
+
});
|
|
272
|
+
return () => controller.abort();
|
|
273
|
+
}, [props.sessionId, cursor]);
|
|
274
|
+
return /* @__PURE__ */ jsx(ImagesGridView, {
|
|
275
|
+
images,
|
|
276
|
+
state,
|
|
277
|
+
error,
|
|
278
|
+
variant: props.variant,
|
|
279
|
+
onOpen: (id) => props.openTransientTab(imageViewerTab(props.sessionId, { id })),
|
|
280
|
+
hasMore: props.variant === "gallery" && nextCursor !== null,
|
|
281
|
+
onMore: () => {
|
|
282
|
+
if (nextCursor !== null && state !== "loading") setCursor(nextCursor);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function SessionImageViewer(props) {
|
|
287
|
+
const [artifact, setArtifact] = useState();
|
|
288
|
+
const [src, setSrc] = useState();
|
|
289
|
+
const [error, setError] = useState("");
|
|
290
|
+
const [state, setState] = useState("loading");
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
const controller = new AbortController();
|
|
293
|
+
let objectUrl;
|
|
294
|
+
setArtifact(void 0);
|
|
295
|
+
setSrc(void 0);
|
|
296
|
+
setError("");
|
|
297
|
+
setState("loading");
|
|
298
|
+
(async () => {
|
|
299
|
+
if (props.sessionId === null) return;
|
|
300
|
+
let data = props.image.inlineData;
|
|
301
|
+
if (!data) {
|
|
302
|
+
let target = props.image.artifact;
|
|
303
|
+
if (!target) {
|
|
304
|
+
const result = await api.session(props.sessionId).readImage({
|
|
305
|
+
query: { id: props.image.id },
|
|
306
|
+
signal: controller.signal
|
|
307
|
+
});
|
|
308
|
+
if (!result.ok) throw new Error(result.error || "This image could not be loaded.");
|
|
309
|
+
target = result.data;
|
|
310
|
+
}
|
|
311
|
+
if (target.sessionId !== props.sessionId) throw new Error("Image belongs to another session.");
|
|
312
|
+
controller.signal.throwIfAborted();
|
|
313
|
+
setArtifact(target);
|
|
314
|
+
data = target.data;
|
|
315
|
+
}
|
|
316
|
+
controller.signal.throwIfAborted();
|
|
317
|
+
objectUrl = pngUrl(data);
|
|
318
|
+
setSrc(objectUrl);
|
|
319
|
+
setState("ready");
|
|
320
|
+
})().catch((reason) => {
|
|
321
|
+
if (controller.signal.aborted) return;
|
|
322
|
+
setError(reason instanceof Error ? reason.message : "This image could not be loaded.");
|
|
323
|
+
setState("error");
|
|
324
|
+
});
|
|
325
|
+
return () => {
|
|
326
|
+
controller.abort();
|
|
327
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
328
|
+
};
|
|
329
|
+
}, [props.sessionId, props.image]);
|
|
330
|
+
const Panel = artifact && props.activeMinorModes?.includes("author") ? props.slotData(imageFeedbackSlot)[0]?.data.panel : void 0;
|
|
331
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
332
|
+
"data-testid": "session-image-viewer",
|
|
333
|
+
"data-image-id": props.image.id,
|
|
334
|
+
className: "flex h-full min-h-0 flex-col",
|
|
335
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
336
|
+
className: "min-h-0 flex-1",
|
|
337
|
+
children: /* @__PURE__ */ jsx(ImageViewerView, {
|
|
338
|
+
image: {
|
|
339
|
+
id: props.image.id,
|
|
340
|
+
label: props.image.label ?? `Image ${props.image.id}`,
|
|
341
|
+
src
|
|
342
|
+
},
|
|
343
|
+
state,
|
|
344
|
+
error,
|
|
345
|
+
feedback: Panel && artifact ? /* @__PURE__ */ jsx(Panel, {
|
|
346
|
+
...props,
|
|
347
|
+
target: artifact
|
|
348
|
+
}) : void 0
|
|
349
|
+
})
|
|
350
|
+
}), props.image.inlineData ? /* @__PURE__ */ jsx("p", {
|
|
351
|
+
className: "px-4 pb-4 text-sm text-doom-dim",
|
|
352
|
+
children: "This inline image was not saved. Original-path refinement is unavailable."
|
|
353
|
+
}) : null]
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/extensions/workspaces/sessions/(frontend)/activity-group/images.web.ts
|
|
358
|
+
var images_web_default = defineActivityGroup({
|
|
359
|
+
name: "Images",
|
|
360
|
+
keys: "",
|
|
361
|
+
activeSource: {
|
|
362
|
+
subscribe: () => () => void 0,
|
|
363
|
+
isActive: () => false
|
|
364
|
+
},
|
|
365
|
+
marksBackgroundWork: false,
|
|
366
|
+
transientTab: imagesGalleryTab,
|
|
367
|
+
order: 35
|
|
368
|
+
});
|
|
369
|
+
//#endregion
|
|
370
|
+
//#region src/extensions/workspaces/sessions/(frontend)/fill/Images.activity.Images.web.tsx
|
|
371
|
+
var Images_activity_Images_web_default = defineFill({
|
|
372
|
+
slot: "activity.Images",
|
|
373
|
+
component: ImagesActivity
|
|
374
|
+
});
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/imagegenToolView.ts
|
|
377
|
+
function imagegenToolView({ args, result }) {
|
|
378
|
+
const prompt = typeof args.prompt === "string" ? args.prompt : "";
|
|
379
|
+
const editing = Array.isArray(args.referenced_image_paths) && args.referenced_image_paths.length > 0;
|
|
380
|
+
const image = result?.content.find((block) => block !== null && typeof block === "object" && "type" in block && block.type === "image" && "mimeType" in block && block.mimeType === "image/png" && "data" in block && typeof block.data === "string");
|
|
381
|
+
const src = image ? `data:image/png;base64,${image.data}` : void 0;
|
|
382
|
+
const details = result?.details;
|
|
383
|
+
return {
|
|
384
|
+
prompt,
|
|
385
|
+
editing,
|
|
386
|
+
src,
|
|
387
|
+
inlineOnly: details !== null && typeof details === "object" && "saveWarning" in details && typeof details.saveWarning === "string"
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/_components/ImagegenToolMessage.tsx
|
|
392
|
+
function ImagegenToolMessage(props) {
|
|
393
|
+
const { result, output, running, isError } = props;
|
|
394
|
+
const view = imagegenToolView(props);
|
|
395
|
+
const [opening, setOpening] = useState(false);
|
|
396
|
+
const [openError, setOpenError] = useState("");
|
|
397
|
+
const request = useRef(void 0);
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
setOpening(false);
|
|
400
|
+
setOpenError("");
|
|
401
|
+
return () => request.current?.abort();
|
|
402
|
+
}, [props.sessionId, props.toolCallId]);
|
|
403
|
+
return /* @__PURE__ */ jsx(MessageItem, {
|
|
404
|
+
tone: toolTone({
|
|
405
|
+
running,
|
|
406
|
+
isError
|
|
407
|
+
}),
|
|
408
|
+
expandable: true,
|
|
409
|
+
defaultExpanded: true,
|
|
410
|
+
children: ({ expanded }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(MessageItemHeader, {
|
|
411
|
+
title: "imagegen",
|
|
412
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
413
|
+
"data-testid": "tool-call-imagegen",
|
|
414
|
+
className: "min-w-0 flex-1 truncate text-doom-text",
|
|
415
|
+
children: view.prompt
|
|
416
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
417
|
+
className: "shrink-0 text-doom-faint",
|
|
418
|
+
children: view.editing ? "edit" : "generate"
|
|
419
|
+
})]
|
|
420
|
+
}), expanded ? /* @__PURE__ */ jsxs(MessageItemBody, {
|
|
421
|
+
"data-testid": "tool-result-imagegen",
|
|
422
|
+
className: "flex flex-col gap-2",
|
|
423
|
+
children: [
|
|
424
|
+
running ? /* @__PURE__ */ jsx(MessageItemStatus, {
|
|
425
|
+
tone: "running",
|
|
426
|
+
children: "creating image"
|
|
427
|
+
}) : null,
|
|
428
|
+
isError ? /* @__PURE__ */ jsx(MessageItemStatus, {
|
|
429
|
+
tone: "error",
|
|
430
|
+
children: "failed"
|
|
431
|
+
}) : null,
|
|
432
|
+
!isError && view.src ? /* @__PURE__ */ jsx("button", {
|
|
433
|
+
type: "button",
|
|
434
|
+
"aria-label": `Open ${view.prompt || "generated image"}`,
|
|
435
|
+
disabled: opening || props.sessionId === null,
|
|
436
|
+
className: "max-w-full self-start rounded-md focus-visible:outline-2 focus-visible:outline-doom-selected",
|
|
437
|
+
onClick: async () => {
|
|
438
|
+
if (props.sessionId === null || !view.src || opening) return;
|
|
439
|
+
const controller = new AbortController();
|
|
440
|
+
request.current?.abort();
|
|
441
|
+
request.current = controller;
|
|
442
|
+
setOpening(true);
|
|
443
|
+
setOpenError("");
|
|
444
|
+
try {
|
|
445
|
+
const tab = await transcriptImageTab(props.sessionId, props.toolCallId, view.src, view.prompt || "Generated image", controller.signal, view.inlineOnly);
|
|
446
|
+
if (!controller.signal.aborted) props.openTransientTab(tab);
|
|
447
|
+
} catch (reason) {
|
|
448
|
+
if (!controller.signal.aborted) setOpenError(reason instanceof Error ? reason.message : "This image could not be opened.");
|
|
449
|
+
} finally {
|
|
450
|
+
if (!controller.signal.aborted) setOpening(false);
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
454
|
+
src: view.src,
|
|
455
|
+
alt: view.prompt || "Generated image",
|
|
456
|
+
className: "max-h-96 max-w-full rounded-md object-contain"
|
|
457
|
+
})
|
|
458
|
+
}) : null,
|
|
459
|
+
openError ? /* @__PURE__ */ jsx("p", {
|
|
460
|
+
role: "alert",
|
|
461
|
+
className: "text-sm text-doom-red",
|
|
462
|
+
children: openError
|
|
463
|
+
}) : null,
|
|
464
|
+
resultTextLines(result, output).map((line, index) => /* @__PURE__ */ jsx("span", {
|
|
465
|
+
className: `whitespace-pre-wrap break-words ${isError ? "text-doom-red" : "text-doom-dim"}`,
|
|
466
|
+
children: line
|
|
467
|
+
}, `${String(index)}-${line}`))
|
|
468
|
+
]
|
|
469
|
+
}) : null] })
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
//#endregion
|
|
473
|
+
//#region src/extensions/workspaces/sessions/(frontend)/tool/imagegen.web.tsx
|
|
474
|
+
var imagegen_web_default = defineToolRenderer({ message: ImagegenToolMessage });
|
|
475
|
+
//#endregion
|
|
476
|
+
//#region generated/web.ts
|
|
477
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
478
|
+
...identity,
|
|
479
|
+
...value
|
|
480
|
+
};
|
|
481
|
+
const webPlugin = defineWebPlugin({
|
|
482
|
+
id: "image-gen",
|
|
483
|
+
session: {
|
|
484
|
+
get activityGroups() {
|
|
485
|
+
return [via({ name: "images" }, images_web_default)];
|
|
486
|
+
},
|
|
487
|
+
get fills() {
|
|
488
|
+
return [via({
|
|
489
|
+
slot: "activity.Images",
|
|
490
|
+
id: "images"
|
|
491
|
+
}, Images_activity_Images_web_default)];
|
|
492
|
+
},
|
|
493
|
+
get toolRenderers() {
|
|
494
|
+
return [via({ tools: ["imagegen"] }, imagegen_web_default)];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
//#endregion
|
|
499
|
+
export { webPlugin };
|
|
500
|
+
|
|
501
|
+
//# sourceMappingURL=web.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.mjs","names":["activityGroupImages","fillImages","toolImagegen"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/constants/imagegen.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/imageFeedback.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/sessionImagesApi.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/SessionImagesView.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/SessionImages.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/images.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Images.activity.Images.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/imagegenToolView.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/ImagegenToolMessage.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/imagegen.web.tsx","../../generated/web.ts"],"sourcesContent":["import { apiResponse, defineApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ImageArtifact, ImageArtifactList } from './imageArtifacts';\n\nexport const routes = defineApiRoutes({\n listImages: {\n method: 'GET',\n path: '/artifacts',\n query: ['limit', 'cursor'],\n response: apiResponse<ImageArtifactList>(),\n },\n readImage: { method: 'GET', path: '/artifact', query: ['id', 'toolCallId'], response: apiResponse<ImageArtifact>() },\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 'images' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'images',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export const IMAGEGEN_NAME = 'imagegen';\nexport const IMAGEGEN_DESCRIPTION =\n 'Generate images from descriptions or edit up to five local images using ChatGPT. Omit referenced_image_paths for a new image. Inspect reference images before editing. Image generation can take a few minutes. Originals are preserved; copy saved images when using another destination.';\nexport const MAX_REFERENCE_BYTES = 20 * 1024 * 1024;\nexport const MAX_TOTAL_REFERENCE_BYTES = 50 * 1024 * 1024;\n","import type { SlotDeclaration, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport type { ComponentType } from 'react';\n\nimport { MAX_REFERENCE_BYTES } from '../../../../../../constants/imagegen';\nimport type { ImageArtifact } from '../../../../../../types/imageArtifacts';\n\ninterface ImageFeedbackContribution {\n version: 1;\n panel: ComponentType<WebPluginSlotProps & { target: ImageArtifact }>;\n}\n\nexport const imageFeedbackSlot = {\n slot: 'author.image-feedback',\n parse(input) {\n if (input === null || typeof input !== 'object') return null;\n const value = input as Partial<ImageFeedbackContribution>;\n return value.version === 1 && typeof value.panel === 'function' ? (value as ImageFeedbackContribution) : null;\n },\n} satisfies SlotDeclaration<ImageFeedbackContribution>;\n\n/** Inline save failures are preview bytes, never filesystem authority. */\nexport function inlineImageData(src: string | undefined): string | undefined {\n const prefix = 'data:image/png;base64,';\n if (src === undefined || !src.startsWith(prefix)) return undefined;\n const data = src.slice(prefix.length);\n if (\n data.length === 0 ||\n data.length > Math.ceil(MAX_REFERENCE_BYTES / 3) * 4 ||\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data)\n )\n return undefined;\n try {\n const bytes = atob(data);\n return bytes.length <= MAX_REFERENCE_BYTES && bytes.startsWith('\\x89PNG\\r\\n\\x1a\\n') ? data : undefined;\n } catch {\n return undefined;\n }\n}\n","import { api } from '../../../../../../../generated/client';\nimport type { ImageArtifact } from '../../../../../../types/imageArtifacts';\nimport { inlineImageData } from './imageFeedback';\n\n/** Resolve transcript identity at the session boundary, not from result metadata paths. */\nexport async function resolveTranscriptImage(\n sessionId: string,\n toolCallId: string,\n src: string,\n signal: AbortSignal,\n inlineOnly = false,\n): Promise<{ id: string; artifact?: ImageArtifact; inlineData?: string }> {\n if (inlineOnly) {\n signal.throwIfAborted();\n const inlineData = inlineImageData(src);\n if (!inlineData) throw new Error('The inline image is not a valid bounded PNG.');\n return { id: `inline-${toolCallId}`, inlineData };\n }\n const response = await api.session(sessionId).readImage({ query: { toolCallId }, signal });\n signal.throwIfAborted();\n if (response.ok) {\n if (response.data.sessionId !== sessionId) throw new Error('Image belongs to another session.');\n return { id: response.data.id, artifact: response.data };\n }\n if (response.status !== 404) throw new Error(response.error || 'This image could not be loaded.');\n const inlineData = inlineImageData(src);\n if (!inlineData) throw new Error('The inline image is not a valid bounded PNG.');\n return { id: `inline-${toolCallId}`, inlineData };\n}\n","import { Button, MediaPreview, Skeleton } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nexport interface ImageTile {\n id: string;\n label: string;\n src?: string;\n}\n\nexport interface ImagesGridViewProps {\n images: readonly ImageTile[];\n state: 'ready' | 'loading' | 'error';\n error?: string;\n onOpen: (id: string) => void;\n variant: 'activity' | 'gallery';\n hasMore?: boolean;\n onMore?: () => void;\n}\n\n/** Presentation only. The session container owns metadata and verified image URLs. */\nexport function ImagesGridView({ images, state, error, onOpen, variant, hasMore, onMore }: ImagesGridViewProps) {\n if (state === 'error')\n return (\n <p role=\"alert\" className=\"p-3 text-sm text-doom-red\">\n {error ?? 'Images could not be loaded.'}\n </p>\n );\n if (state === 'ready' && images.length === 0)\n return <p className=\"p-3 text-sm text-doom-dim\">Images generated in this session will appear here.</p>;\n const visible = variant === 'activity' ? images.slice(0, 6) : images;\n return (\n <div className=\"flex flex-col gap-3 p-3\">\n <div className=\"grid grid-cols-3 gap-2\">\n {state === 'loading' && visible.length === 0\n ? Array.from({ length: 6 }, (_, index) => <Skeleton key={index} className=\"aspect-square rounded-md\" />)\n : visible.map((image) => (\n <button\n key={image.id}\n data-testid={`image-thumbnail-${image.id}`}\n type=\"button\"\n onClick={() => onOpen(image.id)}\n title={image.label}\n aria-label={`Open ${image.label}`}\n className=\"group min-w-0 overflow-hidden rounded-md border border-doom-border bg-doom-deep focus-visible:outline-2 focus-visible:outline-doom-selected\"\n >\n {image.src ? (\n <img\n src={image.src}\n alt={image.label}\n className=\"aspect-square w-full object-cover transition-opacity group-hover:opacity-80\"\n />\n ) : (\n <Skeleton className=\"aspect-square w-full\" />\n )}\n {variant === 'gallery' ? (\n <span className=\"block truncate px-2 py-2 text-sm text-doom-text\">{image.label}</span>\n ) : null}\n </button>\n ))}\n </div>\n {hasMore ? (\n <Button variant=\"outline\" onClick={onMore} disabled={state === 'loading'}>\n Load more images\n </Button>\n ) : null}\n </div>\n );\n}\n\nexport interface ImageViewerViewProps {\n image: ImageTile;\n state: 'ready' | 'loading' | 'error';\n error?: string;\n feedback?: ReactNode;\n}\n\nexport function ImageViewerView({ image, state, error, feedback }: ImageViewerViewProps) {\n return (\n <section className=\"flex h-full min-h-0 flex-col gap-3 overflow-y-auto p-4\">\n <h2 className=\"truncate text-lg font-semibold text-doom-hi\" title={image.label}>\n {image.label}\n </h2>\n {feedback ? (\n feedback\n ) : state === 'error' ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error ?? 'This image could not be loaded.'}\n </p>\n ) : state === 'loading' || !image.src ? (\n <Skeleton className=\"h-64 w-full rounded-md\" />\n ) : (\n <MediaPreview\n src={image.src}\n path={`${image.label}.png`}\n kind=\"image\"\n contentType=\"image/png\"\n className=\"min-h-0 flex-1 object-contain\"\n />\n )}\n {!feedback ? <p className=\"text-sm text-doom-dim\">Original image, read only.</p> : null}\n </section>\n );\n}\n","import { type TransientTab, type WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { api } from '../../../../../../../generated/client';\nimport type { ImageArtifact } from '../../../../../../types/imageArtifacts';\nimport { imageFeedbackSlot } from '../_lib/imageFeedback';\nimport { resolveTranscriptImage } from '../_lib/sessionImagesApi';\nimport { ImagesGridView, ImageViewerView, type ImageTile } from './SessionImagesView';\n\nexport function imagesGalleryTab(): TransientTab {\n return { id: 'image-gen-gallery', label: 'Images', panel: ImagesGallery };\n}\n\nfunction pngUrl(data: string): string {\n const bytes = Uint8Array.from(atob(data), (character) => character.charCodeAt(0));\n return URL.createObjectURL(new Blob([bytes], { type: 'image/png' }));\n}\n\nexport function imageViewerTab(\n sessionId: string,\n image: { id: string; artifact?: ImageArtifact; inlineData?: string; label?: string },\n): TransientTab {\n return {\n id: `${image.inlineData === undefined ? 'image-gen-image' : 'image-gen-inline'}-${encodeURIComponent(image.id)}`,\n label: image.label ?? `Image ${image.id}`,\n panel: (props) => (props.sessionId === sessionId ? <SessionImageViewer {...props} image={image} /> : null),\n };\n}\n\n/** Inline bytes are a fallback only when no durable artifact exists. */\nexport async function transcriptImageTab(\n sessionId: string,\n toolCallId: string,\n src: string,\n label: string,\n signal: AbortSignal,\n inlineOnly = false,\n): Promise<TransientTab> {\n const image = await resolveTranscriptImage(sessionId, toolCallId, src, signal, inlineOnly);\n return imageViewerTab(sessionId, { ...image, label });\n}\n\nexport function ImagesActivity(props: WebPluginSlotProps) {\n return (\n <div data-testid=\"session-images-activity\">\n <SessionImages {...props} variant=\"activity\" />\n </div>\n );\n}\n\nfunction ImagesGallery(props: WebPluginSlotProps) {\n return (\n <section\n data-testid=\"session-images-gallery\"\n className=\"h-full min-h-0 overflow-y-auto p-4\"\n aria-label=\"Session image gallery\"\n >\n <h2 className=\"text-lg font-semibold text-doom-hi\">Images</h2>\n <p className=\"text-sm text-doom-dim\">Generated in this session, newest first.</p>\n <SessionImages {...props} variant=\"gallery\" />\n </section>\n );\n}\n\nfunction SessionImages(props: WebPluginSlotProps & { variant: 'activity' | 'gallery' }) {\n if (props.sessionId === null) return null;\n return (\n <ImagesPage\n key={`${props.sessionId}:${props.statuses['image-gen.images'] ?? ''}`}\n {...props}\n sessionId={props.sessionId}\n />\n );\n}\n\nfunction ImagesPage(props: WebPluginSlotProps & { sessionId: string; variant: 'activity' | 'gallery' }) {\n const [images, setImages] = useState<ImageTile[]>([]);\n const [cursor, setCursor] = useState('0');\n const [nextCursor, setNextCursor] = useState<string | null>(null);\n const [state, setState] = useState<'ready' | 'loading' | 'error'>('loading');\n const [error, setError] = useState('');\n const urls = useRef<string[]>([]);\n useEffect(\n () => () => {\n for (const url of urls.current) URL.revokeObjectURL(url);\n },\n [],\n );\n useEffect(() => {\n const controller = new AbortController();\n setState('loading');\n void (async () => {\n const response = await api\n .session(props.sessionId)\n .listImages({ query: { limit: '6', cursor }, signal: controller.signal });\n if (!response.ok) throw new Error(response.error || 'Images could not be loaded.');\n if (response.data.sessionId !== props.sessionId) throw new Error('Image history belongs to another session.');\n const artifacts = await Promise.all(\n response.data.items.map(async ({ id }) => {\n const result = await api.session(props.sessionId).readImage({ query: { id }, signal: controller.signal });\n if (!result.ok) {\n if (result.status === 404) return null;\n throw new Error(result.error || 'An image could not be loaded.');\n }\n if (result.data.sessionId !== props.sessionId) throw new Error('Image belongs to another session.');\n return result.data;\n }),\n );\n controller.signal.throwIfAborted();\n const tiles = artifacts.flatMap((artifact) => {\n if (!artifact) return [];\n const src = pngUrl(artifact.data);\n urls.current.push(src);\n return [{ id: artifact.id, label: `Image ${artifact.id}`, src }];\n });\n setImages((previous) =>\n cursor === '0' ? tiles : [...previous, ...tiles.filter((tile) => !previous.some(({ id }) => id === tile.id))],\n );\n setNextCursor(response.data.nextCursor);\n setState('ready');\n })().catch((reason: unknown) => {\n if (controller.signal.aborted) return;\n setError(reason instanceof Error ? reason.message : 'Images could not be loaded.');\n setState('error');\n });\n return () => controller.abort();\n }, [props.sessionId, cursor]);\n return (\n <ImagesGridView\n images={images}\n state={state}\n error={error}\n variant={props.variant}\n onOpen={(id) => props.openTransientTab(imageViewerTab(props.sessionId, { id }))}\n hasMore={props.variant === 'gallery' && nextCursor !== null}\n onMore={() => {\n if (nextCursor !== null && state !== 'loading') setCursor(nextCursor);\n }}\n />\n );\n}\n\nfunction SessionImageViewer(\n props: WebPluginSlotProps & { image: { id: string; artifact?: ImageArtifact; inlineData?: string; label?: string } },\n) {\n const [artifact, setArtifact] = useState<ImageArtifact>();\n const [src, setSrc] = useState<string>();\n const [error, setError] = useState('');\n const [state, setState] = useState<'ready' | 'loading' | 'error'>('loading');\n useEffect(() => {\n const controller = new AbortController();\n let objectUrl: string | undefined;\n setArtifact(undefined);\n setSrc(undefined);\n setError('');\n setState('loading');\n void (async () => {\n if (props.sessionId === null) return;\n let data = props.image.inlineData;\n if (!data) {\n let target = props.image.artifact;\n if (!target) {\n const result = await api\n .session(props.sessionId)\n .readImage({ query: { id: props.image.id }, signal: controller.signal });\n if (!result.ok) throw new Error(result.error || 'This image could not be loaded.');\n target = result.data;\n }\n if (target.sessionId !== props.sessionId) throw new Error('Image belongs to another session.');\n controller.signal.throwIfAborted();\n setArtifact(target);\n data = target.data;\n }\n controller.signal.throwIfAborted();\n objectUrl = pngUrl(data);\n setSrc(objectUrl);\n setState('ready');\n })().catch((reason: unknown) => {\n if (controller.signal.aborted) return;\n setError(reason instanceof Error ? reason.message : 'This image could not be loaded.');\n setState('error');\n });\n return () => {\n controller.abort();\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [props.sessionId, props.image]);\n const Panel =\n artifact && props.activeMinorModes?.includes('author')\n ? props.slotData(imageFeedbackSlot)[0]?.data.panel\n : undefined;\n return (\n <section data-testid=\"session-image-viewer\" data-image-id={props.image.id} className=\"flex h-full min-h-0 flex-col\">\n <div className=\"min-h-0 flex-1\">\n <ImageViewerView\n image={{ id: props.image.id, label: props.image.label ?? `Image ${props.image.id}`, src }}\n state={state}\n error={error}\n feedback={Panel && artifact ? <Panel {...props} target={artifact} /> : undefined}\n />\n </div>\n {props.image.inlineData ? (\n <p className=\"px-4 pb-4 text-sm text-doom-dim\">\n This inline image was not saved. Original-path refinement is unavailable.\n </p>\n ) : null}\n </section>\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\n\nimport { imagesGalleryTab } from '../tool/_components/SessionImages';\n\nexport default defineActivityGroup({\n name: 'Images',\n keys: '',\n activeSource: { subscribe: () => () => undefined, isActive: () => false },\n marksBackgroundWork: false,\n transientTab: imagesGalleryTab,\n order: 35,\n});\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ImagesActivity } from '../tool/_components/SessionImages';\n\nexport default defineFill({ slot: 'activity.Images', component: ImagesActivity });\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\n\nexport function imagegenToolView({ args, result }: Pick<ToolMessageRenderProps, 'args' | 'result'>) {\n const prompt = typeof args.prompt === 'string' ? args.prompt : '';\n const editing = Array.isArray(args.referenced_image_paths) && args.referenced_image_paths.length > 0;\n // Only inline PNG results from this tool, never a URL supplied in details.\n const image = result?.content.find(\n (block): block is { type: 'image'; data: string; mimeType: 'image/png' } =>\n block !== null &&\n typeof block === 'object' &&\n 'type' in block &&\n block.type === 'image' &&\n 'mimeType' in block &&\n block.mimeType === 'image/png' &&\n 'data' in block &&\n typeof block.data === 'string',\n );\n const src = image ? `data:image/png;base64,${image.data}` : undefined;\n const details = result?.details;\n const inlineOnly =\n details !== null &&\n typeof details === 'object' &&\n 'saveWarning' in details &&\n typeof details.saveWarning === 'string';\n return { prompt, editing, src, inlineOnly };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\nimport {\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n resultTextLines,\n toolTone,\n} from '@agimon-ai/po-web-components';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { imagegenToolView } from '../_lib/imagegenToolView';\nimport { transcriptImageTab } from './SessionImages';\n\nexport function ImagegenToolMessage(props: ToolMessageRenderProps) {\n const { result, output, running, isError } = props;\n const view = imagegenToolView(props);\n const [opening, setOpening] = useState(false);\n const [openError, setOpenError] = useState('');\n const request = useRef<AbortController | undefined>(undefined);\n useEffect(() => {\n setOpening(false);\n setOpenError('');\n return () => request.current?.abort();\n }, [props.sessionId, props.toolCallId]);\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable defaultExpanded>\n {({ expanded }) => (\n <>\n <MessageItemHeader title=\"imagegen\">\n <span data-testid=\"tool-call-imagegen\" className=\"min-w-0 flex-1 truncate text-doom-text\">\n {view.prompt}\n </span>\n <span className=\"shrink-0 text-doom-faint\">{view.editing ? 'edit' : 'generate'}</span>\n </MessageItemHeader>\n {expanded ? (\n <MessageItemBody data-testid=\"tool-result-imagegen\" className=\"flex flex-col gap-2\">\n {running ? <MessageItemStatus tone=\"running\">creating image</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">failed</MessageItemStatus> : null}\n {!isError && view.src ? (\n <button\n type=\"button\"\n aria-label={`Open ${view.prompt || 'generated image'}`}\n disabled={opening || props.sessionId === null}\n className=\"max-w-full self-start rounded-md focus-visible:outline-2 focus-visible:outline-doom-selected\"\n onClick={async () => {\n if (props.sessionId === null || !view.src || opening) return;\n const controller = new AbortController();\n request.current?.abort();\n request.current = controller;\n setOpening(true);\n setOpenError('');\n try {\n const tab = await transcriptImageTab(\n props.sessionId,\n props.toolCallId,\n view.src,\n view.prompt || 'Generated image',\n controller.signal,\n view.inlineOnly,\n );\n if (!controller.signal.aborted) props.openTransientTab(tab);\n } catch (reason) {\n if (!controller.signal.aborted)\n setOpenError(reason instanceof Error ? reason.message : 'This image could not be opened.');\n } finally {\n if (!controller.signal.aborted) setOpening(false);\n }\n }}\n >\n <img\n src={view.src}\n alt={view.prompt || 'Generated image'}\n className=\"max-h-96 max-w-full rounded-md object-contain\"\n />\n </button>\n ) : null}\n {openError ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {openError}\n </p>\n ) : null}\n {resultTextLines(result, output).map((line, index) => (\n <span\n key={`${String(index)}-${line}`}\n className={`whitespace-pre-wrap break-words ${isError ? 'text-doom-red' : 'text-doom-dim'}`}\n >\n {line}\n </span>\n ))}\n </MessageItemBody>\n ) : null}\n </>\n )}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/po-core/web';\n\nimport { ImagegenToolMessage } from './_components/ImagegenToolMessage';\n\nexport default defineToolRenderer({ message: ImagegenToolMessage });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupImages from '../src/extensions/workspaces/sessions/(frontend)/activity-group/images.web';\nimport fillImages from '../src/extensions/workspaces/sessions/(frontend)/fill/Images.activity.Images.web';\nimport toolImagegen from '../src/extensions/workspaces/sessions/(frontend)/tool/imagegen.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: 'image-gen',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'images' }, activityGroupImages)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.Images', id: 'images' }, fillImages)]; },\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['imagegen'] }, toolImagegen)]; },\n },\n});\n"],"mappings":"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"}
|