@agimon-ai/doompi-style-system 0.0.1-alpha.77 → 0.0.1-alpha.78
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/dist/extensions/web.mjs +153 -5
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/src/services/storyPreview/index.cjs +83 -9
- package/dist/src/services/storyPreview/index.cjs.map +1 -1
- package/dist/src/services/storyPreview/index.d.cts +2 -2
- package/dist/src/services/storyPreview/index.d.cts.map +1 -1
- package/dist/src/services/storyPreview/index.d.mts +2 -2
- package/dist/src/services/storyPreview/index.d.mts.map +1 -1
- package/dist/src/services/storyPreview/index.mjs +82 -9
- package/dist/src/services/storyPreview/index.mjs.map +1 -1
- package/dist/src/services/storyPreview/type.d.cts +7 -1
- package/dist/src/services/storyPreview/type.d.cts.map +1 -1
- package/dist/src/services/storyPreview/type.d.mts +7 -1
- package/dist/src/services/storyPreview/type.d.mts.map +1 -1
- package/dist/src/types/previewApi.d.cts +3 -0
- package/dist/src/types/previewApi.d.cts.map +1 -1
- package/dist/src/types/previewApi.d.mts +3 -0
- package/dist/src/types/previewApi.d.mts.map +1 -1
- package/package.json +5 -5
package/dist/extensions/web.mjs
CHANGED
|
@@ -119,6 +119,18 @@ function normalizedAnnotationRect(start, end) {
|
|
|
119
119
|
height: Math.abs(end.y - start.y)
|
|
120
120
|
};
|
|
121
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
|
+
}
|
|
122
134
|
//#endregion
|
|
123
135
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewPanel.tsx
|
|
124
136
|
function imageDownload(data, storyExport) {
|
|
@@ -140,7 +152,7 @@ function preferredExport(exports) {
|
|
|
140
152
|
function errorText(reason) {
|
|
141
153
|
return reason instanceof Error ? reason.message : String(reason);
|
|
142
154
|
}
|
|
143
|
-
function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
155
|
+
function StoryPreviewPanel({ sessionId, submitCapture, seed, source, activeTool = "select", displayedAnnotations = [], pendingCandidate = false, onAnnotationCandidate }) {
|
|
144
156
|
const contextualSource = source ?? seed?.source;
|
|
145
157
|
const sourcePath = contextualSource?.path ?? "";
|
|
146
158
|
const sourceDirty = contextualSource?.hasUnsavedChanges === true;
|
|
@@ -166,6 +178,7 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
166
178
|
const builtRequestRef = useRef(void 0);
|
|
167
179
|
const workingRef = useRef(false);
|
|
168
180
|
const dirtyRef = useRef(sourceDirty);
|
|
181
|
+
const annotationExportRef = useRef("");
|
|
169
182
|
dirtyRef.current = sourceDirty;
|
|
170
183
|
const request = useMemo(() => ({
|
|
171
184
|
appPath,
|
|
@@ -344,13 +357,74 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
344
357
|
setAnnotationRect(void 0);
|
|
345
358
|
setStatus("Annotation image ready. Drag a region or comment on the whole preview.");
|
|
346
359
|
}
|
|
360
|
+
useEffect(() => {
|
|
361
|
+
if (onAnnotationCandidate === void 0) return;
|
|
362
|
+
if (activeTool === "select") {
|
|
363
|
+
annotationExportRef.current = "";
|
|
364
|
+
setAnnotationImage(void 0);
|
|
365
|
+
setAnnotationStart(void 0);
|
|
366
|
+
setAnnotationRect(void 0);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (preview === void 0 || annotationImage !== void 0 || working || annotationExportRef.current === preview.handle) return;
|
|
370
|
+
const generation = generationRef.current;
|
|
371
|
+
image().then((result) => {
|
|
372
|
+
if (result === void 0 || generationRef.current !== generation) return;
|
|
373
|
+
annotationExportRef.current = preview.handle;
|
|
374
|
+
setAnnotationImage(result);
|
|
375
|
+
setStatus("Frozen source-backed image ready for Author annotations.");
|
|
376
|
+
});
|
|
377
|
+
}, [
|
|
378
|
+
activeTool,
|
|
379
|
+
annotationImage,
|
|
380
|
+
image,
|
|
381
|
+
onAnnotationCandidate,
|
|
382
|
+
preview,
|
|
383
|
+
working
|
|
384
|
+
]);
|
|
347
385
|
function updateAnnotation(event) {
|
|
348
386
|
if (annotationStart === void 0) return;
|
|
349
387
|
const end = point(event);
|
|
350
388
|
setAnnotationRect(normalizedAnnotationRect(annotationStart, end));
|
|
351
389
|
}
|
|
390
|
+
function authorCandidate(location, rect) {
|
|
391
|
+
if (annotationImage === void 0 || onAnnotationCandidate === void 0 || builtRequest === void 0) return;
|
|
392
|
+
const binary = atob(annotationImage.data);
|
|
393
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
394
|
+
const evidence = new Blob([bytes], { type: annotationImage.mimeType });
|
|
395
|
+
const thumbnailUrl = URL.createObjectURL(evidence);
|
|
396
|
+
try {
|
|
397
|
+
onAnnotationCandidate({
|
|
398
|
+
mode: rect === void 0 ? "point" : "region",
|
|
399
|
+
point: location,
|
|
400
|
+
...rect === void 0 ? {} : { rect },
|
|
401
|
+
preview: {
|
|
402
|
+
version: 1,
|
|
403
|
+
provider: "style-system",
|
|
404
|
+
projectPath: builtRequest.appPath,
|
|
405
|
+
storyPath: annotationImage.storyPath,
|
|
406
|
+
storyExport: annotationImage.storyExport,
|
|
407
|
+
buildRevision: annotationImage.sourceSha256,
|
|
408
|
+
viewport: {
|
|
409
|
+
width: annotationImage.width,
|
|
410
|
+
height: annotationImage.height
|
|
411
|
+
},
|
|
412
|
+
sources: [{
|
|
413
|
+
path: annotationImage.storyPath,
|
|
414
|
+
sha256: annotationImage.sourceSha256
|
|
415
|
+
}]
|
|
416
|
+
},
|
|
417
|
+
evidence,
|
|
418
|
+
thumbnailUrl
|
|
419
|
+
});
|
|
420
|
+
} catch (reason) {
|
|
421
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
422
|
+
setStatus(errorText(reason));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
352
425
|
const multipleExports = storyExports !== void 0 && storyExports.length > 1;
|
|
353
426
|
const previewVisible = preview !== void 0;
|
|
427
|
+
const authorAnnotationActive = onAnnotationCandidate !== void 0 && activeTool !== "select";
|
|
354
428
|
return /* @__PURE__ */ jsxs("section", {
|
|
355
429
|
className: "flex h-full min-h-0 min-w-0 flex-1 flex-col gap-3 p-3",
|
|
356
430
|
"data-testid": "style-system-preview-panel",
|
|
@@ -447,12 +521,12 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
447
521
|
onClick: () => void image().then((result) => result && imageDownload(result.data, result.storyExport)),
|
|
448
522
|
children: "Export PNG"
|
|
449
523
|
}),
|
|
450
|
-
/* @__PURE__ */ jsx(Button, {
|
|
524
|
+
onAnnotationCandidate === void 0 ? /* @__PURE__ */ jsx(Button, {
|
|
451
525
|
variant: "outline",
|
|
452
526
|
disabled: preview === void 0 || working,
|
|
453
527
|
onClick: () => void prepareAnnotation(),
|
|
454
528
|
children: "Prepare annotation"
|
|
455
|
-
}),
|
|
529
|
+
}) : null,
|
|
456
530
|
/* @__PURE__ */ jsxs("label", {
|
|
457
531
|
className: "flex items-center gap-1 text-xs text-doom-dim",
|
|
458
532
|
children: [/* @__PURE__ */ jsx("input", {
|
|
@@ -476,6 +550,80 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
476
550
|
preview === void 0 ? /* @__PURE__ */ jsx("div", {
|
|
477
551
|
className: "grid min-h-48 place-items-center rounded border border-dashed border-doom-border text-sm text-doom-faint",
|
|
478
552
|
children: "No preview built."
|
|
553
|
+
}) : authorAnnotationActive && annotationImage !== void 0 ? /* @__PURE__ */ jsx("div", {
|
|
554
|
+
className: "grid min-h-0 flex-1 place-items-center overflow-hidden rounded border border-doom-border bg-doom-deep",
|
|
555
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
556
|
+
className: "relative max-h-full w-fit max-w-full touch-none overflow-hidden",
|
|
557
|
+
"aria-label": activeTool === "comment" ? "Click to place an annotation point" : "Drag to mark an annotation region",
|
|
558
|
+
"data-testid": "style-system-author-annotation-overlay",
|
|
559
|
+
onPointerDown: (event) => {
|
|
560
|
+
if (pendingCandidate || activeTool !== "mark" && activeTool !== "comment") return;
|
|
561
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
562
|
+
const start = point(event);
|
|
563
|
+
setAnnotationStart(start);
|
|
564
|
+
setAnnotationRect(activeTool === "mark" ? {
|
|
565
|
+
...start,
|
|
566
|
+
width: 0,
|
|
567
|
+
height: 0
|
|
568
|
+
} : void 0);
|
|
569
|
+
},
|
|
570
|
+
onPointerMove: (event) => {
|
|
571
|
+
if (activeTool === "mark") updateAnnotation(event);
|
|
572
|
+
},
|
|
573
|
+
onPointerUp: (event) => {
|
|
574
|
+
const start = annotationStart;
|
|
575
|
+
if (start === void 0) return;
|
|
576
|
+
const end = point(event);
|
|
577
|
+
setAnnotationStart(void 0);
|
|
578
|
+
const location = previewAnnotationLocation(activeTool === "comment" ? "comment" : "mark", start, end);
|
|
579
|
+
if (location === null) return;
|
|
580
|
+
setAnnotationRect(location.mode === "region" ? location.rect : void 0);
|
|
581
|
+
authorCandidate(location.point, location.mode === "region" ? location.rect : void 0);
|
|
582
|
+
},
|
|
583
|
+
onPointerCancel: () => {
|
|
584
|
+
setAnnotationStart(void 0);
|
|
585
|
+
setAnnotationRect(void 0);
|
|
586
|
+
},
|
|
587
|
+
children: [
|
|
588
|
+
/* @__PURE__ */ jsx("img", {
|
|
589
|
+
className: "block max-h-full max-w-full select-none",
|
|
590
|
+
src: `data:${annotationImage.mimeType};base64,${annotationImage.data}`,
|
|
591
|
+
alt: "Frozen source-backed story render for Author annotation",
|
|
592
|
+
draggable: false
|
|
593
|
+
}),
|
|
594
|
+
displayedAnnotations.map((annotation) => annotation.mode === "point" || annotation.rect === void 0 ? /* @__PURE__ */ jsx("span", {
|
|
595
|
+
"data-author-point": annotation.ordinal,
|
|
596
|
+
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",
|
|
597
|
+
style: {
|
|
598
|
+
left: `${String(annotation.point.x * 100)}%`,
|
|
599
|
+
top: `${String(annotation.point.y * 100)}%`
|
|
600
|
+
},
|
|
601
|
+
children: annotation.ordinal
|
|
602
|
+
}, annotation.ordinal) : /* @__PURE__ */ jsx("div", {
|
|
603
|
+
"data-author-region": annotation.ordinal,
|
|
604
|
+
className: "pointer-events-none absolute border border-doom-yellow bg-doom-yellow/10",
|
|
605
|
+
style: {
|
|
606
|
+
left: `${String(annotation.rect.x * 100)}%`,
|
|
607
|
+
top: `${String(annotation.rect.y * 100)}%`,
|
|
608
|
+
width: `${String(annotation.rect.width * 100)}%`,
|
|
609
|
+
height: `${String(annotation.rect.height * 100)}%`
|
|
610
|
+
},
|
|
611
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
612
|
+
className: "bg-doom-yellow text-doom-deep",
|
|
613
|
+
children: annotation.ordinal
|
|
614
|
+
})
|
|
615
|
+
}, annotation.ordinal)),
|
|
616
|
+
annotationRect === void 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
617
|
+
className: "pointer-events-none absolute border-2 border-red-500 bg-red-500/10",
|
|
618
|
+
style: {
|
|
619
|
+
left: `${String(annotationRect.x * 100)}%`,
|
|
620
|
+
top: `${String(annotationRect.y * 100)}%`,
|
|
621
|
+
width: `${String(annotationRect.width * 100)}%`,
|
|
622
|
+
height: `${String(annotationRect.height * 100)}%`
|
|
623
|
+
}
|
|
624
|
+
})
|
|
625
|
+
]
|
|
626
|
+
})
|
|
479
627
|
}) : /* @__PURE__ */ jsx("iframe", {
|
|
480
628
|
title: `${preview.storyExport} story preview`,
|
|
481
629
|
sandbox: "allow-scripts",
|
|
@@ -483,7 +631,7 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
483
631
|
srcDoc: isolatedPreviewHtml(preview.html),
|
|
484
632
|
className: "min-h-0 flex-1 rounded border border-doom-border bg-white"
|
|
485
633
|
}, preview.handle),
|
|
486
|
-
annotationImage === void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
634
|
+
annotationImage === void 0 || onAnnotationCandidate !== void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
487
635
|
className: "grid gap-2",
|
|
488
636
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
489
637
|
className: "relative max-h-80 w-fit max-w-full touch-none overflow-hidden rounded border border-doom-border",
|
|
@@ -522,7 +670,7 @@ function StoryPreviewPanel({ sessionId, submitCapture, seed, source }) {
|
|
|
522
670
|
children: "This image is rebuilt from source and does not preserve transient iframe interactions."
|
|
523
671
|
})]
|
|
524
672
|
}),
|
|
525
|
-
preview === void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
673
|
+
preview === void 0 || onAnnotationCandidate !== void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
526
674
|
className: "flex gap-2",
|
|
527
675
|
children: [/* @__PURE__ */ jsxs("label", {
|
|
528
676
|
className: "grid min-w-0 flex-1 gap-1 text-xs text-doom-dim",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.mjs","names":["routes","fillStyleSystem"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/previewApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/previewFrame.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewLauncher.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/StyleSystem.author.preview-action.web.tsx","../../generated/web.ts"],"sourcesContent":["import { apiResponse, defineApiRoutes } from '@agimon-ai/doompi-core/web';\n\nimport type {\n BuildStoryPreviewView,\n DisposeStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataView,\n} from './previewApi';\n\nexport default defineApiRoutes({\n metadata: {\n method: 'POST',\n path: '/metadata',\n response: apiResponse<StoryPreviewMetadataView>(),\n },\n build: {\n method: 'POST',\n path: '/build',\n response: apiResponse<BuildStoryPreviewView>(),\n },\n image: {\n method: 'POST',\n path: '/image',\n response: apiResponse<ExportStoryPreviewImageView>(),\n },\n dispose: {\n method: 'DELETE',\n path: '/artifact',\n response: apiResponse<DisposeStoryPreviewView>(),\n },\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/doompi-core/web';\nimport { sealedTransport } from '@agimon-ai/doompi-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'style-system-preview' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'style-system-preview',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","import { api } from '../../../../../../generated/client';\nimport type {\n BuildStoryPreviewRequest,\n BuildStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataRequest,\n StoryPreviewMetadataView,\n} from '../../../../../types/previewApi';\n\nfunction errorMessage(result: { status: number; error: string }): string {\n if (result.status === 0) return 'The preview service is unreachable.';\n return result.error === '' ? `The preview service answered ${String(result.status)}.` : result.error;\n}\n\nexport async function storyMetadata(\n sessionId: string,\n request: StoryPreviewMetadataRequest,\n): Promise<{ ok: true; metadata: StoryPreviewMetadataView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).metadata({ body: request });\n return result.ok ? { ok: true, metadata: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function buildPreview(\n sessionId: string,\n request: BuildStoryPreviewRequest,\n): Promise<{ ok: true; preview: BuildStoryPreviewView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).build({ body: request });\n return result.ok ? { ok: true, preview: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function exportPreviewImage(\n sessionId: string,\n request: BuildStoryPreviewRequest,\n): Promise<{ ok: true; image: ExportStoryPreviewImageView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).image({ body: request });\n return result.ok ? { ok: true, image: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function disposePreview(sessionId: string, handle: string): Promise<void> {\n await api.session(sessionId).dispose({ body: { handle } });\n}\n","const PREVIEW_CSP =\n \"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'\";\n\nexport interface PreviewPoint {\n x: number;\n y: number;\n}\n\nexport interface PreviewAnnotationRect extends PreviewPoint {\n width: number;\n height: number;\n}\n\nfunction findHeadInsertion(html: string): number | undefined {\n const lower = html.toLowerCase();\n let index = 0;\n while (index < html.length) {\n if (lower.startsWith('<!--', index)) {\n const end = lower.indexOf('-->', index + 4);\n index = end < 0 ? html.length : end + 3;\n continue;\n }\n if (html[index] !== '<') {\n index += 1;\n continue;\n }\n const tag = html.slice(index).match(/^<([a-z][a-z0-9:-]*)(?:\\s[^>]*)?>/i);\n if (tag === null) {\n index += 1;\n continue;\n }\n const name = tag[1]!.toLowerCase();\n const end = index + tag[0].length;\n if (name === 'head') return end;\n if (name === 'script' || name === 'style' || name === 'template' || name === 'textarea' || name === 'title') {\n const closing = lower.indexOf(`</${name}`, end);\n index = closing < 0 ? html.length : closing;\n continue;\n }\n index = end;\n }\n return undefined;\n}\n\nexport function isolatedPreviewHtml(html: string): string {\n const policy = `<meta http-equiv=\"Content-Security-Policy\" content=\"${PREVIEW_CSP}\">`;\n const headInsertion = findHeadInsertion(html);\n if (headInsertion !== undefined) return `${html.slice(0, headInsertion)}${policy}${html.slice(headInsertion)}`;\n const doctype = html.match(/^\\s*<!doctype[^>]*>/i);\n if (doctype !== null) return `${doctype[0]}<head>${policy}</head>${html.slice(doctype[0].length)}`;\n return `<head>${policy}</head>${html}`;\n}\n\nexport function normalizedAnnotationRect(start: PreviewPoint, end: PreviewPoint): PreviewAnnotationRect {\n return {\n x: Math.min(start.x, end.x),\n y: Math.min(start.y, end.y),\n width: Math.abs(end.x - start.x),\n height: Math.abs(end.y - start.y),\n };\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/doompi-core/web';\nimport { Button } from '@agimon-ai/doompi-web-components';\nimport { useCallback, useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from 'react';\n\nimport type {\n BuildStoryPreviewRequest,\n BuildStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataView,\n StoryPreviewSeed,\n} from '../../../../../types/previewApi';\nimport { buildPreview, disposePreview, exportPreviewImage, storyMetadata } from '../_lib/previewApi';\nimport {\n isolatedPreviewHtml,\n normalizedAnnotationRect,\n type PreviewAnnotationRect,\n type PreviewPoint,\n} from '../_lib/previewFrame';\n\nfunction imageDownload(data: string, storyExport: string): void {\n const link = document.createElement('a');\n link.href = `data:image/png;base64,${data}`;\n link.download = `${storyExport.replace(/[^A-Za-z0-9_-]/g, '-')}.png`;\n link.click();\n}\n\nfunction point(event: ReactPointerEvent<HTMLElement>): PreviewPoint {\n const bounds = event.currentTarget.getBoundingClientRect();\n return {\n x: Math.min(1, Math.max(0, (event.clientX - bounds.left) / bounds.width)),\n y: Math.min(1, Math.max(0, (event.clientY - bounds.top) / bounds.height)),\n };\n}\n\ninterface StoryPreviewSource {\n path: string;\n hasUnsavedChanges: boolean;\n kind?: string;\n revision?: number;\n sourceSha256?: string;\n}\n\ninterface StoryPreviewPanelProps extends WebPluginSlotProps {\n seed?: StoryPreviewSeed;\n source?: StoryPreviewSource;\n}\n\nfunction preferredExport(exports: readonly StoryPreviewMetadataView['exports'][number][]): string {\n return exports.find((entry) => entry.exportName === 'Playground')?.exportName ?? exports[0]?.exportName ?? '';\n}\n\nfunction errorText(reason: unknown): string {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\nexport function StoryPreviewPanel({ sessionId, submitCapture, seed, source }: StoryPreviewPanelProps) {\n const contextualSource = source ?? seed?.source;\n const sourcePath = contextualSource?.path ?? '';\n const sourceDirty = contextualSource?.hasUnsavedChanges === true;\n const sourceRevision = contextualSource?.revision ?? 0;\n const sourceSha256 = contextualSource?.sourceSha256 ?? '';\n const contextual = contextualSource !== undefined;\n const [appPath, setAppPath] = useState(contextual ? '' : '.');\n const [storyPath, setStoryPath] = useState(sourcePath);\n const [storyExport, setStoryExport] = useState(contextual ? '' : 'Playground');\n const [storyExports, setStoryExports] = useState<\n readonly StoryPreviewMetadataView['exports'][number][] | undefined\n >();\n const [darkMode, setDarkMode] = useState(false);\n const [preview, setPreview] = useState<BuildStoryPreviewView | undefined>(undefined);\n const [builtRequest, setBuiltRequest] = useState<BuildStoryPreviewRequest | undefined>(undefined);\n const [annotationImage, setAnnotationImage] = useState<ExportStoryPreviewImageView | undefined>(undefined);\n const [annotationStart, setAnnotationStart] = useState<PreviewPoint | undefined>(undefined);\n const [annotationRect, setAnnotationRect] = useState<PreviewAnnotationRect | undefined>(undefined);\n const [feedback, setFeedback] = useState('');\n const [status, setStatus] = useState(\n contextual\n ? sourceDirty\n ? 'Loading story metadata. Preview uses saved source.'\n : 'Loading story metadata…'\n : 'Choose an exact story file and export.',\n );\n const [working, setWorking] = useState(false);\n const generationRef = useRef(0);\n const operationRef = useRef(0);\n const previewRef = useRef<BuildStoryPreviewView | undefined>(undefined);\n const builtRequestRef = useRef<BuildStoryPreviewRequest | undefined>(undefined);\n const workingRef = useRef(false);\n const dirtyRef = useRef(sourceDirty);\n\n dirtyRef.current = sourceDirty;\n\n const request = useMemo<BuildStoryPreviewRequest>(\n () => ({ appPath, storyPath, storyExport, darkMode }),\n [appPath, storyPath, storyExport, darkMode],\n );\n\n useEffect(() => {\n operationRef.current += 1;\n if (!workingRef.current) return;\n workingRef.current = false;\n setWorking(false);\n }, [request]);\n\n useEffect(() => {\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n operationRef.current += 1;\n workingRef.current = false;\n setWorking(false);\n setStoryExports(undefined);\n setAppPath(contextual ? '' : '.');\n setStoryPath(sourcePath);\n setStoryExport(contextual ? '' : 'Playground');\n builtRequestRef.current = undefined;\n setBuiltRequest(undefined);\n setAnnotationImage(undefined);\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n\n if (sessionId === null || sourcePath === '') {\n setPreview(undefined);\n previewRef.current = undefined;\n return () => {\n if (generationRef.current === generation) generationRef.current += 1;\n };\n }\n\n const previous = previewRef.current;\n previewRef.current = undefined;\n setPreview(undefined);\n if (previous !== undefined) void disposePreview(sessionId, previous.handle);\n\n let current = true;\n setStatus(sourceDirty ? 'Loading story metadata. Preview uses saved source.' : 'Loading story metadata…');\n void storyMetadata(sessionId, { storyPath: sourcePath })\n .then((result) => {\n if (!current || generationRef.current !== generation) return;\n if (!result.ok) {\n setStoryExports(undefined);\n setStatus(result.error);\n return;\n }\n const exports = result.metadata.exports;\n setAppPath(result.metadata.appPath);\n setStoryPath(result.metadata.storyPath);\n setStoryExports(exports);\n setStoryExport(preferredExport(exports));\n if (exports.length === 0) {\n setStatus('No story exports were found in this file.');\n } else if (dirtyRef.current) {\n setStatus('Save changes to preview. Preview uses saved source.');\n } else if (exports.length === 1) {\n setStatus('Story found. Building preview…');\n } else {\n setStatus('Story found. Building the Playground export…');\n }\n })\n .catch((reason: unknown) => {\n if (current && generationRef.current === generation) setStatus(errorText(reason));\n });\n\n return () => {\n current = false;\n if (generationRef.current === generation) generationRef.current += 1;\n operationRef.current += 1;\n const ownedPreview = previewRef.current;\n previewRef.current = undefined;\n if (ownedPreview !== undefined) void disposePreview(sessionId, ownedPreview.handle);\n };\n }, [contextual, sessionId, sourcePath, sourceRevision, sourceSha256]);\n\n useEffect(() => {\n if (!contextual || !sourceDirty) return;\n setStatus('Save changes to preview. Preview uses saved source.');\n }, [contextual, sourceDirty]);\n\n const rebuild = useCallback(async (): Promise<boolean> => {\n if (\n sessionId === null ||\n workingRef.current ||\n request.appPath.trim() === '' ||\n request.storyPath.trim() === '' ||\n request.storyExport.trim() === ''\n )\n return false;\n if (dirtyRef.current) {\n setStatus('Save changes to preview. Preview uses saved source.');\n return false;\n }\n\n const generation = generationRef.current;\n const operation = operationRef.current + 1;\n operationRef.current = operation;\n const requestSessionId = sessionId;\n workingRef.current = true;\n setWorking(true);\n setStatus('Building preview…');\n try {\n const result = await buildPreview(requestSessionId, request);\n if (generationRef.current !== generation || operationRef.current !== operation) {\n if (result.ok) void disposePreview(requestSessionId, result.preview.handle);\n return false;\n }\n if (!result.ok) {\n setStatus(result.error);\n return false;\n }\n const previous = previewRef.current;\n previewRef.current = result.preview;\n builtRequestRef.current = request;\n setPreview(result.preview);\n setBuiltRequest(request);\n setAnnotationImage(undefined);\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n if (previous !== undefined && previous.handle !== result.preview.handle) {\n void disposePreview(requestSessionId, previous.handle);\n }\n setStatus(\n dirtyRef.current\n ? 'Preview ready from saved source. Save changes to refresh.'\n : 'Preview ready. Refresh after source edits.',\n );\n return true;\n } catch (reason: unknown) {\n if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));\n return false;\n } finally {\n if (generationRef.current === generation && operationRef.current === operation) {\n workingRef.current = false;\n setWorking(false);\n }\n }\n }, [request, sessionId]);\n\n useEffect(() => {\n if (\n !contextual ||\n sessionId === null ||\n sourceDirty ||\n storyExports === undefined ||\n storyExport.trim() === '' ||\n appPath.trim() === '' ||\n storyPath.trim() === ''\n )\n return;\n void rebuild();\n }, [appPath, contextual, rebuild, sessionId, sourceDirty, storyExport, storyExports, storyPath]);\n\n const image = useCallback(async (): Promise<ExportStoryPreviewImageView | undefined> => {\n const currentRequest = builtRequestRef.current;\n if (sessionId === null || workingRef.current || currentRequest === undefined) return undefined;\n const generation = generationRef.current;\n const operation = operationRef.current + 1;\n operationRef.current = operation;\n const requestSessionId = sessionId;\n workingRef.current = true;\n setWorking(true);\n setStatus('Rendering a fresh source-backed image…');\n try {\n const result = await exportPreviewImage(requestSessionId, currentRequest);\n if (generationRef.current !== generation || operationRef.current !== operation) return undefined;\n if (!result.ok) {\n setStatus(result.error);\n return undefined;\n }\n setStatus('Image rendered from the current source. Transient iframe interactions are not included.');\n return result.image;\n } catch (reason: unknown) {\n if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));\n return undefined;\n } finally {\n if (generationRef.current === generation && operationRef.current === operation) {\n workingRef.current = false;\n setWorking(false);\n }\n }\n }, [sessionId]);\n\n async function prepareAnnotation(): Promise<void> {\n const generation = generationRef.current;\n const result = await image();\n if (result === undefined || generationRef.current !== generation) return;\n setAnnotationImage(result);\n setAnnotationRect(undefined);\n setStatus('Annotation image ready. Drag a region or comment on the whole preview.');\n }\n\n function updateAnnotation(event: ReactPointerEvent<HTMLDivElement>): void {\n if (annotationStart === undefined) return;\n const end = point(event);\n setAnnotationRect(normalizedAnnotationRect(annotationStart, end));\n }\n\n const multipleExports = storyExports !== undefined && storyExports.length > 1;\n const previewVisible = preview !== undefined;\n\n return (\n <section className=\"flex h-full min-h-0 min-w-0 flex-1 flex-col gap-3 p-3\" data-testid=\"style-system-preview-panel\">\n {contextual ? (\n <header className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-doom-dim\">\n <span>\n Story: <strong className=\"text-doom-text\">{storyPath || sourcePath}</strong>\n </span>\n <span>\n Project: <strong className=\"text-doom-text\">{appPath || 'resolving…'}</strong>\n </span>\n {multipleExports ? (\n <label className=\"flex items-center gap-1\">\n Export\n <select\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n aria-label=\"story export\"\n onChange={(event) => {\n setStoryExport(event.target.value);\n builtRequestRef.current = undefined;\n setBuiltRequest(undefined);\n setAnnotationImage(undefined);\n setAnnotationRect(undefined);\n }}\n >\n {storyExports.map((entry) => (\n <option key={entry.exportName} value={entry.exportName}>\n {entry.label ?? entry.exportName}\n </option>\n ))}\n </select>\n </label>\n ) : storyExport !== '' ? (\n <span>\n Export: <strong className=\"text-doom-text\">{storyExport}</strong>\n </span>\n ) : null}\n </header>\n ) : (\n <header className=\"grid gap-2 md:grid-cols-3\">\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Project path\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={appPath}\n onChange={(event) => setAppPath(event.target.value)}\n />\n </label>\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Story file\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n placeholder=\"packages/ui/Button.stories.tsx\"\n value={storyPath}\n onChange={(event) => {\n setStoryPath(event.target.value);\n setStoryExports(undefined);\n }}\n />\n </label>\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Story export\n {storyExports === undefined ? (\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n onChange={(event) => setStoryExport(event.target.value)}\n />\n ) : (\n <select\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n onChange={(event) => setStoryExport(event.target.value)}\n >\n <option value=\"\">Choose an export</option>\n {storyExports.map((entry) => (\n <option key={entry.exportName} value={entry.exportName}>\n {entry.label ?? entry.exportName}\n </option>\n ))}\n </select>\n )}\n </label>\n </header>\n )}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n disabled={\n sessionId === null ||\n working ||\n sourceDirty ||\n request.storyPath.trim() === '' ||\n request.storyExport.trim() === ''\n }\n onClick={() => void rebuild()}\n >\n {previewVisible ? 'Refresh preview' : 'Build preview'}\n </Button>\n <Button\n variant=\"outline\"\n disabled={preview === undefined || working}\n onClick={() => void image().then((result) => result && imageDownload(result.data, result.storyExport))}\n >\n Export PNG\n </Button>\n <Button variant=\"outline\" disabled={preview === undefined || working} onClick={() => void prepareAnnotation()}>\n Prepare annotation\n </Button>\n <label className=\"flex items-center gap-1 text-xs text-doom-dim\">\n <input type=\"checkbox\" checked={darkMode} onChange={(event) => setDarkMode(event.target.checked)} /> Dark mode\n </label>\n <output className=\"text-xs text-doom-dim\" aria-live=\"polite\">\n {status}\n </output>\n </div>\n {sourceDirty ? (\n <p data-testid=\"style-system-preview-dirty\" className=\"text-xs text-doom-yellow\">\n Save changes to preview. Preview uses saved source.\n </p>\n ) : null}\n {preview === undefined ? (\n <div className=\"grid min-h-48 place-items-center rounded border border-dashed border-doom-border text-sm text-doom-faint\">\n No preview built.\n </div>\n ) : (\n <iframe\n key={preview.handle}\n title={`${preview.storyExport} story preview`}\n sandbox=\"allow-scripts\"\n referrerPolicy=\"no-referrer\"\n srcDoc={isolatedPreviewHtml(preview.html)}\n className=\"min-h-0 flex-1 rounded border border-doom-border bg-white\"\n />\n )}\n {annotationImage === undefined ? null : (\n <div className=\"grid gap-2\">\n <div\n className=\"relative max-h-80 w-fit max-w-full touch-none overflow-hidden rounded border border-doom-border\"\n aria-label=\"Drag to mark an annotation region\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n const start = point(event);\n setAnnotationStart(start);\n setAnnotationRect({ ...start, width: 0, height: 0 });\n }}\n onPointerMove={updateAnnotation}\n onPointerUp={(event) => {\n updateAnnotation(event);\n setAnnotationStart(undefined);\n }}\n >\n <img\n className=\"block max-h-80 max-w-full select-none\"\n src={`data:${annotationImage.mimeType};base64,${annotationImage.data}`}\n alt=\"Fresh source-backed story render for annotation\"\n draggable={false}\n />\n {annotationRect === undefined ? null : (\n <div\n className=\"pointer-events-none absolute border-2 border-red-500 bg-red-500/10\"\n style={{\n left: `${String(annotationRect.x * 100)}%`,\n top: `${String(annotationRect.y * 100)}%`,\n width: `${String(annotationRect.width * 100)}%`,\n height: `${String(annotationRect.height * 100)}%`,\n }}\n />\n )}\n </div>\n <p className=\"text-xs text-doom-faint\">\n This image is rebuilt from source and does not preserve transient iframe interactions.\n </p>\n </div>\n )}\n {preview === undefined ? null : (\n <div className=\"flex gap-2\">\n <label className=\"grid min-w-0 flex-1 gap-1 text-xs text-doom-dim\">\n AI design feedback\n <textarea\n className=\"min-h-20 rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={feedback}\n onChange={(event) => setFeedback(event.target.value)}\n />\n </label>\n <Button\n className=\"self-end\"\n variant=\"outline\"\n disabled={working || submitCapture === undefined || feedback.trim() === '' || annotationImage === undefined}\n onClick={() => {\n if (submitCapture === undefined || annotationImage === undefined) return;\n const location =\n annotationRect === undefined\n ? 'Whole preview'\n : `Region: x=${annotationRect.x.toFixed(3)}, y=${annotationRect.y.toFixed(3)}, width=${annotationRect.width.toFixed(3)}, height=${annotationRect.height.toFixed(3)}`;\n const generation = generationRef.current;\n void submitCapture({\n ...annotationImage,\n context: {\n kind: 'author-capture',\n source: 'style-system',\n id: crypto.randomUUID(),\n label: `${annotationImage.storyPath}#${annotationImage.storyExport}`,\n content: [\n 'Edit the underlying story or component source, never the generated preview HTML.',\n `Project: ${builtRequest?.appPath ?? appPath}`,\n `Story: ${annotationImage.storyPath}#${annotationImage.storyExport}`,\n `Story source sha256: ${annotationImage.sourceSha256}`,\n `Source-backed image revision: ${annotationImage.sourceSha256}`,\n location,\n `Feedback: ${feedback.trim()}`,\n 'After editing, rebuild this preview before treating it as visually verified.',\n ].join('\\n'),\n },\n })\n .then(() => {\n if (generationRef.current !== generation) return;\n setStatus('Feedback and the frozen source-backed annotation image were submitted to the agent.');\n setFeedback('');\n })\n .catch((reason: unknown) => {\n if (generationRef.current === generation) setStatus(errorText(reason));\n });\n }}\n >\n Send to AI\n </Button>\n </div>\n )}\n </section>\n );\n}\n","import type { TransientTab } from '@agimon-ai/doompi-core/web';\n\nimport type { StoryPreviewSeed } from '../../../../../types/previewApi';\nimport { StoryPreviewPanel } from './StoryPreviewPanel';\n\nfunction sourceKey(path: string): string {\n let hash = 2_166_136_261;\n for (const character of path) hash = Math.imul(hash ^ character.codePointAt(0)!, 16_777_619);\n return (hash >>> 0).toString(36);\n}\n\nexport function storyPreviewTab(seed?: StoryPreviewSeed): TransientTab {\n const sourcePath = seed?.source?.path;\n return {\n id: sourcePath === undefined ? 'style-system-story-preview' : `style-system-story-preview-${sourceKey(sourcePath)}`,\n label: 'Story preview',\n panel: (props) => <StoryPreviewPanel {...props} seed={seed} />,\n retainComposer: true,\n };\n}\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { storyPreviewTab } from '../_components/StoryPreviewLauncher';\nimport { StoryPreviewPanel } from '../_components/StoryPreviewPanel';\n\nexport default defineFill({\n slot: 'author.preview-action',\n id: 'style-system',\n data: {\n version: 1 as const,\n label: 'Preview',\n detail: 'Build, inspect, refresh, and send source-targeted visual feedback.',\n supportsSource: (source: { kind?: string }) => source.kind === 'story-preview',\n embeddedPanel: StoryPreviewPanel,\n createTab: ({ source }: { source?: { path: string; hasUnsavedChanges: boolean } }) =>\n storyPreviewTab(source === undefined ? undefined : { source }),\n },\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport fillStyleSystem from '../src/extensions/workspaces/sessions/(frontend)/fill/StyleSystem.author.preview-action.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'style-system',\n session: {\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'author.preview-action', id: 'style-system' }, fillStyleSystem)]; },\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"web.mjs","names":["routes","fillStyleSystem"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/previewApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/previewFrame.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/StoryPreviewLauncher.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/StyleSystem.author.preview-action.web.tsx","../../generated/web.ts"],"sourcesContent":["import { apiResponse, defineApiRoutes } from '@agimon-ai/doompi-core/web';\n\nimport type {\n BuildStoryPreviewView,\n DisposeStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataView,\n} from './previewApi';\n\nexport default defineApiRoutes({\n metadata: {\n method: 'POST',\n path: '/metadata',\n response: apiResponse<StoryPreviewMetadataView>(),\n },\n build: {\n method: 'POST',\n path: '/build',\n response: apiResponse<BuildStoryPreviewView>(),\n },\n image: {\n method: 'POST',\n path: '/image',\n response: apiResponse<ExportStoryPreviewImageView>(),\n },\n dispose: {\n method: 'DELETE',\n path: '/artifact',\n response: apiResponse<DisposeStoryPreviewView>(),\n },\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/doompi-core/web';\nimport { sealedTransport } from '@agimon-ai/doompi-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'style-system-preview' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'style-system-preview',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","import { api } from '../../../../../../generated/client';\nimport type {\n BuildStoryPreviewRequest,\n BuildStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataRequest,\n StoryPreviewMetadataView,\n} from '../../../../../types/previewApi';\n\nfunction errorMessage(result: { status: number; error: string }): string {\n if (result.status === 0) return 'The preview service is unreachable.';\n return result.error === '' ? `The preview service answered ${String(result.status)}.` : result.error;\n}\n\nexport async function storyMetadata(\n sessionId: string,\n request: StoryPreviewMetadataRequest,\n): Promise<{ ok: true; metadata: StoryPreviewMetadataView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).metadata({ body: request });\n return result.ok ? { ok: true, metadata: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function buildPreview(\n sessionId: string,\n request: BuildStoryPreviewRequest,\n): Promise<{ ok: true; preview: BuildStoryPreviewView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).build({ body: request });\n return result.ok ? { ok: true, preview: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function exportPreviewImage(\n sessionId: string,\n request: BuildStoryPreviewRequest,\n): Promise<{ ok: true; image: ExportStoryPreviewImageView } | { ok: false; error: string }> {\n const result = await api.session(sessionId).image({ body: request });\n return result.ok ? { ok: true, image: result.data } : { ok: false, error: errorMessage(result) };\n}\n\nexport async function disposePreview(sessionId: string, handle: string): Promise<void> {\n await api.session(sessionId).dispose({ body: { handle } });\n}\n","const PREVIEW_CSP =\n \"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'\";\n\nexport interface PreviewPoint {\n x: number;\n y: number;\n}\n\nexport interface PreviewAnnotationRect extends PreviewPoint {\n width: number;\n height: number;\n}\n\nfunction findHeadInsertion(html: string): number | undefined {\n const lower = html.toLowerCase();\n let index = 0;\n while (index < html.length) {\n if (lower.startsWith('<!--', index)) {\n const end = lower.indexOf('-->', index + 4);\n index = end < 0 ? html.length : end + 3;\n continue;\n }\n if (html[index] !== '<') {\n index += 1;\n continue;\n }\n const tag = html.slice(index).match(/^<([a-z][a-z0-9:-]*)(?:\\s[^>]*)?>/i);\n if (tag === null) {\n index += 1;\n continue;\n }\n const name = tag[1]!.toLowerCase();\n const end = index + tag[0].length;\n if (name === 'head') return end;\n if (name === 'script' || name === 'style' || name === 'template' || name === 'textarea' || name === 'title') {\n const closing = lower.indexOf(`</${name}`, end);\n index = closing < 0 ? html.length : closing;\n continue;\n }\n index = end;\n }\n return undefined;\n}\n\nexport function isolatedPreviewHtml(html: string): string {\n const policy = `<meta http-equiv=\"Content-Security-Policy\" content=\"${PREVIEW_CSP}\">`;\n const headInsertion = findHeadInsertion(html);\n if (headInsertion !== undefined) return `${html.slice(0, headInsertion)}${policy}${html.slice(headInsertion)}`;\n const doctype = html.match(/^\\s*<!doctype[^>]*>/i);\n if (doctype !== null) return `${doctype[0]}<head>${policy}</head>${html.slice(doctype[0].length)}`;\n return `<head>${policy}</head>${html}`;\n}\n\nexport function normalizedAnnotationRect(start: PreviewPoint, end: PreviewPoint): PreviewAnnotationRect {\n return {\n x: Math.min(start.x, end.x),\n y: Math.min(start.y, end.y),\n width: Math.abs(end.x - start.x),\n height: Math.abs(end.y - start.y),\n };\n}\n\nexport function previewAnnotationLocation(\n tool: 'mark' | 'comment',\n start: PreviewPoint,\n end: PreviewPoint,\n):\n | { mode: 'region'; point: PreviewPoint; rect: PreviewAnnotationRect }\n | { mode: 'point'; point: PreviewPoint }\n | null {\n if (tool === 'comment') return { mode: 'point', point: end };\n const rect = normalizedAnnotationRect(start, end);\n return rect.width === 0 || rect.height === 0 ? null : { mode: 'region', point: rect, rect };\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/doompi-core/web';\nimport { Button } from '@agimon-ai/doompi-web-components';\nimport { useCallback, useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from 'react';\n\nimport type {\n BuildStoryPreviewRequest,\n BuildStoryPreviewView,\n ExportStoryPreviewImageView,\n StoryPreviewMetadataView,\n StoryPreviewSeed,\n} from '../../../../../types/previewApi';\nimport { buildPreview, disposePreview, exportPreviewImage, storyMetadata } from '../_lib/previewApi';\nimport {\n isolatedPreviewHtml,\n normalizedAnnotationRect,\n previewAnnotationLocation,\n type PreviewAnnotationRect,\n type PreviewPoint,\n} from '../_lib/previewFrame';\n\nfunction imageDownload(data: string, storyExport: string): void {\n const link = document.createElement('a');\n link.href = `data:image/png;base64,${data}`;\n link.download = `${storyExport.replace(/[^A-Za-z0-9_-]/g, '-')}.png`;\n link.click();\n}\n\nfunction point(event: ReactPointerEvent<HTMLElement>): PreviewPoint {\n const bounds = event.currentTarget.getBoundingClientRect();\n return {\n x: Math.min(1, Math.max(0, (event.clientX - bounds.left) / bounds.width)),\n y: Math.min(1, Math.max(0, (event.clientY - bounds.top) / bounds.height)),\n };\n}\n\ninterface StoryPreviewSource {\n path: string;\n hasUnsavedChanges: boolean;\n kind?: string;\n revision?: number;\n sourceSha256?: string;\n}\n\ninterface StoryPreviewIdentity {\n version: 1;\n provider: string;\n projectPath: string;\n storyPath: string;\n storyExport: string;\n buildRevision: string;\n viewport: { width: number; height: number };\n sources: readonly { path: string; sha256: string }[];\n}\n\ninterface StoryPreviewPanelProps extends WebPluginSlotProps {\n seed?: StoryPreviewSeed;\n source?: StoryPreviewSource;\n activeTool?: 'select' | 'mark' | 'comment';\n displayedAnnotations?: readonly {\n ordinal: number;\n mode: 'region' | 'point';\n point: PreviewPoint;\n rect?: PreviewAnnotationRect;\n }[];\n pendingCandidate?: boolean;\n onAnnotationCandidate?: (candidate: {\n mode: 'region' | 'point';\n point: PreviewPoint;\n rect?: PreviewAnnotationRect;\n preview: StoryPreviewIdentity;\n evidence: Blob;\n thumbnailUrl: string;\n }) => void;\n}\n\nfunction preferredExport(exports: readonly StoryPreviewMetadataView['exports'][number][]): string {\n return exports.find((entry) => entry.exportName === 'Playground')?.exportName ?? exports[0]?.exportName ?? '';\n}\n\nfunction errorText(reason: unknown): string {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\nexport function StoryPreviewPanel({\n sessionId,\n submitCapture,\n seed,\n source,\n activeTool = 'select',\n displayedAnnotations = [],\n pendingCandidate = false,\n onAnnotationCandidate,\n}: StoryPreviewPanelProps) {\n const contextualSource = source ?? seed?.source;\n const sourcePath = contextualSource?.path ?? '';\n const sourceDirty = contextualSource?.hasUnsavedChanges === true;\n const sourceRevision = contextualSource?.revision ?? 0;\n const sourceSha256 = contextualSource?.sourceSha256 ?? '';\n const contextual = contextualSource !== undefined;\n const [appPath, setAppPath] = useState(contextual ? '' : '.');\n const [storyPath, setStoryPath] = useState(sourcePath);\n const [storyExport, setStoryExport] = useState(contextual ? '' : 'Playground');\n const [storyExports, setStoryExports] = useState<\n readonly StoryPreviewMetadataView['exports'][number][] | undefined\n >();\n const [darkMode, setDarkMode] = useState(false);\n const [preview, setPreview] = useState<BuildStoryPreviewView | undefined>(undefined);\n const [builtRequest, setBuiltRequest] = useState<BuildStoryPreviewRequest | undefined>(undefined);\n const [annotationImage, setAnnotationImage] = useState<ExportStoryPreviewImageView | undefined>(undefined);\n const [annotationStart, setAnnotationStart] = useState<PreviewPoint | undefined>(undefined);\n const [annotationRect, setAnnotationRect] = useState<PreviewAnnotationRect | undefined>(undefined);\n const [feedback, setFeedback] = useState('');\n const [status, setStatus] = useState(\n contextual\n ? sourceDirty\n ? 'Loading story metadata. Preview uses saved source.'\n : 'Loading story metadata…'\n : 'Choose an exact story file and export.',\n );\n const [working, setWorking] = useState(false);\n const generationRef = useRef(0);\n const operationRef = useRef(0);\n const previewRef = useRef<BuildStoryPreviewView | undefined>(undefined);\n const builtRequestRef = useRef<BuildStoryPreviewRequest | undefined>(undefined);\n const workingRef = useRef(false);\n const dirtyRef = useRef(sourceDirty);\n const annotationExportRef = useRef('');\n\n dirtyRef.current = sourceDirty;\n\n const request = useMemo<BuildStoryPreviewRequest>(\n () => ({ appPath, storyPath, storyExport, darkMode }),\n [appPath, storyPath, storyExport, darkMode],\n );\n\n useEffect(() => {\n operationRef.current += 1;\n if (!workingRef.current) return;\n workingRef.current = false;\n setWorking(false);\n }, [request]);\n\n useEffect(() => {\n const generation = generationRef.current + 1;\n generationRef.current = generation;\n operationRef.current += 1;\n workingRef.current = false;\n setWorking(false);\n setStoryExports(undefined);\n setAppPath(contextual ? '' : '.');\n setStoryPath(sourcePath);\n setStoryExport(contextual ? '' : 'Playground');\n builtRequestRef.current = undefined;\n setBuiltRequest(undefined);\n setAnnotationImage(undefined);\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n\n if (sessionId === null || sourcePath === '') {\n setPreview(undefined);\n previewRef.current = undefined;\n return () => {\n if (generationRef.current === generation) generationRef.current += 1;\n };\n }\n\n const previous = previewRef.current;\n previewRef.current = undefined;\n setPreview(undefined);\n if (previous !== undefined) void disposePreview(sessionId, previous.handle);\n\n let current = true;\n setStatus(sourceDirty ? 'Loading story metadata. Preview uses saved source.' : 'Loading story metadata…');\n void storyMetadata(sessionId, { storyPath: sourcePath })\n .then((result) => {\n if (!current || generationRef.current !== generation) return;\n if (!result.ok) {\n setStoryExports(undefined);\n setStatus(result.error);\n return;\n }\n const exports = result.metadata.exports;\n setAppPath(result.metadata.appPath);\n setStoryPath(result.metadata.storyPath);\n setStoryExports(exports);\n setStoryExport(preferredExport(exports));\n if (exports.length === 0) {\n setStatus('No story exports were found in this file.');\n } else if (dirtyRef.current) {\n setStatus('Save changes to preview. Preview uses saved source.');\n } else if (exports.length === 1) {\n setStatus('Story found. Building preview…');\n } else {\n setStatus('Story found. Building the Playground export…');\n }\n })\n .catch((reason: unknown) => {\n if (current && generationRef.current === generation) setStatus(errorText(reason));\n });\n\n return () => {\n current = false;\n if (generationRef.current === generation) generationRef.current += 1;\n operationRef.current += 1;\n const ownedPreview = previewRef.current;\n previewRef.current = undefined;\n if (ownedPreview !== undefined) void disposePreview(sessionId, ownedPreview.handle);\n };\n }, [contextual, sessionId, sourcePath, sourceRevision, sourceSha256]);\n\n useEffect(() => {\n if (!contextual || !sourceDirty) return;\n setStatus('Save changes to preview. Preview uses saved source.');\n }, [contextual, sourceDirty]);\n\n const rebuild = useCallback(async (): Promise<boolean> => {\n if (\n sessionId === null ||\n workingRef.current ||\n request.appPath.trim() === '' ||\n request.storyPath.trim() === '' ||\n request.storyExport.trim() === ''\n )\n return false;\n if (dirtyRef.current) {\n setStatus('Save changes to preview. Preview uses saved source.');\n return false;\n }\n\n const generation = generationRef.current;\n const operation = operationRef.current + 1;\n operationRef.current = operation;\n const requestSessionId = sessionId;\n workingRef.current = true;\n setWorking(true);\n setStatus('Building preview…');\n try {\n const result = await buildPreview(requestSessionId, request);\n if (generationRef.current !== generation || operationRef.current !== operation) {\n if (result.ok) void disposePreview(requestSessionId, result.preview.handle);\n return false;\n }\n if (!result.ok) {\n setStatus(result.error);\n return false;\n }\n const previous = previewRef.current;\n previewRef.current = result.preview;\n builtRequestRef.current = request;\n setPreview(result.preview);\n setBuiltRequest(request);\n setAnnotationImage(undefined);\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n if (previous !== undefined && previous.handle !== result.preview.handle) {\n void disposePreview(requestSessionId, previous.handle);\n }\n setStatus(\n dirtyRef.current\n ? 'Preview ready from saved source. Save changes to refresh.'\n : 'Preview ready. Refresh after source edits.',\n );\n return true;\n } catch (reason: unknown) {\n if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));\n return false;\n } finally {\n if (generationRef.current === generation && operationRef.current === operation) {\n workingRef.current = false;\n setWorking(false);\n }\n }\n }, [request, sessionId]);\n\n useEffect(() => {\n if (\n !contextual ||\n sessionId === null ||\n sourceDirty ||\n storyExports === undefined ||\n storyExport.trim() === '' ||\n appPath.trim() === '' ||\n storyPath.trim() === ''\n )\n return;\n void rebuild();\n }, [appPath, contextual, rebuild, sessionId, sourceDirty, storyExport, storyExports, storyPath]);\n\n const image = useCallback(async (): Promise<ExportStoryPreviewImageView | undefined> => {\n const currentRequest = builtRequestRef.current;\n if (sessionId === null || workingRef.current || currentRequest === undefined) return undefined;\n const generation = generationRef.current;\n const operation = operationRef.current + 1;\n operationRef.current = operation;\n const requestSessionId = sessionId;\n workingRef.current = true;\n setWorking(true);\n setStatus('Rendering a fresh source-backed image…');\n try {\n const result = await exportPreviewImage(requestSessionId, currentRequest);\n if (generationRef.current !== generation || operationRef.current !== operation) return undefined;\n if (!result.ok) {\n setStatus(result.error);\n return undefined;\n }\n setStatus('Image rendered from the current source. Transient iframe interactions are not included.');\n return result.image;\n } catch (reason: unknown) {\n if (generationRef.current === generation && operationRef.current === operation) setStatus(errorText(reason));\n return undefined;\n } finally {\n if (generationRef.current === generation && operationRef.current === operation) {\n workingRef.current = false;\n setWorking(false);\n }\n }\n }, [sessionId]);\n\n async function prepareAnnotation(): Promise<void> {\n const generation = generationRef.current;\n const result = await image();\n if (result === undefined || generationRef.current !== generation) return;\n setAnnotationImage(result);\n setAnnotationRect(undefined);\n setStatus('Annotation image ready. Drag a region or comment on the whole preview.');\n }\n\n useEffect(() => {\n if (onAnnotationCandidate === undefined) return;\n if (activeTool === 'select') {\n annotationExportRef.current = '';\n setAnnotationImage(undefined);\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n return;\n }\n if (\n preview === undefined ||\n annotationImage !== undefined ||\n working ||\n annotationExportRef.current === preview.handle\n )\n return;\n const generation = generationRef.current;\n void image().then((result) => {\n if (result === undefined || generationRef.current !== generation) return;\n annotationExportRef.current = preview.handle;\n setAnnotationImage(result);\n setStatus('Frozen source-backed image ready for Author annotations.');\n });\n }, [activeTool, annotationImage, image, onAnnotationCandidate, preview, working]);\n\n function updateAnnotation(event: ReactPointerEvent<HTMLDivElement>): void {\n if (annotationStart === undefined) return;\n const end = point(event);\n setAnnotationRect(normalizedAnnotationRect(annotationStart, end));\n }\n\n function authorCandidate(location: PreviewPoint, rect?: PreviewAnnotationRect): void {\n if (annotationImage === undefined || onAnnotationCandidate === undefined || builtRequest === undefined) return;\n const binary = atob(annotationImage.data);\n const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));\n const evidence = new Blob([bytes], { type: annotationImage.mimeType });\n const thumbnailUrl = URL.createObjectURL(evidence);\n try {\n onAnnotationCandidate({\n mode: rect === undefined ? 'point' : 'region',\n point: location,\n ...(rect === undefined ? {} : { rect }),\n preview: {\n version: 1,\n provider: 'style-system',\n projectPath: builtRequest.appPath,\n storyPath: annotationImage.storyPath,\n storyExport: annotationImage.storyExport,\n buildRevision: annotationImage.sourceSha256,\n viewport: { width: annotationImage.width, height: annotationImage.height },\n sources: [{ path: annotationImage.storyPath, sha256: annotationImage.sourceSha256 }],\n },\n evidence,\n thumbnailUrl,\n });\n } catch (reason) {\n URL.revokeObjectURL(thumbnailUrl);\n setStatus(errorText(reason));\n }\n }\n\n const multipleExports = storyExports !== undefined && storyExports.length > 1;\n const previewVisible = preview !== undefined;\n const authorAnnotationActive = onAnnotationCandidate !== undefined && activeTool !== 'select';\n\n return (\n <section className=\"flex h-full min-h-0 min-w-0 flex-1 flex-col gap-3 p-3\" data-testid=\"style-system-preview-panel\">\n {contextual ? (\n <header className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-doom-dim\">\n <span>\n Story: <strong className=\"text-doom-text\">{storyPath || sourcePath}</strong>\n </span>\n <span>\n Project: <strong className=\"text-doom-text\">{appPath || 'resolving…'}</strong>\n </span>\n {multipleExports ? (\n <label className=\"flex items-center gap-1\">\n Export\n <select\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n aria-label=\"story export\"\n onChange={(event) => {\n setStoryExport(event.target.value);\n builtRequestRef.current = undefined;\n setBuiltRequest(undefined);\n setAnnotationImage(undefined);\n setAnnotationRect(undefined);\n }}\n >\n {storyExports.map((entry) => (\n <option key={entry.exportName} value={entry.exportName}>\n {entry.label ?? entry.exportName}\n </option>\n ))}\n </select>\n </label>\n ) : storyExport !== '' ? (\n <span>\n Export: <strong className=\"text-doom-text\">{storyExport}</strong>\n </span>\n ) : null}\n </header>\n ) : (\n <header className=\"grid gap-2 md:grid-cols-3\">\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Project path\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={appPath}\n onChange={(event) => setAppPath(event.target.value)}\n />\n </label>\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Story file\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n placeholder=\"packages/ui/Button.stories.tsx\"\n value={storyPath}\n onChange={(event) => {\n setStoryPath(event.target.value);\n setStoryExports(undefined);\n }}\n />\n </label>\n <label className=\"grid gap-1 text-xs text-doom-dim\">\n Story export\n {storyExports === undefined ? (\n <input\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n onChange={(event) => setStoryExport(event.target.value)}\n />\n ) : (\n <select\n className=\"rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={storyExport}\n onChange={(event) => setStoryExport(event.target.value)}\n >\n <option value=\"\">Choose an export</option>\n {storyExports.map((entry) => (\n <option key={entry.exportName} value={entry.exportName}>\n {entry.label ?? entry.exportName}\n </option>\n ))}\n </select>\n )}\n </label>\n </header>\n )}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n disabled={\n sessionId === null ||\n working ||\n sourceDirty ||\n request.storyPath.trim() === '' ||\n request.storyExport.trim() === ''\n }\n onClick={() => void rebuild()}\n >\n {previewVisible ? 'Refresh preview' : 'Build preview'}\n </Button>\n <Button\n variant=\"outline\"\n disabled={preview === undefined || working}\n onClick={() => void image().then((result) => result && imageDownload(result.data, result.storyExport))}\n >\n Export PNG\n </Button>\n {onAnnotationCandidate === undefined ? (\n <Button\n variant=\"outline\"\n disabled={preview === undefined || working}\n onClick={() => void prepareAnnotation()}\n >\n Prepare annotation\n </Button>\n ) : null}\n <label className=\"flex items-center gap-1 text-xs text-doom-dim\">\n <input type=\"checkbox\" checked={darkMode} onChange={(event) => setDarkMode(event.target.checked)} /> Dark mode\n </label>\n <output className=\"text-xs text-doom-dim\" aria-live=\"polite\">\n {status}\n </output>\n </div>\n {sourceDirty ? (\n <p data-testid=\"style-system-preview-dirty\" className=\"text-xs text-doom-yellow\">\n Save changes to preview. Preview uses saved source.\n </p>\n ) : null}\n {preview === undefined ? (\n <div className=\"grid min-h-48 place-items-center rounded border border-dashed border-doom-border text-sm text-doom-faint\">\n No preview built.\n </div>\n ) : authorAnnotationActive && annotationImage !== undefined ? (\n <div className=\"grid min-h-0 flex-1 place-items-center overflow-hidden rounded border border-doom-border bg-doom-deep\">\n <div\n className=\"relative max-h-full w-fit max-w-full touch-none overflow-hidden\"\n aria-label={\n activeTool === 'comment' ? 'Click to place an annotation point' : 'Drag to mark an annotation region'\n }\n data-testid=\"style-system-author-annotation-overlay\"\n onPointerDown={(event) => {\n if (pendingCandidate || (activeTool !== 'mark' && activeTool !== 'comment')) return;\n event.currentTarget.setPointerCapture(event.pointerId);\n const start = point(event);\n setAnnotationStart(start);\n setAnnotationRect(activeTool === 'mark' ? { ...start, width: 0, height: 0 } : undefined);\n }}\n onPointerMove={(event) => {\n if (activeTool === 'mark') updateAnnotation(event);\n }}\n onPointerUp={(event) => {\n const start = annotationStart;\n if (start === undefined) return;\n const end = point(event);\n setAnnotationStart(undefined);\n const location = previewAnnotationLocation(activeTool === 'comment' ? 'comment' : 'mark', start, end);\n if (location === null) return;\n setAnnotationRect(location.mode === 'region' ? location.rect : undefined);\n authorCandidate(location.point, location.mode === 'region' ? location.rect : undefined);\n }}\n onPointerCancel={() => {\n setAnnotationStart(undefined);\n setAnnotationRect(undefined);\n }}\n >\n <img\n className=\"block max-h-full max-w-full select-none\"\n src={`data:${annotationImage.mimeType};base64,${annotationImage.data}`}\n alt=\"Frozen source-backed story render for Author annotation\"\n draggable={false}\n />\n {displayedAnnotations.map((annotation) =>\n annotation.mode === 'point' || annotation.rect === undefined ? (\n <span\n key={annotation.ordinal}\n data-author-point={annotation.ordinal}\n 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\"\n style={{\n left: `${String(annotation.point.x * 100)}%`,\n top: `${String(annotation.point.y * 100)}%`,\n }}\n >\n {annotation.ordinal}\n </span>\n ) : (\n <div\n key={annotation.ordinal}\n data-author-region={annotation.ordinal}\n className=\"pointer-events-none absolute border border-doom-yellow bg-doom-yellow/10\"\n style={{\n left: `${String(annotation.rect.x * 100)}%`,\n top: `${String(annotation.rect.y * 100)}%`,\n width: `${String(annotation.rect.width * 100)}%`,\n height: `${String(annotation.rect.height * 100)}%`,\n }}\n >\n <span className=\"bg-doom-yellow text-doom-deep\">{annotation.ordinal}</span>\n </div>\n ),\n )}\n {annotationRect === undefined ? null : (\n <div\n className=\"pointer-events-none absolute border-2 border-red-500 bg-red-500/10\"\n style={{\n left: `${String(annotationRect.x * 100)}%`,\n top: `${String(annotationRect.y * 100)}%`,\n width: `${String(annotationRect.width * 100)}%`,\n height: `${String(annotationRect.height * 100)}%`,\n }}\n />\n )}\n </div>\n </div>\n ) : (\n <iframe\n key={preview.handle}\n title={`${preview.storyExport} story preview`}\n sandbox=\"allow-scripts\"\n referrerPolicy=\"no-referrer\"\n srcDoc={isolatedPreviewHtml(preview.html)}\n className=\"min-h-0 flex-1 rounded border border-doom-border bg-white\"\n />\n )}\n {annotationImage === undefined || onAnnotationCandidate !== undefined ? null : (\n <div className=\"grid gap-2\">\n <div\n className=\"relative max-h-80 w-fit max-w-full touch-none overflow-hidden rounded border border-doom-border\"\n aria-label=\"Drag to mark an annotation region\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n const start = point(event);\n setAnnotationStart(start);\n setAnnotationRect({ ...start, width: 0, height: 0 });\n }}\n onPointerMove={updateAnnotation}\n onPointerUp={(event) => {\n updateAnnotation(event);\n setAnnotationStart(undefined);\n }}\n >\n <img\n className=\"block max-h-80 max-w-full select-none\"\n src={`data:${annotationImage.mimeType};base64,${annotationImage.data}`}\n alt=\"Fresh source-backed story render for annotation\"\n draggable={false}\n />\n {annotationRect === undefined ? null : (\n <div\n className=\"pointer-events-none absolute border-2 border-red-500 bg-red-500/10\"\n style={{\n left: `${String(annotationRect.x * 100)}%`,\n top: `${String(annotationRect.y * 100)}%`,\n width: `${String(annotationRect.width * 100)}%`,\n height: `${String(annotationRect.height * 100)}%`,\n }}\n />\n )}\n </div>\n <p className=\"text-xs text-doom-faint\">\n This image is rebuilt from source and does not preserve transient iframe interactions.\n </p>\n </div>\n )}\n {preview === undefined || onAnnotationCandidate !== undefined ? null : (\n <div className=\"flex gap-2\">\n <label className=\"grid min-w-0 flex-1 gap-1 text-xs text-doom-dim\">\n AI design feedback\n <textarea\n className=\"min-h-20 rounded border border-doom-border bg-doom-bg px-2 py-1 text-doom-text\"\n value={feedback}\n onChange={(event) => setFeedback(event.target.value)}\n />\n </label>\n <Button\n className=\"self-end\"\n variant=\"outline\"\n disabled={working || submitCapture === undefined || feedback.trim() === '' || annotationImage === undefined}\n onClick={() => {\n if (submitCapture === undefined || annotationImage === undefined) return;\n const location =\n annotationRect === undefined\n ? 'Whole preview'\n : `Region: x=${annotationRect.x.toFixed(3)}, y=${annotationRect.y.toFixed(3)}, width=${annotationRect.width.toFixed(3)}, height=${annotationRect.height.toFixed(3)}`;\n const generation = generationRef.current;\n void submitCapture({\n ...annotationImage,\n context: {\n kind: 'author-capture',\n source: 'style-system',\n id: crypto.randomUUID(),\n label: `${annotationImage.storyPath}#${annotationImage.storyExport}`,\n content: [\n 'Edit the underlying story or component source, never the generated preview HTML.',\n `Project: ${builtRequest?.appPath ?? appPath}`,\n `Story: ${annotationImage.storyPath}#${annotationImage.storyExport}`,\n `Story source sha256: ${annotationImage.sourceSha256}`,\n `Source-backed image revision: ${annotationImage.sourceSha256}`,\n location,\n `Feedback: ${feedback.trim()}`,\n 'After editing, rebuild this preview before treating it as visually verified.',\n ].join('\\n'),\n },\n })\n .then(() => {\n if (generationRef.current !== generation) return;\n setStatus('Feedback and the frozen source-backed annotation image were submitted to the agent.');\n setFeedback('');\n })\n .catch((reason: unknown) => {\n if (generationRef.current === generation) setStatus(errorText(reason));\n });\n }}\n >\n Send to AI\n </Button>\n </div>\n )}\n </section>\n );\n}\n","import type { TransientTab } from '@agimon-ai/doompi-core/web';\n\nimport type { StoryPreviewSeed } from '../../../../../types/previewApi';\nimport { StoryPreviewPanel } from './StoryPreviewPanel';\n\nfunction sourceKey(path: string): string {\n let hash = 2_166_136_261;\n for (const character of path) hash = Math.imul(hash ^ character.codePointAt(0)!, 16_777_619);\n return (hash >>> 0).toString(36);\n}\n\nexport function storyPreviewTab(seed?: StoryPreviewSeed): TransientTab {\n const sourcePath = seed?.source?.path;\n return {\n id: sourcePath === undefined ? 'style-system-story-preview' : `style-system-story-preview-${sourceKey(sourcePath)}`,\n label: 'Story preview',\n panel: (props) => <StoryPreviewPanel {...props} seed={seed} />,\n retainComposer: true,\n };\n}\n","import { defineFill } from '@agimon-ai/doompi-core/web';\n\nimport { storyPreviewTab } from '../_components/StoryPreviewLauncher';\nimport { StoryPreviewPanel } from '../_components/StoryPreviewPanel';\n\nexport default defineFill({\n slot: 'author.preview-action',\n id: 'style-system',\n data: {\n version: 1 as const,\n label: 'Preview',\n detail: 'Build, inspect, refresh, and send source-targeted visual feedback.',\n supportsSource: (source: { kind?: string }) => source.kind === 'story-preview',\n embeddedPanel: StoryPreviewPanel,\n createTab: ({ source }: { source?: { path: string; hasUnsavedChanges: boolean } }) =>\n storyPreviewTab(source === undefined ? undefined : { source }),\n },\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport fillStyleSystem from '../src/extensions/workspaces/sessions/(frontend)/fill/StyleSystem.author.preview-action.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'style-system',\n session: {\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'author.preview-action', id: 'style-system' }, fillStyleSystem)]; },\n },\n});\n"],"mappings":"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"}
|
|
@@ -3,10 +3,24 @@ let node_fs = require("node:fs");
|
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
node_path = require_runtime.__toESM(node_path, 1);
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
|
+
let node_os = require("node:os");
|
|
7
|
+
node_os = require_runtime.__toESM(node_os, 1);
|
|
6
8
|
let _agimon_ai_style_system = require("@agimon-ai/style-system");
|
|
7
9
|
//#region src/services/storyPreview/index.ts
|
|
8
10
|
const STORY_FILE_PATTERN = /\.stories\.(?:js|jsx|ts|tsx)$/u;
|
|
9
11
|
const STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
12
|
+
const PNG_SIGNATURE = Buffer.from([
|
|
13
|
+
137,
|
|
14
|
+
80,
|
|
15
|
+
78,
|
|
16
|
+
71,
|
|
17
|
+
13,
|
|
18
|
+
10,
|
|
19
|
+
26,
|
|
20
|
+
10
|
|
21
|
+
]);
|
|
22
|
+
const MAX_IMAGE_BYTES = 2097152;
|
|
23
|
+
const MAX_IMAGE_DIMENSION = 1600;
|
|
10
24
|
const NON_STORY_EXPORTS = /* @__PURE__ */ new Set([
|
|
11
25
|
"afterAll",
|
|
12
26
|
"afterEach",
|
|
@@ -120,16 +134,67 @@ const defaultDependencies = {
|
|
|
120
134
|
createBundler(_config) {
|
|
121
135
|
return (0, _agimon_ai_style_system.createDefaultBundlerService)();
|
|
122
136
|
},
|
|
137
|
+
createRenderer(config, appPath) {
|
|
138
|
+
return new _agimon_ai_style_system.ComponentRendererService(config, appPath);
|
|
139
|
+
},
|
|
140
|
+
rendererTemporaryRoot: () => node_path.default.join(node_os.default.tmpdir(), "style-system"),
|
|
123
141
|
createHandle: node_crypto.randomUUID
|
|
124
142
|
};
|
|
143
|
+
function pngCrc(data) {
|
|
144
|
+
let crc = 4294967295;
|
|
145
|
+
for (const byte of data) {
|
|
146
|
+
crc ^= byte;
|
|
147
|
+
for (let bit = 0; bit < 8; bit += 1) crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
148
|
+
}
|
|
149
|
+
return (crc ^ 4294967295) >>> 0;
|
|
150
|
+
}
|
|
151
|
+
function pngDimensions(image) {
|
|
152
|
+
if (image.length < PNG_SIGNATURE.length || !image.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)) throw new Error("Style-system returned an invalid PNG image");
|
|
153
|
+
let offset = PNG_SIGNATURE.length;
|
|
154
|
+
let width = 0;
|
|
155
|
+
let height = 0;
|
|
156
|
+
let chunks = 0;
|
|
157
|
+
let hasImageData = false;
|
|
158
|
+
let hasEnd = false;
|
|
159
|
+
while (offset < image.length) {
|
|
160
|
+
if (image.length - offset < 12) throw new Error("Style-system returned an invalid PNG image");
|
|
161
|
+
const length = image.readUInt32BE(offset);
|
|
162
|
+
const end = offset + 12 + length;
|
|
163
|
+
if (end > image.length) throw new Error("Style-system returned an invalid PNG image");
|
|
164
|
+
const type = image.toString("ascii", offset + 4, offset + 8);
|
|
165
|
+
if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error("Style-system returned an invalid PNG image");
|
|
166
|
+
if (image.readUInt32BE(end - 4) !== pngCrc(image.subarray(offset + 4, end - 4))) throw new Error("Style-system returned an invalid PNG image");
|
|
167
|
+
if (chunks === 0) {
|
|
168
|
+
if (type !== "IHDR" || length !== 13) throw new Error("Style-system returned an invalid PNG image");
|
|
169
|
+
width = image.readUInt32BE(offset + 8);
|
|
170
|
+
height = image.readUInt32BE(offset + 12);
|
|
171
|
+
} else if (type === "IHDR") throw new Error("Style-system returned an invalid PNG image");
|
|
172
|
+
if (type === "IDAT") hasImageData = true;
|
|
173
|
+
if (type === "IEND") {
|
|
174
|
+
if (length !== 0 || end !== image.length) throw new Error("Style-system returned an invalid PNG image");
|
|
175
|
+
hasEnd = true;
|
|
176
|
+
}
|
|
177
|
+
chunks += 1;
|
|
178
|
+
offset = end;
|
|
179
|
+
}
|
|
180
|
+
if (!hasImageData || !hasEnd) throw new Error("Style-system returned an invalid PNG image");
|
|
181
|
+
if (width === 0 || height === 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) throw new Error("Style-system returned PNG dimensions outside the supported range");
|
|
182
|
+
return {
|
|
183
|
+
width,
|
|
184
|
+
height
|
|
185
|
+
};
|
|
186
|
+
}
|
|
125
187
|
/** Owns disposable static story-preview artifacts for one trusted workspace. */
|
|
126
188
|
var StoryPreviewService = class {
|
|
127
189
|
root;
|
|
128
190
|
dependencies;
|
|
129
191
|
artifacts = /* @__PURE__ */ new Map();
|
|
130
|
-
constructor(workspaceRoot, dependencies =
|
|
192
|
+
constructor(workspaceRoot, dependencies = {}) {
|
|
131
193
|
this.root = node_path.default.resolve(workspaceRoot);
|
|
132
|
-
this.dependencies =
|
|
194
|
+
this.dependencies = {
|
|
195
|
+
...defaultDependencies,
|
|
196
|
+
...dependencies
|
|
197
|
+
};
|
|
133
198
|
}
|
|
134
199
|
async metadata(input) {
|
|
135
200
|
const root = await node_fs.promises.realpath(this.root);
|
|
@@ -233,27 +298,36 @@ var StoryPreviewService = class {
|
|
|
233
298
|
const storyPath = await this.resolveContainedPath(root, input.storyPath, "storyPath");
|
|
234
299
|
if (!STORY_FILE_PATTERN.test(storyPath)) throw new Error("storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file");
|
|
235
300
|
const [source, config] = await Promise.all([node_fs.promises.readFile(storyPath, "utf8"), this.dependencies.loadConfig(appPath)]);
|
|
236
|
-
const renderer =
|
|
237
|
-
let
|
|
301
|
+
const renderer = this.dependencies.createRenderer(config, appPath);
|
|
302
|
+
let ownedImagePath;
|
|
238
303
|
try {
|
|
239
304
|
const component = await this.exactStory(root, storyPath, input.storyExport);
|
|
240
305
|
const rendered = await renderer.renderComponent(component, {
|
|
241
306
|
storyName: input.storyExport,
|
|
242
307
|
darkMode: input.darkMode
|
|
243
308
|
});
|
|
244
|
-
const renderedImagePath = await node_fs.promises.realpath(rendered.imagePath);
|
|
245
|
-
if (!isContained(
|
|
246
|
-
|
|
309
|
+
const [renderedImagePath, imageRoot] = await Promise.all([node_fs.promises.realpath(rendered.imagePath), node_fs.promises.realpath(this.dependencies.rendererTemporaryRoot())]);
|
|
310
|
+
if (renderedImagePath === imageRoot || !isContained(imageRoot, renderedImagePath)) throw new Error("Style-system returned an image artifact outside its temporary directory");
|
|
311
|
+
const imageStat = await node_fs.promises.stat(renderedImagePath);
|
|
312
|
+
if (!imageStat.isFile()) throw new Error("Style-system returned a non-regular PNG image");
|
|
313
|
+
ownedImagePath = renderedImagePath;
|
|
314
|
+
if (imageStat.size > MAX_IMAGE_BYTES) throw new Error("Style-system returned an oversized PNG image");
|
|
315
|
+
const image = await node_fs.promises.readFile(renderedImagePath);
|
|
316
|
+
if (image.length !== imageStat.size) throw new Error("Style-system PNG changed while it was being read");
|
|
317
|
+
const { width, height } = pngDimensions(image);
|
|
247
318
|
return {
|
|
248
|
-
data:
|
|
319
|
+
data: image.toString("base64"),
|
|
249
320
|
mimeType: "image/png",
|
|
321
|
+
captureId: this.dependencies.createHandle(),
|
|
322
|
+
width,
|
|
323
|
+
height,
|
|
250
324
|
storyPath: relativeWorkspacePath(root, storyPath),
|
|
251
325
|
storyExport: input.storyExport,
|
|
252
326
|
sourceSha256: (0, node_crypto.createHash)("sha256").update(source).digest("hex")
|
|
253
327
|
};
|
|
254
328
|
} finally {
|
|
255
329
|
try {
|
|
256
|
-
if (
|
|
330
|
+
if (ownedImagePath !== void 0) await node_fs.promises.rm(ownedImagePath, { force: true });
|
|
257
331
|
} finally {
|
|
258
332
|
await renderer.dispose();
|
|
259
333
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","getAppDesignSystemConfig","createDefaultBundlerService","randomUUID","StoriesIndexService","createHash","ComponentRendererService"],"sources":["../../../../src/services/storyPreview/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport {\n ComponentRendererService,\n createDefaultBundlerService,\n getAppDesignSystemConfig,\n StoriesIndexService,\n type BaseBundlerService,\n type DesignSystemConfig,\n} from '@agimon-ai/style-system';\n\nimport type { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from '../../types/previewApi';\nimport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n\nconst STORY_FILE_PATTERN = /\\.stories\\.(?:js|jsx|ts|tsx)$/u;\nconst STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst NON_STORY_EXPORTS = new Set([\n 'afterAll',\n 'afterEach',\n 'argTypes',\n 'beforeAll',\n 'beforeEach',\n 'component',\n 'decorators',\n 'excludeStories',\n 'globals',\n 'includeStories',\n 'loaders',\n 'meta',\n 'parameters',\n 'render',\n 'tags',\n]);\n\ninterface OwnedArtifact {\n directory: string;\n}\n\nfunction isContained(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\n}\n\nfunction stripComments(source: string): string {\n let output = '';\n let quote: \"'\" | '\"' | '`' | undefined;\n let escaped = false;\n for (let index = 0; index < source.length; index += 1) {\n const character = source[index];\n const next = source[index + 1];\n if (quote !== undefined) {\n output += character === '\\n' ? '\\n' : ' ';\n if (escaped) escaped = false;\n else if (character === '\\\\') escaped = true;\n else if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\" || character === '`') {\n quote = character;\n output += ' ';\n } else if (character === '/' && next === '/') {\n output += ' ';\n index += 2;\n while (index < source.length && source[index] !== '\\n') {\n output += ' ';\n index += 1;\n }\n if (index < source.length) output += '\\n';\n } else if (character === '/' && next === '*') {\n output += ' ';\n index += 2;\n while (index < source.length && !(source[index - 1] === '*' && source[index] === '/')) {\n output += source[index] === '\\n' ? '\\n' : ' ';\n index += 1;\n }\n } else output += character;\n }\n return output;\n}\n\nfunction storyLabel(source: string, clean: string, exportName: string): string | undefined {\n const escapedExport = exportName.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n const declaration = new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${escapedExport}\\\\b[^=]*=\\\\s*\\\\{`, 'u');\n const match = clean.match(declaration);\n if (match === null || match.index === undefined) return undefined;\n const opening = clean.indexOf('{', match.index);\n let depth = 0;\n for (let index = opening; index < clean.length; index += 1) {\n if (clean[index] === '{') depth += 1;\n else if (clean[index] === '}') {\n depth -= 1;\n if (depth === 0) {\n const label = source\n .slice(opening, index)\n .match(/\\bname\\s*:\\s*(['\"])(.*?)\\1/u)?.[2]\n ?.trim();\n return label === '' ? undefined : label;\n }\n }\n }\n return undefined;\n}\n\n/** Extracts named CSF candidates without evaluating workspace source. */\nexport function extractStoryExports(source: string): readonly StoryPreviewExport[] {\n const names: string[] = [];\n const seen = new Set<string>();\n const add = (name: string): void => {\n if (name === 'default' || NON_STORY_EXPORTS.has(name) || seen.has(name)) return;\n seen.add(name);\n names.push(name);\n };\n const clean = stripComments(source);\n const declarations = /\\bexport\\s+(?:async\\s+)?(?:const|let|var|function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)/gu;\n for (const match of clean.matchAll(declarations)) add(match[1]!);\n const lists = /\\bexport\\s*\\{([^}]*)\\}/gu;\n for (const match of clean.matchAll(lists)) {\n for (const item of match[1]!.split(',')) {\n const parts = item.trim().split(/\\s+as\\s+/u);\n const name = (parts.at(-1) ?? '').trim();\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(name)) add(name);\n }\n }\n return names.map((exportName) => {\n const label = storyLabel(source, clean, exportName);\n return label === undefined ? { exportName } : { exportName, label };\n });\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction relativeWorkspacePath(root: string, candidate: string): string {\n const relative = path.relative(root, candidate).split(path.sep).join('/');\n return relative === '' ? '.' : relative;\n}\n\nconst defaultDependencies: StoryPreviewServiceDependencies = {\n loadConfig: getAppDesignSystemConfig,\n createBundler(_config: DesignSystemConfig): BaseBundlerService {\n return createDefaultBundlerService();\n },\n createHandle: randomUUID,\n};\n\n/** Owns disposable static story-preview artifacts for one trusted workspace. */\nexport class StoryPreviewService {\n private readonly root: string;\n private readonly dependencies: StoryPreviewServiceDependencies;\n private readonly artifacts = new Map<string, OwnedArtifact>();\n\n constructor(workspaceRoot: string, dependencies: StoryPreviewServiceDependencies = defaultDependencies) {\n this.root = path.resolve(workspaceRoot);\n this.dependencies = dependencies;\n }\n\n async metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView> {\n const root = await fs.realpath(this.root);\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n const project = await this.resolveProject(root, storyPath, input.appPath);\n const source = await fs.readFile(storyPath, 'utf8');\n return {\n storyPath: relativeWorkspacePath(root, storyPath),\n appPath: relativeWorkspacePath(root, project.path),\n projectResolution: project.resolution,\n exports: extractStoryExports(source),\n };\n }\n\n private async resolveProject(\n root: string,\n storyPath: string,\n requestedPath: string | undefined,\n ): Promise<{ path: string; resolution: StoryPreviewMetadataView['projectResolution'] }> {\n if (requestedPath !== undefined && requestedPath.trim() !== '') {\n return { path: await this.resolveContainedPath(root, requestedPath, 'appPath'), resolution: 'explicit' };\n }\n let directory = path.dirname(storyPath);\n let packagePath: string | undefined;\n while (isContained(root, directory)) {\n if (await exists(path.join(directory, 'style-system.config.yaml'))) {\n return { path: directory, resolution: 'config' };\n }\n if (packagePath === undefined && (await exists(path.join(directory, 'package.json')))) packagePath = directory;\n if (directory === root) break;\n directory = path.dirname(directory);\n }\n return packagePath === undefined\n ? { path: root, resolution: 'workspace' }\n : { path: packagePath, resolution: 'package' };\n }\n\n private async exactStory(root: string, storyPath: string, storyExport: string) {\n const source = await fs.readFile(storyPath, 'utf8');\n if (!extractStoryExports(source).some((entry) => entry.exportName === storyExport)) {\n throw new Error(`Story export \"${storyExport}\" was not found in ${relativeWorkspacePath(root, storyPath)}.`);\n }\n const index = new StoriesIndexService({ workspaceRoot: root, storyFiles: [storyPath], searchRoots: [] });\n await index.initialize();\n const component = index.getAllComponents().find((entry) => path.resolve(entry.filePath) === storyPath);\n if (component === undefined) throw new Error('The requested story file could not be indexed.');\n return component;\n }\n\n async build(input: StoryPreviewBuildInput): Promise<StoryPreviewBuildResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n await this.exactStory(root, storyPath, input.storyExport);\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const bundler = this.dependencies.createBundler(config);\n let artifactDirectory: string | undefined;\n try {\n const rendered = await bundler.prerenderComponent({\n componentPath: storyPath,\n storyName: input.storyExport,\n args: input.args,\n darkMode: input.darkMode,\n appPath,\n themePath: config.themeProvider,\n cssFiles: config.cssFiles,\n rootComponent: config.rootComponent,\n unistylesConfig: config.unistylesConfig,\n });\n\n const htmlPath = await fs.realpath(rendered.htmlFilePath);\n const candidateDirectory = path.dirname(htmlPath);\n const temporaryRoot = path.join(appPath, '.tmp');\n if (candidateDirectory === temporaryRoot || !isContained(temporaryRoot, candidateDirectory)) {\n throw new Error('Style-system returned a preview artifact outside the project temporary directory');\n }\n artifactDirectory = candidateDirectory;\n\n const handle = this.dependencies.createHandle();\n const html = await fs.readFile(htmlPath, 'utf8');\n this.artifacts.set(handle, { directory: artifactDirectory });\n return {\n handle,\n html,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } catch (reason) {\n if (artifactDirectory !== undefined) await fs.rm(artifactDirectory, { recursive: true, force: true });\n throw reason;\n }\n }\n\n /** Renders a fresh source-backed PNG. This does not preserve transient iframe interaction state. */\n async exportImage(input: StoryPreviewBuildInput): Promise<StoryPreviewImageResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const renderer = new ComponentRendererService(config, appPath);\n let imagePath: string | undefined;\n try {\n const component = await this.exactStory(root, storyPath, input.storyExport);\n const rendered = await renderer.renderComponent(component, {\n storyName: input.storyExport,\n darkMode: input.darkMode,\n });\n const renderedImagePath = await fs.realpath(rendered.imagePath);\n const imageRoot = await fs.realpath(path.join(appPath, '.tmp'));\n if (!isContained(imageRoot, renderedImagePath)) {\n throw new Error('Style-system returned an image artifact outside the project temporary directory');\n }\n imagePath = renderedImagePath;\n const image = await fs.readFile(imagePath);\n return {\n data: image.toString('base64'),\n mimeType: 'image/png',\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } finally {\n try {\n if (imagePath !== undefined) await fs.rm(imagePath, { force: true });\n } finally {\n await renderer.dispose();\n }\n }\n }\n\n async dispose(handle: string): Promise<boolean> {\n const artifact = this.artifacts.get(handle);\n if (artifact === undefined) return false;\n this.artifacts.delete(handle);\n await fs.rm(artifact.directory, { recursive: true, force: true });\n return true;\n }\n\n async disposeAll(): Promise<void> {\n await Promise.all([...this.artifacts.keys()].map((handle) => this.dispose(handle)));\n }\n\n private async resolveContainedPath(root: string, inputPath: string, field: string): Promise<string> {\n if (inputPath.trim() === '') throw new Error(`${field} is required`);\n const candidate = path.isAbsolute(inputPath) ? inputPath : path.resolve(root, inputPath);\n const canonical = await fs.realpath(candidate);\n if (!isContained(root, canonical)) throw new Error(`${field} must remain inside the workspace root`);\n return canonical;\n }\n}\n\nexport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","getAppDesignSystemConfig","createDefaultBundlerService","ComponentRendererService","os","randomUUID","StoriesIndexService","createHash"],"sources":["../../../../src/services/storyPreview/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n ComponentRendererService,\n createDefaultBundlerService,\n getAppDesignSystemConfig,\n StoriesIndexService,\n type BaseBundlerService,\n type DesignSystemConfig,\n} from '@agimon-ai/style-system';\n\nimport type { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from '../../types/previewApi';\nimport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n\nconst STORY_FILE_PATTERN = /\\.stories\\.(?:js|jsx|ts|tsx)$/u;\nconst STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\nconst MAX_IMAGE_BYTES = 2 * 1024 * 1024;\nconst MAX_IMAGE_DIMENSION = 1600;\nconst NON_STORY_EXPORTS = new Set([\n 'afterAll',\n 'afterEach',\n 'argTypes',\n 'beforeAll',\n 'beforeEach',\n 'component',\n 'decorators',\n 'excludeStories',\n 'globals',\n 'includeStories',\n 'loaders',\n 'meta',\n 'parameters',\n 'render',\n 'tags',\n]);\n\ninterface OwnedArtifact {\n directory: string;\n}\n\nfunction isContained(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\n}\n\nfunction stripComments(source: string): string {\n let output = '';\n let quote: \"'\" | '\"' | '`' | undefined;\n let escaped = false;\n for (let index = 0; index < source.length; index += 1) {\n const character = source[index];\n const next = source[index + 1];\n if (quote !== undefined) {\n output += character === '\\n' ? '\\n' : ' ';\n if (escaped) escaped = false;\n else if (character === '\\\\') escaped = true;\n else if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\" || character === '`') {\n quote = character;\n output += ' ';\n } else if (character === '/' && next === '/') {\n output += ' ';\n index += 2;\n while (index < source.length && source[index] !== '\\n') {\n output += ' ';\n index += 1;\n }\n if (index < source.length) output += '\\n';\n } else if (character === '/' && next === '*') {\n output += ' ';\n index += 2;\n while (index < source.length && !(source[index - 1] === '*' && source[index] === '/')) {\n output += source[index] === '\\n' ? '\\n' : ' ';\n index += 1;\n }\n } else output += character;\n }\n return output;\n}\n\nfunction storyLabel(source: string, clean: string, exportName: string): string | undefined {\n const escapedExport = exportName.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n const declaration = new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${escapedExport}\\\\b[^=]*=\\\\s*\\\\{`, 'u');\n const match = clean.match(declaration);\n if (match === null || match.index === undefined) return undefined;\n const opening = clean.indexOf('{', match.index);\n let depth = 0;\n for (let index = opening; index < clean.length; index += 1) {\n if (clean[index] === '{') depth += 1;\n else if (clean[index] === '}') {\n depth -= 1;\n if (depth === 0) {\n const label = source\n .slice(opening, index)\n .match(/\\bname\\s*:\\s*(['\"])(.*?)\\1/u)?.[2]\n ?.trim();\n return label === '' ? undefined : label;\n }\n }\n }\n return undefined;\n}\n\n/** Extracts named CSF candidates without evaluating workspace source. */\nexport function extractStoryExports(source: string): readonly StoryPreviewExport[] {\n const names: string[] = [];\n const seen = new Set<string>();\n const add = (name: string): void => {\n if (name === 'default' || NON_STORY_EXPORTS.has(name) || seen.has(name)) return;\n seen.add(name);\n names.push(name);\n };\n const clean = stripComments(source);\n const declarations = /\\bexport\\s+(?:async\\s+)?(?:const|let|var|function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)/gu;\n for (const match of clean.matchAll(declarations)) add(match[1]!);\n const lists = /\\bexport\\s*\\{([^}]*)\\}/gu;\n for (const match of clean.matchAll(lists)) {\n for (const item of match[1]!.split(',')) {\n const parts = item.trim().split(/\\s+as\\s+/u);\n const name = (parts.at(-1) ?? '').trim();\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(name)) add(name);\n }\n }\n return names.map((exportName) => {\n const label = storyLabel(source, clean, exportName);\n return label === undefined ? { exportName } : { exportName, label };\n });\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction relativeWorkspacePath(root: string, candidate: string): string {\n const relative = path.relative(root, candidate).split(path.sep).join('/');\n return relative === '' ? '.' : relative;\n}\n\nconst defaultDependencies: StoryPreviewServiceDependencies = {\n loadConfig: getAppDesignSystemConfig,\n createBundler(_config: DesignSystemConfig): BaseBundlerService {\n return createDefaultBundlerService();\n },\n createRenderer(config, appPath) {\n return new ComponentRendererService(config, appPath);\n },\n rendererTemporaryRoot: () => path.join(os.tmpdir(), 'style-system'),\n createHandle: randomUUID,\n};\n\nfunction pngCrc(data: Buffer): number {\n let crc = 0xffffffff;\n for (const byte of data) {\n crc ^= byte;\n for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction pngDimensions(image: Buffer): { width: number; height: number } {\n if (image.length < PNG_SIGNATURE.length || !image.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)) {\n throw new Error('Style-system returned an invalid PNG image');\n }\n let offset = PNG_SIGNATURE.length;\n let width = 0;\n let height = 0;\n let chunks = 0;\n let hasImageData = false;\n let hasEnd = false;\n while (offset < image.length) {\n if (image.length - offset < 12) throw new Error('Style-system returned an invalid PNG image');\n const length = image.readUInt32BE(offset);\n const end = offset + 12 + length;\n if (end > image.length) throw new Error('Style-system returned an invalid PNG image');\n const type = image.toString('ascii', offset + 4, offset + 8);\n if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error('Style-system returned an invalid PNG image');\n if (image.readUInt32BE(end - 4) !== pngCrc(image.subarray(offset + 4, end - 4))) {\n throw new Error('Style-system returned an invalid PNG image');\n }\n if (chunks === 0) {\n if (type !== 'IHDR' || length !== 13) throw new Error('Style-system returned an invalid PNG image');\n width = image.readUInt32BE(offset + 8);\n height = image.readUInt32BE(offset + 12);\n } else if (type === 'IHDR') throw new Error('Style-system returned an invalid PNG image');\n if (type === 'IDAT') hasImageData = true;\n if (type === 'IEND') {\n if (length !== 0 || end !== image.length) throw new Error('Style-system returned an invalid PNG image');\n hasEnd = true;\n }\n chunks += 1;\n offset = end;\n }\n if (!hasImageData || !hasEnd) throw new Error('Style-system returned an invalid PNG image');\n if (width === 0 || height === 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) {\n throw new Error('Style-system returned PNG dimensions outside the supported range');\n }\n return { width, height };\n}\n\n/** Owns disposable static story-preview artifacts for one trusted workspace. */\nexport class StoryPreviewService {\n private readonly root: string;\n private readonly dependencies: StoryPreviewServiceDependencies;\n private readonly artifacts = new Map<string, OwnedArtifact>();\n\n constructor(workspaceRoot: string, dependencies: Partial<StoryPreviewServiceDependencies> = {}) {\n this.root = path.resolve(workspaceRoot);\n this.dependencies = { ...defaultDependencies, ...dependencies };\n }\n\n async metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView> {\n const root = await fs.realpath(this.root);\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n const project = await this.resolveProject(root, storyPath, input.appPath);\n const source = await fs.readFile(storyPath, 'utf8');\n return {\n storyPath: relativeWorkspacePath(root, storyPath),\n appPath: relativeWorkspacePath(root, project.path),\n projectResolution: project.resolution,\n exports: extractStoryExports(source),\n };\n }\n\n private async resolveProject(\n root: string,\n storyPath: string,\n requestedPath: string | undefined,\n ): Promise<{ path: string; resolution: StoryPreviewMetadataView['projectResolution'] }> {\n if (requestedPath !== undefined && requestedPath.trim() !== '') {\n return { path: await this.resolveContainedPath(root, requestedPath, 'appPath'), resolution: 'explicit' };\n }\n let directory = path.dirname(storyPath);\n let packagePath: string | undefined;\n while (isContained(root, directory)) {\n if (await exists(path.join(directory, 'style-system.config.yaml'))) {\n return { path: directory, resolution: 'config' };\n }\n if (packagePath === undefined && (await exists(path.join(directory, 'package.json')))) packagePath = directory;\n if (directory === root) break;\n directory = path.dirname(directory);\n }\n return packagePath === undefined\n ? { path: root, resolution: 'workspace' }\n : { path: packagePath, resolution: 'package' };\n }\n\n private async exactStory(root: string, storyPath: string, storyExport: string) {\n const source = await fs.readFile(storyPath, 'utf8');\n if (!extractStoryExports(source).some((entry) => entry.exportName === storyExport)) {\n throw new Error(`Story export \"${storyExport}\" was not found in ${relativeWorkspacePath(root, storyPath)}.`);\n }\n const index = new StoriesIndexService({ workspaceRoot: root, storyFiles: [storyPath], searchRoots: [] });\n await index.initialize();\n const component = index.getAllComponents().find((entry) => path.resolve(entry.filePath) === storyPath);\n if (component === undefined) throw new Error('The requested story file could not be indexed.');\n return component;\n }\n\n async build(input: StoryPreviewBuildInput): Promise<StoryPreviewBuildResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n await this.exactStory(root, storyPath, input.storyExport);\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const bundler = this.dependencies.createBundler(config);\n let artifactDirectory: string | undefined;\n try {\n const rendered = await bundler.prerenderComponent({\n componentPath: storyPath,\n storyName: input.storyExport,\n args: input.args,\n darkMode: input.darkMode,\n appPath,\n themePath: config.themeProvider,\n cssFiles: config.cssFiles,\n rootComponent: config.rootComponent,\n unistylesConfig: config.unistylesConfig,\n });\n\n const htmlPath = await fs.realpath(rendered.htmlFilePath);\n const candidateDirectory = path.dirname(htmlPath);\n const temporaryRoot = path.join(appPath, '.tmp');\n if (candidateDirectory === temporaryRoot || !isContained(temporaryRoot, candidateDirectory)) {\n throw new Error('Style-system returned a preview artifact outside the project temporary directory');\n }\n artifactDirectory = candidateDirectory;\n\n const handle = this.dependencies.createHandle();\n const html = await fs.readFile(htmlPath, 'utf8');\n this.artifacts.set(handle, { directory: artifactDirectory });\n return {\n handle,\n html,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } catch (reason) {\n if (artifactDirectory !== undefined) await fs.rm(artifactDirectory, { recursive: true, force: true });\n throw reason;\n }\n }\n\n /** Renders a fresh source-backed PNG. This does not preserve transient iframe interaction state. */\n async exportImage(input: StoryPreviewBuildInput): Promise<StoryPreviewImageResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const renderer = this.dependencies.createRenderer(config, appPath);\n let ownedImagePath: string | undefined;\n try {\n const component = await this.exactStory(root, storyPath, input.storyExport);\n const rendered = await renderer.renderComponent(component, {\n storyName: input.storyExport,\n darkMode: input.darkMode,\n });\n const [renderedImagePath, imageRoot] = await Promise.all([\n fs.realpath(rendered.imagePath),\n fs.realpath(this.dependencies.rendererTemporaryRoot()),\n ]);\n if (renderedImagePath === imageRoot || !isContained(imageRoot, renderedImagePath)) {\n throw new Error('Style-system returned an image artifact outside its temporary directory');\n }\n const imageStat = await fs.stat(renderedImagePath);\n if (!imageStat.isFile()) throw new Error('Style-system returned a non-regular PNG image');\n ownedImagePath = renderedImagePath;\n if (imageStat.size > MAX_IMAGE_BYTES) throw new Error('Style-system returned an oversized PNG image');\n const image = await fs.readFile(renderedImagePath);\n if (image.length !== imageStat.size) throw new Error('Style-system PNG changed while it was being read');\n const { width, height } = pngDimensions(image);\n return {\n data: image.toString('base64'),\n mimeType: 'image/png',\n captureId: this.dependencies.createHandle(),\n width,\n height,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } finally {\n try {\n if (ownedImagePath !== undefined) await fs.rm(ownedImagePath, { force: true });\n } finally {\n await renderer.dispose();\n }\n }\n }\n\n async dispose(handle: string): Promise<boolean> {\n const artifact = this.artifacts.get(handle);\n if (artifact === undefined) return false;\n this.artifacts.delete(handle);\n await fs.rm(artifact.directory, { recursive: true, force: true });\n return true;\n }\n\n async disposeAll(): Promise<void> {\n await Promise.all([...this.artifacts.keys()].map((handle) => this.dispose(handle)));\n }\n\n private async resolveContainedPath(root: string, inputPath: string, field: string): Promise<string> {\n if (inputPath.trim() === '') throw new Error(`${field} is required`);\n const candidate = path.isAbsolute(inputPath) ? inputPath : path.resolve(root, inputPath);\n const canonical = await fs.realpath(candidate);\n if (!isContained(root, canonical)) throw new Error(`${field} must remain inside the workspace root`);\n return canonical;\n }\n}\n\nexport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewRenderer,\n StoryPreviewServiceDependencies,\n} from './type';\n"],"mappings":";;;;;;;;;AAsBA,MAAM,qBAAqB;AAC3B,MAAM,uBAAuB;AAC7B,MAAM,gBAAgB,OAAO,KAAK;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI,CAAC;AAClF,MAAM,kBAAkB;AACxB,MAAM,sBAAsB;AAC5B,MAAM,oCAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAMD,SAAS,YAAY,MAAc,WAA4B;CAC7D,MAAM,WAAWA,UAAAA,QAAK,SAAS,MAAM,SAAS;CAC9C,OAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAKA,UAAAA,QAAK,KAAK,KAAK,aAAa,QAAQ,CAACA,UAAAA,QAAK,WAAW,QAAQ;AACpH;AAEA,SAAS,cAAc,QAAwB;CAC7C,IAAI,SAAS;CACb,IAAI;CACJ,IAAI,UAAU;CACd,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;EACrD,MAAM,YAAY,OAAO;EACzB,MAAM,OAAO,OAAO,QAAQ;EAC5B,IAAI,UAAU,KAAA,GAAW;GACvB,UAAU,cAAc,OAAO,OAAO;GACtC,IAAI,SAAS,UAAU;QAClB,IAAI,cAAc,MAAM,UAAU;QAClC,IAAI,cAAc,OAAO,QAAQ,KAAA;GACtC;EACF;EACA,IAAI,cAAc,QAAO,cAAc,OAAO,cAAc,KAAK;GAC/D,QAAQ;GACR,UAAU;EACZ,OAAO,IAAI,cAAc,OAAO,SAAS,KAAK;GAC5C,UAAU;GACV,SAAS;GACT,OAAO,QAAQ,OAAO,UAAU,OAAO,WAAW,MAAM;IACtD,UAAU;IACV,SAAS;GACX;GACA,IAAI,QAAQ,OAAO,QAAQ,UAAU;EACvC,OAAO,IAAI,cAAc,OAAO,SAAS,KAAK;GAC5C,UAAU;GACV,SAAS;GACT,OAAO,QAAQ,OAAO,UAAU,EAAE,OAAO,QAAQ,OAAO,OAAO,OAAO,WAAW,MAAM;IACrF,UAAU,OAAO,WAAW,OAAO,OAAO;IAC1C,SAAS;GACX;EACF,OAAO,UAAU;CACnB;CACA,OAAO;AACT;AAEA,SAAS,WAAW,QAAgB,OAAe,YAAwC;CACzF,MAAM,gBAAgB,WAAW,QAAQ,wBAAwB,MAAM;CACvE,MAAM,cAAc,IAAI,OAAO,qCAAqC,cAAc,mBAAmB,GAAG;CACxG,MAAM,QAAQ,MAAM,MAAM,WAAW;CACrC,IAAI,UAAU,QAAQ,MAAM,UAAU,KAAA,GAAW,OAAO,KAAA;CACxD,MAAM,UAAU,MAAM,QAAQ,KAAK,MAAM,KAAK;CAC9C,IAAI,QAAQ;CACZ,KAAK,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,SAAS,GACvD,IAAI,MAAM,WAAW,KAAK,SAAS;MAC9B,IAAI,MAAM,WAAW,KAAK;EAC7B,SAAS;EACT,IAAI,UAAU,GAAG;GACf,MAAM,QAAQ,OACX,MAAM,SAAS,KAAK,CAAC,CACrB,MAAM,6BAA6B,CAAC,GAAG,EAAE,EACxC,KAAK;GACT,OAAO,UAAU,KAAK,KAAA,IAAY;EACpC;CACF;AAGJ;;AAGA,SAAgB,oBAAoB,QAA+C;CACjF,MAAM,QAAkB,CAAC;CACzB,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,OAAO,SAAuB;EAClC,IAAI,SAAS,aAAa,kBAAkB,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG;EACzE,KAAK,IAAI,IAAI;EACb,MAAM,KAAK,IAAI;CACjB;CACA,MAAM,QAAQ,cAAc,MAAM;CAElC,KAAK,MAAM,SAAS,MAAM,SAAS,yFAAY,GAAG,IAAI,MAAM,EAAG;CAE/D,KAAK,MAAM,SAAS,MAAM,SAAS,0BAAK,GACtC,KAAK,MAAM,QAAQ,MAAM,EAAE,CAAE,MAAM,GAAG,GAAG;EAEvC,MAAM,QADQ,KAAK,KAAK,CAAC,CAAC,MAAM,WACd,CAAC,CAAC,GAAG,EAAE,KAAK,GAAA,CAAI,KAAK;EACvC,IAAI,8BAA8B,KAAK,IAAI,GAAG,IAAI,IAAI;CACxD;CAEF,OAAO,MAAM,KAAK,eAAe;EAC/B,MAAM,QAAQ,WAAW,QAAQ,OAAO,UAAU;EAClD,OAAO,UAAU,KAAA,IAAY,EAAE,WAAW,IAAI;GAAE;GAAY;EAAM;CACpE,CAAC;AACH;AAEA,eAAe,OAAO,UAAoC;CACxD,IAAI;EACF,MAAMC,QAAAA,SAAG,KAAK,QAAQ;EACtB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,sBAAsB,MAAc,WAA2B;CACtE,MAAM,WAAWD,UAAAA,QAAK,SAAS,MAAM,SAAS,CAAC,CAAC,MAAMA,UAAAA,QAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CACxE,OAAO,aAAa,KAAK,MAAM;AACjC;AAEA,MAAM,sBAAuD;CAC3D,YAAYE,wBAAAA;CACZ,cAAc,SAAiD;EAC7D,QAAA,GAAOC,wBAAAA,4BAAAA,CAA4B;CACrC;CACA,eAAe,QAAQ,SAAS;EAC9B,OAAO,IAAIC,wBAAAA,yBAAyB,QAAQ,OAAO;CACrD;CACA,6BAA6BJ,UAAAA,QAAK,KAAKK,QAAAA,QAAG,OAAO,GAAG,cAAc;CAClE,cAAcC,YAAAA;AAChB;AAEA,SAAS,OAAO,MAAsB;CACpC,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,MAAM;EACvB,OAAO;EACP,KAAK,IAAI,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAO,QAAQ,KAAM,MAAM,IAAI,aAAa;CACnF;CACA,QAAQ,MAAM,gBAAgB;AAChC;AAEA,SAAS,cAAc,OAAkD;CACvE,IAAI,MAAM,SAAS,cAAc,UAAU,CAAC,MAAM,SAAS,GAAG,cAAc,MAAM,CAAC,CAAC,OAAO,aAAa,GACtG,MAAM,IAAI,MAAM,4CAA4C;CAE9D,IAAI,SAAS,cAAc;CAC3B,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,eAAe;CACnB,IAAI,SAAS;CACb,OAAO,SAAS,MAAM,QAAQ;EAC5B,IAAI,MAAM,SAAS,SAAS,IAAI,MAAM,IAAI,MAAM,4CAA4C;EAC5F,MAAM,SAAS,MAAM,aAAa,MAAM;EACxC,MAAM,MAAM,SAAS,KAAK;EAC1B,IAAI,MAAM,MAAM,QAAQ,MAAM,IAAI,MAAM,4CAA4C;EACpF,MAAM,OAAO,MAAM,SAAS,SAAS,SAAS,GAAG,SAAS,CAAC;EAC3D,IAAI,CAAC,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,MAAM,4CAA4C;EAC9F,IAAI,MAAM,aAAa,MAAM,CAAC,MAAM,OAAO,MAAM,SAAS,SAAS,GAAG,MAAM,CAAC,CAAC,GAC5E,MAAM,IAAI,MAAM,4CAA4C;EAE9D,IAAI,WAAW,GAAG;GAChB,IAAI,SAAS,UAAU,WAAW,IAAI,MAAM,IAAI,MAAM,4CAA4C;GAClG,QAAQ,MAAM,aAAa,SAAS,CAAC;GACrC,SAAS,MAAM,aAAa,SAAS,EAAE;EACzC,OAAO,IAAI,SAAS,QAAQ,MAAM,IAAI,MAAM,4CAA4C;EACxF,IAAI,SAAS,QAAQ,eAAe;EACpC,IAAI,SAAS,QAAQ;GACnB,IAAI,WAAW,KAAK,QAAQ,MAAM,QAAQ,MAAM,IAAI,MAAM,4CAA4C;GACtG,SAAS;EACX;EACA,UAAU;EACV,SAAS;CACX;CACA,IAAI,CAAC,gBAAgB,CAAC,QAAQ,MAAM,IAAI,MAAM,4CAA4C;CAC1F,IAAI,UAAU,KAAK,WAAW,KAAK,QAAQ,uBAAuB,SAAS,qBACzE,MAAM,IAAI,MAAM,kEAAkE;CAEpF,OAAO;EAAE;EAAO;CAAO;AACzB;;AAGA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA,4BAA6B,IAAI,IAA2B;CAE5D,YAAY,eAAuB,eAAyD,CAAC,GAAG;EAC9F,KAAK,OAAON,UAAAA,QAAK,QAAQ,aAAa;EACtC,KAAK,eAAe;GAAE,GAAG;GAAqB,GAAG;EAAa;CAChE;CAEA,MAAM,SAAS,OAAuE;EACpF,MAAM,OAAO,MAAMC,QAAAA,SAAG,SAAS,KAAK,IAAI;EACxC,MAAM,YAAY,MAAM,KAAK,qBAAqB,MAAM,MAAM,WAAW,WAAW;EACpF,IAAI,CAAC,mBAAmB,KAAK,SAAS,GACpC,MAAM,IAAI,MAAM,oFAAoF;EACtG,MAAM,UAAU,MAAM,KAAK,eAAe,MAAM,WAAW,MAAM,OAAO;EACxE,MAAM,SAAS,MAAMA,QAAAA,SAAG,SAAS,WAAW,MAAM;EAClD,OAAO;GACL,WAAW,sBAAsB,MAAM,SAAS;GAChD,SAAS,sBAAsB,MAAM,QAAQ,IAAI;GACjD,mBAAmB,QAAQ;GAC3B,SAAS,oBAAoB,MAAM;EACrC;CACF;CAEA,MAAc,eACZ,MACA,WACA,eACsF;EACtF,IAAI,kBAAkB,KAAA,KAAa,cAAc,KAAK,MAAM,IAC1D,OAAO;GAAE,MAAM,MAAM,KAAK,qBAAqB,MAAM,eAAe,SAAS;GAAG,YAAY;EAAW;EAEzG,IAAI,YAAYD,UAAAA,QAAK,QAAQ,SAAS;EACtC,IAAI;EACJ,OAAO,YAAY,MAAM,SAAS,GAAG;GACnC,IAAI,MAAM,OAAOA,UAAAA,QAAK,KAAK,WAAW,0BAA0B,CAAC,GAC/D,OAAO;IAAE,MAAM;IAAW,YAAY;GAAS;GAEjD,IAAI,gBAAgB,KAAA,KAAc,MAAM,OAAOA,UAAAA,QAAK,KAAK,WAAW,cAAc,CAAC,GAAI,cAAc;GACrG,IAAI,cAAc,MAAM;GACxB,YAAYA,UAAAA,QAAK,QAAQ,SAAS;EACpC;EACA,OAAO,gBAAgB,KAAA,IACnB;GAAE,MAAM;GAAM,YAAY;EAAY,IACtC;GAAE,MAAM;GAAa,YAAY;EAAU;CACjD;CAEA,MAAc,WAAW,MAAc,WAAmB,aAAqB;EAE7E,IAAI,CAAC,oBAAoB,MADJC,QAAAA,SAAG,SAAS,WAAW,MAAM,CACnB,CAAC,CAAC,MAAM,UAAU,MAAM,eAAe,WAAW,GAC/E,MAAM,IAAI,MAAM,iBAAiB,YAAY,qBAAqB,sBAAsB,MAAM,SAAS,EAAE,EAAE;EAE7G,MAAM,QAAQ,IAAIM,wBAAAA,oBAAoB;GAAE,eAAe;GAAM,YAAY,CAAC,SAAS;GAAG,aAAa,CAAC;EAAE,CAAC;EACvG,MAAM,MAAM,WAAW;EACvB,MAAM,YAAY,MAAM,iBAAiB,CAAC,CAAC,MAAM,UAAUP,UAAAA,QAAK,QAAQ,MAAM,QAAQ,MAAM,SAAS;EACrG,IAAI,cAAc,KAAA,GAAW,MAAM,IAAI,MAAM,gDAAgD;EAC7F,OAAO;CACT;CAEA,MAAM,MAAM,OAAiE;EAC3E,IAAI,CAAC,qBAAqB,KAAK,MAAM,WAAW,GAC9C,MAAM,IAAI,MAAM,sDAAsD;EAGxE,MAAM,OAAO,MAAMC,QAAAA,SAAG,SAAS,KAAK,IAAI;EACxC,MAAM,UAAU,MAAM,KAAK,qBAAqB,MAAM,MAAM,SAAS,SAAS;EAC9E,MAAM,YAAY,MAAM,KAAK,qBAAqB,MAAM,MAAM,WAAW,WAAW;EACpF,IAAI,CAAC,mBAAmB,KAAK,SAAS,GACpC,MAAM,IAAI,MAAM,oFAAoF;EACtG,MAAM,KAAK,WAAW,MAAM,WAAW,MAAM,WAAW;EAExD,MAAM,CAAC,QAAQ,UAAU,MAAM,QAAQ,IAAI,CAACA,QAAAA,SAAG,SAAS,WAAW,MAAM,GAAG,KAAK,aAAa,WAAW,OAAO,CAAC,CAAC;EAClH,MAAM,UAAU,KAAK,aAAa,cAAc,MAAM;EACtD,IAAI;EACJ,IAAI;GACF,MAAM,WAAW,MAAM,QAAQ,mBAAmB;IAChD,eAAe;IACf,WAAW,MAAM;IACjB,MAAM,MAAM;IACZ,UAAU,MAAM;IAChB;IACA,WAAW,OAAO;IAClB,UAAU,OAAO;IACjB,eAAe,OAAO;IACtB,iBAAiB,OAAO;GAC1B,CAAC;GAED,MAAM,WAAW,MAAMA,QAAAA,SAAG,SAAS,SAAS,YAAY;GACxD,MAAM,qBAAqBD,UAAAA,QAAK,QAAQ,QAAQ;GAChD,MAAM,gBAAgBA,UAAAA,QAAK,KAAK,SAAS,MAAM;GAC/C,IAAI,uBAAuB,iBAAiB,CAAC,YAAY,eAAe,kBAAkB,GACxF,MAAM,IAAI,MAAM,kFAAkF;GAEpG,oBAAoB;GAEpB,MAAM,SAAS,KAAK,aAAa,aAAa;GAC9C,MAAM,OAAO,MAAMC,QAAAA,SAAG,SAAS,UAAU,MAAM;GAC/C,KAAK,UAAU,IAAI,QAAQ,EAAE,WAAW,kBAAkB,CAAC;GAC3D,OAAO;IACL;IACA;IACA,WAAW,sBAAsB,MAAM,SAAS;IAChD,aAAa,MAAM;IACnB,eAAA,GAAcO,YAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;GAChE;EACF,SAAS,QAAQ;GACf,IAAI,sBAAsB,KAAA,GAAW,MAAMP,QAAAA,SAAG,GAAG,mBAAmB;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;GACpG,MAAM;EACR;CACF;;CAGA,MAAM,YAAY,OAAiE;EACjF,IAAI,CAAC,qBAAqB,KAAK,MAAM,WAAW,GAC9C,MAAM,IAAI,MAAM,sDAAsD;EAExE,MAAM,OAAO,MAAMA,QAAAA,SAAG,SAAS,KAAK,IAAI;EACxC,MAAM,UAAU,MAAM,KAAK,qBAAqB,MAAM,MAAM,SAAS,SAAS;EAC9E,MAAM,YAAY,MAAM,KAAK,qBAAqB,MAAM,MAAM,WAAW,WAAW;EACpF,IAAI,CAAC,mBAAmB,KAAK,SAAS,GACpC,MAAM,IAAI,MAAM,oFAAoF;EAEtG,MAAM,CAAC,QAAQ,UAAU,MAAM,QAAQ,IAAI,CAACA,QAAAA,SAAG,SAAS,WAAW,MAAM,GAAG,KAAK,aAAa,WAAW,OAAO,CAAC,CAAC;EAClH,MAAM,WAAW,KAAK,aAAa,eAAe,QAAQ,OAAO;EACjE,IAAI;EACJ,IAAI;GACF,MAAM,YAAY,MAAM,KAAK,WAAW,MAAM,WAAW,MAAM,WAAW;GAC1E,MAAM,WAAW,MAAM,SAAS,gBAAgB,WAAW;IACzD,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GACD,MAAM,CAAC,mBAAmB,aAAa,MAAM,QAAQ,IAAI,CACvDA,QAAAA,SAAG,SAAS,SAAS,SAAS,GAC9BA,QAAAA,SAAG,SAAS,KAAK,aAAa,sBAAsB,CAAC,CACvD,CAAC;GACD,IAAI,sBAAsB,aAAa,CAAC,YAAY,WAAW,iBAAiB,GAC9E,MAAM,IAAI,MAAM,yEAAyE;GAE3F,MAAM,YAAY,MAAMA,QAAAA,SAAG,KAAK,iBAAiB;GACjD,IAAI,CAAC,UAAU,OAAO,GAAG,MAAM,IAAI,MAAM,+CAA+C;GACxF,iBAAiB;GACjB,IAAI,UAAU,OAAO,iBAAiB,MAAM,IAAI,MAAM,8CAA8C;GACpG,MAAM,QAAQ,MAAMA,QAAAA,SAAG,SAAS,iBAAiB;GACjD,IAAI,MAAM,WAAW,UAAU,MAAM,MAAM,IAAI,MAAM,kDAAkD;GACvG,MAAM,EAAE,OAAO,WAAW,cAAc,KAAK;GAC7C,OAAO;IACL,MAAM,MAAM,SAAS,QAAQ;IAC7B,UAAU;IACV,WAAW,KAAK,aAAa,aAAa;IAC1C;IACA;IACA,WAAW,sBAAsB,MAAM,SAAS;IAChD,aAAa,MAAM;IACnB,eAAA,GAAcO,YAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;GAChE;EACF,UAAU;GACR,IAAI;IACF,IAAI,mBAAmB,KAAA,GAAW,MAAMP,QAAAA,SAAG,GAAG,gBAAgB,EAAE,OAAO,KAAK,CAAC;GAC/E,UAAU;IACR,MAAM,SAAS,QAAQ;GACzB;EACF;CACF;CAEA,MAAM,QAAQ,QAAkC;EAC9C,MAAM,WAAW,KAAK,UAAU,IAAI,MAAM;EAC1C,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,KAAK,UAAU,OAAO,MAAM;EAC5B,MAAMA,QAAAA,SAAG,GAAG,SAAS,WAAW;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAChE,OAAO;CACT;CAEA,MAAM,aAA4B;EAChC,MAAM,QAAQ,IAAI,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,KAAK,WAAW,KAAK,QAAQ,MAAM,CAAC,CAAC;CACpF;CAEA,MAAc,qBAAqB,MAAc,WAAmB,OAAgC;EAClG,IAAI,UAAU,KAAK,MAAM,IAAI,MAAM,IAAI,MAAM,GAAG,MAAM,aAAa;EACnE,MAAM,YAAYD,UAAAA,QAAK,WAAW,SAAS,IAAI,YAAYA,UAAAA,QAAK,QAAQ,MAAM,SAAS;EACvF,MAAM,YAAY,MAAMC,QAAAA,SAAG,SAAS,SAAS;EAC7C,IAAI,CAAC,YAAY,MAAM,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,uCAAuC;EACnG,OAAO;CACT;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from "../../types/previewApi.cjs";
|
|
2
|
-
import { StoryPreviewBuildInput, StoryPreviewBuildResult, StoryPreviewImageResult, StoryPreviewServiceDependencies } from "./type.cjs";
|
|
2
|
+
import { StoryPreviewBuildInput, StoryPreviewBuildResult, StoryPreviewImageResult, StoryPreviewRenderer, StoryPreviewServiceDependencies } from "./type.cjs";
|
|
3
3
|
//#region src/services/storyPreview/index.d.ts
|
|
4
4
|
/** Extracts named CSF candidates without evaluating workspace source. */
|
|
5
5
|
export declare function extractStoryExports(source: string): readonly StoryPreviewExport[];
|
|
@@ -8,7 +8,7 @@ export declare class StoryPreviewService {
|
|
|
8
8
|
private readonly root;
|
|
9
9
|
private readonly dependencies;
|
|
10
10
|
private readonly artifacts;
|
|
11
|
-
constructor(workspaceRoot: string, dependencies?: StoryPreviewServiceDependencies);
|
|
11
|
+
constructor(workspaceRoot: string, dependencies?: Partial<StoryPreviewServiceDependencies>);
|
|
12
12
|
metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView>;
|
|
13
13
|
private resolveProject;
|
|
14
14
|
private exactStory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/services/storyPreview/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/services/storyPreview/index.ts"],"mappings":";;;;wBAmHgB,oBAAoB,0BAA0B;;qBAqGjD;mBACM;mBACA;mBACA;EAEjB,YAAY,uBAAuB,eAAc,QAAQ;EAKnD,SAAS,OAAO,8BAA8B,QAAQ;UAe9C;UAuBA;EAYR,MAAM,OAAO,yBAAyB,QAAQ;;EAqD9C,YAAY,OAAO,yBAAyB,QAAQ;EAoDpD,QAAQ,iBAAiB;EAQzB,cAAc;UAIN"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from "../../types/previewApi.mjs";
|
|
2
|
-
import { StoryPreviewBuildInput, StoryPreviewBuildResult, StoryPreviewImageResult, StoryPreviewServiceDependencies } from "./type.mjs";
|
|
2
|
+
import { StoryPreviewBuildInput, StoryPreviewBuildResult, StoryPreviewImageResult, StoryPreviewRenderer, StoryPreviewServiceDependencies } from "./type.mjs";
|
|
3
3
|
//#region src/services/storyPreview/index.d.ts
|
|
4
4
|
/** Extracts named CSF candidates without evaluating workspace source. */
|
|
5
5
|
export declare function extractStoryExports(source: string): readonly StoryPreviewExport[];
|
|
@@ -8,7 +8,7 @@ export declare class StoryPreviewService {
|
|
|
8
8
|
private readonly root;
|
|
9
9
|
private readonly dependencies;
|
|
10
10
|
private readonly artifacts;
|
|
11
|
-
constructor(workspaceRoot: string, dependencies?: StoryPreviewServiceDependencies);
|
|
11
|
+
constructor(workspaceRoot: string, dependencies?: Partial<StoryPreviewServiceDependencies>);
|
|
12
12
|
metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView>;
|
|
13
13
|
private resolveProject;
|
|
14
14
|
private exactStory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/storyPreview/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/storyPreview/index.ts"],"mappings":";;;;wBAmHgB,oBAAoB,0BAA0B;;qBAqGjD;mBACM;mBACA;mBACA;EAEjB,YAAY,uBAAuB,eAAc,QAAQ;EAKnD,SAAS,OAAO,8BAA8B,QAAQ;UAe9C;UAuBA;EAYR,MAAM,OAAO,yBAAyB,QAAQ;;EAqD9C,YAAY,OAAO,yBAAyB,QAAQ;EAoDpD,QAAQ,iBAAiB;EAQzB,cAAc;UAIN"}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { promises } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import { ComponentRendererService, StoriesIndexService, createDefaultBundlerService, getAppDesignSystemConfig } from "@agimon-ai/style-system";
|
|
5
6
|
//#region src/services/storyPreview/index.ts
|
|
6
7
|
const STORY_FILE_PATTERN = /\.stories\.(?:js|jsx|ts|tsx)$/u;
|
|
7
8
|
const STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
9
|
+
const PNG_SIGNATURE = Buffer.from([
|
|
10
|
+
137,
|
|
11
|
+
80,
|
|
12
|
+
78,
|
|
13
|
+
71,
|
|
14
|
+
13,
|
|
15
|
+
10,
|
|
16
|
+
26,
|
|
17
|
+
10
|
|
18
|
+
]);
|
|
19
|
+
const MAX_IMAGE_BYTES = 2097152;
|
|
20
|
+
const MAX_IMAGE_DIMENSION = 1600;
|
|
8
21
|
const NON_STORY_EXPORTS = /* @__PURE__ */ new Set([
|
|
9
22
|
"afterAll",
|
|
10
23
|
"afterEach",
|
|
@@ -118,16 +131,67 @@ const defaultDependencies = {
|
|
|
118
131
|
createBundler(_config) {
|
|
119
132
|
return createDefaultBundlerService();
|
|
120
133
|
},
|
|
134
|
+
createRenderer(config, appPath) {
|
|
135
|
+
return new ComponentRendererService(config, appPath);
|
|
136
|
+
},
|
|
137
|
+
rendererTemporaryRoot: () => path.join(os.tmpdir(), "style-system"),
|
|
121
138
|
createHandle: randomUUID
|
|
122
139
|
};
|
|
140
|
+
function pngCrc(data) {
|
|
141
|
+
let crc = 4294967295;
|
|
142
|
+
for (const byte of data) {
|
|
143
|
+
crc ^= byte;
|
|
144
|
+
for (let bit = 0; bit < 8; bit += 1) crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
145
|
+
}
|
|
146
|
+
return (crc ^ 4294967295) >>> 0;
|
|
147
|
+
}
|
|
148
|
+
function pngDimensions(image) {
|
|
149
|
+
if (image.length < PNG_SIGNATURE.length || !image.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)) throw new Error("Style-system returned an invalid PNG image");
|
|
150
|
+
let offset = PNG_SIGNATURE.length;
|
|
151
|
+
let width = 0;
|
|
152
|
+
let height = 0;
|
|
153
|
+
let chunks = 0;
|
|
154
|
+
let hasImageData = false;
|
|
155
|
+
let hasEnd = false;
|
|
156
|
+
while (offset < image.length) {
|
|
157
|
+
if (image.length - offset < 12) throw new Error("Style-system returned an invalid PNG image");
|
|
158
|
+
const length = image.readUInt32BE(offset);
|
|
159
|
+
const end = offset + 12 + length;
|
|
160
|
+
if (end > image.length) throw new Error("Style-system returned an invalid PNG image");
|
|
161
|
+
const type = image.toString("ascii", offset + 4, offset + 8);
|
|
162
|
+
if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error("Style-system returned an invalid PNG image");
|
|
163
|
+
if (image.readUInt32BE(end - 4) !== pngCrc(image.subarray(offset + 4, end - 4))) throw new Error("Style-system returned an invalid PNG image");
|
|
164
|
+
if (chunks === 0) {
|
|
165
|
+
if (type !== "IHDR" || length !== 13) throw new Error("Style-system returned an invalid PNG image");
|
|
166
|
+
width = image.readUInt32BE(offset + 8);
|
|
167
|
+
height = image.readUInt32BE(offset + 12);
|
|
168
|
+
} else if (type === "IHDR") throw new Error("Style-system returned an invalid PNG image");
|
|
169
|
+
if (type === "IDAT") hasImageData = true;
|
|
170
|
+
if (type === "IEND") {
|
|
171
|
+
if (length !== 0 || end !== image.length) throw new Error("Style-system returned an invalid PNG image");
|
|
172
|
+
hasEnd = true;
|
|
173
|
+
}
|
|
174
|
+
chunks += 1;
|
|
175
|
+
offset = end;
|
|
176
|
+
}
|
|
177
|
+
if (!hasImageData || !hasEnd) throw new Error("Style-system returned an invalid PNG image");
|
|
178
|
+
if (width === 0 || height === 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) throw new Error("Style-system returned PNG dimensions outside the supported range");
|
|
179
|
+
return {
|
|
180
|
+
width,
|
|
181
|
+
height
|
|
182
|
+
};
|
|
183
|
+
}
|
|
123
184
|
/** Owns disposable static story-preview artifacts for one trusted workspace. */
|
|
124
185
|
var StoryPreviewService = class {
|
|
125
186
|
root;
|
|
126
187
|
dependencies;
|
|
127
188
|
artifacts = /* @__PURE__ */ new Map();
|
|
128
|
-
constructor(workspaceRoot, dependencies =
|
|
189
|
+
constructor(workspaceRoot, dependencies = {}) {
|
|
129
190
|
this.root = path.resolve(workspaceRoot);
|
|
130
|
-
this.dependencies =
|
|
191
|
+
this.dependencies = {
|
|
192
|
+
...defaultDependencies,
|
|
193
|
+
...dependencies
|
|
194
|
+
};
|
|
131
195
|
}
|
|
132
196
|
async metadata(input) {
|
|
133
197
|
const root = await promises.realpath(this.root);
|
|
@@ -231,27 +295,36 @@ var StoryPreviewService = class {
|
|
|
231
295
|
const storyPath = await this.resolveContainedPath(root, input.storyPath, "storyPath");
|
|
232
296
|
if (!STORY_FILE_PATTERN.test(storyPath)) throw new Error("storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file");
|
|
233
297
|
const [source, config] = await Promise.all([promises.readFile(storyPath, "utf8"), this.dependencies.loadConfig(appPath)]);
|
|
234
|
-
const renderer =
|
|
235
|
-
let
|
|
298
|
+
const renderer = this.dependencies.createRenderer(config, appPath);
|
|
299
|
+
let ownedImagePath;
|
|
236
300
|
try {
|
|
237
301
|
const component = await this.exactStory(root, storyPath, input.storyExport);
|
|
238
302
|
const rendered = await renderer.renderComponent(component, {
|
|
239
303
|
storyName: input.storyExport,
|
|
240
304
|
darkMode: input.darkMode
|
|
241
305
|
});
|
|
242
|
-
const renderedImagePath = await promises.realpath(rendered.imagePath);
|
|
243
|
-
if (!isContained(
|
|
244
|
-
|
|
306
|
+
const [renderedImagePath, imageRoot] = await Promise.all([promises.realpath(rendered.imagePath), promises.realpath(this.dependencies.rendererTemporaryRoot())]);
|
|
307
|
+
if (renderedImagePath === imageRoot || !isContained(imageRoot, renderedImagePath)) throw new Error("Style-system returned an image artifact outside its temporary directory");
|
|
308
|
+
const imageStat = await promises.stat(renderedImagePath);
|
|
309
|
+
if (!imageStat.isFile()) throw new Error("Style-system returned a non-regular PNG image");
|
|
310
|
+
ownedImagePath = renderedImagePath;
|
|
311
|
+
if (imageStat.size > MAX_IMAGE_BYTES) throw new Error("Style-system returned an oversized PNG image");
|
|
312
|
+
const image = await promises.readFile(renderedImagePath);
|
|
313
|
+
if (image.length !== imageStat.size) throw new Error("Style-system PNG changed while it was being read");
|
|
314
|
+
const { width, height } = pngDimensions(image);
|
|
245
315
|
return {
|
|
246
|
-
data:
|
|
316
|
+
data: image.toString("base64"),
|
|
247
317
|
mimeType: "image/png",
|
|
318
|
+
captureId: this.dependencies.createHandle(),
|
|
319
|
+
width,
|
|
320
|
+
height,
|
|
248
321
|
storyPath: relativeWorkspacePath(root, storyPath),
|
|
249
322
|
storyExport: input.storyExport,
|
|
250
323
|
sourceSha256: createHash("sha256").update(source).digest("hex")
|
|
251
324
|
};
|
|
252
325
|
} finally {
|
|
253
326
|
try {
|
|
254
|
-
if (
|
|
327
|
+
if (ownedImagePath !== void 0) await promises.rm(ownedImagePath, { force: true });
|
|
255
328
|
} finally {
|
|
256
329
|
await renderer.dispose();
|
|
257
330
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["fs"],"sources":["../../../../src/services/storyPreview/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport {\n ComponentRendererService,\n createDefaultBundlerService,\n getAppDesignSystemConfig,\n StoriesIndexService,\n type BaseBundlerService,\n type DesignSystemConfig,\n} from '@agimon-ai/style-system';\n\nimport type { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from '../../types/previewApi';\nimport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n\nconst STORY_FILE_PATTERN = /\\.stories\\.(?:js|jsx|ts|tsx)$/u;\nconst STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst NON_STORY_EXPORTS = new Set([\n 'afterAll',\n 'afterEach',\n 'argTypes',\n 'beforeAll',\n 'beforeEach',\n 'component',\n 'decorators',\n 'excludeStories',\n 'globals',\n 'includeStories',\n 'loaders',\n 'meta',\n 'parameters',\n 'render',\n 'tags',\n]);\n\ninterface OwnedArtifact {\n directory: string;\n}\n\nfunction isContained(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\n}\n\nfunction stripComments(source: string): string {\n let output = '';\n let quote: \"'\" | '\"' | '`' | undefined;\n let escaped = false;\n for (let index = 0; index < source.length; index += 1) {\n const character = source[index];\n const next = source[index + 1];\n if (quote !== undefined) {\n output += character === '\\n' ? '\\n' : ' ';\n if (escaped) escaped = false;\n else if (character === '\\\\') escaped = true;\n else if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\" || character === '`') {\n quote = character;\n output += ' ';\n } else if (character === '/' && next === '/') {\n output += ' ';\n index += 2;\n while (index < source.length && source[index] !== '\\n') {\n output += ' ';\n index += 1;\n }\n if (index < source.length) output += '\\n';\n } else if (character === '/' && next === '*') {\n output += ' ';\n index += 2;\n while (index < source.length && !(source[index - 1] === '*' && source[index] === '/')) {\n output += source[index] === '\\n' ? '\\n' : ' ';\n index += 1;\n }\n } else output += character;\n }\n return output;\n}\n\nfunction storyLabel(source: string, clean: string, exportName: string): string | undefined {\n const escapedExport = exportName.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n const declaration = new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${escapedExport}\\\\b[^=]*=\\\\s*\\\\{`, 'u');\n const match = clean.match(declaration);\n if (match === null || match.index === undefined) return undefined;\n const opening = clean.indexOf('{', match.index);\n let depth = 0;\n for (let index = opening; index < clean.length; index += 1) {\n if (clean[index] === '{') depth += 1;\n else if (clean[index] === '}') {\n depth -= 1;\n if (depth === 0) {\n const label = source\n .slice(opening, index)\n .match(/\\bname\\s*:\\s*(['\"])(.*?)\\1/u)?.[2]\n ?.trim();\n return label === '' ? undefined : label;\n }\n }\n }\n return undefined;\n}\n\n/** Extracts named CSF candidates without evaluating workspace source. */\nexport function extractStoryExports(source: string): readonly StoryPreviewExport[] {\n const names: string[] = [];\n const seen = new Set<string>();\n const add = (name: string): void => {\n if (name === 'default' || NON_STORY_EXPORTS.has(name) || seen.has(name)) return;\n seen.add(name);\n names.push(name);\n };\n const clean = stripComments(source);\n const declarations = /\\bexport\\s+(?:async\\s+)?(?:const|let|var|function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)/gu;\n for (const match of clean.matchAll(declarations)) add(match[1]!);\n const lists = /\\bexport\\s*\\{([^}]*)\\}/gu;\n for (const match of clean.matchAll(lists)) {\n for (const item of match[1]!.split(',')) {\n const parts = item.trim().split(/\\s+as\\s+/u);\n const name = (parts.at(-1) ?? '').trim();\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(name)) add(name);\n }\n }\n return names.map((exportName) => {\n const label = storyLabel(source, clean, exportName);\n return label === undefined ? { exportName } : { exportName, label };\n });\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction relativeWorkspacePath(root: string, candidate: string): string {\n const relative = path.relative(root, candidate).split(path.sep).join('/');\n return relative === '' ? '.' : relative;\n}\n\nconst defaultDependencies: StoryPreviewServiceDependencies = {\n loadConfig: getAppDesignSystemConfig,\n createBundler(_config: DesignSystemConfig): BaseBundlerService {\n return createDefaultBundlerService();\n },\n createHandle: randomUUID,\n};\n\n/** Owns disposable static story-preview artifacts for one trusted workspace. */\nexport class StoryPreviewService {\n private readonly root: string;\n private readonly dependencies: StoryPreviewServiceDependencies;\n private readonly artifacts = new Map<string, OwnedArtifact>();\n\n constructor(workspaceRoot: string, dependencies: StoryPreviewServiceDependencies = defaultDependencies) {\n this.root = path.resolve(workspaceRoot);\n this.dependencies = dependencies;\n }\n\n async metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView> {\n const root = await fs.realpath(this.root);\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n const project = await this.resolveProject(root, storyPath, input.appPath);\n const source = await fs.readFile(storyPath, 'utf8');\n return {\n storyPath: relativeWorkspacePath(root, storyPath),\n appPath: relativeWorkspacePath(root, project.path),\n projectResolution: project.resolution,\n exports: extractStoryExports(source),\n };\n }\n\n private async resolveProject(\n root: string,\n storyPath: string,\n requestedPath: string | undefined,\n ): Promise<{ path: string; resolution: StoryPreviewMetadataView['projectResolution'] }> {\n if (requestedPath !== undefined && requestedPath.trim() !== '') {\n return { path: await this.resolveContainedPath(root, requestedPath, 'appPath'), resolution: 'explicit' };\n }\n let directory = path.dirname(storyPath);\n let packagePath: string | undefined;\n while (isContained(root, directory)) {\n if (await exists(path.join(directory, 'style-system.config.yaml'))) {\n return { path: directory, resolution: 'config' };\n }\n if (packagePath === undefined && (await exists(path.join(directory, 'package.json')))) packagePath = directory;\n if (directory === root) break;\n directory = path.dirname(directory);\n }\n return packagePath === undefined\n ? { path: root, resolution: 'workspace' }\n : { path: packagePath, resolution: 'package' };\n }\n\n private async exactStory(root: string, storyPath: string, storyExport: string) {\n const source = await fs.readFile(storyPath, 'utf8');\n if (!extractStoryExports(source).some((entry) => entry.exportName === storyExport)) {\n throw new Error(`Story export \"${storyExport}\" was not found in ${relativeWorkspacePath(root, storyPath)}.`);\n }\n const index = new StoriesIndexService({ workspaceRoot: root, storyFiles: [storyPath], searchRoots: [] });\n await index.initialize();\n const component = index.getAllComponents().find((entry) => path.resolve(entry.filePath) === storyPath);\n if (component === undefined) throw new Error('The requested story file could not be indexed.');\n return component;\n }\n\n async build(input: StoryPreviewBuildInput): Promise<StoryPreviewBuildResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n await this.exactStory(root, storyPath, input.storyExport);\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const bundler = this.dependencies.createBundler(config);\n let artifactDirectory: string | undefined;\n try {\n const rendered = await bundler.prerenderComponent({\n componentPath: storyPath,\n storyName: input.storyExport,\n args: input.args,\n darkMode: input.darkMode,\n appPath,\n themePath: config.themeProvider,\n cssFiles: config.cssFiles,\n rootComponent: config.rootComponent,\n unistylesConfig: config.unistylesConfig,\n });\n\n const htmlPath = await fs.realpath(rendered.htmlFilePath);\n const candidateDirectory = path.dirname(htmlPath);\n const temporaryRoot = path.join(appPath, '.tmp');\n if (candidateDirectory === temporaryRoot || !isContained(temporaryRoot, candidateDirectory)) {\n throw new Error('Style-system returned a preview artifact outside the project temporary directory');\n }\n artifactDirectory = candidateDirectory;\n\n const handle = this.dependencies.createHandle();\n const html = await fs.readFile(htmlPath, 'utf8');\n this.artifacts.set(handle, { directory: artifactDirectory });\n return {\n handle,\n html,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } catch (reason) {\n if (artifactDirectory !== undefined) await fs.rm(artifactDirectory, { recursive: true, force: true });\n throw reason;\n }\n }\n\n /** Renders a fresh source-backed PNG. This does not preserve transient iframe interaction state. */\n async exportImage(input: StoryPreviewBuildInput): Promise<StoryPreviewImageResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const renderer = new ComponentRendererService(config, appPath);\n let imagePath: string | undefined;\n try {\n const component = await this.exactStory(root, storyPath, input.storyExport);\n const rendered = await renderer.renderComponent(component, {\n storyName: input.storyExport,\n darkMode: input.darkMode,\n });\n const renderedImagePath = await fs.realpath(rendered.imagePath);\n const imageRoot = await fs.realpath(path.join(appPath, '.tmp'));\n if (!isContained(imageRoot, renderedImagePath)) {\n throw new Error('Style-system returned an image artifact outside the project temporary directory');\n }\n imagePath = renderedImagePath;\n const image = await fs.readFile(imagePath);\n return {\n data: image.toString('base64'),\n mimeType: 'image/png',\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } finally {\n try {\n if (imagePath !== undefined) await fs.rm(imagePath, { force: true });\n } finally {\n await renderer.dispose();\n }\n }\n }\n\n async dispose(handle: string): Promise<boolean> {\n const artifact = this.artifacts.get(handle);\n if (artifact === undefined) return false;\n this.artifacts.delete(handle);\n await fs.rm(artifact.directory, { recursive: true, force: true });\n return true;\n }\n\n async disposeAll(): Promise<void> {\n await Promise.all([...this.artifacts.keys()].map((handle) => this.dispose(handle)));\n }\n\n private async resolveContainedPath(root: string, inputPath: string, field: string): Promise<string> {\n if (inputPath.trim() === '') throw new Error(`${field} is required`);\n const candidate = path.isAbsolute(inputPath) ? inputPath : path.resolve(root, inputPath);\n const canonical = await fs.realpath(candidate);\n if (!isContained(root, canonical)) throw new Error(`${field} must remain inside the workspace root`);\n return canonical;\n }\n}\n\nexport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["fs"],"sources":["../../../../src/services/storyPreview/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n ComponentRendererService,\n createDefaultBundlerService,\n getAppDesignSystemConfig,\n StoriesIndexService,\n type BaseBundlerService,\n type DesignSystemConfig,\n} from '@agimon-ai/style-system';\n\nimport type { StoryPreviewExport, StoryPreviewMetadataRequest, StoryPreviewMetadataView } from '../../types/previewApi';\nimport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewServiceDependencies,\n} from './type';\n\nconst STORY_FILE_PATTERN = /\\.stories\\.(?:js|jsx|ts|tsx)$/u;\nconst STORY_EXPORT_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\nconst MAX_IMAGE_BYTES = 2 * 1024 * 1024;\nconst MAX_IMAGE_DIMENSION = 1600;\nconst NON_STORY_EXPORTS = new Set([\n 'afterAll',\n 'afterEach',\n 'argTypes',\n 'beforeAll',\n 'beforeEach',\n 'component',\n 'decorators',\n 'excludeStories',\n 'globals',\n 'includeStories',\n 'loaders',\n 'meta',\n 'parameters',\n 'render',\n 'tags',\n]);\n\ninterface OwnedArtifact {\n directory: string;\n}\n\nfunction isContained(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\n}\n\nfunction stripComments(source: string): string {\n let output = '';\n let quote: \"'\" | '\"' | '`' | undefined;\n let escaped = false;\n for (let index = 0; index < source.length; index += 1) {\n const character = source[index];\n const next = source[index + 1];\n if (quote !== undefined) {\n output += character === '\\n' ? '\\n' : ' ';\n if (escaped) escaped = false;\n else if (character === '\\\\') escaped = true;\n else if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\" || character === '`') {\n quote = character;\n output += ' ';\n } else if (character === '/' && next === '/') {\n output += ' ';\n index += 2;\n while (index < source.length && source[index] !== '\\n') {\n output += ' ';\n index += 1;\n }\n if (index < source.length) output += '\\n';\n } else if (character === '/' && next === '*') {\n output += ' ';\n index += 2;\n while (index < source.length && !(source[index - 1] === '*' && source[index] === '/')) {\n output += source[index] === '\\n' ? '\\n' : ' ';\n index += 1;\n }\n } else output += character;\n }\n return output;\n}\n\nfunction storyLabel(source: string, clean: string, exportName: string): string | undefined {\n const escapedExport = exportName.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n const declaration = new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${escapedExport}\\\\b[^=]*=\\\\s*\\\\{`, 'u');\n const match = clean.match(declaration);\n if (match === null || match.index === undefined) return undefined;\n const opening = clean.indexOf('{', match.index);\n let depth = 0;\n for (let index = opening; index < clean.length; index += 1) {\n if (clean[index] === '{') depth += 1;\n else if (clean[index] === '}') {\n depth -= 1;\n if (depth === 0) {\n const label = source\n .slice(opening, index)\n .match(/\\bname\\s*:\\s*(['\"])(.*?)\\1/u)?.[2]\n ?.trim();\n return label === '' ? undefined : label;\n }\n }\n }\n return undefined;\n}\n\n/** Extracts named CSF candidates without evaluating workspace source. */\nexport function extractStoryExports(source: string): readonly StoryPreviewExport[] {\n const names: string[] = [];\n const seen = new Set<string>();\n const add = (name: string): void => {\n if (name === 'default' || NON_STORY_EXPORTS.has(name) || seen.has(name)) return;\n seen.add(name);\n names.push(name);\n };\n const clean = stripComments(source);\n const declarations = /\\bexport\\s+(?:async\\s+)?(?:const|let|var|function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)/gu;\n for (const match of clean.matchAll(declarations)) add(match[1]!);\n const lists = /\\bexport\\s*\\{([^}]*)\\}/gu;\n for (const match of clean.matchAll(lists)) {\n for (const item of match[1]!.split(',')) {\n const parts = item.trim().split(/\\s+as\\s+/u);\n const name = (parts.at(-1) ?? '').trim();\n if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(name)) add(name);\n }\n }\n return names.map((exportName) => {\n const label = storyLabel(source, clean, exportName);\n return label === undefined ? { exportName } : { exportName, label };\n });\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction relativeWorkspacePath(root: string, candidate: string): string {\n const relative = path.relative(root, candidate).split(path.sep).join('/');\n return relative === '' ? '.' : relative;\n}\n\nconst defaultDependencies: StoryPreviewServiceDependencies = {\n loadConfig: getAppDesignSystemConfig,\n createBundler(_config: DesignSystemConfig): BaseBundlerService {\n return createDefaultBundlerService();\n },\n createRenderer(config, appPath) {\n return new ComponentRendererService(config, appPath);\n },\n rendererTemporaryRoot: () => path.join(os.tmpdir(), 'style-system'),\n createHandle: randomUUID,\n};\n\nfunction pngCrc(data: Buffer): number {\n let crc = 0xffffffff;\n for (const byte of data) {\n crc ^= byte;\n for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction pngDimensions(image: Buffer): { width: number; height: number } {\n if (image.length < PNG_SIGNATURE.length || !image.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)) {\n throw new Error('Style-system returned an invalid PNG image');\n }\n let offset = PNG_SIGNATURE.length;\n let width = 0;\n let height = 0;\n let chunks = 0;\n let hasImageData = false;\n let hasEnd = false;\n while (offset < image.length) {\n if (image.length - offset < 12) throw new Error('Style-system returned an invalid PNG image');\n const length = image.readUInt32BE(offset);\n const end = offset + 12 + length;\n if (end > image.length) throw new Error('Style-system returned an invalid PNG image');\n const type = image.toString('ascii', offset + 4, offset + 8);\n if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error('Style-system returned an invalid PNG image');\n if (image.readUInt32BE(end - 4) !== pngCrc(image.subarray(offset + 4, end - 4))) {\n throw new Error('Style-system returned an invalid PNG image');\n }\n if (chunks === 0) {\n if (type !== 'IHDR' || length !== 13) throw new Error('Style-system returned an invalid PNG image');\n width = image.readUInt32BE(offset + 8);\n height = image.readUInt32BE(offset + 12);\n } else if (type === 'IHDR') throw new Error('Style-system returned an invalid PNG image');\n if (type === 'IDAT') hasImageData = true;\n if (type === 'IEND') {\n if (length !== 0 || end !== image.length) throw new Error('Style-system returned an invalid PNG image');\n hasEnd = true;\n }\n chunks += 1;\n offset = end;\n }\n if (!hasImageData || !hasEnd) throw new Error('Style-system returned an invalid PNG image');\n if (width === 0 || height === 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) {\n throw new Error('Style-system returned PNG dimensions outside the supported range');\n }\n return { width, height };\n}\n\n/** Owns disposable static story-preview artifacts for one trusted workspace. */\nexport class StoryPreviewService {\n private readonly root: string;\n private readonly dependencies: StoryPreviewServiceDependencies;\n private readonly artifacts = new Map<string, OwnedArtifact>();\n\n constructor(workspaceRoot: string, dependencies: Partial<StoryPreviewServiceDependencies> = {}) {\n this.root = path.resolve(workspaceRoot);\n this.dependencies = { ...defaultDependencies, ...dependencies };\n }\n\n async metadata(input: StoryPreviewMetadataRequest): Promise<StoryPreviewMetadataView> {\n const root = await fs.realpath(this.root);\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n const project = await this.resolveProject(root, storyPath, input.appPath);\n const source = await fs.readFile(storyPath, 'utf8');\n return {\n storyPath: relativeWorkspacePath(root, storyPath),\n appPath: relativeWorkspacePath(root, project.path),\n projectResolution: project.resolution,\n exports: extractStoryExports(source),\n };\n }\n\n private async resolveProject(\n root: string,\n storyPath: string,\n requestedPath: string | undefined,\n ): Promise<{ path: string; resolution: StoryPreviewMetadataView['projectResolution'] }> {\n if (requestedPath !== undefined && requestedPath.trim() !== '') {\n return { path: await this.resolveContainedPath(root, requestedPath, 'appPath'), resolution: 'explicit' };\n }\n let directory = path.dirname(storyPath);\n let packagePath: string | undefined;\n while (isContained(root, directory)) {\n if (await exists(path.join(directory, 'style-system.config.yaml'))) {\n return { path: directory, resolution: 'config' };\n }\n if (packagePath === undefined && (await exists(path.join(directory, 'package.json')))) packagePath = directory;\n if (directory === root) break;\n directory = path.dirname(directory);\n }\n return packagePath === undefined\n ? { path: root, resolution: 'workspace' }\n : { path: packagePath, resolution: 'package' };\n }\n\n private async exactStory(root: string, storyPath: string, storyExport: string) {\n const source = await fs.readFile(storyPath, 'utf8');\n if (!extractStoryExports(source).some((entry) => entry.exportName === storyExport)) {\n throw new Error(`Story export \"${storyExport}\" was not found in ${relativeWorkspacePath(root, storyPath)}.`);\n }\n const index = new StoriesIndexService({ workspaceRoot: root, storyFiles: [storyPath], searchRoots: [] });\n await index.initialize();\n const component = index.getAllComponents().find((entry) => path.resolve(entry.filePath) === storyPath);\n if (component === undefined) throw new Error('The requested story file could not be indexed.');\n return component;\n }\n\n async build(input: StoryPreviewBuildInput): Promise<StoryPreviewBuildResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n await this.exactStory(root, storyPath, input.storyExport);\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const bundler = this.dependencies.createBundler(config);\n let artifactDirectory: string | undefined;\n try {\n const rendered = await bundler.prerenderComponent({\n componentPath: storyPath,\n storyName: input.storyExport,\n args: input.args,\n darkMode: input.darkMode,\n appPath,\n themePath: config.themeProvider,\n cssFiles: config.cssFiles,\n rootComponent: config.rootComponent,\n unistylesConfig: config.unistylesConfig,\n });\n\n const htmlPath = await fs.realpath(rendered.htmlFilePath);\n const candidateDirectory = path.dirname(htmlPath);\n const temporaryRoot = path.join(appPath, '.tmp');\n if (candidateDirectory === temporaryRoot || !isContained(temporaryRoot, candidateDirectory)) {\n throw new Error('Style-system returned a preview artifact outside the project temporary directory');\n }\n artifactDirectory = candidateDirectory;\n\n const handle = this.dependencies.createHandle();\n const html = await fs.readFile(htmlPath, 'utf8');\n this.artifacts.set(handle, { directory: artifactDirectory });\n return {\n handle,\n html,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } catch (reason) {\n if (artifactDirectory !== undefined) await fs.rm(artifactDirectory, { recursive: true, force: true });\n throw reason;\n }\n }\n\n /** Renders a fresh source-backed PNG. This does not preserve transient iframe interaction state. */\n async exportImage(input: StoryPreviewBuildInput): Promise<StoryPreviewImageResult> {\n if (!STORY_EXPORT_PATTERN.test(input.storyExport)) {\n throw new Error('storyExport must be an exact JavaScript named export');\n }\n const root = await fs.realpath(this.root);\n const appPath = await this.resolveContainedPath(root, input.appPath, 'appPath');\n const storyPath = await this.resolveContainedPath(root, input.storyPath, 'storyPath');\n if (!STORY_FILE_PATTERN.test(storyPath))\n throw new Error('storyPath must name a .stories.js, .stories.jsx, .stories.ts, or .stories.tsx file');\n\n const [source, config] = await Promise.all([fs.readFile(storyPath, 'utf8'), this.dependencies.loadConfig(appPath)]);\n const renderer = this.dependencies.createRenderer(config, appPath);\n let ownedImagePath: string | undefined;\n try {\n const component = await this.exactStory(root, storyPath, input.storyExport);\n const rendered = await renderer.renderComponent(component, {\n storyName: input.storyExport,\n darkMode: input.darkMode,\n });\n const [renderedImagePath, imageRoot] = await Promise.all([\n fs.realpath(rendered.imagePath),\n fs.realpath(this.dependencies.rendererTemporaryRoot()),\n ]);\n if (renderedImagePath === imageRoot || !isContained(imageRoot, renderedImagePath)) {\n throw new Error('Style-system returned an image artifact outside its temporary directory');\n }\n const imageStat = await fs.stat(renderedImagePath);\n if (!imageStat.isFile()) throw new Error('Style-system returned a non-regular PNG image');\n ownedImagePath = renderedImagePath;\n if (imageStat.size > MAX_IMAGE_BYTES) throw new Error('Style-system returned an oversized PNG image');\n const image = await fs.readFile(renderedImagePath);\n if (image.length !== imageStat.size) throw new Error('Style-system PNG changed while it was being read');\n const { width, height } = pngDimensions(image);\n return {\n data: image.toString('base64'),\n mimeType: 'image/png',\n captureId: this.dependencies.createHandle(),\n width,\n height,\n storyPath: relativeWorkspacePath(root, storyPath),\n storyExport: input.storyExport,\n sourceSha256: createHash('sha256').update(source).digest('hex'),\n };\n } finally {\n try {\n if (ownedImagePath !== undefined) await fs.rm(ownedImagePath, { force: true });\n } finally {\n await renderer.dispose();\n }\n }\n }\n\n async dispose(handle: string): Promise<boolean> {\n const artifact = this.artifacts.get(handle);\n if (artifact === undefined) return false;\n this.artifacts.delete(handle);\n await fs.rm(artifact.directory, { recursive: true, force: true });\n return true;\n }\n\n async disposeAll(): Promise<void> {\n await Promise.all([...this.artifacts.keys()].map((handle) => this.dispose(handle)));\n }\n\n private async resolveContainedPath(root: string, inputPath: string, field: string): Promise<string> {\n if (inputPath.trim() === '') throw new Error(`${field} is required`);\n const candidate = path.isAbsolute(inputPath) ? inputPath : path.resolve(root, inputPath);\n const canonical = await fs.realpath(candidate);\n if (!isContained(root, canonical)) throw new Error(`${field} must remain inside the workspace root`);\n return canonical;\n }\n}\n\nexport type {\n StoryPreviewBuildInput,\n StoryPreviewBuildResult,\n StoryPreviewImageResult,\n StoryPreviewRenderer,\n StoryPreviewServiceDependencies,\n} from './type';\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseBundlerService, DesignSystemConfig } from "@agimon-ai/style-system";
|
|
1
|
+
import { BaseBundlerService, ComponentRendererService, DesignSystemConfig } from "@agimon-ai/style-system";
|
|
2
2
|
//#region src/services/storyPreview/type.d.ts
|
|
3
3
|
export interface StoryPreviewBuildInput {
|
|
4
4
|
appPath: string;
|
|
@@ -17,13 +17,19 @@ export interface StoryPreviewBuildResult {
|
|
|
17
17
|
export interface StoryPreviewImageResult {
|
|
18
18
|
data: string;
|
|
19
19
|
mimeType: 'image/png';
|
|
20
|
+
captureId: string;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
20
23
|
storyPath: string;
|
|
21
24
|
storyExport: string;
|
|
22
25
|
sourceSha256: string;
|
|
23
26
|
}
|
|
27
|
+
export type StoryPreviewRenderer = Pick<ComponentRendererService, 'renderComponent' | 'dispose'>;
|
|
24
28
|
export interface StoryPreviewServiceDependencies {
|
|
25
29
|
loadConfig(appPath: string): Promise<DesignSystemConfig>;
|
|
26
30
|
createBundler(config: DesignSystemConfig): BaseBundlerService;
|
|
31
|
+
createRenderer(config: DesignSystemConfig, appPath: string): StoryPreviewRenderer;
|
|
32
|
+
rendererTemporaryRoot(): string;
|
|
27
33
|
createHandle(): string;
|
|
28
34
|
}
|
|
29
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.d.cts","names":[],"sources":["../../../../src/services/storyPreview/type.ts"],"mappings":";;iBAEiB;EACf;EACA;EACA;EACA,OAAO;EACP;;iBAGe;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;
|
|
1
|
+
{"version":3,"file":"type.d.cts","names":[],"sources":["../../../../src/services/storyPreview/type.ts"],"mappings":";;iBAEiB;EACf;EACA;EACA;EACA,OAAO;EACP;;iBAGe;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAGU,uBAAuB,KAAK;iBAEvB;EACf,WAAW,kBAAkB,QAAQ;EACrC,cAAc,QAAQ,qBAAqB;EAC3C,eAAe,QAAQ,oBAAoB,kBAAkB;EAC7D;EACA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseBundlerService, DesignSystemConfig } from "@agimon-ai/style-system";
|
|
1
|
+
import { BaseBundlerService, ComponentRendererService, DesignSystemConfig } from "@agimon-ai/style-system";
|
|
2
2
|
//#region src/services/storyPreview/type.d.ts
|
|
3
3
|
export interface StoryPreviewBuildInput {
|
|
4
4
|
appPath: string;
|
|
@@ -17,13 +17,19 @@ export interface StoryPreviewBuildResult {
|
|
|
17
17
|
export interface StoryPreviewImageResult {
|
|
18
18
|
data: string;
|
|
19
19
|
mimeType: 'image/png';
|
|
20
|
+
captureId: string;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
20
23
|
storyPath: string;
|
|
21
24
|
storyExport: string;
|
|
22
25
|
sourceSha256: string;
|
|
23
26
|
}
|
|
27
|
+
export type StoryPreviewRenderer = Pick<ComponentRendererService, 'renderComponent' | 'dispose'>;
|
|
24
28
|
export interface StoryPreviewServiceDependencies {
|
|
25
29
|
loadConfig(appPath: string): Promise<DesignSystemConfig>;
|
|
26
30
|
createBundler(config: DesignSystemConfig): BaseBundlerService;
|
|
31
|
+
createRenderer(config: DesignSystemConfig, appPath: string): StoryPreviewRenderer;
|
|
32
|
+
rendererTemporaryRoot(): string;
|
|
27
33
|
createHandle(): string;
|
|
28
34
|
}
|
|
29
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.d.mts","names":[],"sources":["../../../../src/services/storyPreview/type.ts"],"mappings":";;iBAEiB;EACf;EACA;EACA;EACA,OAAO;EACP;;iBAGe;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;
|
|
1
|
+
{"version":3,"file":"type.d.mts","names":[],"sources":["../../../../src/services/storyPreview/type.ts"],"mappings":";;iBAEiB;EACf;EACA;EACA;EACA,OAAO;EACP;;iBAGe;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAGU,uBAAuB,KAAK;iBAEvB;EACf,WAAW,kBAAkB,QAAQ;EACrC,cAAc,QAAQ,qBAAqB;EAC3C,eAAe,QAAQ,oBAAoB,kBAAkB;EAC7D;EACA"}
|
|
@@ -40,6 +40,9 @@ export type ExportStoryPreviewImageRequest = BuildStoryPreviewRequest;
|
|
|
40
40
|
export interface ExportStoryPreviewImageView {
|
|
41
41
|
data: string;
|
|
42
42
|
mimeType: 'image/png';
|
|
43
|
+
captureId: string;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
43
46
|
storyPath: string;
|
|
44
47
|
storyExport: string;
|
|
45
48
|
sourceSha256: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"previewApi.d.cts","names":[],"sources":["../../../src/types/previewApi.ts"],"mappings":";qBAAa;iBAEI;EACf;IACE;IACA;IACA;IACA;IACA;;;iBAIa;EACf;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;EACA,kBAAkB;;iBAGH;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;YAGU,iCAAiC;iBAE5B;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;;iBAGe;EACf"}
|
|
1
|
+
{"version":3,"file":"previewApi.d.cts","names":[],"sources":["../../../src/types/previewApi.ts"],"mappings":";qBAAa;iBAEI;EACf;IACE;IACA;IACA;IACA;IACA;;;iBAIa;EACf;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;EACA,kBAAkB;;iBAGH;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;YAGU,iCAAiC;iBAE5B;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;;iBAGe;EACf"}
|
|
@@ -40,6 +40,9 @@ export type ExportStoryPreviewImageRequest = BuildStoryPreviewRequest;
|
|
|
40
40
|
export interface ExportStoryPreviewImageView {
|
|
41
41
|
data: string;
|
|
42
42
|
mimeType: 'image/png';
|
|
43
|
+
captureId: string;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
43
46
|
storyPath: string;
|
|
44
47
|
storyExport: string;
|
|
45
48
|
sourceSha256: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"previewApi.d.mts","names":[],"sources":["../../../src/types/previewApi.ts"],"mappings":";qBAAa;iBAEI;EACf;IACE;IACA;IACA;IACA;IACA;;;iBAIa;EACf;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;EACA,kBAAkB;;iBAGH;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;YAGU,iCAAiC;iBAE5B;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;;iBAGe;EACf"}
|
|
1
|
+
{"version":3,"file":"previewApi.d.mts","names":[],"sources":["../../../src/types/previewApi.ts"],"mappings":";qBAAa;iBAEI;EACf;IACE;IACA;IACA;IACA;IACA;;;iBAIa;EACf;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;EACA,kBAAkB;;iBAGH;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;;YAGU,iCAAiC;iBAE5B;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;;iBAGe;EACf"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-style-system",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.78",
|
|
4
4
|
"description": "Optional DoomPi story preview integration for the style-system package.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -70,15 +70,15 @@
|
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@agimon-ai/doompi-core": "0.0.1-alpha.
|
|
74
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.
|
|
75
|
-
"@agimon-ai/doompi-web-security": "0.0.1-alpha.
|
|
73
|
+
"@agimon-ai/doompi-core": "0.0.1-alpha.77",
|
|
74
|
+
"@agimon-ai/doompi-web-components": "0.0.1-alpha.35",
|
|
75
|
+
"@agimon-ai/doompi-web-security": "0.0.1-alpha.37",
|
|
76
76
|
"@agimon-ai/style-system": "0.1.3",
|
|
77
77
|
"hono": "4.13.7",
|
|
78
78
|
"typebox": "1.3.30"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@agimon-ai/doompi-build": "0.0.1-alpha.
|
|
81
|
+
"@agimon-ai/doompi-build": "0.0.1-alpha.6",
|
|
82
82
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
83
83
|
"@types/node": "26.5.1",
|
|
84
84
|
"@types/react": "19.3.0",
|