@pixerate/editor-react 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/canvas/index.cjs +406 -142
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +66 -3
- package/dist/canvas/index.d.ts +66 -3
- package/dist/canvas/index.js +277 -0
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-I3E4KSA2.js +515 -0
- package/dist/chunk-I3E4KSA2.js.map +1 -0
- package/dist/chunk-JR5ZBDR7.js +2012 -0
- package/dist/chunk-JR5ZBDR7.js.map +1 -0
- package/dist/{chunk-XTFR3IMK.js → chunk-ZU54BMDC.js} +473 -160
- package/dist/chunk-ZU54BMDC.js.map +1 -0
- package/dist/generation/index.cjs +290 -0
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +66 -2
- package/dist/generation/index.d.ts +66 -2
- package/dist/generation/index.js +7 -1
- package/dist/image-editor/index.cjs +490 -187
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +34 -2
- package/dist/image-editor/index.d.ts +34 -2
- package/dist/image-editor/index.js +5 -1
- package/dist/index.cjs +2372 -731
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -6
- package/dist/index.d.ts +88 -6
- package/dist/index.js +334 -96
- package/dist/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +943 -112
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +119 -7
- package/dist/spreadsheet/index.d.ts +119 -7
- package/dist/spreadsheet/index.js +11 -1
- package/dist/tokens/index.cjs +395 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +54 -0
- package/dist/tokens/index.d.ts +54 -0
- package/dist/tokens/index.js +375 -0
- package/dist/tokens/index.js.map +1 -0
- package/package.json +10 -2
- package/dist/chunk-7SPRQZ6H.js +0 -216
- package/dist/chunk-7SPRQZ6H.js.map +0 -1
- package/dist/chunk-FYIPXHAE.js +0 -1169
- package/dist/chunk-FYIPXHAE.js.map +0 -1
- package/dist/chunk-XTFR3IMK.js.map +0 -1
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// src/generation/useGenerationPlan.ts
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
estimateJobProgress,
|
|
5
|
+
planGenerationRequest
|
|
6
|
+
} from "@pixerate/editor/generation";
|
|
7
|
+
function useGenerationPlan(request, capabilities, options = {}) {
|
|
8
|
+
const key = JSON.stringify([request, capabilities, options]);
|
|
9
|
+
return useMemo(() => planGenerationRequest(request, capabilities, options), [key]);
|
|
10
|
+
}
|
|
11
|
+
function useJobProgress(options) {
|
|
12
|
+
const { startedAt, expectedMs, done, now, intervalMs = 1e3 } = options;
|
|
13
|
+
const [tick, setTick] = useState(() => Date.now());
|
|
14
|
+
const ticking = !done && now === void 0;
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!ticking) return;
|
|
17
|
+
setTick(Date.now());
|
|
18
|
+
const id = setInterval(() => setTick(Date.now()), Math.max(16, intervalMs));
|
|
19
|
+
return () => clearInterval(id);
|
|
20
|
+
}, [ticking, intervalMs]);
|
|
21
|
+
return estimateJobProgress({ startedAt, expectedMs, done, now: now ?? tick });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/generation/GenerationSettings.tsx
|
|
25
|
+
import { useId, useMemo as useMemo2 } from "react";
|
|
26
|
+
import {
|
|
27
|
+
getGenerationControls,
|
|
28
|
+
getReferenceSlots,
|
|
29
|
+
getRequestValue,
|
|
30
|
+
parseControlValue,
|
|
31
|
+
removeReference,
|
|
32
|
+
setRequestValue
|
|
33
|
+
} from "@pixerate/editor/generation";
|
|
34
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
35
|
+
var inputClass = "w-full rounded-md border border-border bg-background px-2 py-1 text-sm outline-none focus:ring-1 focus:ring-ring";
|
|
36
|
+
function Control({
|
|
37
|
+
control,
|
|
38
|
+
value,
|
|
39
|
+
id,
|
|
40
|
+
onValue
|
|
41
|
+
}) {
|
|
42
|
+
const shown = value ?? control.defaultValue;
|
|
43
|
+
if (control.kind === "checkbox") {
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
"input",
|
|
46
|
+
{
|
|
47
|
+
id,
|
|
48
|
+
type: "checkbox",
|
|
49
|
+
"data-field": control.key,
|
|
50
|
+
checked: Boolean(shown),
|
|
51
|
+
onChange: (e) => onValue(parseControlValue(control, e.target.checked))
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (control.kind === "select") {
|
|
56
|
+
return /* @__PURE__ */ jsxs(
|
|
57
|
+
"select",
|
|
58
|
+
{
|
|
59
|
+
id,
|
|
60
|
+
"data-field": control.key,
|
|
61
|
+
className: inputClass,
|
|
62
|
+
value: shown === void 0 ? "" : String(shown),
|
|
63
|
+
onChange: (e) => onValue(parseControlValue(control, e.target.value)),
|
|
64
|
+
children: [
|
|
65
|
+
shown === void 0 && /* @__PURE__ */ jsx("option", { value: "", children: "Default" }),
|
|
66
|
+
control.options.map((option) => /* @__PURE__ */ jsx("option", { value: String(option.value), children: option.label }, String(option.value)))
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (control.kind === "textarea") {
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
"textarea",
|
|
74
|
+
{
|
|
75
|
+
id,
|
|
76
|
+
"data-field": control.key,
|
|
77
|
+
className: inputClass,
|
|
78
|
+
rows: 2,
|
|
79
|
+
maxLength: control.maxLength,
|
|
80
|
+
value: typeof value === "string" ? value : "",
|
|
81
|
+
onChange: (e) => onValue(parseControlValue(control, e.target.value))
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const numeric = typeof shown === "number" ? shown : void 0;
|
|
86
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
"input",
|
|
89
|
+
{
|
|
90
|
+
id,
|
|
91
|
+
type: control.kind === "range" ? "range" : "number",
|
|
92
|
+
"data-field": control.key,
|
|
93
|
+
className: control.kind === "range" ? "flex-1" : inputClass,
|
|
94
|
+
min: control.min,
|
|
95
|
+
max: control.max,
|
|
96
|
+
step: control.step ?? "any",
|
|
97
|
+
value: numeric ?? (control.kind === "range" ? control.min ?? 0 : ""),
|
|
98
|
+
onChange: (e) => onValue(parseControlValue(control, e.target.value))
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
control.kind === "range" && /* @__PURE__ */ jsx("span", { className: "w-10 text-right tabular-nums text-muted-foreground", "data-field-value": "", children: numeric ?? "\u2013" })
|
|
102
|
+
] });
|
|
103
|
+
}
|
|
104
|
+
var GenerationSettings = ({
|
|
105
|
+
capabilities,
|
|
106
|
+
value,
|
|
107
|
+
onChange,
|
|
108
|
+
plan: planProp,
|
|
109
|
+
strategy,
|
|
110
|
+
showAdjustments = true,
|
|
111
|
+
onAddReference,
|
|
112
|
+
renderReference,
|
|
113
|
+
className = ""
|
|
114
|
+
}) => {
|
|
115
|
+
const baseId = useId();
|
|
116
|
+
const controls = useMemo2(() => getGenerationControls(capabilities), [capabilities]);
|
|
117
|
+
const computed = useGenerationPlan(value, capabilities, { strategy });
|
|
118
|
+
const plan = planProp ?? computed;
|
|
119
|
+
const slots = getReferenceSlots(capabilities, value);
|
|
120
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-3 text-sm ${className}`, "data-generation-settings": "", "data-model": capabilities.id, children: [
|
|
121
|
+
controls.map((control) => {
|
|
122
|
+
const id = `${baseId}-${control.key}`;
|
|
123
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", "data-control": control.key, children: [
|
|
124
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-muted-foreground", title: control.description, children: control.label }),
|
|
125
|
+
/* @__PURE__ */ jsx(
|
|
126
|
+
Control,
|
|
127
|
+
{
|
|
128
|
+
control,
|
|
129
|
+
id,
|
|
130
|
+
value: getRequestValue(value, control.key),
|
|
131
|
+
onValue: (next) => onChange(setRequestValue(value, control.key, next))
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }, control.key);
|
|
135
|
+
}),
|
|
136
|
+
slots.map((slot) => /* @__PURE__ */ jsxs("fieldset", { className: "flex flex-col gap-1", "data-reference-role": slot.role, children: [
|
|
137
|
+
/* @__PURE__ */ jsxs("legend", { className: "text-xs font-medium text-muted-foreground", title: slot.description, children: [
|
|
138
|
+
slot.label,
|
|
139
|
+
" ",
|
|
140
|
+
/* @__PURE__ */ jsxs("span", { className: "tabular-nums", "data-reference-count": "", children: [
|
|
141
|
+
"(",
|
|
142
|
+
slot.references.length,
|
|
143
|
+
"/",
|
|
144
|
+
slot.max,
|
|
145
|
+
")"
|
|
146
|
+
] }),
|
|
147
|
+
slot.min > 0 && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: " *" })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ jsxs("ul", { className: "flex flex-wrap gap-1", children: [
|
|
150
|
+
slot.references.map((ref) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1 rounded border border-border px-1.5 py-0.5", "data-reference-id": ref.id, children: [
|
|
151
|
+
renderReference ? renderReference(ref) : /* @__PURE__ */ jsx("span", { className: "max-w-[10rem] truncate", children: ref.id }),
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
type: "button",
|
|
156
|
+
"aria-label": `Remove ${slot.label} ${ref.id}`,
|
|
157
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
158
|
+
onClick: () => onChange(removeReference(value, slot.role, ref.id)),
|
|
159
|
+
children: "\xD7"
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
] }, ref.id)),
|
|
163
|
+
onAddReference && slot.canAdd && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
164
|
+
"button",
|
|
165
|
+
{
|
|
166
|
+
type: "button",
|
|
167
|
+
className: "rounded border border-dashed border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground",
|
|
168
|
+
onClick: () => onAddReference(slot.role),
|
|
169
|
+
children: "Add"
|
|
170
|
+
}
|
|
171
|
+
) })
|
|
172
|
+
] })
|
|
173
|
+
] }, slot.role)),
|
|
174
|
+
showAdjustments && plan.adjustments.length > 0 && /* @__PURE__ */ jsx("ul", { role: "status", className: "flex flex-col gap-0.5 text-xs text-muted-foreground", "data-generation-adjustments": "", children: plan.adjustments.map((adjustment, index) => /* @__PURE__ */ jsx("li", { "data-adjustment-field": adjustment.field, "data-adjustment-kind": adjustment.kind, children: adjustment.reason }, index)) }),
|
|
175
|
+
showAdjustments && plan.errors.length > 0 && /* @__PURE__ */ jsx("ul", { role: "alert", className: "flex flex-col gap-0.5 text-xs text-destructive", "data-generation-errors": "", children: plan.errors.map((error, index) => /* @__PURE__ */ jsx("li", { "data-error-field": error.field, children: error.message }, index)) })
|
|
176
|
+
] });
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// src/generation/JobProgress.tsx
|
|
180
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
181
|
+
var JobProgress = ({ title, className = "", ...options }) => {
|
|
182
|
+
const progress = useJobProgress(options);
|
|
183
|
+
return /* @__PURE__ */ jsxs2("div", { className: `flex flex-col gap-1 text-sm ${className}`, "data-job-progress": "", "data-done": options.done ? "" : void 0, children: [
|
|
184
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2 text-muted-foreground", children: [
|
|
185
|
+
title && /* @__PURE__ */ jsx2("span", { className: "truncate text-foreground", children: title }),
|
|
186
|
+
/* @__PURE__ */ jsx2("span", { "data-job-progress-label": "", "aria-live": "polite", children: progress.label })
|
|
187
|
+
] }),
|
|
188
|
+
/* @__PURE__ */ jsx2(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
role: "progressbar",
|
|
192
|
+
"aria-label": title ?? "Generation progress",
|
|
193
|
+
"aria-valuemin": 0,
|
|
194
|
+
"aria-valuemax": 100,
|
|
195
|
+
"aria-valuenow": progress.percent,
|
|
196
|
+
"aria-valuetext": `${progress.percent}%, ${progress.label}`,
|
|
197
|
+
className: "h-1.5 w-full overflow-hidden rounded-full bg-muted",
|
|
198
|
+
children: /* @__PURE__ */ jsx2(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
className: "h-full rounded-full bg-primary transition-[width] duration-500",
|
|
202
|
+
style: { width: `${progress.percent}%`, background: "var(--primary, currentColor)" }
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
] });
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/generation/AssetMentionChips.tsx
|
|
211
|
+
import { useEffect as useEffect2, useMemo as useMemo3, useRef, useState as useState2 } from "react";
|
|
212
|
+
import {
|
|
213
|
+
defaultAssetRoleOptions,
|
|
214
|
+
describeAssetMentions,
|
|
215
|
+
findTextMentions,
|
|
216
|
+
getAssetRoleOptions,
|
|
217
|
+
setMentionRoleInText
|
|
218
|
+
} from "@pixerate/editor/generation";
|
|
219
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
220
|
+
var AssetMentionChips = ({
|
|
221
|
+
content,
|
|
222
|
+
onChange,
|
|
223
|
+
assets,
|
|
224
|
+
capabilities,
|
|
225
|
+
roles,
|
|
226
|
+
renderAsset,
|
|
227
|
+
className = ""
|
|
228
|
+
}) => {
|
|
229
|
+
const [open, setOpen] = useState2(null);
|
|
230
|
+
const rootRef = useRef(null);
|
|
231
|
+
const chips = useMemo3(
|
|
232
|
+
() => describeAssetMentions(findTextMentions(content), assets, capabilities),
|
|
233
|
+
[content, assets, capabilities]
|
|
234
|
+
);
|
|
235
|
+
const options = roles ?? (capabilities ? getAssetRoleOptions(capabilities) : defaultAssetRoleOptions());
|
|
236
|
+
useEffect2(() => {
|
|
237
|
+
if (open === null) return;
|
|
238
|
+
const onPointer = (event) => {
|
|
239
|
+
if (!rootRef.current?.contains(event.target)) setOpen(null);
|
|
240
|
+
};
|
|
241
|
+
document.addEventListener("mousedown", onPointer);
|
|
242
|
+
return () => document.removeEventListener("mousedown", onPointer);
|
|
243
|
+
}, [open]);
|
|
244
|
+
const pick = (chip, role) => {
|
|
245
|
+
setOpen(null);
|
|
246
|
+
if (chip.start === void 0) return;
|
|
247
|
+
onChange(setMentionRoleInText(content, chip.start, role));
|
|
248
|
+
};
|
|
249
|
+
if (chips.length === 0) return null;
|
|
250
|
+
return /* @__PURE__ */ jsx3("div", { ref: rootRef, className: `flex flex-wrap gap-1.5 ${className}`, "data-asset-mention-chips": "", children: chips.map((chip) => {
|
|
251
|
+
const error = chip.issues.find((issue) => issue.severity === "error");
|
|
252
|
+
const warning = chip.issues.find((issue) => issue.severity === "warning");
|
|
253
|
+
const problem = error ?? warning;
|
|
254
|
+
const isOpen = open === chip.index;
|
|
255
|
+
return /* @__PURE__ */ jsxs3("span", { className: "relative inline-flex", "data-asset-chip": chip.asset.id, children: [
|
|
256
|
+
/* @__PURE__ */ jsxs3(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
"aria-haspopup": "listbox",
|
|
261
|
+
"aria-expanded": isOpen,
|
|
262
|
+
title: problem?.message,
|
|
263
|
+
"data-issue": problem?.code,
|
|
264
|
+
onClick: () => setOpen(isOpen ? null : chip.index),
|
|
265
|
+
onKeyDown: (e) => {
|
|
266
|
+
if (e.key === "Escape") setOpen(null);
|
|
267
|
+
},
|
|
268
|
+
className: `inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs ${error ? "border-red-500/60" : warning ? "border-amber-500/60" : "border-border"} bg-muted/40 hover:bg-muted`,
|
|
269
|
+
children: [
|
|
270
|
+
/* @__PURE__ */ jsx3("span", { children: renderAsset ? renderAsset(chip) : `@${chip.asset.label ?? chip.handle}` }),
|
|
271
|
+
/* @__PURE__ */ jsx3(
|
|
272
|
+
"span",
|
|
273
|
+
{
|
|
274
|
+
className: `rounded px-1 text-[10px] uppercase tracking-wide ${chip.role ? "bg-primary/15" : "opacity-60"}`,
|
|
275
|
+
"data-role-badge": chip.role ?? "",
|
|
276
|
+
children: chip.roleLabel ?? "No role"
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
),
|
|
282
|
+
isOpen && /* @__PURE__ */ jsxs3(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
role: "listbox",
|
|
286
|
+
"aria-label": `Role for ${chip.asset.label ?? chip.handle}`,
|
|
287
|
+
className: "absolute left-0 top-full z-50 mt-1 flex min-w-[12rem] flex-col rounded-md border border-border bg-background p-1 text-xs shadow-lg",
|
|
288
|
+
"data-role-picker": "",
|
|
289
|
+
onKeyDown: (e) => {
|
|
290
|
+
if (e.key === "Escape") setOpen(null);
|
|
291
|
+
},
|
|
292
|
+
children: [
|
|
293
|
+
options.map((option) => /* @__PURE__ */ jsxs3(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
type: "button",
|
|
297
|
+
role: "option",
|
|
298
|
+
"aria-selected": chip.role === option.role,
|
|
299
|
+
"data-role-option": option.role,
|
|
300
|
+
title: option.description,
|
|
301
|
+
onClick: () => pick(chip, option.role),
|
|
302
|
+
className: "flex items-center justify-between gap-2 rounded px-2 py-1 text-left hover:bg-muted",
|
|
303
|
+
children: [
|
|
304
|
+
/* @__PURE__ */ jsx3("span", { children: option.label }),
|
|
305
|
+
option.accepts?.length ? /* @__PURE__ */ jsx3("span", { className: "text-[10px] opacity-60", children: option.accepts.join(", ") }) : null
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
option.role
|
|
309
|
+
)),
|
|
310
|
+
/* @__PURE__ */ jsx3(
|
|
311
|
+
"button",
|
|
312
|
+
{
|
|
313
|
+
type: "button",
|
|
314
|
+
role: "option",
|
|
315
|
+
"aria-selected": !chip.role,
|
|
316
|
+
"data-role-option": "",
|
|
317
|
+
onClick: () => pick(chip, null),
|
|
318
|
+
className: "rounded px-2 py-1 text-left opacity-70 hover:bg-muted",
|
|
319
|
+
children: "No role"
|
|
320
|
+
}
|
|
321
|
+
),
|
|
322
|
+
chip.issues.length > 0 && /* @__PURE__ */ jsx3("ul", { className: "mt-1 border-t border-border px-2 pt-1 text-[10px]", "data-chip-issues": "", children: chip.issues.map((issue, i) => /* @__PURE__ */ jsx3("li", { className: issue.severity === "error" ? "text-red-500" : "text-amber-500", children: issue.message }, i)) })
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
] }, `${chip.start}-${chip.index}`);
|
|
327
|
+
}) });
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/generation/GenerationHistory.tsx
|
|
331
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo4, useState as useState3 } from "react";
|
|
332
|
+
import {
|
|
333
|
+
DRIFT_TYPE_LABELS,
|
|
334
|
+
recreateRequest,
|
|
335
|
+
referenceRoleLabel,
|
|
336
|
+
stableStringify,
|
|
337
|
+
variationRequest
|
|
338
|
+
} from "@pixerate/editor/generation";
|
|
339
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
340
|
+
function useGenerationRecords(store, filter) {
|
|
341
|
+
const [records, setRecords] = useState3([]);
|
|
342
|
+
const [loading, setLoading] = useState3(!!store);
|
|
343
|
+
const [error, setError] = useState3(null);
|
|
344
|
+
const filterKey = stableStringify(filter ?? {});
|
|
345
|
+
const refresh = useCallback(async () => {
|
|
346
|
+
if (!store) {
|
|
347
|
+
setRecords([]);
|
|
348
|
+
setLoading(false);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
setLoading(true);
|
|
352
|
+
try {
|
|
353
|
+
const next = await store.list(filter);
|
|
354
|
+
setRecords(next);
|
|
355
|
+
setError(null);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
setError(err);
|
|
358
|
+
} finally {
|
|
359
|
+
setLoading(false);
|
|
360
|
+
}
|
|
361
|
+
}, [store, filterKey]);
|
|
362
|
+
useEffect3(() => {
|
|
363
|
+
void refresh();
|
|
364
|
+
if (!store) return;
|
|
365
|
+
return store.subscribe(() => void refresh());
|
|
366
|
+
}, [store, refresh]);
|
|
367
|
+
return { records, loading, error, refresh };
|
|
368
|
+
}
|
|
369
|
+
var severityClass = {
|
|
370
|
+
info: "text-sky-600 dark:text-sky-300",
|
|
371
|
+
warning: "text-amber-600 dark:text-amber-300",
|
|
372
|
+
error: "text-red-600 dark:text-red-400"
|
|
373
|
+
};
|
|
374
|
+
var GenerationHistory = ({
|
|
375
|
+
records,
|
|
376
|
+
templates,
|
|
377
|
+
capabilities,
|
|
378
|
+
assets,
|
|
379
|
+
mode: modeProp,
|
|
380
|
+
onModeChange,
|
|
381
|
+
onRecreate,
|
|
382
|
+
onVariation,
|
|
383
|
+
onRemove,
|
|
384
|
+
renderPreview,
|
|
385
|
+
emptyText = "No generations yet.",
|
|
386
|
+
className = ""
|
|
387
|
+
}) => {
|
|
388
|
+
const [innerMode, setInnerMode] = useState3("exact");
|
|
389
|
+
const mode = modeProp ?? innerMode;
|
|
390
|
+
const setMode = (next) => {
|
|
391
|
+
if (modeProp === void 0) setInnerMode(next);
|
|
392
|
+
onModeChange?.(next);
|
|
393
|
+
};
|
|
394
|
+
const options = useMemo4(() => ({ templates, capabilities, assets, mode }), [templates, capabilities, assets, mode]);
|
|
395
|
+
const results = useMemo4(() => records.map((record) => recreateRequest(record, options)), [records, options]);
|
|
396
|
+
const models = useMemo4(
|
|
397
|
+
() => capabilities ? Array.isArray(capabilities) ? capabilities : [capabilities] : [],
|
|
398
|
+
[capabilities]
|
|
399
|
+
);
|
|
400
|
+
return /* @__PURE__ */ jsxs4("div", { className: `flex flex-col gap-2 text-sm ${className}`, "data-generation-history": "", children: [
|
|
401
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center gap-2 text-xs", role: "radiogroup", "aria-label": "Recreate mode", children: ["exact", "latest"].map((value) => /* @__PURE__ */ jsxs4("label", { className: "inline-flex items-center gap-1", children: [
|
|
402
|
+
/* @__PURE__ */ jsx4(
|
|
403
|
+
"input",
|
|
404
|
+
{
|
|
405
|
+
type: "radio",
|
|
406
|
+
name: "generation-history-mode",
|
|
407
|
+
value,
|
|
408
|
+
checked: mode === value,
|
|
409
|
+
onChange: () => setMode(value),
|
|
410
|
+
"data-mode": value
|
|
411
|
+
}
|
|
412
|
+
),
|
|
413
|
+
value === "exact" ? "Exact (recorded versions)" : "Latest templates & settings"
|
|
414
|
+
] }, value)) }),
|
|
415
|
+
records.length === 0 && /* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: emptyText }),
|
|
416
|
+
/* @__PURE__ */ jsx4("ul", { className: "flex flex-col gap-2", children: records.map((record, i) => {
|
|
417
|
+
const result = results[i];
|
|
418
|
+
const model = models.find((m) => m.id === record.provenance.modelId);
|
|
419
|
+
const blocked = !result.ok;
|
|
420
|
+
return /* @__PURE__ */ jsxs4(
|
|
421
|
+
"li",
|
|
422
|
+
{
|
|
423
|
+
className: "rounded-md border border-border p-2",
|
|
424
|
+
"data-record": record.id,
|
|
425
|
+
"data-drift": result.drift.length,
|
|
426
|
+
children: [
|
|
427
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center justify-between gap-2 text-xs", children: [
|
|
428
|
+
/* @__PURE__ */ jsx4("span", { className: "font-medium", children: model?.name ?? record.provenance.modelId }),
|
|
429
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
|
|
430
|
+
record.kind !== "generate" && /* @__PURE__ */ jsxs4("span", { "data-kind": "", children: [
|
|
431
|
+
record.kind,
|
|
432
|
+
" \xB7 "
|
|
433
|
+
] }),
|
|
434
|
+
/* @__PURE__ */ jsx4("time", { dateTime: record.createdAt, children: record.createdAt.replace("T", " ").slice(0, 16) }),
|
|
435
|
+
record.seed !== void 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
436
|
+
" \xB7 seed ",
|
|
437
|
+
record.seed
|
|
438
|
+
] })
|
|
439
|
+
] })
|
|
440
|
+
] }),
|
|
441
|
+
/* @__PURE__ */ jsx4("p", { className: "mt-1 line-clamp-3 break-words text-xs", "data-record-prompt": "", children: record.provenance.resolvedPrompt }),
|
|
442
|
+
record.assets.length > 0 && /* @__PURE__ */ jsx4("div", { className: "mt-1 flex flex-wrap gap-1", children: record.assets.map((asset) => /* @__PURE__ */ jsxs4(
|
|
443
|
+
"span",
|
|
444
|
+
{
|
|
445
|
+
className: "rounded-full bg-muted px-2 py-0.5 text-[10px]",
|
|
446
|
+
"data-asset-role": asset.role,
|
|
447
|
+
children: [
|
|
448
|
+
asset.id,
|
|
449
|
+
" \xB7 ",
|
|
450
|
+
referenceRoleLabel(asset.role, model)
|
|
451
|
+
]
|
|
452
|
+
},
|
|
453
|
+
`${asset.role}:${asset.id}`
|
|
454
|
+
)) }),
|
|
455
|
+
renderPreview?.(record),
|
|
456
|
+
result.drift.length > 0 && /* @__PURE__ */ jsx4("ul", { className: "mt-1 flex flex-col gap-0.5 text-[11px]", "data-drift-report": "", children: result.drift.map((item, j) => /* @__PURE__ */ jsxs4("li", { className: severityClass[item.severity], "data-drift-type": item.type, children: [
|
|
457
|
+
/* @__PURE__ */ jsxs4("strong", { children: [
|
|
458
|
+
DRIFT_TYPE_LABELS[item.type],
|
|
459
|
+
":"
|
|
460
|
+
] }),
|
|
461
|
+
" ",
|
|
462
|
+
item.message
|
|
463
|
+
] }, j)) }),
|
|
464
|
+
/* @__PURE__ */ jsxs4("div", { className: "mt-2 flex gap-2", children: [
|
|
465
|
+
onRecreate && /* @__PURE__ */ jsx4(
|
|
466
|
+
"button",
|
|
467
|
+
{
|
|
468
|
+
type: "button",
|
|
469
|
+
disabled: blocked,
|
|
470
|
+
title: blocked ? "This generation cannot be recreated as recorded." : void 0,
|
|
471
|
+
onClick: () => onRecreate(result, record),
|
|
472
|
+
className: "rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-50",
|
|
473
|
+
"data-action": "recreate",
|
|
474
|
+
children: "Recreate"
|
|
475
|
+
}
|
|
476
|
+
),
|
|
477
|
+
onVariation && /* @__PURE__ */ jsx4(
|
|
478
|
+
"button",
|
|
479
|
+
{
|
|
480
|
+
type: "button",
|
|
481
|
+
onClick: () => onVariation(variationRequest(record, {}, options), record),
|
|
482
|
+
className: "rounded border border-border px-2 py-0.5 text-xs hover:bg-muted",
|
|
483
|
+
"data-action": "variation",
|
|
484
|
+
children: "Variation"
|
|
485
|
+
}
|
|
486
|
+
),
|
|
487
|
+
onRemove && /* @__PURE__ */ jsx4(
|
|
488
|
+
"button",
|
|
489
|
+
{
|
|
490
|
+
type: "button",
|
|
491
|
+
onClick: () => onRemove(record),
|
|
492
|
+
className: "ml-auto rounded px-2 py-0.5 text-xs text-muted-foreground hover:bg-muted",
|
|
493
|
+
"data-action": "remove",
|
|
494
|
+
children: "Remove"
|
|
495
|
+
}
|
|
496
|
+
)
|
|
497
|
+
] })
|
|
498
|
+
]
|
|
499
|
+
},
|
|
500
|
+
record.id
|
|
501
|
+
);
|
|
502
|
+
}) })
|
|
503
|
+
] });
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
export {
|
|
507
|
+
useGenerationPlan,
|
|
508
|
+
useJobProgress,
|
|
509
|
+
GenerationSettings,
|
|
510
|
+
JobProgress,
|
|
511
|
+
AssetMentionChips,
|
|
512
|
+
useGenerationRecords,
|
|
513
|
+
GenerationHistory
|
|
514
|
+
};
|
|
515
|
+
//# sourceMappingURL=chunk-I3E4KSA2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generation/useGenerationPlan.ts","../src/generation/GenerationSettings.tsx","../src/generation/JobProgress.tsx","../src/generation/AssetMentionChips.tsx","../src/generation/GenerationHistory.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport {\n estimateJobProgress,\n planGenerationRequest,\n type GenerationPlan,\n type GenerationRequest,\n type JobProgressEstimate,\n type ModelCapabilities,\n type PlanOptions,\n} from '@pixerate/editor/generation';\n\n/**\n * Plans `request` against `capabilities` (see `planGenerationRequest`) and\n * returns the adjusted request, adjustments and errors. Recomputed only when\n * the request, capabilities or options change by value, so inline objects are fine.\n */\nexport function useGenerationPlan(\n request: GenerationRequest,\n capabilities: ModelCapabilities,\n options: PlanOptions = {},\n): GenerationPlan {\n const key = JSON.stringify([request, capabilities, options]);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => planGenerationRequest(request, capabilities, options), [key]);\n}\n\nexport interface UseJobProgressOptions {\n startedAt: number | Date | string;\n expectedMs?: number;\n done?: boolean;\n /** Fixed current time (disables ticking; useful for SSR and tests). */\n now?: number | Date | string;\n /** Re-render interval while running in ms (default 1000). */\n intervalMs?: number;\n}\n\n/** Ticking `estimateJobProgress` for a running job; stops ticking when `done`. */\nexport function useJobProgress(options: UseJobProgressOptions): JobProgressEstimate {\n const { startedAt, expectedMs, done, now, intervalMs = 1000 } = options;\n const [tick, setTick] = useState(() => Date.now());\n const ticking = !done && now === undefined;\n useEffect(() => {\n if (!ticking) return;\n setTick(Date.now());\n const id = setInterval(() => setTick(Date.now()), Math.max(16, intervalMs));\n return () => clearInterval(id);\n }, [ticking, intervalMs]);\n return estimateJobProgress({ startedAt, expectedMs, done, now: now ?? tick });\n}\n","import React, { useId, useMemo } from 'react';\nimport {\n getGenerationControls,\n getReferenceSlots,\n getRequestValue,\n parseControlValue,\n removeReference,\n setRequestValue,\n type GenerationControl,\n type GenerationPlan,\n type GenerationReference,\n type GenerationRequest,\n type ModelCapabilities,\n type PlanStrategy,\n type ReferenceRole,\n} from '@pixerate/editor/generation';\nimport { useGenerationPlan } from './useGenerationPlan';\n\nexport interface GenerationSettingsProps {\n capabilities: ModelCapabilities;\n /** The request being edited (controlled). */\n value: GenerationRequest;\n onChange: (next: GenerationRequest) => void;\n /** A plan computed elsewhere; otherwise one is computed with `strategy`. */\n plan?: GenerationPlan;\n /** Strategy for the built-in plan (default `nearest`). */\n strategy?: PlanStrategy;\n /** List plan adjustments and errors under the controls (default `true`). */\n showAdjustments?: boolean;\n /** Shows an \"Add\" button per reference role; you pick the asset and call `onChange`. */\n onAddReference?: (role: ReferenceRole) => void;\n /** Custom rendering of a reference chip (default: its id). */\n renderReference?: (reference: GenerationReference) => React.ReactNode;\n className?: string;\n}\n\nconst inputClass =\n 'w-full rounded-md border border-border bg-background px-2 py-1 text-sm outline-none focus:ring-1 focus:ring-ring';\n\nfunction Control({\n control,\n value,\n id,\n onValue,\n}: {\n control: GenerationControl;\n value: unknown;\n id: string;\n onValue: (value: unknown) => void;\n}) {\n const shown = value ?? control.defaultValue;\n if (control.kind === 'checkbox') {\n return (\n <input\n id={id}\n type=\"checkbox\"\n data-field={control.key}\n checked={Boolean(shown)}\n onChange={(e) => onValue(parseControlValue(control, e.target.checked))}\n />\n );\n }\n if (control.kind === 'select') {\n return (\n <select\n id={id}\n data-field={control.key}\n className={inputClass}\n value={shown === undefined ? '' : String(shown)}\n onChange={(e) => onValue(parseControlValue(control, e.target.value))}\n >\n {shown === undefined && <option value=\"\">Default</option>}\n {control.options!.map((option) => (\n <option key={String(option.value)} value={String(option.value)}>\n {option.label}\n </option>\n ))}\n </select>\n );\n }\n if (control.kind === 'textarea') {\n return (\n <textarea\n id={id}\n data-field={control.key}\n className={inputClass}\n rows={2}\n maxLength={control.maxLength}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onValue(parseControlValue(control, e.target.value))}\n />\n );\n }\n const numeric = typeof shown === 'number' ? shown : undefined;\n return (\n <div className=\"flex items-center gap-2\">\n <input\n id={id}\n type={control.kind === 'range' ? 'range' : 'number'}\n data-field={control.key}\n className={control.kind === 'range' ? 'flex-1' : inputClass}\n min={control.min}\n max={control.max}\n step={control.step ?? 'any'}\n value={numeric ?? (control.kind === 'range' ? (control.min ?? 0) : '')}\n onChange={(e) => onValue(parseControlValue(control, e.target.value))}\n />\n {control.kind === 'range' && (\n <span className=\"w-10 text-right tabular-nums text-muted-foreground\" data-field-value=\"\">\n {numeric ?? '–'}\n </span>\n )}\n </div>\n );\n}\n\n/**\n * Headless-styled settings form generated from a model's capabilities:\n * selects for choices, sliders/number inputs for ranges, text areas for\n * negative/system prompts, checkboxes for boolean params, reference slots by\n * role, and the plan's adjustments and errors. Controlled via `value` / `onChange`.\n */\nexport const GenerationSettings: React.FC<GenerationSettingsProps> = ({\n capabilities,\n value,\n onChange,\n plan: planProp,\n strategy,\n showAdjustments = true,\n onAddReference,\n renderReference,\n className = '',\n}) => {\n const baseId = useId();\n const controls = useMemo(() => getGenerationControls(capabilities), [capabilities]);\n const computed = useGenerationPlan(value, capabilities, { strategy });\n const plan = planProp ?? computed;\n const slots = getReferenceSlots(capabilities, value);\n\n return (\n <div className={`flex flex-col gap-3 text-sm ${className}`} data-generation-settings=\"\" data-model={capabilities.id}>\n {controls.map((control) => {\n const id = `${baseId}-${control.key}`;\n return (\n <div key={control.key} className=\"flex flex-col gap-1\" data-control={control.key}>\n <label htmlFor={id} className=\"text-xs font-medium text-muted-foreground\" title={control.description}>\n {control.label}\n </label>\n <Control\n control={control}\n id={id}\n value={getRequestValue(value, control.key)}\n onValue={(next) => onChange(setRequestValue(value, control.key, next))}\n />\n </div>\n );\n })}\n\n {slots.map((slot) => (\n <fieldset key={slot.role} className=\"flex flex-col gap-1\" data-reference-role={slot.role}>\n <legend className=\"text-xs font-medium text-muted-foreground\" title={slot.description}>\n {slot.label}{' '}\n <span className=\"tabular-nums\" data-reference-count=\"\">\n ({slot.references.length}/{slot.max})\n </span>\n {slot.min > 0 && <span className=\"text-destructive\"> *</span>}\n </legend>\n <ul className=\"flex flex-wrap gap-1\">\n {slot.references.map((ref) => (\n <li key={ref.id} className=\"flex items-center gap-1 rounded border border-border px-1.5 py-0.5\" data-reference-id={ref.id}>\n {renderReference ? renderReference(ref) : <span className=\"max-w-[10rem] truncate\">{ref.id}</span>}\n <button\n type=\"button\"\n aria-label={`Remove ${slot.label} ${ref.id}`}\n className=\"text-muted-foreground hover:text-foreground\"\n onClick={() => onChange(removeReference(value, slot.role, ref.id))}\n >\n ×\n </button>\n </li>\n ))}\n {onAddReference && slot.canAdd && (\n <li>\n <button\n type=\"button\"\n className=\"rounded border border-dashed border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground\"\n onClick={() => onAddReference(slot.role)}\n >\n Add\n </button>\n </li>\n )}\n </ul>\n </fieldset>\n ))}\n\n {showAdjustments && plan.adjustments.length > 0 && (\n <ul role=\"status\" className=\"flex flex-col gap-0.5 text-xs text-muted-foreground\" data-generation-adjustments=\"\">\n {plan.adjustments.map((adjustment, index) => (\n <li key={index} data-adjustment-field={adjustment.field} data-adjustment-kind={adjustment.kind}>\n {adjustment.reason}\n </li>\n ))}\n </ul>\n )}\n {showAdjustments && plan.errors.length > 0 && (\n <ul role=\"alert\" className=\"flex flex-col gap-0.5 text-xs text-destructive\" data-generation-errors=\"\">\n {plan.errors.map((error, index) => (\n <li key={index} data-error-field={error.field}>\n {error.message}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { useJobProgress, type UseJobProgressOptions } from './useGenerationPlan';\n\nexport interface JobProgressProps extends UseJobProgressOptions {\n /** Text shown before the time label, e.g. the model name. */\n title?: string;\n /** Extra classes for the root. */\n className?: string;\n}\n\n/**\n * Headless-styled progress bar for a generation job that reports no progress\n * of its own: time-based percent (held at 95% until `done`) with a\n * \"~2m left\" / \"Almost done…\" label. Exposes `role=\"progressbar\"`.\n */\nexport const JobProgress: React.FC<JobProgressProps> = ({ title, className = '', ...options }) => {\n const progress = useJobProgress(options);\n return (\n <div className={`flex flex-col gap-1 text-sm ${className}`} data-job-progress=\"\" data-done={options.done ? '' : undefined}>\n <div className=\"flex items-center justify-between gap-2 text-muted-foreground\">\n {title && <span className=\"truncate text-foreground\">{title}</span>}\n <span data-job-progress-label=\"\" aria-live=\"polite\">\n {progress.label}\n </span>\n </div>\n <div\n role=\"progressbar\"\n aria-label={title ?? 'Generation progress'}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={progress.percent}\n aria-valuetext={`${progress.percent}%, ${progress.label}`}\n className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\"\n >\n <div\n className=\"h-full rounded-full bg-primary transition-[width] duration-500\"\n style={{ width: `${progress.percent}%`, background: 'var(--primary, currentColor)' }}\n />\n </div>\n </div>\n );\n};\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n defaultAssetRoleOptions,\n describeAssetMentions,\n findTextMentions,\n getAssetRoleOptions,\n setMentionRoleInText,\n type AssetInfo,\n type AssetMentionChip,\n type AssetRoleOption,\n type ModelCapabilities,\n} from '@pixerate/editor/generation';\n\nexport interface AssetMentionChipsProps {\n /** Prompt text containing `@asset` / `@asset:role` mentions. */\n content: string;\n /** Called with the text after a role is picked (`@fox` → `@fox:style_ref`). */\n onChange: (next: string) => void;\n /** Mentionable assets; mentions that match none (by id or label) are not shown. */\n assets: readonly AssetInfo[];\n /** The selected model: its roles are offered and mentions are checked against it. */\n capabilities?: ModelCapabilities;\n /** Role options (default: the model's roles, else the built-in roles). */\n roles?: readonly AssetRoleOption[];\n /** Custom chip content (default: the asset label). */\n renderAsset?: (chip: AssetMentionChip) => React.ReactNode;\n className?: string;\n}\n\n/**\n * The asset mentions of a prompt as chips with role badges. Clicking a chip\n * opens a role picker popover; picking a role rewrites the mention's `:role`\n * suffix in the text. Chips with problems for the model (unsupported role,\n * wrong media type, too many for the role) are flagged.\n */\nexport const AssetMentionChips: React.FC<AssetMentionChipsProps> = ({\n content,\n onChange,\n assets,\n capabilities,\n roles,\n renderAsset,\n className = '',\n}) => {\n const [open, setOpen] = useState<number | null>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const chips = useMemo(\n () => describeAssetMentions(findTextMentions(content), assets, capabilities),\n [content, assets, capabilities],\n );\n const options = roles ?? (capabilities ? getAssetRoleOptions(capabilities) : defaultAssetRoleOptions());\n\n useEffect(() => {\n if (open === null) return;\n const onPointer = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) setOpen(null);\n };\n document.addEventListener('mousedown', onPointer);\n return () => document.removeEventListener('mousedown', onPointer);\n }, [open]);\n\n const pick = (chip: AssetMentionChip, role: string | null) => {\n setOpen(null);\n if (chip.start === undefined) return;\n onChange(setMentionRoleInText(content, chip.start, role));\n };\n\n if (chips.length === 0) return null;\n return (\n <div ref={rootRef} className={`flex flex-wrap gap-1.5 ${className}`} data-asset-mention-chips=\"\">\n {chips.map((chip) => {\n const error = chip.issues.find((issue) => issue.severity === 'error');\n const warning = chip.issues.find((issue) => issue.severity === 'warning');\n const problem = error ?? warning;\n const isOpen = open === chip.index;\n return (\n <span key={`${chip.start}-${chip.index}`} className=\"relative inline-flex\" data-asset-chip={chip.asset.id}>\n <button\n type=\"button\"\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n title={problem?.message}\n data-issue={problem?.code}\n onClick={() => setOpen(isOpen ? null : chip.index)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(null);\n }}\n className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs ${\n error ? 'border-red-500/60' : warning ? 'border-amber-500/60' : 'border-border'\n } bg-muted/40 hover:bg-muted`}\n >\n <span>{renderAsset ? renderAsset(chip) : `@${chip.asset.label ?? chip.handle}`}</span>\n <span\n className={`rounded px-1 text-[10px] uppercase tracking-wide ${chip.role ? 'bg-primary/15' : 'opacity-60'}`}\n data-role-badge={chip.role ?? ''}\n >\n {chip.roleLabel ?? 'No role'}\n </span>\n </button>\n {isOpen && (\n <div\n role=\"listbox\"\n aria-label={`Role for ${chip.asset.label ?? chip.handle}`}\n className=\"absolute left-0 top-full z-50 mt-1 flex min-w-[12rem] flex-col rounded-md border border-border bg-background p-1 text-xs shadow-lg\"\n data-role-picker=\"\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(null);\n }}\n >\n {options.map((option) => (\n <button\n key={option.role}\n type=\"button\"\n role=\"option\"\n aria-selected={chip.role === option.role}\n data-role-option={option.role}\n title={option.description}\n onClick={() => pick(chip, option.role)}\n className=\"flex items-center justify-between gap-2 rounded px-2 py-1 text-left hover:bg-muted\"\n >\n <span>{option.label}</span>\n {option.accepts?.length ? (\n <span className=\"text-[10px] opacity-60\">{option.accepts.join(', ')}</span>\n ) : null}\n </button>\n ))}\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={!chip.role}\n data-role-option=\"\"\n onClick={() => pick(chip, null)}\n className=\"rounded px-2 py-1 text-left opacity-70 hover:bg-muted\"\n >\n No role\n </button>\n {chip.issues.length > 0 && (\n <ul className=\"mt-1 border-t border-border px-2 pt-1 text-[10px]\" data-chip-issues=\"\">\n {chip.issues.map((issue, i) => (\n <li key={i} className={issue.severity === 'error' ? 'text-red-500' : 'text-amber-500'}>\n {issue.message}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n </span>\n );\n })}\n </div>\n );\n};\n","import React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n DRIFT_TYPE_LABELS,\n recreateRequest,\n referenceRoleLabel,\n stableStringify,\n variationRequest,\n type AssetLookup,\n type GenerationRecord,\n type GenerationRecordFilter,\n type GenerationRecordStore,\n type ModelCapabilities,\n type RecreateMode,\n type RecreateOptions,\n type RecreateResult,\n} from '@pixerate/editor/generation';\n\ntype Template = NonNullable<RecreateOptions['templates']>[number];\n\nexport interface UseGenerationRecordsResult {\n records: GenerationRecord[];\n loading: boolean;\n error: unknown;\n refresh: () => Promise<void>;\n}\n\n/**\n * Loads records from a `GenerationRecordStore` (newest first) and reloads\n * whenever the store changes. `filter` is compared by value.\n */\nexport function useGenerationRecords(\n store: GenerationRecordStore | null | undefined,\n filter?: GenerationRecordFilter,\n): UseGenerationRecordsResult {\n const [records, setRecords] = useState<GenerationRecord[]>([]);\n const [loading, setLoading] = useState(!!store);\n const [error, setError] = useState<unknown>(null);\n const filterKey = stableStringify(filter ?? {});\n\n const refresh = useCallback(async () => {\n if (!store) {\n setRecords([]);\n setLoading(false);\n return;\n }\n setLoading(true);\n try {\n const next = await store.list(filter);\n setRecords(next);\n setError(null);\n } catch (err) {\n setError(err);\n } finally {\n setLoading(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, filterKey]);\n\n useEffect(() => {\n void refresh();\n if (!store) return;\n return store.subscribe(() => void refresh());\n }, [store, refresh]);\n\n return { records, loading, error, refresh };\n}\n\nexport interface GenerationHistoryProps {\n records: readonly GenerationRecord[];\n /** Current templates (drift detection, and resolution in `latest` mode). */\n templates?: readonly Template[];\n /** Current models (capability drift and re-planning). */\n capabilities?: ModelCapabilities | readonly ModelCapabilities[];\n /** Current assets (missing / changed asset drift). */\n assets?: AssetLookup;\n /** Recreate mode (controlled); otherwise a toggle starting at `exact`. */\n mode?: RecreateMode;\n onModeChange?: (mode: RecreateMode) => void;\n /** \"Recreate\": receives the rebuilt request and drift report. */\n onRecreate?: (result: RecreateResult, record: GenerationRecord) => void;\n /** \"Variation\": same request with a new seed. */\n onVariation?: (result: RecreateResult, record: GenerationRecord) => void;\n onRemove?: (record: GenerationRecord) => void;\n /** Extra content per record (e.g. output thumbnails). */\n renderPreview?: (record: GenerationRecord) => React.ReactNode;\n emptyText?: string;\n className?: string;\n}\n\nconst severityClass = {\n info: 'text-sky-600 dark:text-sky-300',\n warning: 'text-amber-600 dark:text-amber-300',\n error: 'text-red-600 dark:text-red-400',\n} as const;\n\n/**\n * Generation history: one entry per record with its model, prompt, asset\n * role badges and the drift since it was made (templates edited, model\n * capabilities changed, settings no longer supported, assets missing), plus\n * Recreate and Variation buttons. Recreate is disabled when drift blocks it.\n */\nexport const GenerationHistory: React.FC<GenerationHistoryProps> = ({\n records,\n templates,\n capabilities,\n assets,\n mode: modeProp,\n onModeChange,\n onRecreate,\n onVariation,\n onRemove,\n renderPreview,\n emptyText = 'No generations yet.',\n className = '',\n}) => {\n const [innerMode, setInnerMode] = useState<RecreateMode>('exact');\n const mode = modeProp ?? innerMode;\n const setMode = (next: RecreateMode) => {\n if (modeProp === undefined) setInnerMode(next);\n onModeChange?.(next);\n };\n const options = useMemo(() => ({ templates, capabilities, assets, mode }), [templates, capabilities, assets, mode]);\n const results = useMemo(() => records.map((record) => recreateRequest(record, options)), [records, options]);\n const models = useMemo(\n () => (capabilities ? (Array.isArray(capabilities) ? capabilities : [capabilities as ModelCapabilities]) : []),\n [capabilities],\n );\n\n return (\n <div className={`flex flex-col gap-2 text-sm ${className}`} data-generation-history=\"\">\n <div className=\"flex items-center gap-2 text-xs\" role=\"radiogroup\" aria-label=\"Recreate mode\">\n {(['exact', 'latest'] as const).map((value) => (\n <label key={value} className=\"inline-flex items-center gap-1\">\n <input\n type=\"radio\"\n name=\"generation-history-mode\"\n value={value}\n checked={mode === value}\n onChange={() => setMode(value)}\n data-mode={value}\n />\n {value === 'exact' ? 'Exact (recorded versions)' : 'Latest templates & settings'}\n </label>\n ))}\n </div>\n {records.length === 0 && <p className=\"text-xs text-muted-foreground\">{emptyText}</p>}\n <ul className=\"flex flex-col gap-2\">\n {records.map((record, i) => {\n const result = results[i];\n const model = models.find((m) => m.id === record.provenance.modelId);\n const blocked = !result.ok;\n return (\n <li\n key={record.id}\n className=\"rounded-md border border-border p-2\"\n data-record={record.id}\n data-drift={result.drift.length}\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2 text-xs\">\n <span className=\"font-medium\">{model?.name ?? record.provenance.modelId}</span>\n <span className=\"text-muted-foreground\">\n {record.kind !== 'generate' && <span data-kind=\"\">{record.kind} · </span>}\n <time dateTime={record.createdAt}>{record.createdAt.replace('T', ' ').slice(0, 16)}</time>\n {record.seed !== undefined && <> · seed {record.seed}</>}\n </span>\n </div>\n <p className=\"mt-1 line-clamp-3 break-words text-xs\" data-record-prompt=\"\">\n {record.provenance.resolvedPrompt}\n </p>\n {record.assets.length > 0 && (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {record.assets.map((asset) => (\n <span\n key={`${asset.role}:${asset.id}`}\n className=\"rounded-full bg-muted px-2 py-0.5 text-[10px]\"\n data-asset-role={asset.role}\n >\n {asset.id} · {referenceRoleLabel(asset.role, model)}\n </span>\n ))}\n </div>\n )}\n {renderPreview?.(record)}\n {result.drift.length > 0 && (\n <ul className=\"mt-1 flex flex-col gap-0.5 text-[11px]\" data-drift-report=\"\">\n {result.drift.map((item, j) => (\n <li key={j} className={severityClass[item.severity]} data-drift-type={item.type}>\n <strong>{DRIFT_TYPE_LABELS[item.type]}:</strong> {item.message}\n </li>\n ))}\n </ul>\n )}\n <div className=\"mt-2 flex gap-2\">\n {onRecreate && (\n <button\n type=\"button\"\n disabled={blocked}\n title={blocked ? 'This generation cannot be recreated as recorded.' : undefined}\n onClick={() => onRecreate(result, record)}\n className=\"rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-50\"\n data-action=\"recreate\"\n >\n Recreate\n </button>\n )}\n {onVariation && (\n <button\n type=\"button\"\n onClick={() => onVariation(variationRequest(record, {}, options), record)}\n className=\"rounded border border-border px-2 py-0.5 text-xs hover:bg-muted\"\n data-action=\"variation\"\n >\n Variation\n </button>\n )}\n {onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(record)}\n className=\"ml-auto rounded px-2 py-0.5 text-xs text-muted-foreground hover:bg-muted\"\n data-action=\"remove\"\n >\n Remove\n </button>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n );\n};\n"],"mappings":"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","names":["useMemo","useMemo","jsx","jsxs","useEffect","useMemo","useState","jsx","jsxs","useEffect","useMemo","useState","jsx","jsxs"]}
|