@treasuryspatial/prompt-ui-kit 0.1.15 → 0.1.21

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.
Files changed (44) hide show
  1. package/dist/FeedbackPanel.d.ts +20 -1
  2. package/dist/FeedbackPanel.d.ts.map +1 -1
  3. package/dist/FeedbackPanel.js +204 -95
  4. package/dist/FullscreenImage.d.ts +12 -1
  5. package/dist/FullscreenImage.d.ts.map +1 -1
  6. package/dist/FullscreenImage.js +319 -41
  7. package/dist/GalleryThumbCard.d.ts +2 -1
  8. package/dist/GalleryThumbCard.d.ts.map +1 -1
  9. package/dist/GalleryThumbCard.js +121 -3
  10. package/dist/ImageReferenceStrip.d.ts +9 -0
  11. package/dist/ImageReferenceStrip.d.ts.map +1 -0
  12. package/dist/ImageReferenceStrip.js +47 -0
  13. package/dist/PromptDebugPanel.d.ts +1 -2
  14. package/dist/PromptDebugPanel.d.ts.map +1 -1
  15. package/dist/PromptDebugPanel.js +74 -2
  16. package/dist/RenderHistoryPanel.d.ts +6 -1
  17. package/dist/RenderHistoryPanel.d.ts.map +1 -1
  18. package/dist/RenderHistoryPanel.js +55 -55
  19. package/dist/RenderImageStage.d.ts +10 -0
  20. package/dist/RenderImageStage.d.ts.map +1 -0
  21. package/dist/RenderImageStage.js +90 -0
  22. package/dist/RenderLightboxModal.d.ts +4 -2
  23. package/dist/RenderLightboxModal.d.ts.map +1 -1
  24. package/dist/RenderLightboxModal.js +54 -18
  25. package/dist/RenderPanel.d.ts +17 -16
  26. package/dist/RenderPanel.d.ts.map +1 -1
  27. package/dist/RenderPanel.js +178 -204
  28. package/dist/createRenderHistoryActions.d.ts +11 -0
  29. package/dist/createRenderHistoryActions.d.ts.map +1 -0
  30. package/dist/createRenderHistoryActions.js +63 -0
  31. package/dist/imageDetailPanels.d.ts +14 -0
  32. package/dist/imageDetailPanels.d.ts.map +1 -0
  33. package/dist/imageDetailPanels.js +128 -0
  34. package/dist/index.d.ts +7 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -0
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/dist/useRenderImageFlow.d.ts +2 -2
  39. package/dist/useRenderImageFlow.d.ts.map +1 -1
  40. package/dist/useRenderImageFlow.js +1 -1
  41. package/dist/useRenderLightbox.d.ts +14 -2
  42. package/dist/useRenderLightbox.d.ts.map +1 -1
  43. package/dist/useRenderLightbox.js +333 -34
  44. package/package.json +3 -3
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useState } from 'react';
4
- export default function RenderPanel({ isImagineMode, activeTab, promptPack, promptFieldBindings, locationOptions, timeOptions, imageStyleOptions: _imageStyleOptions, styleOptions: _styleOptions, renderLocation, onLocationChange, renderTimeOfDay, onTimeOfDayChange, renderImageStyle, onImageStyleChange, renderStyle, onStyleChange, renderPrompt, onRenderPromptChange, hasPromptScaffold, renderToggles, eraToggleRules, toggleLabels: _toggleLabels, onToggleChange, renderBusy, renderHasGenerated, renderOutputImage, onCaptureRender, renderButtonLabel = 'capture + render', adaptPrompt, onAdaptPromptChange, onAdaptRender, renderFeatureEnabled, onRenderFeatureEnabledChange, renderReferenceMode, onRenderReferenceModeChange, renderReferenceImages, adaptFeatureEnabled, onAdaptFeatureEnabledChange, adaptReferenceMode, onAdaptReferenceModeChange, adaptReferenceImages, styleReferenceLimit, onRenderReferenceImagesAdd, onRenderReferenceImageRemove, onAdaptReferenceImagesAdd, onAdaptReferenceImageRemove, renderError, renderStatus, renderPromptHistory, adaptPromptHistory, fullPromptHistory, blockedDesigners = [], featureRoofType, onFeatureRoofTypeChange, featureRoofscape, onFeatureRoofscapeChange, featureRoofMaterials, onFeatureRoofMaterialsChange, featureMassingHeight, onFeatureMassingHeightChange, featureMassingPerimeter, onFeatureMassingPerimeterChange, featureMassingForm, onFeatureMassingFormChange, featureMassingDiversity, onFeatureMassingDiversityChange, featureMassingDimensions, onFeatureMassingDimensionsChange, featureBuildingProgram, onFeatureBuildingProgramChange, featureCourtyardProgram, onFeatureCourtyardProgramChange, featureMaterialsPrimary, onFeatureMaterialsPrimaryChange, featureMaterialsGround, onFeatureMaterialsGroundChange, featureMaterialsUpper, onFeatureMaterialsUpperChange, featureFacadeVariation, onFeatureFacadeVariationChange, featureFacadeBays, onFeatureFacadeBaysChange, featurePublicRealmStreet, onFeaturePublicRealmStreetChange, viewpointPreset, onViewpointPresetChange, viewVariationMode, onViewVariationModeChange, viewComposition, onViewCompositionChange, canCreateImageSet, onCreateImageSet, imageSetBusy, renderLimitRemaining, adaptOpen, onAdaptOpenChange, showTaskBlockToggle = false, taskBlockEnabled = true, onTaskBlockEnabledChange, }) {
4
+ import { createUiAudio, playUiAudio, resolveOptionSource } from '@treasuryspatial/render-kit';
5
+ import FullscreenImage from './FullscreenImage.js';
6
+ export default function RenderPanel({ isImagineMode, activeTab, promptPack, promptFieldBindings, locationOptions, timeOptions, imageStyleOptions: _imageStyleOptions, styleOptions: _styleOptions, renderLocation, onLocationChange, renderTimeOfDay, onTimeOfDayChange, renderImageStyle, onImageStyleChange, renderStyle, onStyleChange, renderPrompt, onRenderPromptChange, hasPromptBlocks, renderToggles, eraToggleRules, toggleLabels: _toggleLabels, onToggleChange, renderBusy, renderHasGenerated, renderOutputImage, renderProvider = 'gemini', renderProviderToggleEnabled = false, onRenderProviderChange, hasRenderInteractionContext = false, onCaptureRender, renderButtonLabel = 'capture + render', adaptPrompt, onAdaptPromptChange, onAdaptRender, renderFeatureEnabled, onRenderFeatureEnabledChange, renderReferenceMode, onRenderReferenceModeChange, renderReferenceImages, adaptFeatureEnabled, onAdaptFeatureEnabledChange, adaptReferenceMode, onAdaptReferenceModeChange, adaptReferenceImages, styleReferenceLimit, onRenderReferenceImagesAdd, onRenderReferenceImageRemove, onAdaptReferenceImagesAdd, onAdaptReferenceImageRemove, renderError, renderStatus, renderPromptHistory, adaptPromptHistory, fullPromptHistory, blockedDesigners = [], featureRoofType, onFeatureRoofTypeChange, featureRoofscape, onFeatureRoofscapeChange, featureRoofMaterials, onFeatureRoofMaterialsChange, featureMassingHeight, onFeatureMassingHeightChange, featureMassingPerimeter, onFeatureMassingPerimeterChange, featureMassingForm, onFeatureMassingFormChange, featureMassingDiversity, onFeatureMassingDiversityChange, featureMassingDimensions, onFeatureMassingDimensionsChange, featureBuildingProgram, onFeatureBuildingProgramChange, featureCourtyardProgram, onFeatureCourtyardProgramChange, featureMaterialsPrimary, onFeatureMaterialsPrimaryChange, featureMaterialsGround, onFeatureMaterialsGroundChange, featureMaterialsUpper, onFeatureMaterialsUpperChange, featureFacadeVariation, onFeatureFacadeVariationChange, featureFacadeBays, onFeatureFacadeBaysChange, featurePublicRealmStreet, onFeaturePublicRealmStreetChange, cameraProfile, onCameraProfileChange, cameraVariation, onCameraVariationChange, imageFormat, onImageFormatChange, canCreateImageSet, onCreateImageSet, imageSetBusy, renderLimitRemaining, adaptOpen, onAdaptOpenChange, }) {
5
7
  const PROMPT_PACK = promptPack;
6
8
  const [renderDropActive, setRenderDropActive] = useState(false);
7
9
  const [adaptDropActive, setAdaptDropActive] = useState(false);
@@ -20,22 +22,61 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
20
22
  const [styleRequestOpen, setStyleRequestOpen] = useState(false);
21
23
  const [styleRequestValue, setStyleRequestValue] = useState('');
22
24
  const [styleRequestStatus, setStyleRequestStatus] = useState('idle');
25
+ const [activeReferenceImage, setActiveReferenceImage] = useState(null);
23
26
  const styleOptions = useMemo(() => _styleOptions ?? [], [_styleOptions]);
24
- const createAudio = useMemo(() => (src, volume = 0.35) => {
25
- if (typeof Audio === 'undefined')
26
- return null;
27
- const audio = new Audio(src);
28
- audio.volume = volume;
29
- return audio;
30
- }, []);
31
- const renderClickSound = useMemo(() => createAudio('/sfx/render-start.wav'), [createAudio]);
32
- const adaptClickSound = useMemo(() => createAudio('/sfx/adapt-chime.wav'), [createAudio]);
33
- const promptSelectSound = useMemo(() => createAudio('/sfx/prompt-select.wav', 0.25), [createAudio]);
27
+ const renderClickSound = useMemo(() => createUiAudio('/sfx/render-start.wav', { volume: 0.35 }), []);
28
+ const adaptClickSound = useMemo(() => createUiAudio('/sfx/adapt-chime.wav', { volume: 0.35 }), []);
29
+ const promptSelectSound = useMemo(() => createUiAudio('/sfx/prompt-select.wav', { volume: 0.25 }), []);
34
30
  const packPanels = useMemo(() => PROMPT_PACK.ui?.panels ?? [], [PROMPT_PACK]);
31
+ const defaultVariationValue = PROMPT_PACK.mode === 'design' ? 'deterministic' : 'variation';
35
32
  const [panelOpen, setPanelOpen] = useState(() => (PROMPT_PACK.ui?.panels ?? []).reduce((acc, panel) => {
36
33
  acc[panel.id] = true;
37
34
  return acc;
38
35
  }, {}));
36
+ const normalizePromptLabel = (value) => (value || '')
37
+ .trim()
38
+ .toLowerCase()
39
+ .replace(/\s+/g, ' ');
40
+ const shouldShowGroupLabel = (panel, group) => {
41
+ const groupFields = group.fields
42
+ .map((fieldId) => PROMPT_PACK.fields[fieldId])
43
+ .filter(Boolean);
44
+ if (groupFields.length !== 1)
45
+ return true;
46
+ const field = groupFields[0];
47
+ const normalizedGroupLabel = normalizePromptLabel(group.label);
48
+ const normalizedFieldLabel = normalizePromptLabel(field.label ?? field.id);
49
+ const normalizedPanelLabel = normalizePromptLabel(panel.label);
50
+ if (!normalizedGroupLabel)
51
+ return false;
52
+ if (normalizedGroupLabel === normalizedFieldLabel)
53
+ return false;
54
+ if (normalizedGroupLabel === normalizedPanelLabel)
55
+ return false;
56
+ return true;
57
+ };
58
+ const shouldSuppressFieldLabel = (panel, group, fieldId) => {
59
+ const groupFields = group.fields
60
+ .map((candidateId) => PROMPT_PACK.fields[candidateId])
61
+ .filter(Boolean);
62
+ if (groupFields.length !== 1)
63
+ return false;
64
+ const field = groupFields[0];
65
+ if (field.id !== fieldId)
66
+ return false;
67
+ const normalizedGroupLabel = normalizePromptLabel(group.label);
68
+ const normalizedFieldLabel = normalizePromptLabel(field.label ?? field.id);
69
+ if (!normalizedGroupLabel)
70
+ return false;
71
+ return normalizedGroupLabel === normalizedFieldLabel;
72
+ };
73
+ const getCollapsedPanelGroup = (panel) => {
74
+ if ((panel.fields?.length ?? 0) > 0)
75
+ return null;
76
+ if ((panel.groups?.length ?? 0) !== 1)
77
+ return null;
78
+ return panel.groups?.[0] ?? null;
79
+ };
39
80
  useEffect(() => {
40
81
  if (!packPanels.length)
41
82
  return;
@@ -49,7 +90,6 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
49
90
  return next;
50
91
  });
51
92
  }, [packPanels]);
52
- const InfoBadge = (_) => null;
53
93
  const downloadTextFile = (filename, contents, mime = 'text/plain') => {
54
94
  const blob = new Blob([contents], { type: mime });
55
95
  const url = URL.createObjectURL(blob);
@@ -72,10 +112,12 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
72
112
  }
73
113
  };
74
114
  const promptSectionOrder = [
75
- { tag: 'task', label: 'Task' },
115
+ { tag: 'mode_instruction', label: 'Mode instruction' },
116
+ { tag: 'camera_setup', label: 'Camera setup' },
117
+ { tag: 'image_format', label: 'Image format' },
76
118
  { tag: 'form', label: 'Form' },
77
- { tag: 'view', label: 'View' },
78
- { tag: 'style', label: 'Style' },
119
+ { tag: 'image_style', label: 'Image style' },
120
+ { tag: 'scene_style', label: 'Scene style' },
79
121
  { tag: 'program', label: 'Program' },
80
122
  { tag: 'context', label: 'Context' },
81
123
  ];
@@ -95,7 +137,7 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
95
137
  sections.push({ label, content });
96
138
  }
97
139
  });
98
- const userPrompt = parseTagContent(prompt.prompt, 'user_prompt');
140
+ const userPrompt = parseTagContent(prompt.prompt, 'user_request') || parseTagContent(prompt.prompt, 'user_prompt');
99
141
  if (userPrompt) {
100
142
  sections.push({ label: 'User prompt', content: userPrompt });
101
143
  }
@@ -134,90 +176,18 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
134
176
  if (field.optionSource === 'render-kit:time-of-day') {
135
177
  return timeOptions.map((value) => ({ value, label: value.replace(/_/g, ' ') }));
136
178
  }
179
+ if (field.optionSource) {
180
+ const local = PROMPT_PACK.optionSources?.[field.optionSource];
181
+ if (local?.length)
182
+ return local;
183
+ const resolved = resolveOptionSource(field.optionSource);
184
+ if (resolved?.length)
185
+ return resolved;
186
+ }
137
187
  return [];
138
188
  };
139
- const packFieldBindings = useMemo(() => promptFieldBindings ??
140
- ({
141
- 'form.perimeter': { value: featureMassingPerimeter, set: onFeatureMassingPerimeterChange },
142
- 'form.geometry': { value: featureMassingForm, set: onFeatureMassingFormChange },
143
- 'form.diversity': { value: featureMassingDiversity, set: onFeatureMassingDiversityChange },
144
- 'form.height': { value: featureMassingHeight, set: onFeatureMassingHeightChange },
145
- 'form.dimensions': { value: featureMassingDimensions, set: onFeatureMassingDimensionsChange },
146
- 'view.viewpoint': { value: viewpointPreset, set: onViewpointPresetChange },
147
- 'view.variation': { value: viewVariationMode, set: onViewVariationModeChange },
148
- 'view.composition': { value: viewComposition, set: onViewCompositionChange },
149
- 'style.roofType': { value: featureRoofType, set: onFeatureRoofTypeChange },
150
- 'style.roofscape': { value: featureRoofscape, set: onFeatureRoofscapeChange },
151
- 'style.roofMaterials': { value: featureRoofMaterials, set: onFeatureRoofMaterialsChange },
152
- 'style.facadePrimary': { value: featureMaterialsPrimary, set: onFeatureMaterialsPrimaryChange },
153
- 'style.facadeGround': { value: featureMaterialsGround, set: onFeatureMaterialsGroundChange },
154
- 'style.facadeUpper': { value: featureMaterialsUpper, set: onFeatureMaterialsUpperChange },
155
- 'style.facadeVariation': { value: featureFacadeVariation, set: onFeatureFacadeVariationChange },
156
- 'style.facadeBays': { value: featureFacadeBays, set: onFeatureFacadeBaysChange },
157
- 'program.building': { value: featureBuildingProgram, set: onFeatureBuildingProgramChange },
158
- 'program.courtyard': { value: featureCourtyardProgram, set: onFeatureCourtyardProgramChange },
159
- 'program.street': { value: featurePublicRealmStreet, set: onFeaturePublicRealmStreetChange },
160
- 'toggle.landscaping': { value: renderToggles.landscaping, set: (next) => setToggleValue('landscaping', next) },
161
- 'toggle.people': { value: renderToggles.people, set: (next) => setToggleValue('people', next) },
162
- 'toggle.bicycles': { value: renderToggles.bicycles, set: (next) => setToggleValue('bicycles', next) },
163
- 'toggle.cars': { value: renderToggles.cars, set: (next) => setToggleValue('cars', next) },
164
- 'toggle.trams': { value: renderToggles.trams, set: (next) => setToggleValue('trams', next) },
165
- 'toggle.tech': { value: renderToggles.tech, set: (next) => setToggleValue('tech', next) },
166
- 'context.location': { value: renderLocation, set: onLocationChange },
167
- 'context.timeOfDay': { value: renderTimeOfDay, set: onTimeOfDayChange },
168
- 'context.imageStyle': { value: renderImageStyle, set: onImageStyleChange },
169
- 'context.styleReference': { value: renderStyle, set: onStyleChange },
170
- }), [
171
- promptFieldBindings,
172
- featureMassingPerimeter,
173
- onFeatureMassingPerimeterChange,
174
- featureMassingForm,
175
- onFeatureMassingFormChange,
176
- featureMassingDiversity,
177
- onFeatureMassingDiversityChange,
178
- featureMassingHeight,
179
- onFeatureMassingHeightChange,
180
- featureMassingDimensions,
181
- onFeatureMassingDimensionsChange,
182
- viewpointPreset,
183
- onViewpointPresetChange,
184
- viewVariationMode,
185
- onViewVariationModeChange,
186
- viewComposition,
187
- onViewCompositionChange,
188
- featureRoofType,
189
- onFeatureRoofTypeChange,
190
- featureRoofscape,
191
- onFeatureRoofscapeChange,
192
- featureRoofMaterials,
193
- onFeatureRoofMaterialsChange,
194
- featureMaterialsPrimary,
195
- onFeatureMaterialsPrimaryChange,
196
- featureMaterialsGround,
197
- onFeatureMaterialsGroundChange,
198
- featureMaterialsUpper,
199
- onFeatureMaterialsUpperChange,
200
- featureFacadeVariation,
201
- onFeatureFacadeVariationChange,
202
- featureFacadeBays,
203
- onFeatureFacadeBaysChange,
204
- featureBuildingProgram,
205
- onFeatureBuildingProgramChange,
206
- featureCourtyardProgram,
207
- onFeatureCourtyardProgramChange,
208
- featurePublicRealmStreet,
209
- onFeaturePublicRealmStreetChange,
210
- renderToggles,
211
- renderLocation,
212
- onLocationChange,
213
- renderTimeOfDay,
214
- onTimeOfDayChange,
215
- renderImageStyle,
216
- onImageStyleChange,
217
- renderStyle,
218
- onStyleChange,
219
- ]);
220
- const renderPackField = (fieldId) => {
189
+ const packFieldBindings = useMemo(() => promptFieldBindings ?? {}, [promptFieldBindings]);
190
+ const renderPackField = (fieldId, fieldOptions = {}) => {
221
191
  const field = PROMPT_PACK.fields[fieldId];
222
192
  const binding = packFieldBindings[fieldId];
223
193
  if (!field || !binding)
@@ -225,40 +195,53 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
225
195
  const options = resolvePackOptions(field);
226
196
  const label = field.label ?? field.id;
227
197
  const description = field.description ?? '';
198
+ const showLabel = !fieldOptions.suppressLabel;
228
199
  if (field.type === 'toggle' && field.ui === 'chip') {
229
200
  return null;
230
201
  }
231
- if (field.id === 'context.styleReference') {
202
+ if (field.id.endsWith('styleReference')) {
232
203
  const value = typeof binding.value === 'string' ? binding.value : '';
233
- return (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "block text-[11px] lowercase viewer-ui-kit-text-muted", title: "Optional licensed designer style reference.", children: label }), description ? _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: description }) : null, _jsxs("select", { value: value, onChange: (event) => {
204
+ const styleFieldOptions = options.length > 0
205
+ ? options
206
+ : styleOptions.map((style) => ({
207
+ value: style,
208
+ label: style,
209
+ detail: 'Licensed Treasury style reference.',
210
+ }));
211
+ const selectedStyleOption = styleFieldOptions.find((option) => option.value === value);
212
+ const detail = selectedStyleOption
213
+ ? ('detail' in selectedStyleOption ? selectedStyleOption.detail : undefined) ??
214
+ ('meta' in selectedStyleOption ? selectedStyleOption.meta?.detail : undefined)
215
+ : undefined;
216
+ return (_jsxs("div", { className: "panel-field", children: [showLabel ? (_jsx("label", { className: fieldLabelClass, title: "Optional licensed designer style reference.", children: label })) : null, description ? _jsx("div", { className: fieldDescriptionClass, children: description }) : null, _jsxs("select", { value: value, onChange: (event) => {
234
217
  playPromptSelect();
235
218
  binding.set(event.target.value);
236
- }, className: "w-full px-2 py-1 border rounded text-xs viewer-ui-kit-control", children: [_jsx("option", { value: "", children: "none" }), styleOptions.map((style) => (_jsx("option", { value: style, children: style }, style)))] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("button", { type: "button", onClick: () => setStyleRequestOpen((prev) => !prev), className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: styleRequestOpen ? 'hide style request' : 'request a style' }), styleRequestStatus === 'sending' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "sending\u2026" })), styleRequestStatus === 'sent' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "sent" })), styleRequestStatus === 'error' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-error", children: "error" }))] }), styleRequestOpen && (_jsxs("div", { className: "space-y-2 rounded border viewer-ui-kit-border viewer-ui-kit-surface px-3 py-3 text-[11px] viewer-ui-kit-text ", children: [_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "request a licensed style reference for this prompt." }), _jsx("textarea", { rows: 2, value: styleRequestValue, onChange: (event) => setStyleRequestValue(event.target.value), className: "w-full rounded border px-2 py-1 text-xs viewer-ui-kit-surface", placeholder: "designer, studio, or reference name" }), _jsx("button", { type: "button", onClick: () => submitStyleRequest(), disabled: !styleRequestValue.trim() || styleRequestStatus === 'sending', className: "w-full rounded px-3 py-2 text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-button viewer-ui-kit-button-secondary", children: "send request" })] }))] }, fieldId));
219
+ }, className: "cfg-select", children: [_jsx("option", { value: "", children: "none" }), styleFieldOptions.map((option) => (_jsx("option", { value: option.value, children: option.label }, String(option.value))))] }), detail ? _jsx("div", { className: fieldDescriptionClass, children: String(detail) }) : null, _jsxs("div", { className: utilityRowClass, children: [_jsx("button", { type: "button", onClick: () => setStyleRequestOpen((prev) => !prev), className: utilityButtonClass, children: styleRequestOpen ? 'hide style request' : 'request a style' }), styleRequestStatus === 'sending' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "sending\u2026" })), styleRequestStatus === 'sent' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "sent" })), styleRequestStatus === 'error' && (_jsx("span", { className: "text-[10px] lowercase viewer-ui-kit-text-error", children: "error" }))] }), styleRequestOpen && (_jsxs("div", { className: `${subCardClass} text-[11px] viewer-ui-kit-text`, children: [_jsx("textarea", { rows: 2, value: styleRequestValue, onChange: (event) => setStyleRequestValue(event.target.value), className: "cfg-textarea", placeholder: "designer, studio, or reference name" }), _jsx("button", { type: "button", onClick: () => submitStyleRequest(), disabled: !styleRequestValue.trim() || styleRequestStatus === 'sending', className: "cfg-button cfg-button--secondary", children: "send request" })] }))] }, fieldId));
237
220
  }
238
221
  if (field.type === 'toggle') {
239
222
  const active = Boolean(binding.value);
240
223
  const toggleKey = field.id.startsWith('toggle.') ? field.id.replace('toggle.', '') : null;
241
224
  const disabled = toggleKey ? !eraToggleRules[toggleKey] : false;
242
- return (_jsxs("div", { className: "space-y-2", children: [_jsxs("button", { type: "button", onClick: () => {
243
- if (disabled)
244
- return;
245
- playPromptSelect();
246
- binding.set(!active);
247
- }, className: `flex w-full items-center justify-between rounded border px-3 py-2 text-[11px] lowercase transition ${active
248
- ? 'viewer-ui-kit-button viewer-ui-kit-button-primary viewer-ui-kit-border'
249
- : 'viewer-ui-kit-surface viewer-ui-kit-text-muted viewer-ui-kit-border-subtle'} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`, children: [_jsx("span", { children: label }), _jsx("span", { className: "text-[10px] uppercase tracking-[0.2em]", children: active ? 'on' : 'off' })] }), description ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: description })) : null, disabled ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "not available for the current era" })) : null] }, fieldId));
225
+ return (_jsxs("div", { className: "panel-field panel-field--toggle", children: [showLabel ? _jsx("label", { className: fieldLabelClass, children: label }) : null, _jsx("div", { className: "cfg-toggle-group cfg-toggle-group--single", children: _jsx("button", { type: "button", onClick: () => {
226
+ if (disabled)
227
+ return;
228
+ playPromptSelect();
229
+ binding.set(!active);
230
+ }, className: disabled ? 'cfg-toggle opacity-50 cursor-not-allowed' : 'cfg-toggle', "data-active": active, children: active ? 'on' : 'off' }) }), description ? _jsx("div", { className: fieldDescriptionClass, children: description }) : null, disabled ? (_jsx("div", { className: fieldDescriptionClass, children: "not available for the current era" })) : null] }, fieldId));
250
231
  }
251
232
  if (field.type === 'multi') {
252
233
  const list = Array.isArray(binding.value) ? binding.value : [];
253
- return (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "block text-[11px] lowercase viewer-ui-kit-text-muted", children: label }), description ? _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: description }) : null, _jsx("div", { className: "flex flex-wrap gap-2", children: options.map((option) => (_jsx("button", { type: "button", onClick: () => {
234
+ return (_jsxs("div", { className: "panel-field", children: [showLabel ? _jsx("label", { className: fieldLabelClass, children: label }) : null, description ? _jsx("div", { className: fieldDescriptionClass, children: description }) : null, _jsx("div", { className: "panel-chip-row", children: options.map((option) => (_jsx("button", { type: "button", onClick: () => {
254
235
  playPromptSelect();
255
236
  toggleList(list, option.value, (next) => binding.set(next));
256
- }, className: chipClass(list.includes(option.value)), title: option.detail ?? option.meta?.detail?.toString() ?? option.label, children: option.label }, `${fieldId}-${option.value}`))) })] }, fieldId));
237
+ }, className: "cfg-toggle", "data-active": list.includes(option.value), title: option.detail ?? option.meta?.detail?.toString() ?? option.label, children: option.label }, `${fieldId}-${option.value}`))) })] }, fieldId));
257
238
  }
258
239
  if (field.type === 'select') {
259
240
  const value = typeof binding.value === 'string' ? binding.value : '';
260
241
  const hasNone = options.some((option) => option.value === 'none');
261
- const showEmpty = !hasNone && field.id !== 'view.variation';
242
+ const isCameraVariationField = field.id === 'camera.variation';
243
+ const isCameraProfileField = field.id === 'camera.profile';
244
+ const showEmpty = !hasNone && !isCameraVariationField;
262
245
  const detail = options.find((option) => option.value === value)?.detail ?? options.find((option) => option.value === value)?.meta?.detail;
263
246
  const groupOrder = ['Overview', 'Interior', 'Exterior'];
264
247
  const groupedOptions = options.reduce((acc, option) => {
@@ -270,26 +253,32 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
270
253
  acc[group].push(option);
271
254
  return acc;
272
255
  }, {});
273
- const hasGroups = field.id === 'view.viewpoint' && Object.keys(groupedOptions).length > 0;
274
- const selectClassName = `w-full px-2 py-1 border rounded text-xs viewer-ui-kit-control ${field.id === 'view.viewpoint' ? 'normal-case' : 'lowercase'}`;
275
- return (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "block text-[11px] lowercase viewer-ui-kit-text-muted", children: label }), description ? _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: description }) : null, _jsxs("select", { value: value, onChange: (event) => {
256
+ const hasGroups = isCameraProfileField && Object.keys(groupedOptions).length > 0;
257
+ return (_jsxs("div", { className: "panel-field", children: [showLabel ? _jsx("label", { className: fieldLabelClass, children: label }) : null, description ? _jsx("div", { className: fieldDescriptionClass, children: description }) : null, _jsxs("select", { value: value, onChange: (event) => {
276
258
  playPromptSelect();
277
259
  binding.set(event.target.value);
278
- }, className: selectClassName, children: [showEmpty && _jsx("option", { value: "", children: "none" }), hasGroups
260
+ }, className: isCameraProfileField ? 'cfg-select normal-case' : 'cfg-select lowercase', children: [showEmpty && _jsx("option", { value: "", children: "none" }), hasGroups
279
261
  ? groupOrder
280
262
  .filter((group) => groupedOptions[group]?.length)
281
263
  .map((group) => (_jsx("optgroup", { label: group, children: groupedOptions[group].map((option) => (_jsx("option", { value: option.value, children: option.label }, `${fieldId}-${option.value}`))) }, `${fieldId}-${group}`)))
282
- : options.map((option) => (_jsx("option", { value: option.value, children: option.label }, `${fieldId}-${option.value}`)))] }), detail ? _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: detail }) : null] }, fieldId));
264
+ : options.map((option) => (_jsx("option", { value: option.value, children: option.label }, `${fieldId}-${option.value}`)))] }), detail ? _jsx("div", { className: fieldDescriptionClass, children: detail }) : null] }, fieldId));
283
265
  }
284
266
  if (field.type === 'text') {
285
267
  const value = typeof binding.value === 'string' ? binding.value : '';
286
- return (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "block text-[11px] lowercase viewer-ui-kit-text-muted", children: label }), description ? _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: description }) : null, _jsx("input", { type: "text", value: value, onChange: (event) => binding.set(event.target.value), className: "w-full px-2 py-1 border rounded text-xs viewer-ui-kit-control" })] }, fieldId));
268
+ return (_jsxs("div", { className: "panel-field", children: [showLabel ? _jsx("label", { className: fieldLabelClass, children: label }) : null, description ? _jsx("div", { className: fieldDescriptionClass, children: description }) : null, _jsx("input", { type: "text", value: value, onChange: (event) => binding.set(event.target.value), className: "cfg-input" })] }, fieldId));
287
269
  }
288
270
  return null;
289
271
  };
290
- const chipClass = (active) => `rounded-full px-3 py-1 text-[11px] lowercase border transition ${active
291
- ? 'viewer-ui-kit-button viewer-ui-kit-button-primary viewer-ui-kit-border'
292
- : 'bg-[var(--viewer-ui-color-tab-bg)] text-[var(--viewer-ui-color-text-muted)] viewer-ui-kit-border-subtle hover:text-[var(--viewer-ui-color-text-strong)]'}`;
272
+ const utilityButtonClass = 'panel-utility-button';
273
+ const fieldLabelClass = 'panel-field-label-text';
274
+ const fieldDescriptionClass = 'panel-field-help';
275
+ const subCardClass = 'panel-inline-card';
276
+ const utilityRowClass = 'panel-utility-row';
277
+ const utilityMetaClass = 'panel-utility-meta';
278
+ const sectionStackClass = 'panel-section-stack';
279
+ const sectionGroupActionsClass = 'panel-section-group-actions';
280
+ const sectionGroupActionsStackClass = 'panel-section-group-actions panel-section-group-actions--stack';
281
+ const sectionNoteClass = 'panel-section-note';
293
282
  const renderToggleChip = (fieldId) => {
294
283
  const field = PROMPT_PACK.fields[fieldId];
295
284
  const binding = packFieldBindings[fieldId];
@@ -303,7 +292,7 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
303
292
  return;
304
293
  playPromptSelect();
305
294
  binding.set(!active);
306
- }, className: `${chipClass(active)} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`, title: field.description ?? field.label ?? field.id, children: field.label ?? field.id }, fieldId));
295
+ }, className: disabled ? 'cfg-toggle opacity-50 cursor-not-allowed' : 'cfg-toggle', "data-active": active, title: field.description ?? field.label ?? field.id, children: field.label ?? field.id }, fieldId));
307
296
  };
308
297
  const handleRenderKeyDown = (event) => {
309
298
  if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
@@ -319,24 +308,13 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
319
308
  handleAdaptRender();
320
309
  }
321
310
  };
322
- const playSound = (audio) => {
323
- if (!audio)
324
- return;
325
- try {
326
- audio.currentTime = 0;
327
- void audio.play();
328
- }
329
- catch {
330
- // Ignore audio playback errors.
331
- }
332
- };
333
- const playPromptSelect = () => playSound(promptSelectSound);
311
+ const playPromptSelect = () => playUiAudio(promptSelectSound);
334
312
  const handleCaptureRender = () => {
335
- playSound(renderClickSound);
313
+ playUiAudio(renderClickSound);
336
314
  onCaptureRender();
337
315
  };
338
316
  const handleAdaptRender = () => {
339
- playSound(adaptClickSound);
317
+ playUiAudio(adaptClickSound);
340
318
  onAdaptRender();
341
319
  };
342
320
  const handleRenderDragEnter = (event) => {
@@ -450,17 +428,13 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
450
428
  const resolvedTab = activeTab ?? 'all';
451
429
  const showRender = resolvedTab === 'render' || resolvedTab === 'all';
452
430
  const showPresets = resolvedTab === 'presets' || resolvedTab === 'all';
453
- const showContext = resolvedTab === 'context' || resolvedTab === 'all' || (isImagineMode && resolvedTab === 'render');
454
- const promptPlaceholder = isImagineMode
455
- ? hasPromptScaffold
456
- ? 'Optional: add a prompt to refine the render, or rely on the prompt blocks and context below.'
457
- : 'Add a prompt (see user guide), and/or use the prompt blocks and viewpoint/context below.'
458
- : renderHasGenerated
459
- ? 'New render only — captures a fresh screenshot. Use Adapt below to edit the last render.'
460
- : hasPromptScaffold
461
- ? 'Optional: add a prompt to refine the render, or rely on the prompt blocks and context below.'
462
- : 'Add a prompt (see user guide), and/or use the prompt blocks and viewpoint/context below.';
463
- const adaptPlaceholder = 'Describe changes to the last render';
431
+ const showContext = resolvedTab === 'context' || resolvedTab === 'all' || resolvedTab === 'render';
432
+ const promptPlaceholder = 'render prompt';
433
+ const adaptPlaceholder = 'adapt prompt';
434
+ const providerToggle = renderProviderToggleEnabled ? (_jsx("div", { className: sectionGroupActionsClass, role: "group", "aria-label": "Render provider", children: ['gemini', 'openai-responses'].map((provider) => {
435
+ const active = renderProvider === provider;
436
+ return (_jsx("button", { type: "button", onClick: () => onRenderProviderChange?.(provider), disabled: renderBusy || !onRenderProviderChange, className: `cfg-button ${active ? 'cfg-button--primary' : 'cfg-button--secondary'}`, "aria-pressed": active, children: provider === 'gemini' ? 'gemini' : 'openai' }, provider));
437
+ }) })) : null;
464
438
  const getPanelFieldIds = (panel) => {
465
439
  const groupFields = panel.groups?.flatMap((group) => group.fields) ?? [];
466
440
  return [...(panel.fields ?? []), ...groupFields];
@@ -480,8 +454,8 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
480
454
  binding.set(false);
481
455
  return;
482
456
  }
483
- if (field.id === 'view.variation') {
484
- binding.set('variation');
457
+ if (field.id === 'camera.variation') {
458
+ binding.set(defaultVariationValue);
485
459
  return;
486
460
  }
487
461
  const options = resolvePackOptions(field);
@@ -515,60 +489,60 @@ export default function RenderPanel({ isImagineMode, activeTab, promptPack, prom
515
489
  // ignore copy failures
516
490
  }
517
491
  };
518
- const renderFeatureTransfer = (open, onToggle, images, canAddRefs, dropActive, onAdd, onRemove, onDragEnter, onDragOver, onDragLeave, onDrop, mode, onModeChange, contextLabel, allowScaffold) => (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: onToggle, className: "rounded border viewer-ui-kit-border-subtle px-2 py-1 text-[10px] uppercase tracking-[0.12em] viewer-ui-kit-text-muted hover:viewer-ui-kit-text", children: "feature transfer / reference images" }), open && (_jsxs("div", { className: "mt-2 space-y-2 rounded border viewer-ui-kit-border viewer-ui-kit-surface px-3 py-3 text-[11px] viewer-ui-kit-text ", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "text-[10px] uppercase tracking-[0.12em] viewer-ui-kit-text-muted", children: "use as" }), allowScaffold ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => onModeChange('scaffold'), className: `rounded-full border px-2 py-1 text-[9px] uppercase tracking-[0.2em] ${mode === 'scaffold'
519
- ? 'viewer-ui-kit-border bg-[var(--viewer-ui-color-control-hover)] viewer-ui-kit-text'
520
- : 'viewer-ui-kit-border-subtle viewer-ui-kit-text-muted'}`, children: "scaffold" }), _jsx("button", { type: "button", onClick: () => onModeChange('style'), className: `rounded-full border px-2 py-1 text-[9px] uppercase tracking-[0.2em] ${mode === 'style'
521
- ? 'viewer-ui-kit-border bg-[var(--viewer-ui-color-control-hover)] viewer-ui-kit-text'
522
- : 'viewer-ui-kit-border-subtle viewer-ui-kit-text-muted'}`, children: "style transfer" })] })) : (_jsx("div", { className: "rounded-full border viewer-ui-kit-border-subtle px-2 py-1 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted", children: "style transfer" }))] }), _jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: allowScaffold && mode === 'scaffold'
523
- ? `Use a reference image as the structural scaffold (replaces the volumetric scaffold) for this ${contextLabel}.`
524
- : `Use references to transfer palette, materials, and lighting into this ${contextLabel}.` }), _jsxs("div", { className: `relative flex flex-col items-center justify-center gap-1 rounded border border-dashed px-3 py-4 text-center text-[11px] transition ${dropActive
525
- ? 'viewer-ui-kit-border bg-[var(--viewer-ui-color-control-hover)] viewer-ui-kit-text'
526
- : 'border-[var(--viewer-ui-color-panel-border)] viewer-ui-kit-surface viewer-ui-kit-text'} ${!canAddRefs ? 'opacity-50' : ''}`, role: "button", tabIndex: 0, onDragEnter: onDragEnter, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, title: "Drop reference images here or click to upload.", children: [_jsx("div", { className: "text-[10px] uppercase tracking-[0.12em]", children: "drop reference images" }), _jsx("div", { className: "text-[10px] lowercase", children: "or click to upload (png/jpg)" }), _jsxs("div", { className: "mt-1 text-[10px] lowercase viewer-ui-kit-text-subtle", children: ["upload up to ", styleReferenceLimit, " images. images are resized for upload."] }), _jsx("div", { className: "mt-2 text-[9px] lowercase viewer-ui-kit-text-subtle", children: "use only licensed or owned styles. for copyrighted styles, request licensed use in the context section below." }), _jsx("input", { type: "file", accept: "image/*", multiple: true, onChange: (e) => {
527
- onAdd(e.target.files);
528
- e.currentTarget.value = '';
529
- }, disabled: !canAddRefs, className: "absolute inset-0 h-full w-full cursor-pointer opacity-0" })] }), images.length > 0 ? (_jsx("div", { className: "grid grid-cols-3 gap-2", children: images
530
- .filter((image) => Boolean(image.url))
531
- .map((image) => (_jsxs("div", { className: "relative", children: [_jsx("img", { src: image.url, alt: image.name, className: "h-20 w-full rounded object-cover" }), _jsx("button", { type: "button", onClick: () => onRemove(image.id), className: "absolute top-1 right-1 rounded bg-black/70 px-1 py-0.5 text-[10px] lowercase text-white", title: "Remove reference image", children: "remove" })] }, image.id))) })) : (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "no references yet" })), !canAddRefs && (_jsxs("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: ["max ", styleReferenceLimit, " images"] }))] }))] }));
532
- return (_jsxs("div", { className: "viewer-ui-panel-renderer viewer-ui-panel-renderer--flat viewer-ui-render-panel space-y-6", children: [showRender && (_jsxs("div", { className: "panel-section space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-[16px] font-semibold tracking-[0.01em] viewer-ui-kit-text-strong", children: "your prompt" }), _jsx(InfoBadge, { text: "Write the primary render prompt for the current camera view." })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "text-[13px] font-medium uppercase tracking-[0.14em] viewer-ui-kit-text-strong", children: "render" }), _jsx("textarea", { value: renderPrompt, onChange: (e) => onRenderPromptChange(e.target.value), onKeyDown: handleRenderKeyDown, rows: 3, className: "w-full rounded border px-3 py-2 text-[12px] viewer-ui-kit-surface viewer-ui-kit-text", placeholder: promptPlaceholder, "aria-label": "Render prompt" }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("button", { type: "button", onClick: () => setShowRenderPrompts((prev) => !prev), className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: showRenderPrompts ? 'hide previous prompts' : 'show previous prompts' }), showRenderPrompts && renderPromptHistory.length > 0 && (_jsxs("span", { className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: [renderPromptHistory.length, " saved"] }))] }), showRenderPrompts && renderPromptHistory.length === 0 && (_jsx("div", { className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: "no prompts yet" })), showRenderPrompts && renderPromptHistory.length > 0 && (_jsx("div", { className: "max-h-40 overflow-auto space-y-2", children: renderPromptHistory.map((item, index) => {
533
- const key = `render-${index}`;
534
- return (_jsx("div", { className: "rounded border viewer-ui-kit-border viewer-ui-kit-surface px-3 py-2 text-[11px] viewer-ui-kit-text", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "whitespace-pre-wrap", children: item }), _jsx("button", { type: "button", onClick: () => copyToClipboard(key, item), className: "shrink-0 rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: copiedKey === key ? 'copied' : 'copy' })] }) }, `${key}-${item}`));
535
- }) })), _jsx("button", { type: "button", onClick: handleCaptureRender, disabled: renderBusy, className: "w-full rounded-full px-4 py-2.5 text-[12px] font-semibold uppercase tracking-[0.08em] transition-colors viewer-ui-kit-button viewer-ui-kit-button-primary", children: renderBusy ? 'rendering…' : renderButtonLabel }), canCreateImageSet && (_jsx("button", { type: "button", onClick: onCreateImageSet, disabled: renderBusy || imageSetBusy, className: "w-full rounded-full px-4 py-2.5 text-[12px] font-semibold uppercase tracking-[0.08em] transition-colors viewer-ui-kit-button viewer-ui-kit-button-secondary", children: imageSetBusy ? 'creating image set…' : 'create image set' })), canCreateImageSet && (_jsxs("div", { className: "text-[10px] viewer-ui-kit-text-muted", children: ["generates 4 new views \u00B7 ", renderLimitRemaining, " renders remaining this session"] })), renderStatus && _jsx("div", { className: "text-xs viewer-ui-kit-text-muted", children: renderStatus }), renderError && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs viewer-ui-kit-text-error", children: renderError }), blockedDesigners.length > 0 && (_jsxs("div", { className: "text-[10px] viewer-ui-kit-text-muted", children: ["licensable:", ' ', blockedDesigners.map((term, index) => (_jsxs("span", { children: [_jsx("button", { type: "button", onClick: () => submitBlockedRequest(term), title: "Request licensing from Treasury", className: "underline decoration-dotted underline-offset-2 hover:text-[var(--viewer-ui-color-text-strong)]", children: term }), index < blockedDesigners.length - 1 ? ', ' : ''] }, `${term}-${index}`)))] })), blockedRequestStatus === 'sending' && (_jsxs("div", { className: "text-[10px] viewer-ui-kit-text-muted", children: ["requesting license for ", blockedRequestTerm, "\u2026"] })), blockedRequestStatus === 'sent' && (_jsx("div", { className: "text-[10px] viewer-ui-kit-text-muted", children: "thanks \u2014 your licensing request was sent." })), blockedRequestStatus === 'error' && (_jsx("div", { className: "text-[10px] viewer-ui-kit-text-error", children: "licensing request failed \u2014 try again." }))] })), _jsx("div", { children: renderFeatureTransfer(renderFeatureOpen, () => {
536
- const next = !renderFeatureOpen;
537
- setRenderFeatureOpen(next);
538
- onRenderFeatureEnabledChange(next);
539
- }, renderReferenceImages, canAddRenderRefs, renderDropActive, onRenderReferenceImagesAdd, onRenderReferenceImageRemove, handleRenderDragEnter, handleRenderDragOver, handleRenderDragLeave, handleRenderDrop, renderReferenceMode, onRenderReferenceModeChange, 'render', isImagineMode) })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "flex items-center justify-between gap-2", children: _jsx("div", { className: "text-[13px] font-medium uppercase tracking-[0.14em] viewer-ui-kit-text-strong", children: "adapt" }) }), _jsxs("div", { className: "space-y-3", children: [_jsx("textarea", { value: adaptPrompt, onChange: (e) => onAdaptPromptChange(e.target.value), onKeyDown: handleAdaptKeyDown, rows: 3, className: "w-full rounded border px-3 py-2 text-[12px] viewer-ui-kit-surface viewer-ui-kit-text", placeholder: adaptPlaceholder, "aria-label": "Adapt prompt", disabled: !renderOutputImage }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("button", { type: "button", onClick: () => setShowAdaptPrompts((prev) => !prev), className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: showAdaptPrompts ? 'hide previous prompts' : 'show previous prompts' }), showAdaptPrompts && adaptPromptHistory.length > 0 && (_jsxs("span", { className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: [adaptPromptHistory.length, " saved"] }))] }), showAdaptPrompts && adaptPromptHistory.length === 0 && (_jsx("div", { className: "text-[10px] uppercase tracking-[0.14em] viewer-ui-kit-text-muted", children: "no prompts yet" })), showAdaptPrompts && adaptPromptHistory.length > 0 && (_jsx("div", { className: "max-h-40 overflow-auto space-y-2", children: adaptPromptHistory.map((item, index) => {
540
- const key = `adapt-${index}`;
541
- return (_jsx("div", { className: "rounded border viewer-ui-kit-border viewer-ui-kit-surface px-3 py-2 text-[11px] viewer-ui-kit-text", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "whitespace-pre-wrap", children: item }), _jsx("button", { type: "button", onClick: () => copyToClipboard(key, item), className: "shrink-0 rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: copiedKey === key ? 'copied' : 'copy' })] }) }, `${key}-${item}`));
542
- }) })), _jsx("button", { type: "button", onClick: handleAdaptRender, disabled: renderBusy || !renderOutputImage, className: "w-full rounded-full px-4 py-2.5 text-[12px] font-semibold uppercase tracking-[0.08em] transition-colors viewer-ui-kit-button viewer-ui-kit-button-secondary", children: renderBusy ? 'adapting…' : 'adapt image' }), _jsx("div", { className: "text-[10px] viewer-ui-kit-text-muted", children: "note: each adapt step reduces image quality slightly. start a new render for full fidelity." }), _jsx("div", { children: renderFeatureTransfer(adaptFeatureOpen, () => {
543
- const next = !adaptFeatureOpen;
544
- setAdaptFeatureOpen(next);
545
- onAdaptFeatureEnabledChange(next);
546
- }, adaptReferenceImages, canAddAdaptRefs, adaptDropActive, onAdaptReferenceImagesAdd, onAdaptReferenceImageRemove, handleAdaptDragEnter, handleAdaptDragOver, handleAdaptDragLeave, handleAdaptDrop, adaptReferenceMode, onAdaptReferenceModeChange, 'adapt', isImagineMode) })] })] })] })), showContext && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "panel-section space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("div", { className: "text-[16px] font-semibold tracking-[0.01em] viewer-ui-kit-text-strong", children: "prompt blocks" }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("button", { type: "button", onClick: resetCourtyardFeatures, className: "rounded-full border viewer-ui-kit-border-subtle px-3 py-1 text-[9px] uppercase tracking-[0.24em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: "reset blocks" }) })] }), showTaskBlockToggle && (_jsx("div", { className: "pt-3", children: _jsxs("button", { type: "button", onClick: () => onTaskBlockEnabledChange?.(!taskBlockEnabled), className: `flex w-full items-center justify-between rounded border px-3 py-2 text-[11px] lowercase transition ${taskBlockEnabled
547
- ? 'viewer-ui-kit-button viewer-ui-kit-button-primary viewer-ui-kit-border'
548
- : 'viewer-ui-kit-surface viewer-ui-kit-text-muted viewer-ui-kit-border-subtle'}`, children: [_jsx("span", { children: "task prompt" }), _jsx("span", { className: "text-[10px] uppercase tracking-[0.2em]", children: taskBlockEnabled ? 'on' : 'off' })] }) }))] }), _jsx(_Fragment, { children: packPanels.map((panel) => {
492
+ const renderPromptHistoryList = (items, kind, emptyLabel) => {
493
+ if (items.length === 0) {
494
+ return _jsx("div", { className: "panel-field-help", children: emptyLabel });
495
+ }
496
+ return (_jsx("div", { className: sectionStackClass, children: items.map((item, index) => {
497
+ const key = `${kind}-${index}`;
498
+ return (_jsx("div", { className: `${subCardClass} text-[11px] viewer-ui-kit-text`, children: _jsxs("div", { className: utilityRowClass, style: { alignItems: 'flex-start', justifyContent: 'space-between' }, children: [_jsx("div", { className: "whitespace-pre-wrap", children: item }), _jsx("button", { type: "button", onClick: () => copyToClipboard(key, item), className: utilityButtonClass, children: copiedKey === key ? 'copied' : 'copy' })] }) }, `${key}-${item}`));
499
+ }) }));
500
+ };
501
+ const renderFeatureTransferToggle = (open, onToggle) => (_jsx("button", { type: "button", onClick: onToggle, className: utilityButtonClass, children: open ? 'hide references' : 'references' }));
502
+ const renderFeatureTransferPanel = (open, images, canAddRefs, dropActive, onAdd, onRemove, onDragEnter, onDragOver, onDragLeave, onDrop, mode, onModeChange, contextLabel, allowStructure) => open ? (_jsxs("div", { className: subCardClass, children: [_jsx("div", { className: "panel-reference-mode-row", children: allowStructure ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => onModeChange('structure'), className: "cfg-toggle", "data-active": mode === 'structure', children: "structure" }), _jsx("button", { type: "button", onClick: () => onModeChange('style'), className: "cfg-toggle", "data-active": mode === 'style', children: "style transfer" })] })) : (_jsx("div", { className: "panel-reference-mode-chip", children: "style transfer" })) }), _jsxs("div", { className: "panel-reference-upload", "data-drop-active": dropActive ? 'true' : 'false', "data-disabled": !canAddRefs ? 'true' : 'false', onDragEnter: onDragEnter, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, title: "Drop reference images here or click to upload.", children: [_jsx("span", { className: "panel-reference-upload-label", children: "reference images" }), _jsx("span", { className: "panel-reference-upload-meta", children: "drop images here or click to upload" }), _jsxs("span", { className: "panel-reference-upload-meta", children: ["max ", styleReferenceLimit] }), _jsx("input", { type: "file", accept: "image/*", multiple: true, "aria-label": `${contextLabel} reference image upload`, onChange: (e) => {
503
+ onAdd(e.target.files);
504
+ e.currentTarget.value = '';
505
+ }, disabled: !canAddRefs, className: "panel-reference-upload-input" })] }), images.length > 0 ? (_jsx("div", { className: "panel-reference-grid", children: images
506
+ .filter((image) => Boolean(image.url))
507
+ .map((image) => (_jsxs("div", { className: "panel-reference-thumb", children: [_jsx("button", { type: "button", onClick: () => setActiveReferenceImage({ name: image.name, url: image.url }), className: "panel-reference-thumb-open", title: "Open reference image", children: _jsx("img", { src: image.url, alt: image.name, className: "panel-reference-thumb-image" }) }), _jsx("button", { type: "button", onClick: () => onRemove(image.id), className: "panel-reference-thumb-remove", title: "Remove reference image", children: "remove" }), _jsx("div", { className: "panel-reference-thumb-name", title: image.name, children: image.name })] }, image.id))) })) : (_jsx("div", { className: "panel-reference-empty", children: "no references yet" }))] })) : null;
508
+ return (_jsxs("div", { className: "viewer-ui-panel-renderer viewer-ui-panel-renderer--sectioned viewer-ui-render-panel space-y-6", children: [showRender && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "panel-section", children: [_jsx("div", { className: "panel-section-header", children: _jsx("h3", { children: "render" }) }), _jsxs("div", { className: sectionStackClass, children: [_jsx("div", { className: "panel-section-group", children: _jsxs("div", { className: "panel-section-fields", children: [providerToggle, _jsx("textarea", { value: renderPrompt, onChange: (e) => onRenderPromptChange(e.target.value), onKeyDown: handleRenderKeyDown, rows: 3, className: "cfg-textarea", placeholder: promptPlaceholder, "aria-label": "Render prompt" }), _jsxs("div", { className: sectionGroupActionsStackClass, children: [_jsxs("div", { className: sectionGroupActionsClass, children: [_jsx("button", { type: "button", onClick: handleCaptureRender, disabled: renderBusy, className: "cfg-button cfg-button--primary", children: renderBusy ? 'rendering…' : renderButtonLabel }), canCreateImageSet ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: onCreateImageSet, disabled: renderBusy || imageSetBusy, className: "cfg-button cfg-button--secondary", children: imageSetBusy ? 'creating image set…' : 'create image set' }), _jsxs("span", { className: utilityMetaClass, children: [renderLimitRemaining, " renders remaining"] })] })) : null] }), renderStatus ? _jsx("div", { className: sectionNoteClass, children: renderStatus }) : null, renderError ? (_jsxs("div", { className: `${subCardClass} space-y-2`, children: [_jsx("div", { className: "text-xs viewer-ui-kit-text-error", children: renderError }), blockedDesigners.length > 0 ? (_jsxs("div", { className: fieldDescriptionClass, children: ["licensable:", ' ', blockedDesigners.map((term, index) => (_jsxs("span", { children: [_jsx("button", { type: "button", onClick: () => submitBlockedRequest(term), title: "Request licensing from Treasury", className: "underline decoration-dotted underline-offset-2 hover:text-[var(--viewer-ui-color-text-strong)]", children: term }), index < blockedDesigners.length - 1 ? ', ' : ''] }, `${term}-${index}`)))] })) : null, blockedRequestStatus === 'sending' ? (_jsxs("div", { className: fieldDescriptionClass, children: ["requesting license for ", blockedRequestTerm, "\u2026"] })) : null, blockedRequestStatus === 'sent' ? (_jsx("div", { className: fieldDescriptionClass, children: "thanks \u2014 your licensing request was sent." })) : null, blockedRequestStatus === 'error' ? (_jsx("div", { className: "text-[10px] viewer-ui-kit-text-error", children: "licensing request failed \u2014 try again." })) : null] })) : null] })] }) }), _jsxs("div", { className: "panel-section-group", children: [_jsxs("div", { className: sectionGroupActionsClass, children: [_jsx("button", { type: "button", onClick: () => setShowRenderPrompts((prev) => !prev), className: utilityButtonClass, children: showRenderPrompts ? 'hide prompt history' : 'prompt history' }), showRenderPrompts && renderPromptHistory.length > 0 ? (_jsxs("span", { className: utilityMetaClass, children: [renderPromptHistory.length, " saved"] })) : null, renderFeatureTransferToggle(renderFeatureOpen, () => {
509
+ const next = !renderFeatureOpen;
510
+ setRenderFeatureOpen(next);
511
+ onRenderFeatureEnabledChange(next);
512
+ })] }), showRenderPrompts ? renderPromptHistoryList(renderPromptHistory, 'render', 'no prompts yet') : null, renderFeatureTransferPanel(renderFeatureOpen, renderReferenceImages, canAddRenderRefs, renderDropActive, onRenderReferenceImagesAdd, onRenderReferenceImageRemove, handleRenderDragEnter, handleRenderDragOver, handleRenderDragLeave, handleRenderDrop, renderReferenceMode, onRenderReferenceModeChange, 'render', isImagineMode)] })] })] }), _jsxs("div", { className: "panel-section", children: [_jsx("div", { className: "panel-section-header", children: _jsx("h3", { children: "adapt" }) }), _jsxs("div", { className: sectionStackClass, children: [_jsx("div", { className: "panel-section-group", children: _jsxs("div", { className: "panel-section-fields", children: [_jsx("textarea", { value: adaptPrompt, onChange: (e) => onAdaptPromptChange(e.target.value), onKeyDown: handleAdaptKeyDown, rows: 3, className: "cfg-textarea", placeholder: adaptPlaceholder, "aria-label": "Adapt prompt", disabled: !renderOutputImage }), _jsx("div", { className: sectionGroupActionsClass, children: _jsx("button", { type: "button", onClick: handleAdaptRender, disabled: renderBusy || !renderOutputImage || !hasRenderInteractionContext, className: "cfg-button cfg-button--secondary", children: renderBusy ? 'adapting…' : 'adapt image' }) }), !renderOutputImage ? (_jsx("div", { className: fieldDescriptionClass, children: "render an image before adapting." })) : !hasRenderInteractionContext ? (_jsx("div", { className: fieldDescriptionClass, children: "this image has no provider interaction id; render again before adapting." })) : (_jsx("div", { className: fieldDescriptionClass, children: "adapts the current generated interaction." })), renderBusy && renderStatus ? _jsx("div", { className: sectionNoteClass, children: renderStatus }) : null, renderError ? _jsx("div", { className: "text-xs viewer-ui-kit-text-error", children: renderError }) : null] }) }), _jsxs("div", { className: "panel-section-group", children: [_jsxs("div", { className: sectionGroupActionsClass, children: [_jsx("button", { type: "button", onClick: () => setShowAdaptPrompts((prev) => !prev), className: utilityButtonClass, children: showAdaptPrompts ? 'hide prompt history' : 'prompt history' }), showAdaptPrompts && adaptPromptHistory.length > 0 ? (_jsxs("span", { className: utilityMetaClass, children: [adaptPromptHistory.length, " saved"] })) : null, renderFeatureTransferToggle(adaptFeatureOpen, () => {
513
+ const next = !adaptFeatureOpen;
514
+ setAdaptFeatureOpen(next);
515
+ onAdaptFeatureEnabledChange(next);
516
+ })] }), showAdaptPrompts ? renderPromptHistoryList(adaptPromptHistory, 'adapt', 'no prompts yet') : null, renderFeatureTransferPanel(adaptFeatureOpen, adaptReferenceImages, canAddAdaptRefs, adaptDropActive, onAdaptReferenceImagesAdd, onAdaptReferenceImageRemove, handleAdaptDragEnter, handleAdaptDragOver, handleAdaptDragLeave, handleAdaptDrop, adaptReferenceMode, onAdaptReferenceModeChange, 'adapt', isImagineMode)] })] })] })] })), showContext && (_jsxs(_Fragment, { children: [_jsx("div", { className: "panel-section", children: _jsxs("div", { className: "panel-section-header", children: [_jsx("h3", { children: "prompt blocks" }), _jsx("div", { className: `${utilityRowClass} justify-end`, children: _jsx("button", { type: "button", onClick: resetCourtyardFeatures, className: utilityButtonClass, children: "reset current blocks" }) })] }) }), _jsx(_Fragment, { children: packPanels.map((panel) => {
549
517
  const open = panelOpen[panel.id] ?? true;
550
- return (_jsxs("div", { className: "panel-section space-y-4", children: [_jsxs("button", { type: "button", onClick: () => setPanelOpen((prev) => ({ ...prev, [panel.id]: !open })), className: "flex w-full items-center justify-between text-xl text-header-main lowercase viewer-ui-kit-text-strong", children: [_jsx("span", { children: panel.label }), _jsx("span", { className: `transition-transform ${open ? 'rotate-180' : ''}`, children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "h-4 w-4", children: _jsx("path", { fillRule: "evenodd", d: "M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.24a.75.75 0 01-1.06 0L5.21 8.29a.75.75 0 01.02-1.08z", clipRule: "evenodd" }) }) })] }), !open ? (_jsxs("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: [panel.label, " closed"] })) : (_jsxs("div", { className: "space-y-4", children: [panel.description ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: panel.description })) : null, panel.groups?.length ? (_jsx("div", { className: "space-y-6", children: panel.groups.map((group) => {
551
- const groupToggleFields = group.fields.filter((fieldId) => {
552
- const field = PROMPT_PACK.fields[fieldId];
553
- return field?.type === 'toggle' && field.ui === 'chip';
554
- });
555
- const groupFields = group.fields.filter((fieldId) => {
556
- const field = PROMPT_PACK.fields[fieldId];
557
- return !(field?.type === 'toggle' && field.ui === 'chip');
558
- });
559
- return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "text-[11px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted", children: group.label }), group.description ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: group.description })) : null, groupFields.map((fieldId) => renderPackField(fieldId)), groupToggleFields.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: groupToggleFields.map((fieldId) => renderToggleChip(fieldId)) })) : null] }, group.id ?? group.label));
560
- }) })) : (_jsx("div", { className: "space-y-4", children: panel.fields?.map((fieldId) => renderPackField(fieldId)) }))] }))] }, panel.id));
561
- }) })] })), showPresets && (_jsxs("div", { className: "space-y-4 border-b viewer-ui-kit-border pb-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-3xl text-header-main lowercase viewer-ui-kit-text-strong", children: "presets" }), _jsx(InfoBadge, { text: "Saved prompt mixes and prompt history live here." })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "text-[11px] uppercase tracking-[0.24em] font-semibold viewer-ui-kit-text-muted", children: "render prompt history" }), renderPromptHistory.length === 0 ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "no render prompts yet" })) : (_jsx("div", { className: "max-h-40 overflow-auto space-y-2", children: renderPromptHistory.map((item, index) => {
562
- const key = `render-history-${index}`;
563
- return (_jsx("div", { className: "rounded border viewer-ui-kit-border viewer-ui-kit-surface px-2 py-2 text-[11px] viewer-ui-kit-text ", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "whitespace-pre-wrap", children: item }), _jsx("button", { type: "button", onClick: () => copyToClipboard(key, item), className: "shrink-0 rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: "copy" })] }) }, `${key}-${item}`));
564
- }) }))] }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "text-[11px] uppercase tracking-[0.24em] font-semibold viewer-ui-kit-text-muted", children: "adapt prompt history" }), adaptPromptHistory.length === 0 ? (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "no adapt prompts yet" })) : (_jsx("div", { className: "max-h-40 overflow-auto space-y-2", children: adaptPromptHistory.map((item, index) => {
565
- const key = `adapt-history-${index}`;
566
- return (_jsx("div", { className: "rounded border viewer-ui-kit-border viewer-ui-kit-surface px-2 py-2 text-[11px] viewer-ui-kit-text ", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "whitespace-pre-wrap", children: item }), _jsx("button", { type: "button", onClick: () => copyToClipboard(key, item), className: "shrink-0 rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: "copy" })] }) }, `${key}-${item}`));
567
- }) }))] })] })), showPresets && (_jsxs("div", { className: "space-y-2 pt-2 border-t viewer-ui-kit-border", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("button", { type: "button", onClick: () => setFullPromptOpen((prev) => !prev), className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: fullPromptOpen ? 'hide full prompt log' : 'show full prompt log' }), _jsxs("span", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: [fullPromptHistory.length, " total"] })] }), fullPromptOpen && fullPromptHistory.length === 0 && (_jsx("div", { className: "text-[10px] lowercase viewer-ui-kit-text-muted", children: "no full prompts yet" })), fullPromptOpen && fullPromptHistory.length > 0 && (_jsxs("div", { className: "max-h-64 overflow-auto space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 text-[10px] lowercase viewer-ui-kit-text-muted", children: [_jsxs("div", { children: ["prompt #", activeFullPrompt?.seq ?? fullPromptHistory.length, " of ", totalPromptCount] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => setActiveFullPromptIndex((prev) => Math.min(fullPromptHistory.length - 1, prev + 1)), className: "rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", disabled: activeFullPromptIndex >= fullPromptHistory.length - 1, children: "prev" }), _jsx("button", { type: "button", onClick: () => setActiveFullPromptIndex((prev) => Math.max(0, prev - 1)), className: "rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", disabled: activeFullPromptIndex === 0, children: "next" })] })] }), fullPromptHistory.map((item, index) => {
568
- if (index !== activeFullPromptIndex)
569
- return null;
570
- const stamp = new Date().toISOString().replace(/[:.]/g, '-');
571
- const sections = formatPromptSections(item);
572
- return (_jsxs("div", { className: "rounded border viewer-ui-kit-border viewer-ui-kit-surface px-3 py-3 text-[10px] viewer-ui-kit-text space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: [_jsxs("span", { children: [item.kind, " \u00B7 #", item.seq] }), _jsx("span", { children: new Date(item.ts).toLocaleTimeString() })] }), _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "space-y-3", children: sections.length === 0 ? (_jsx("div", { className: "whitespace-pre-wrap", children: item.prompt })) : (sections.map((section) => (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-[11px] font-semibold viewer-ui-kit-text-strong", children: section.label }), _jsx("div", { className: "whitespace-pre-wrap leading-relaxed", children: section.content })] }, `${section.label}-${index}`)))) }), _jsxs("div", { className: "shrink-0 flex flex-col gap-1", children: [_jsx("button", { type: "button", onClick: () => handleCopyPrompt(item.prompt, index), className: "rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: copiedPromptIndex === index ? 'copied' : 'copy' }), _jsx("button", { type: "button", onClick: () => downloadTextFile(`prompt-${stamp}-${item.seq}.txt`, item.prompt), className: "rounded border viewer-ui-kit-border-subtle px-2 py-0.5 text-[9px] uppercase tracking-[0.2em] viewer-ui-kit-text-muted viewer-ui-kit-text-hover", children: "download" })] })] })] }, `full-${index}`));
573
- })] }))] }))] }));
518
+ const collapsedGroup = getCollapsedPanelGroup(panel);
519
+ const collapsedGroupKey = collapsedGroup ? (collapsedGroup.id ?? collapsedGroup.label) : null;
520
+ const collapsedGroupLabel = collapsedGroup ? normalizePromptLabel(collapsedGroup.label) : '';
521
+ const collapsedIntoPanelHeader = Boolean(collapsedGroup) && collapsedGroupLabel !== normalizePromptLabel(panel.label);
522
+ const panelDescription = panel.description?.trim() || '';
523
+ const panelSubLabel = collapsedIntoPanelHeader && collapsedGroup ? collapsedGroup.label : '';
524
+ return (_jsxs("section", { className: "panel-section", "data-panel-id": panel.id, children: [_jsxs("div", { className: "panel-section-header", children: [_jsxs("div", { children: [_jsx("h3", { children: panel.label }), panelSubLabel ? _jsx("div", { className: fieldDescriptionClass, children: panelSubLabel }) : null, panelDescription ? _jsx("div", { className: fieldDescriptionClass, children: panelDescription }) : null] }), _jsx("button", { type: "button", onClick: () => setPanelOpen((prev) => ({ ...prev, [panel.id]: !open })), className: utilityButtonClass, children: open ? 'hide' : 'show' })] }), !open ? (null) : (_jsx("div", { className: "panel-section-fields", children: panel.groups?.length ? (_jsx(_Fragment, { children: panel.groups.map((group) => {
525
+ const groupToggleFields = group.fields.filter((fieldId) => {
526
+ const field = PROMPT_PACK.fields[fieldId];
527
+ return field?.type === 'toggle' && field.ui === 'chip';
528
+ });
529
+ const groupFields = group.fields.filter((fieldId) => {
530
+ const field = PROMPT_PACK.fields[fieldId];
531
+ return !(field?.type === 'toggle' && field.ui === 'chip');
532
+ });
533
+ const showGroupLabel = shouldShowGroupLabel(panel, group);
534
+ const collapseGroupHeading = Boolean(collapsedGroupKey) &&
535
+ collapsedIntoPanelHeader &&
536
+ collapsedGroupKey === (group.id ?? group.label);
537
+ return (_jsxs("div", { className: "panel-section-group", "data-group-id": group.id ?? group.label, children: [showGroupLabel && !collapseGroupHeading ? _jsx("h4", { children: group.label }) : null, group.description && !collapseGroupHeading ? (_jsx("div", { className: fieldDescriptionClass, children: group.description })) : null, _jsx("div", { className: "panel-section-fields", children: groupFields.map((fieldId) => renderPackField(fieldId, {
538
+ suppressLabel: showGroupLabel && shouldSuppressFieldLabel(panel, group, fieldId),
539
+ })) }), groupToggleFields.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2 pt-1", children: groupToggleFields.map((fieldId) => renderToggleChip(fieldId)) })) : null] }, group.id ?? group.label));
540
+ }) })) : (_jsx("div", { className: "panel-section-fields", children: panel.fields?.map((fieldId) => renderPackField(fieldId)) })) }))] }, panel.id));
541
+ }) })] })), showPresets && (_jsxs(_Fragment, { children: [_jsxs("section", { className: "panel-section", "data-panel-id": "render-history", children: [_jsx("div", { className: "panel-section-header", children: _jsx("h3", { children: "render prompt history" }) }), renderPromptHistoryList(renderPromptHistory, 'render-history', 'no render prompts yet')] }), _jsxs("section", { className: "panel-section", "data-panel-id": "adapt-history", children: [_jsx("div", { className: "panel-section-header", children: _jsx("h3", { children: "adapt prompt history" }) }), renderPromptHistoryList(adaptPromptHistory, 'adapt-history', 'no adapt prompts yet')] })] })), showPresets && (_jsxs("section", { className: "panel-section", "data-panel-id": "full-prompt-log", children: [_jsx("div", { className: "panel-section-header", children: _jsx("h3", { children: "full prompt log" }) }), _jsxs("div", { className: "panel-section-fields", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("button", { type: "button", onClick: () => setFullPromptOpen((prev) => !prev), className: utilityButtonClass, children: fullPromptOpen ? 'hide prompt log' : 'show prompt log' }), _jsxs("span", { className: "panel-field-help", children: [fullPromptHistory.length, " total"] })] }), fullPromptOpen && fullPromptHistory.length === 0 && (_jsx("div", { className: "panel-field-help", children: "no full prompts yet" })), fullPromptOpen && fullPromptHistory.length > 0 && (_jsxs("div", { className: "panel-section-fields", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 text-[10px] lowercase viewer-ui-kit-text-muted", children: [_jsxs("div", { children: ["prompt #", activeFullPrompt?.seq ?? fullPromptHistory.length, " of ", totalPromptCount] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => setActiveFullPromptIndex((prev) => Math.min(fullPromptHistory.length - 1, prev + 1)), className: utilityButtonClass, disabled: activeFullPromptIndex >= fullPromptHistory.length - 1, children: "prev" }), _jsx("button", { type: "button", onClick: () => setActiveFullPromptIndex((prev) => Math.max(0, prev - 1)), className: utilityButtonClass, disabled: activeFullPromptIndex === 0, children: "next" })] })] }), fullPromptHistory.map((item, index) => {
542
+ if (index !== activeFullPromptIndex)
543
+ return null;
544
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
545
+ const sections = formatPromptSections(item);
546
+ return (_jsxs("div", { className: "rounded-[18px] border viewer-ui-kit-border-subtle viewer-ui-kit-surface px-4 py-4 text-[10px] viewer-ui-kit-text space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: [_jsxs("span", { children: [item.kind, " \u00B7 #", item.seq] }), _jsx("span", { children: new Date(item.ts).toLocaleTimeString() })] }), _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("div", { className: "space-y-3", children: sections.length === 0 ? (_jsx("div", { className: "whitespace-pre-wrap", children: item.prompt })) : (sections.map((section) => (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-[11px] font-semibold viewer-ui-kit-text-strong", children: section.label }), _jsx("div", { className: "whitespace-pre-wrap leading-relaxed", children: section.content })] }, `${section.label}-${index}`)))) }), _jsxs("div", { className: "shrink-0 flex flex-col gap-1", children: [_jsx("button", { type: "button", onClick: () => handleCopyPrompt(item.prompt, index), className: utilityButtonClass, children: copiedPromptIndex === index ? 'copied' : 'copy' }), _jsx("button", { type: "button", onClick: () => downloadTextFile(`prompt-${stamp}-${item.seq}.txt`, item.prompt), className: utilityButtonClass, children: "download" })] })] })] }, `full-${index}`));
547
+ })] }))] })] })), _jsx(FullscreenImage, { open: Boolean(activeReferenceImage), src: activeReferenceImage?.url || '', alt: activeReferenceImage?.name || 'reference image', title: activeReferenceImage?.name || 'reference image', modeLabel: "reference", onClose: () => setActiveReferenceImage(null) })] }));
574
548
  }