@agimon-ai/doompi-style-system 0.0.1-alpha.100
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 +53 -0
- package/dist/apiContracts.cjs +118 -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 +113 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/cli.cjs +242 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.mts +4 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +239 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/designCheck-7P9r9ijq.mjs +475 -0
- package/dist/designCheck-7P9r9ijq.mjs.map +1 -0
- package/dist/designCheck-zx4PsRvE.cjs +495 -0
- package/dist/designCheck-zx4PsRvE.cjs.map +1 -0
- package/dist/extensions/pi.cjs +94 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.mts +5 -0
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +87 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +136 -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 +131 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +942 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.mts +171 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/storyPreview-D-hGhlig.cjs +371 -0
- package/dist/storyPreview-D-hGhlig.cjs.map +1 -0
- package/dist/storyPreview-Dhejqhos.mjs +357 -0
- package/dist/storyPreview-Dhejqhos.mjs.map +1 -0
- package/package.json +132 -0
- package/skills/design-workflow/SKILL.md +84 -0
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
import { apiResponse, createApiClient, defineApiRoutes, defineFill, defineWebPlugin, sessionApiAddress } from "@agimon-ai/doompi-core/web";
|
|
2
|
+
import { Button } from "@agimon-ai/doompi-web-components";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { sealedTransport } from "@agimon-ai/doompi-web-security/browser";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/types/apiRoutes.ts
|
|
7
|
+
var apiRoutes_default = defineApiRoutes({
|
|
8
|
+
metadata: {
|
|
9
|
+
method: "POST",
|
|
10
|
+
path: "/metadata",
|
|
11
|
+
response: apiResponse()
|
|
12
|
+
},
|
|
13
|
+
build: {
|
|
14
|
+
method: "POST",
|
|
15
|
+
path: "/build",
|
|
16
|
+
response: apiResponse()
|
|
17
|
+
},
|
|
18
|
+
image: {
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: "/image",
|
|
21
|
+
response: apiResponse()
|
|
22
|
+
},
|
|
23
|
+
dispose: {
|
|
24
|
+
method: "DELETE",
|
|
25
|
+
path: "/artifact",
|
|
26
|
+
response: apiResponse()
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const api = createApiClient(apiRoutes_default, {
|
|
30
|
+
scopes: ["session"],
|
|
31
|
+
basePath: "style-system-preview",
|
|
32
|
+
transport: (input, init) => sealedTransport.fetch(input, init),
|
|
33
|
+
sessionAddress: sessionApiAddress
|
|
34
|
+
});
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_lib/previewApi.ts
|
|
37
|
+
function errorMessage(result) {
|
|
38
|
+
if (result.status === 0) return "The preview service is unreachable.";
|
|
39
|
+
return result.error === "" ? `The preview service answered ${String(result.status)}.` : result.error;
|
|
40
|
+
}
|
|
41
|
+
async function storyMetadata(sessionId, request) {
|
|
42
|
+
const result = await api.session(sessionId).metadata({ body: request });
|
|
43
|
+
return result.ok ? {
|
|
44
|
+
ok: true,
|
|
45
|
+
metadata: result.data
|
|
46
|
+
} : {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: errorMessage(result)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function buildPreview(sessionId, request) {
|
|
52
|
+
const result = await api.session(sessionId).build({ body: request });
|
|
53
|
+
return result.ok ? {
|
|
54
|
+
ok: true,
|
|
55
|
+
preview: result.data
|
|
56
|
+
} : {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: errorMessage(result)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function exportPreviewImage(sessionId, request) {
|
|
62
|
+
const result = await api.session(sessionId).image({ body: request });
|
|
63
|
+
return result.ok ? {
|
|
64
|
+
ok: true,
|
|
65
|
+
image: result.data
|
|
66
|
+
} : {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: errorMessage(result)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function disposePreview(sessionId, handle) {
|
|
72
|
+
await api.session(sessionId).dispose({ body: { handle } });
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_lib/previewFrame.ts
|
|
76
|
+
const PREVIEW_CSP = "default-src 'none'; base-uri 'none'; connect-src 'none'; font-src data:; form-action 'none'; frame-src 'none'; img-src data: blob:; media-src data: blob:; object-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'";
|
|
77
|
+
function findHeadInsertion(html) {
|
|
78
|
+
const lower = html.toLowerCase();
|
|
79
|
+
let index = 0;
|
|
80
|
+
while (index < html.length) {
|
|
81
|
+
if (lower.startsWith("<!--", index)) {
|
|
82
|
+
const end = lower.indexOf("-->", index + 4);
|
|
83
|
+
index = end < 0 ? html.length : end + 3;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (html[index] !== "<") {
|
|
87
|
+
index += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const tag = html.slice(index).match(/^<([a-z][a-z0-9:-]*)(?:\s[^>]*)?>/i);
|
|
91
|
+
if (tag === null) {
|
|
92
|
+
index += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const name = tag[1].toLowerCase();
|
|
96
|
+
const end = index + tag[0].length;
|
|
97
|
+
if (name === "head") return end;
|
|
98
|
+
if (name === "script" || name === "style" || name === "template" || name === "textarea" || name === "title") {
|
|
99
|
+
const closing = lower.indexOf(`</${name}`, end);
|
|
100
|
+
index = closing < 0 ? html.length : closing;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
index = end;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function isolatedPreviewHtml(html) {
|
|
107
|
+
const policy = `<meta http-equiv="Content-Security-Policy" content="${PREVIEW_CSP}">`;
|
|
108
|
+
const headInsertion = findHeadInsertion(html);
|
|
109
|
+
if (headInsertion !== void 0) return `${html.slice(0, headInsertion)}${policy}${html.slice(headInsertion)}`;
|
|
110
|
+
const doctype = html.match(/^\s*<!doctype[^>]*>/i);
|
|
111
|
+
if (doctype !== null) return `${doctype[0]}<head>${policy}</head>${html.slice(doctype[0].length)}`;
|
|
112
|
+
return `<head>${policy}</head>${html}`;
|
|
113
|
+
}
|
|
114
|
+
function normalizedAnnotationRect(start, end) {
|
|
115
|
+
return {
|
|
116
|
+
x: Math.min(start.x, end.x),
|
|
117
|
+
y: Math.min(start.y, end.y),
|
|
118
|
+
width: Math.abs(end.x - start.x),
|
|
119
|
+
height: Math.abs(end.y - start.y)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function previewAnnotationLocation(tool, start, end) {
|
|
123
|
+
if (tool === "comment") return {
|
|
124
|
+
mode: "point",
|
|
125
|
+
point: end
|
|
126
|
+
};
|
|
127
|
+
const rect = normalizedAnnotationRect(start, end);
|
|
128
|
+
return rect.width === 0 || rect.height === 0 ? null : {
|
|
129
|
+
mode: "region",
|
|
130
|
+
point: rect,
|
|
131
|
+
rect
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewPanel.tsx
|
|
136
|
+
function imageDownload(data, storyExport) {
|
|
137
|
+
const link = document.createElement("a");
|
|
138
|
+
link.href = `data:image/png;base64,${data}`;
|
|
139
|
+
link.download = `${storyExport.replace(/[^A-Za-z0-9_-]/g, "-")}.png`;
|
|
140
|
+
link.click();
|
|
141
|
+
}
|
|
142
|
+
function point(event) {
|
|
143
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
144
|
+
return {
|
|
145
|
+
x: Math.min(1, Math.max(0, (event.clientX - bounds.left) / bounds.width)),
|
|
146
|
+
y: Math.min(1, Math.max(0, (event.clientY - bounds.top) / bounds.height))
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function preferredExport(exports) {
|
|
150
|
+
return exports.find((entry) => entry.exportName === "Playground")?.exportName ?? exports[0]?.exportName ?? "";
|
|
151
|
+
}
|
|
152
|
+
function errorText(reason) {
|
|
153
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
154
|
+
}
|
|
155
|
+
function StoryPreviewPanel({ sessionId, submitCapture, seed, source, activeTool = "select", displayedAnnotations = [], pendingCandidate = false, onAnnotationCandidate }) {
|
|
156
|
+
const contextualSource = source ?? seed?.source;
|
|
157
|
+
const sourcePath = contextualSource?.path ?? "";
|
|
158
|
+
const sourceDirty = contextualSource?.hasUnsavedChanges === true;
|
|
159
|
+
const sourceRevision = contextualSource?.revision ?? 0;
|
|
160
|
+
const sourceSha256 = contextualSource?.sourceSha256 ?? "";
|
|
161
|
+
const contextual = contextualSource !== void 0;
|
|
162
|
+
const [appPath, setAppPath] = useState(contextual ? "" : ".");
|
|
163
|
+
const [storyPath, setStoryPath] = useState(sourcePath);
|
|
164
|
+
const [storyExport, setStoryExport] = useState(contextual ? "" : "Playground");
|
|
165
|
+
const [storyExports, setStoryExports] = useState();
|
|
166
|
+
const [darkMode, setDarkMode] = useState(false);
|
|
167
|
+
const [preview, setPreview] = useState(void 0);
|
|
168
|
+
const [builtRequest, setBuiltRequest] = useState(void 0);
|
|
169
|
+
const [annotationImage, setAnnotationImage] = useState(void 0);
|
|
170
|
+
const [annotationStart, setAnnotationStart] = useState(void 0);
|
|
171
|
+
const [annotationRect, setAnnotationRect] = useState(void 0);
|
|
172
|
+
const [feedback, setFeedback] = useState("");
|
|
173
|
+
const [status, setStatus] = useState(contextual ? sourceDirty ? "Loading story metadata. Preview uses saved source." : "Loading story metadata…" : "Choose an exact story file and export.");
|
|
174
|
+
const [working, setWorking] = useState(false);
|
|
175
|
+
const generationRef = useRef(0);
|
|
176
|
+
const operationRef = useRef(0);
|
|
177
|
+
const previewRef = useRef(void 0);
|
|
178
|
+
const builtRequestRef = useRef(void 0);
|
|
179
|
+
const workingRef = useRef(false);
|
|
180
|
+
const dirtyRef = useRef(sourceDirty);
|
|
181
|
+
const annotationExportRef = useRef("");
|
|
182
|
+
const [stroke, setStroke] = useState([]);
|
|
183
|
+
const [zoom, setZoom] = useState(1);
|
|
184
|
+
const [gesture, setGesture] = useState();
|
|
185
|
+
const pointers = useRef(/* @__PURE__ */ new Map());
|
|
186
|
+
const pinch = useRef(void 0);
|
|
187
|
+
const pan = useRef(void 0);
|
|
188
|
+
const imageScroll = useRef(null);
|
|
189
|
+
dirtyRef.current = sourceDirty;
|
|
190
|
+
const request = useMemo(() => ({
|
|
191
|
+
appPath,
|
|
192
|
+
storyPath,
|
|
193
|
+
storyExport,
|
|
194
|
+
darkMode
|
|
195
|
+
}), [
|
|
196
|
+
appPath,
|
|
197
|
+
storyPath,
|
|
198
|
+
storyExport,
|
|
199
|
+
darkMode
|
|
200
|
+
]);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
operationRef.current += 1;
|
|
203
|
+
if (!workingRef.current) return;
|
|
204
|
+
workingRef.current = false;
|
|
205
|
+
setWorking(false);
|
|
206
|
+
}, [request]);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
const generation = generationRef.current + 1;
|
|
209
|
+
generationRef.current = generation;
|
|
210
|
+
operationRef.current += 1;
|
|
211
|
+
workingRef.current = false;
|
|
212
|
+
setWorking(false);
|
|
213
|
+
setStoryExports(void 0);
|
|
214
|
+
setAppPath(contextual ? "" : ".");
|
|
215
|
+
setStoryPath(sourcePath);
|
|
216
|
+
setStoryExport(contextual ? "" : "Playground");
|
|
217
|
+
builtRequestRef.current = void 0;
|
|
218
|
+
setBuiltRequest(void 0);
|
|
219
|
+
setAnnotationImage(void 0);
|
|
220
|
+
setAnnotationStart(void 0);
|
|
221
|
+
setAnnotationRect(void 0);
|
|
222
|
+
setStroke([]);
|
|
223
|
+
setZoom(1);
|
|
224
|
+
pointers.current.clear();
|
|
225
|
+
pinch.current = void 0;
|
|
226
|
+
if (sessionId === null || sourcePath === "") {
|
|
227
|
+
setPreview(void 0);
|
|
228
|
+
previewRef.current = void 0;
|
|
229
|
+
return () => {
|
|
230
|
+
if (generationRef.current === generation) generationRef.current += 1;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const previous = previewRef.current;
|
|
234
|
+
previewRef.current = void 0;
|
|
235
|
+
setPreview(void 0);
|
|
236
|
+
if (previous !== void 0) disposePreview(sessionId, previous.handle);
|
|
237
|
+
let current = true;
|
|
238
|
+
setStatus(sourceDirty ? "Loading story metadata. Preview uses saved source." : "Loading story metadata…");
|
|
239
|
+
storyMetadata(sessionId, { storyPath: sourcePath }).then((result) => {
|
|
240
|
+
if (!current || generationRef.current !== generation) return;
|
|
241
|
+
if (!result.ok) {
|
|
242
|
+
setStoryExports(void 0);
|
|
243
|
+
setStatus(result.error);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const exports = result.metadata.exports;
|
|
247
|
+
setAppPath(result.metadata.appPath);
|
|
248
|
+
setStoryPath(result.metadata.storyPath);
|
|
249
|
+
setStoryExports(exports);
|
|
250
|
+
setStoryExport(preferredExport(exports));
|
|
251
|
+
if (exports.length === 0) setStatus("No story exports were found in this file.");
|
|
252
|
+
else if (dirtyRef.current) setStatus("Save changes to preview. Preview uses saved source.");
|
|
253
|
+
else if (exports.length === 1) setStatus("Story found. Building preview…");
|
|
254
|
+
else setStatus("Story found. Building the Playground export…");
|
|
255
|
+
}).catch((reason) => {
|
|
256
|
+
if (current && generationRef.current === generation) setStatus(errorText(reason));
|
|
257
|
+
});
|
|
258
|
+
return () => {
|
|
259
|
+
current = false;
|
|
260
|
+
if (generationRef.current === generation) generationRef.current += 1;
|
|
261
|
+
operationRef.current += 1;
|
|
262
|
+
const ownedPreview = previewRef.current;
|
|
263
|
+
previewRef.current = void 0;
|
|
264
|
+
if (ownedPreview !== void 0) disposePreview(sessionId, ownedPreview.handle);
|
|
265
|
+
};
|
|
266
|
+
}, [
|
|
267
|
+
contextual,
|
|
268
|
+
sessionId,
|
|
269
|
+
sourcePath,
|
|
270
|
+
sourceRevision,
|
|
271
|
+
sourceSha256
|
|
272
|
+
]);
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (!contextual || !sourceDirty) return;
|
|
275
|
+
setStatus("Save changes to preview. Preview uses saved source.");
|
|
276
|
+
}, [contextual, sourceDirty]);
|
|
277
|
+
const rebuild = useCallback(async () => {
|
|
278
|
+
if (sessionId === null || workingRef.current || request.appPath.trim() === "" || request.storyPath.trim() === "" || request.storyExport.trim() === "") return false;
|
|
279
|
+
if (dirtyRef.current) {
|
|
280
|
+
setStatus("Save changes to preview. Preview uses saved source.");
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
const generation = generationRef.current;
|
|
284
|
+
const operation = operationRef.current + 1;
|
|
285
|
+
operationRef.current = operation;
|
|
286
|
+
const requestSessionId = sessionId;
|
|
287
|
+
workingRef.current = true;
|
|
288
|
+
setWorking(true);
|
|
289
|
+
setStatus("Building preview…");
|
|
290
|
+
try {
|
|
291
|
+
const result = await buildPreview(requestSessionId, request);
|
|
292
|
+
if (generationRef.current !== generation || operationRef.current !== operation) {
|
|
293
|
+
if (result.ok) disposePreview(requestSessionId, result.preview.handle);
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
if (!result.ok) {
|
|
297
|
+
setStatus(result.error);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const previous = previewRef.current;
|
|
301
|
+
previewRef.current = result.preview;
|
|
302
|
+
builtRequestRef.current = request;
|
|
303
|
+
setPreview(result.preview);
|
|
304
|
+
setBuiltRequest(request);
|
|
305
|
+
setAnnotationImage(void 0);
|
|
306
|
+
setAnnotationStart(void 0);
|
|
307
|
+
setAnnotationRect(void 0);
|
|
308
|
+
if (previous !== void 0 && previous.handle !== result.preview.handle) disposePreview(requestSessionId, previous.handle);
|
|
309
|
+
setStatus(dirtyRef.current ? "Preview ready from saved source. Save changes to refresh." : "Preview ready. Refresh after source edits.");
|
|
310
|
+
return true;
|
|
311
|
+
} catch (reason) {
|
|
312
|
+
if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));
|
|
313
|
+
return false;
|
|
314
|
+
} finally {
|
|
315
|
+
if (generationRef.current === generation && operationRef.current === operation) {
|
|
316
|
+
workingRef.current = false;
|
|
317
|
+
setWorking(false);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}, [request, sessionId]);
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
if (!contextual || sessionId === null || sourceDirty || storyExports === void 0 || storyExport.trim() === "" || appPath.trim() === "" || storyPath.trim() === "") return;
|
|
323
|
+
rebuild();
|
|
324
|
+
}, [
|
|
325
|
+
appPath,
|
|
326
|
+
contextual,
|
|
327
|
+
rebuild,
|
|
328
|
+
sessionId,
|
|
329
|
+
sourceDirty,
|
|
330
|
+
storyExport,
|
|
331
|
+
storyExports,
|
|
332
|
+
storyPath
|
|
333
|
+
]);
|
|
334
|
+
const image = useCallback(async () => {
|
|
335
|
+
const currentRequest = builtRequestRef.current;
|
|
336
|
+
if (sessionId === null || workingRef.current || currentRequest === void 0) return void 0;
|
|
337
|
+
const generation = generationRef.current;
|
|
338
|
+
const operation = operationRef.current + 1;
|
|
339
|
+
operationRef.current = operation;
|
|
340
|
+
const requestSessionId = sessionId;
|
|
341
|
+
workingRef.current = true;
|
|
342
|
+
setWorking(true);
|
|
343
|
+
setStatus("Rendering a fresh source-backed image…");
|
|
344
|
+
try {
|
|
345
|
+
const result = await exportPreviewImage(requestSessionId, currentRequest);
|
|
346
|
+
if (generationRef.current !== generation || operationRef.current !== operation) return void 0;
|
|
347
|
+
if (!result.ok) {
|
|
348
|
+
setStatus(result.error);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
setStatus("Image rendered from the current source. Transient iframe interactions are not included.");
|
|
352
|
+
return result.image;
|
|
353
|
+
} catch (reason) {
|
|
354
|
+
if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));
|
|
355
|
+
return;
|
|
356
|
+
} finally {
|
|
357
|
+
if (generationRef.current === generation && operationRef.current === operation) {
|
|
358
|
+
workingRef.current = false;
|
|
359
|
+
setWorking(false);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}, [sessionId]);
|
|
363
|
+
async function prepareAnnotation() {
|
|
364
|
+
const generation = generationRef.current;
|
|
365
|
+
const result = await image();
|
|
366
|
+
if (result === void 0 || generationRef.current !== generation) return;
|
|
367
|
+
setAnnotationImage(result);
|
|
368
|
+
setAnnotationRect(void 0);
|
|
369
|
+
setStatus("Annotation image ready. Drag a region or comment on the whole preview.");
|
|
370
|
+
}
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
if (onAnnotationCandidate === void 0) return;
|
|
373
|
+
if (activeTool === "select") {
|
|
374
|
+
annotationExportRef.current = "";
|
|
375
|
+
setAnnotationImage(void 0);
|
|
376
|
+
setAnnotationStart(void 0);
|
|
377
|
+
setAnnotationRect(void 0);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (preview === void 0 || annotationImage !== void 0 || working || annotationExportRef.current === preview.handle) return;
|
|
381
|
+
const generation = generationRef.current;
|
|
382
|
+
image().then((result) => {
|
|
383
|
+
if (result === void 0 || generationRef.current !== generation) return;
|
|
384
|
+
annotationExportRef.current = preview.handle;
|
|
385
|
+
setAnnotationImage(result);
|
|
386
|
+
setStatus("Frozen source-backed image ready for Author annotations.");
|
|
387
|
+
});
|
|
388
|
+
}, [
|
|
389
|
+
activeTool,
|
|
390
|
+
annotationImage,
|
|
391
|
+
image,
|
|
392
|
+
onAnnotationCandidate,
|
|
393
|
+
preview,
|
|
394
|
+
working
|
|
395
|
+
]);
|
|
396
|
+
function updateAnnotation(event) {
|
|
397
|
+
if (annotationStart === void 0) return;
|
|
398
|
+
const end = point(event);
|
|
399
|
+
setAnnotationRect(normalizedAnnotationRect(annotationStart, end));
|
|
400
|
+
}
|
|
401
|
+
function authorCandidate(location, rect, points) {
|
|
402
|
+
if (annotationImage === void 0 || onAnnotationCandidate === void 0 || builtRequest === void 0) return;
|
|
403
|
+
const binary = atob(annotationImage.data);
|
|
404
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
405
|
+
const evidence = new Blob([bytes], { type: annotationImage.mimeType });
|
|
406
|
+
const thumbnailUrl = URL.createObjectURL(evidence);
|
|
407
|
+
try {
|
|
408
|
+
onAnnotationCandidate({
|
|
409
|
+
mode: rect === void 0 ? "point" : "region",
|
|
410
|
+
point: location,
|
|
411
|
+
...rect === void 0 ? {} : { rect },
|
|
412
|
+
...points === void 0 ? {} : { stroke: points.map((point) => ({ ...point })) },
|
|
413
|
+
preview: {
|
|
414
|
+
version: 1,
|
|
415
|
+
provider: "style-system",
|
|
416
|
+
projectPath: builtRequest.appPath,
|
|
417
|
+
storyPath: annotationImage.storyPath,
|
|
418
|
+
storyExport: annotationImage.storyExport,
|
|
419
|
+
buildRevision: annotationImage.sourceSha256,
|
|
420
|
+
viewport: {
|
|
421
|
+
width: annotationImage.width,
|
|
422
|
+
height: annotationImage.height
|
|
423
|
+
},
|
|
424
|
+
sources: [{
|
|
425
|
+
path: annotationImage.storyPath,
|
|
426
|
+
sha256: annotationImage.sourceSha256
|
|
427
|
+
}]
|
|
428
|
+
},
|
|
429
|
+
evidence,
|
|
430
|
+
thumbnailUrl
|
|
431
|
+
});
|
|
432
|
+
} catch (reason) {
|
|
433
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
434
|
+
setStatus(errorText(reason));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const multipleExports = storyExports !== void 0 && storyExports.length > 1;
|
|
438
|
+
const previewVisible = preview !== void 0;
|
|
439
|
+
const authorAnnotationActive = onAnnotationCandidate !== void 0 && activeTool !== "select";
|
|
440
|
+
useEffect(() => {
|
|
441
|
+
setStroke([]);
|
|
442
|
+
setAnnotationStart(void 0);
|
|
443
|
+
pointers.current.clear();
|
|
444
|
+
pinch.current = void 0;
|
|
445
|
+
pan.current = void 0;
|
|
446
|
+
}, [activeTool, annotationImage?.sourceSha256]);
|
|
447
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
448
|
+
className: "flex h-full min-h-0 min-w-0 flex-1 flex-col gap-3 p-3",
|
|
449
|
+
"data-testid": "style-system-preview-panel",
|
|
450
|
+
children: [
|
|
451
|
+
contextual ? /* @__PURE__ */ jsxs("header", {
|
|
452
|
+
className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-doom-dim",
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ jsxs("span", { children: ["Story: ", /* @__PURE__ */ jsx("strong", {
|
|
455
|
+
className: "text-doom-text",
|
|
456
|
+
children: storyPath || sourcePath
|
|
457
|
+
})] }),
|
|
458
|
+
/* @__PURE__ */ jsxs("span", { children: ["Project: ", /* @__PURE__ */ jsx("strong", {
|
|
459
|
+
className: "text-doom-text",
|
|
460
|
+
children: appPath || "resolving…"
|
|
461
|
+
})] }),
|
|
462
|
+
multipleExports ? /* @__PURE__ */ jsxs("label", {
|
|
463
|
+
className: "flex items-center gap-1",
|
|
464
|
+
children: ["Export", /* @__PURE__ */ jsx("select", {
|
|
465
|
+
className: "rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
466
|
+
value: storyExport,
|
|
467
|
+
"aria-label": "story export",
|
|
468
|
+
onChange: (event) => {
|
|
469
|
+
setStoryExport(event.target.value);
|
|
470
|
+
builtRequestRef.current = void 0;
|
|
471
|
+
setBuiltRequest(void 0);
|
|
472
|
+
setAnnotationImage(void 0);
|
|
473
|
+
setAnnotationRect(void 0);
|
|
474
|
+
},
|
|
475
|
+
children: storyExports.map((entry) => /* @__PURE__ */ jsx("option", {
|
|
476
|
+
value: entry.exportName,
|
|
477
|
+
children: entry.label ?? entry.exportName
|
|
478
|
+
}, entry.exportName))
|
|
479
|
+
})]
|
|
480
|
+
}) : storyExport !== "" ? /* @__PURE__ */ jsxs("span", { children: ["Export: ", /* @__PURE__ */ jsx("strong", {
|
|
481
|
+
className: "text-doom-text",
|
|
482
|
+
children: storyExport
|
|
483
|
+
})] }) : null
|
|
484
|
+
]
|
|
485
|
+
}) : /* @__PURE__ */ jsxs("header", {
|
|
486
|
+
className: "grid gap-2 md:grid-cols-3",
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ jsxs("label", {
|
|
489
|
+
className: "grid gap-1 text-xs text-doom-dim",
|
|
490
|
+
children: ["Project path", /* @__PURE__ */ jsx("input", {
|
|
491
|
+
className: "rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
492
|
+
value: appPath,
|
|
493
|
+
onChange: (event) => setAppPath(event.target.value)
|
|
494
|
+
})]
|
|
495
|
+
}),
|
|
496
|
+
/* @__PURE__ */ jsxs("label", {
|
|
497
|
+
className: "grid gap-1 text-xs text-doom-dim",
|
|
498
|
+
children: ["Story file", /* @__PURE__ */ jsx("input", {
|
|
499
|
+
className: "rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
500
|
+
placeholder: "packages/ui/Button.stories.tsx",
|
|
501
|
+
value: storyPath,
|
|
502
|
+
onChange: (event) => {
|
|
503
|
+
setStoryPath(event.target.value);
|
|
504
|
+
setStoryExports(void 0);
|
|
505
|
+
}
|
|
506
|
+
})]
|
|
507
|
+
}),
|
|
508
|
+
/* @__PURE__ */ jsxs("label", {
|
|
509
|
+
className: "grid gap-1 text-xs text-doom-dim",
|
|
510
|
+
children: ["Story export", storyExports === void 0 ? /* @__PURE__ */ jsx("input", {
|
|
511
|
+
className: "rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
512
|
+
value: storyExport,
|
|
513
|
+
onChange: (event) => setStoryExport(event.target.value)
|
|
514
|
+
}) : /* @__PURE__ */ jsxs("select", {
|
|
515
|
+
className: "rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
516
|
+
value: storyExport,
|
|
517
|
+
onChange: (event) => setStoryExport(event.target.value),
|
|
518
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
519
|
+
value: "",
|
|
520
|
+
children: "Choose an export"
|
|
521
|
+
}), storyExports.map((entry) => /* @__PURE__ */ jsx("option", {
|
|
522
|
+
value: entry.exportName,
|
|
523
|
+
children: entry.label ?? entry.exportName
|
|
524
|
+
}, entry.exportName))]
|
|
525
|
+
})]
|
|
526
|
+
})
|
|
527
|
+
]
|
|
528
|
+
}),
|
|
529
|
+
/* @__PURE__ */ jsxs("div", {
|
|
530
|
+
className: "flex flex-wrap items-center gap-2",
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsx(Button, {
|
|
533
|
+
disabled: sessionId === null || working || sourceDirty || request.storyPath.trim() === "" || request.storyExport.trim() === "",
|
|
534
|
+
onClick: () => void rebuild(),
|
|
535
|
+
children: previewVisible ? "Refresh preview" : "Build preview"
|
|
536
|
+
}),
|
|
537
|
+
/* @__PURE__ */ jsx(Button, {
|
|
538
|
+
variant: "outline",
|
|
539
|
+
disabled: preview === void 0 || working,
|
|
540
|
+
onClick: () => void image().then((result) => result && imageDownload(result.data, result.storyExport)),
|
|
541
|
+
children: "Export PNG"
|
|
542
|
+
}),
|
|
543
|
+
onAnnotationCandidate === void 0 ? /* @__PURE__ */ jsx(Button, {
|
|
544
|
+
variant: "outline",
|
|
545
|
+
disabled: preview === void 0 || working,
|
|
546
|
+
onClick: () => void prepareAnnotation(),
|
|
547
|
+
children: "Prepare annotation"
|
|
548
|
+
}) : null,
|
|
549
|
+
/* @__PURE__ */ jsxs("label", {
|
|
550
|
+
className: "flex items-center gap-1 text-xs text-doom-dim",
|
|
551
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
552
|
+
type: "checkbox",
|
|
553
|
+
checked: darkMode,
|
|
554
|
+
onChange: (event) => setDarkMode(event.target.checked)
|
|
555
|
+
}), " Dark mode"]
|
|
556
|
+
}),
|
|
557
|
+
/* @__PURE__ */ jsx("output", {
|
|
558
|
+
className: "text-xs text-doom-dim",
|
|
559
|
+
"aria-live": "polite",
|
|
560
|
+
children: status
|
|
561
|
+
})
|
|
562
|
+
]
|
|
563
|
+
}),
|
|
564
|
+
sourceDirty ? /* @__PURE__ */ jsx("p", {
|
|
565
|
+
"data-testid": "style-system-preview-dirty",
|
|
566
|
+
className: "text-xs text-doom-yellow",
|
|
567
|
+
children: "Save changes to preview. Preview uses saved source."
|
|
568
|
+
}) : null,
|
|
569
|
+
preview === void 0 ? /* @__PURE__ */ jsx("div", {
|
|
570
|
+
className: "grid min-h-48 place-items-center rounded border border-dashed border-doom-border text-sm text-doom-faint",
|
|
571
|
+
children: "No preview built."
|
|
572
|
+
}) : authorAnnotationActive && annotationImage !== void 0 ? /* @__PURE__ */ jsxs("div", {
|
|
573
|
+
className: "flex min-h-0 flex-1 flex-col overflow-hidden rounded border border-doom-border bg-doom-deep",
|
|
574
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
575
|
+
className: "flex items-center gap-2 p-2",
|
|
576
|
+
role: "group",
|
|
577
|
+
"aria-label": "Preview zoom",
|
|
578
|
+
children: [
|
|
579
|
+
/* @__PURE__ */ jsx(Button, {
|
|
580
|
+
variant: "outline",
|
|
581
|
+
className: "min-h-11 min-w-11",
|
|
582
|
+
"aria-label": "Zoom out",
|
|
583
|
+
onClick: () => setZoom((value) => Math.max(1, value / 1.5)),
|
|
584
|
+
children: "−"
|
|
585
|
+
}),
|
|
586
|
+
/* @__PURE__ */ jsxs("output", {
|
|
587
|
+
className: "text-xs text-doom-dim",
|
|
588
|
+
children: [Math.round(zoom * 100), "%"]
|
|
589
|
+
}),
|
|
590
|
+
/* @__PURE__ */ jsx(Button, {
|
|
591
|
+
variant: "outline",
|
|
592
|
+
className: "min-h-11 min-w-11",
|
|
593
|
+
"aria-label": "Zoom in",
|
|
594
|
+
onClick: () => setZoom((value) => Math.min(8, value * 1.5)),
|
|
595
|
+
children: "+"
|
|
596
|
+
}),
|
|
597
|
+
/* @__PURE__ */ jsx(Button, {
|
|
598
|
+
variant: "outline",
|
|
599
|
+
className: "min-h-11",
|
|
600
|
+
"aria-label": "Fit preview",
|
|
601
|
+
onClick: () => setZoom(1),
|
|
602
|
+
children: "Fit"
|
|
603
|
+
})
|
|
604
|
+
]
|
|
605
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
606
|
+
ref: imageScroll,
|
|
607
|
+
className: "min-h-0 flex-1 overflow-auto",
|
|
608
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
609
|
+
className: "relative w-fit max-w-full overflow-hidden",
|
|
610
|
+
style: {
|
|
611
|
+
zoom,
|
|
612
|
+
touchAction: "none"
|
|
613
|
+
},
|
|
614
|
+
"aria-label": activeTool === "comment" ? "Click to place an annotation point" : activeTool === "draw" ? "Draw a feedback location mark" : activeTool === "pan" ? "Drag to pan preview" : "Drag to mark an annotation region",
|
|
615
|
+
"data-testid": "style-system-author-annotation-overlay",
|
|
616
|
+
onPointerDown: (event) => {
|
|
617
|
+
if (pendingCandidate) return;
|
|
618
|
+
pointers.current.set(event.pointerId, {
|
|
619
|
+
x: event.clientX,
|
|
620
|
+
y: event.clientY
|
|
621
|
+
});
|
|
622
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
623
|
+
if (pointers.current.size === 2) {
|
|
624
|
+
setStroke([]);
|
|
625
|
+
setAnnotationStart(void 0);
|
|
626
|
+
const [a, b] = [...pointers.current.values()];
|
|
627
|
+
pinch.current = {
|
|
628
|
+
distance: Math.hypot(a.x - b.x, a.y - b.y),
|
|
629
|
+
zoom
|
|
630
|
+
};
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (activeTool === "pan") {
|
|
634
|
+
const container = imageScroll.current;
|
|
635
|
+
if (container) pan.current = {
|
|
636
|
+
x: event.clientX,
|
|
637
|
+
y: event.clientY,
|
|
638
|
+
left: container.scrollLeft,
|
|
639
|
+
top: container.scrollTop
|
|
640
|
+
};
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (activeTool !== "mark" && activeTool !== "comment" && activeTool !== "draw") return;
|
|
644
|
+
setGesture(event.pointerId);
|
|
645
|
+
if (activeTool === "draw") setStroke([point(event)]);
|
|
646
|
+
const start = point(event);
|
|
647
|
+
setAnnotationStart(start);
|
|
648
|
+
setAnnotationRect(activeTool === "mark" ? {
|
|
649
|
+
...start,
|
|
650
|
+
width: 0,
|
|
651
|
+
height: 0
|
|
652
|
+
} : void 0);
|
|
653
|
+
},
|
|
654
|
+
onPointerMove: (event) => {
|
|
655
|
+
if (pointers.current.has(event.pointerId)) pointers.current.set(event.pointerId, {
|
|
656
|
+
x: event.clientX,
|
|
657
|
+
y: event.clientY
|
|
658
|
+
});
|
|
659
|
+
if (pinch.current !== void 0 && pointers.current.size >= 2) {
|
|
660
|
+
const [a, b] = [...pointers.current.values()];
|
|
661
|
+
if (pinch.current.distance > 0) setZoom(Math.max(1, Math.min(8, pinch.current.zoom * Math.hypot(a.x - b.x, a.y - b.y) / pinch.current.distance)));
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (pan.current !== void 0 && activeTool === "pan") {
|
|
665
|
+
const container = imageScroll.current;
|
|
666
|
+
if (container) {
|
|
667
|
+
container.scrollLeft = pan.current.left + pan.current.x - event.clientX;
|
|
668
|
+
container.scrollTop = pan.current.top + pan.current.y - event.clientY;
|
|
669
|
+
}
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (gesture !== event.pointerId) return;
|
|
673
|
+
if (activeTool === "mark") updateAnnotation(event);
|
|
674
|
+
if (activeTool === "draw") setStroke((points) => points.length >= 128 ? points : [...points, point(event)]);
|
|
675
|
+
},
|
|
676
|
+
onPointerUp: (event) => {
|
|
677
|
+
pointers.current.delete(event.pointerId);
|
|
678
|
+
if (pinch.current !== void 0 || pan.current !== void 0) {
|
|
679
|
+
if (pointers.current.size < 2) pinch.current = void 0;
|
|
680
|
+
pan.current = void 0;
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const start = annotationStart;
|
|
684
|
+
if (start === void 0 || gesture !== event.pointerId) return;
|
|
685
|
+
if (activeTool === "draw") {
|
|
686
|
+
const points = stroke.length >= 128 ? stroke : [...stroke, point(event)];
|
|
687
|
+
setStroke([]);
|
|
688
|
+
setAnnotationStart(void 0);
|
|
689
|
+
if (points.length < 2 || !points.some((item) => item.x !== points[0]?.x || item.y !== points[0]?.y)) return;
|
|
690
|
+
const x = Math.min(...points.map((item) => item.x));
|
|
691
|
+
const y = Math.min(...points.map((item) => item.y));
|
|
692
|
+
const rect = {
|
|
693
|
+
x: Math.min(x, .999),
|
|
694
|
+
y: Math.min(y, .999),
|
|
695
|
+
width: Math.max(.001, Math.max(...points.map((item) => item.x)) - x),
|
|
696
|
+
height: Math.max(.001, Math.max(...points.map((item) => item.y)) - y)
|
|
697
|
+
};
|
|
698
|
+
authorCandidate(points[0], rect, points);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
const end = point(event);
|
|
702
|
+
setAnnotationStart(void 0);
|
|
703
|
+
const location = previewAnnotationLocation(activeTool === "comment" ? "comment" : "mark", start, end);
|
|
704
|
+
if (location === null) return;
|
|
705
|
+
setAnnotationRect(location.mode === "region" ? location.rect : void 0);
|
|
706
|
+
authorCandidate(location.point, location.mode === "region" ? location.rect : void 0);
|
|
707
|
+
},
|
|
708
|
+
onPointerCancel: () => {
|
|
709
|
+
setAnnotationStart(void 0);
|
|
710
|
+
setAnnotationRect(void 0);
|
|
711
|
+
setStroke([]);
|
|
712
|
+
pointers.current.clear();
|
|
713
|
+
pinch.current = void 0;
|
|
714
|
+
pan.current = void 0;
|
|
715
|
+
},
|
|
716
|
+
children: [
|
|
717
|
+
/* @__PURE__ */ jsx("img", {
|
|
718
|
+
className: "block max-h-full max-w-full select-none",
|
|
719
|
+
src: `data:${annotationImage.mimeType};base64,${annotationImage.data}`,
|
|
720
|
+
alt: "Frozen source-backed story render for Author annotation",
|
|
721
|
+
draggable: false
|
|
722
|
+
}),
|
|
723
|
+
displayedAnnotations.map((annotation) => annotation.stroke !== void 0 ? /* @__PURE__ */ jsxs("div", {
|
|
724
|
+
"data-author-stroke": annotation.ordinal,
|
|
725
|
+
className: "pointer-events-none absolute inset-0",
|
|
726
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
727
|
+
"aria-hidden": "true",
|
|
728
|
+
className: "absolute inset-0 h-full w-full text-doom-yellow",
|
|
729
|
+
viewBox: "0 0 1000 1000",
|
|
730
|
+
preserveAspectRatio: "none",
|
|
731
|
+
children: /* @__PURE__ */ jsx("polyline", {
|
|
732
|
+
points: annotation.stroke.map((point) => `${String(point.x * 1e3)},${String(point.y * 1e3)}`).join(" "),
|
|
733
|
+
fill: "none",
|
|
734
|
+
stroke: "currentColor",
|
|
735
|
+
strokeWidth: "5",
|
|
736
|
+
strokeLinecap: "round",
|
|
737
|
+
strokeLinejoin: "round"
|
|
738
|
+
})
|
|
739
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
740
|
+
className: "absolute flex h-5 min-w-5 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-doom-yellow px-1 text-2xs font-bold text-doom-deep",
|
|
741
|
+
style: {
|
|
742
|
+
left: `${String(annotation.stroke[0].x * 100)}%`,
|
|
743
|
+
top: `${String(annotation.stroke[0].y * 100)}%`
|
|
744
|
+
},
|
|
745
|
+
children: annotation.ordinal
|
|
746
|
+
})]
|
|
747
|
+
}, annotation.ordinal) : annotation.mode === "point" || annotation.rect === void 0 ? /* @__PURE__ */ jsx("span", {
|
|
748
|
+
"data-author-point": annotation.ordinal,
|
|
749
|
+
className: "pointer-events-none absolute z-10 flex h-5 min-w-5 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border-2 border-doom-deep bg-doom-yellow px-1 text-2xs font-bold text-doom-deep",
|
|
750
|
+
style: {
|
|
751
|
+
left: `${String(annotation.point.x * 100)}%`,
|
|
752
|
+
top: `${String(annotation.point.y * 100)}%`
|
|
753
|
+
},
|
|
754
|
+
children: annotation.ordinal
|
|
755
|
+
}, annotation.ordinal) : /* @__PURE__ */ jsx("div", {
|
|
756
|
+
"data-author-region": annotation.ordinal,
|
|
757
|
+
className: "pointer-events-none absolute border border-doom-yellow bg-doom-yellow/10",
|
|
758
|
+
style: {
|
|
759
|
+
left: `${String(annotation.rect.x * 100)}%`,
|
|
760
|
+
top: `${String(annotation.rect.y * 100)}%`,
|
|
761
|
+
width: `${String(annotation.rect.width * 100)}%`,
|
|
762
|
+
height: `${String(annotation.rect.height * 100)}%`
|
|
763
|
+
},
|
|
764
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
765
|
+
className: "bg-doom-yellow text-doom-deep",
|
|
766
|
+
children: annotation.ordinal
|
|
767
|
+
})
|
|
768
|
+
}, annotation.ordinal)),
|
|
769
|
+
stroke.length < 2 ? null : /* @__PURE__ */ jsx("svg", {
|
|
770
|
+
"aria-hidden": "true",
|
|
771
|
+
"data-testid": "style-system-author-stroke-selection",
|
|
772
|
+
className: "pointer-events-none absolute inset-0 h-full w-full text-doom-red",
|
|
773
|
+
viewBox: "0 0 1000 1000",
|
|
774
|
+
preserveAspectRatio: "none",
|
|
775
|
+
children: /* @__PURE__ */ jsx("polyline", {
|
|
776
|
+
points: stroke.map((point) => `${String(point.x * 1e3)},${String(point.y * 1e3)}`).join(" "),
|
|
777
|
+
fill: "none",
|
|
778
|
+
stroke: "currentColor",
|
|
779
|
+
strokeWidth: "5",
|
|
780
|
+
strokeLinecap: "round",
|
|
781
|
+
strokeLinejoin: "round"
|
|
782
|
+
})
|
|
783
|
+
}),
|
|
784
|
+
annotationRect === void 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
785
|
+
className: "pointer-events-none absolute border-2 border-red-500 bg-red-500/10",
|
|
786
|
+
style: {
|
|
787
|
+
left: `${String(annotationRect.x * 100)}%`,
|
|
788
|
+
top: `${String(annotationRect.y * 100)}%`,
|
|
789
|
+
width: `${String(annotationRect.width * 100)}%`,
|
|
790
|
+
height: `${String(annotationRect.height * 100)}%`
|
|
791
|
+
}
|
|
792
|
+
})
|
|
793
|
+
]
|
|
794
|
+
})
|
|
795
|
+
})]
|
|
796
|
+
}) : /* @__PURE__ */ jsx("iframe", {
|
|
797
|
+
title: `${preview.storyExport} story preview`,
|
|
798
|
+
sandbox: "allow-scripts",
|
|
799
|
+
referrerPolicy: "no-referrer",
|
|
800
|
+
srcDoc: isolatedPreviewHtml(preview.html),
|
|
801
|
+
className: "min-h-0 flex-1 rounded border border-doom-border bg-white"
|
|
802
|
+
}, preview.handle),
|
|
803
|
+
annotationImage === void 0 || onAnnotationCandidate !== void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
804
|
+
className: "grid gap-2",
|
|
805
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
806
|
+
className: "relative max-h-80 w-fit max-w-full touch-none overflow-hidden rounded border border-doom-border",
|
|
807
|
+
"aria-label": "Drag to mark an annotation region",
|
|
808
|
+
onPointerDown: (event) => {
|
|
809
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
810
|
+
const start = point(event);
|
|
811
|
+
setAnnotationStart(start);
|
|
812
|
+
setAnnotationRect({
|
|
813
|
+
...start,
|
|
814
|
+
width: 0,
|
|
815
|
+
height: 0
|
|
816
|
+
});
|
|
817
|
+
},
|
|
818
|
+
onPointerMove: updateAnnotation,
|
|
819
|
+
onPointerUp: (event) => {
|
|
820
|
+
updateAnnotation(event);
|
|
821
|
+
setAnnotationStart(void 0);
|
|
822
|
+
},
|
|
823
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
824
|
+
className: "block max-h-80 max-w-full select-none",
|
|
825
|
+
src: `data:${annotationImage.mimeType};base64,${annotationImage.data}`,
|
|
826
|
+
alt: "Fresh source-backed story render for annotation",
|
|
827
|
+
draggable: false
|
|
828
|
+
}), annotationRect === void 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
829
|
+
className: "pointer-events-none absolute border-2 border-red-500 bg-red-500/10",
|
|
830
|
+
style: {
|
|
831
|
+
left: `${String(annotationRect.x * 100)}%`,
|
|
832
|
+
top: `${String(annotationRect.y * 100)}%`,
|
|
833
|
+
width: `${String(annotationRect.width * 100)}%`,
|
|
834
|
+
height: `${String(annotationRect.height * 100)}%`
|
|
835
|
+
}
|
|
836
|
+
})]
|
|
837
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
838
|
+
className: "text-xs text-doom-faint",
|
|
839
|
+
children: "This image is rebuilt from source and does not preserve transient iframe interactions."
|
|
840
|
+
})]
|
|
841
|
+
}),
|
|
842
|
+
preview === void 0 || onAnnotationCandidate !== void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
843
|
+
className: "flex gap-2",
|
|
844
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
845
|
+
className: "grid min-w-0 flex-1 gap-1 text-xs text-doom-dim",
|
|
846
|
+
children: ["AI design feedback", /* @__PURE__ */ jsx("textarea", {
|
|
847
|
+
className: "min-h-20 rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text",
|
|
848
|
+
value: feedback,
|
|
849
|
+
onChange: (event) => setFeedback(event.target.value)
|
|
850
|
+
})]
|
|
851
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
852
|
+
className: "self-end",
|
|
853
|
+
variant: "outline",
|
|
854
|
+
disabled: working || submitCapture === void 0 || feedback.trim() === "" || annotationImage === void 0,
|
|
855
|
+
onClick: () => {
|
|
856
|
+
if (submitCapture === void 0 || annotationImage === void 0) return;
|
|
857
|
+
const location = annotationRect === void 0 ? "Whole preview" : `Region: x=${annotationRect.x.toFixed(3)}, y=${annotationRect.y.toFixed(3)}, width=${annotationRect.width.toFixed(3)}, height=${annotationRect.height.toFixed(3)}`;
|
|
858
|
+
const generation = generationRef.current;
|
|
859
|
+
submitCapture({
|
|
860
|
+
...annotationImage,
|
|
861
|
+
context: {
|
|
862
|
+
kind: "author-capture",
|
|
863
|
+
source: "style-system",
|
|
864
|
+
id: crypto.randomUUID(),
|
|
865
|
+
label: `${annotationImage.storyPath}#${annotationImage.storyExport}`,
|
|
866
|
+
content: [
|
|
867
|
+
"Edit the underlying story or component source, never the generated preview HTML.",
|
|
868
|
+
`Project: ${builtRequest?.appPath ?? appPath}`,
|
|
869
|
+
`Story: ${annotationImage.storyPath}#${annotationImage.storyExport}`,
|
|
870
|
+
`Story source sha256: ${annotationImage.sourceSha256}`,
|
|
871
|
+
`Source-backed image revision: ${annotationImage.sourceSha256}`,
|
|
872
|
+
location,
|
|
873
|
+
`Feedback: ${feedback.trim()}`,
|
|
874
|
+
"After editing, rebuild this preview before treating it as visually verified."
|
|
875
|
+
].join("\n")
|
|
876
|
+
}
|
|
877
|
+
}).then(() => {
|
|
878
|
+
if (generationRef.current !== generation) return;
|
|
879
|
+
setStatus("Feedback and the frozen source-backed annotation image were submitted to the agent.");
|
|
880
|
+
setFeedback("");
|
|
881
|
+
}).catch((reason) => {
|
|
882
|
+
if (generationRef.current === generation) setStatus(errorText(reason));
|
|
883
|
+
});
|
|
884
|
+
},
|
|
885
|
+
children: "Send to AI"
|
|
886
|
+
})]
|
|
887
|
+
})
|
|
888
|
+
]
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewLauncher.tsx
|
|
893
|
+
function sourceKey(path) {
|
|
894
|
+
let hash = 2166136261;
|
|
895
|
+
for (const character of path) hash = Math.imul(hash ^ character.codePointAt(0), 16777619);
|
|
896
|
+
return (hash >>> 0).toString(36);
|
|
897
|
+
}
|
|
898
|
+
function storyPreviewTab(seed) {
|
|
899
|
+
const sourcePath = seed?.source?.path;
|
|
900
|
+
return {
|
|
901
|
+
id: sourcePath === void 0 ? "style-system-story-preview" : `style-system-story-preview-${sourceKey(sourcePath)}`,
|
|
902
|
+
label: "Story preview",
|
|
903
|
+
panel: (props) => /* @__PURE__ */ jsx(StoryPreviewPanel, {
|
|
904
|
+
...props,
|
|
905
|
+
seed
|
|
906
|
+
}),
|
|
907
|
+
retainComposer: true
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/extensions/workspaces/sessions/(frontend)/fill/StyleSystem.author.preview-action.web.tsx
|
|
912
|
+
var StyleSystem_author_preview_action_web_default = defineFill({
|
|
913
|
+
slot: "author.preview-action",
|
|
914
|
+
id: "style-system",
|
|
915
|
+
data: {
|
|
916
|
+
version: 1,
|
|
917
|
+
label: "Preview",
|
|
918
|
+
detail: "Build, inspect, refresh, and send source-targeted visual feedback.",
|
|
919
|
+
supportsSource: (source) => source.kind === "story-preview",
|
|
920
|
+
embeddedPanel: StoryPreviewPanel,
|
|
921
|
+
createTab: ({ source }) => storyPreviewTab(source === void 0 ? void 0 : { source })
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region generated/web.ts
|
|
926
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
927
|
+
...identity,
|
|
928
|
+
...value
|
|
929
|
+
};
|
|
930
|
+
const webPlugin = defineWebPlugin({
|
|
931
|
+
id: "style-system",
|
|
932
|
+
session: { get fills() {
|
|
933
|
+
return [via({
|
|
934
|
+
slot: "author.preview-action",
|
|
935
|
+
id: "style-system"
|
|
936
|
+
}, StyleSystem_author_preview_action_web_default)];
|
|
937
|
+
} }
|
|
938
|
+
});
|
|
939
|
+
//#endregion
|
|
940
|
+
export { webPlugin };
|
|
941
|
+
|
|
942
|
+
//# sourceMappingURL=web.mjs.map
|