@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,68 +1,346 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useMemo, useState } from 'react';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import styled from 'styled-components';
5
+ const Overlay = styled.div `
6
+ position: fixed;
7
+ inset: 0;
8
+ z-index: 140;
9
+ background: rgba(0, 0, 0, 0.92);
10
+ color: white;
11
+ `;
12
+ const BackdropButton = styled.button `
13
+ position: absolute;
14
+ inset: 0;
15
+ border: 0;
16
+ background: transparent;
17
+ cursor: default;
18
+ `;
19
+ const Shell = styled.div `
20
+ position: absolute;
21
+ inset: 0;
22
+ z-index: 1;
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 12px;
26
+ padding: 14px;
27
+ pointer-events: none;
28
+
29
+ @media (max-width: 760px) {
30
+ padding: 10px;
31
+ }
32
+ `;
33
+ const TopBar = styled.div `
34
+ pointer-events: auto;
35
+ display: flex;
36
+ align-items: flex-start;
37
+ justify-content: space-between;
38
+ gap: 16px;
39
+ border: 1px solid rgba(255, 255, 255, 0.14);
40
+ background: rgba(0, 0, 0, 0.58);
41
+ backdrop-filter: blur(18px);
42
+ border-radius: 16px;
43
+ padding: 12px 14px;
44
+
45
+ @media (max-width: 760px) {
46
+ flex-direction: column;
47
+ }
48
+ `;
49
+ const HeaderText = styled.div `
50
+ min-width: 0;
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: 6px;
54
+ `;
55
+ const Title = styled.div `
56
+ font-family: var(--viewer-ui-font-display, inherit);
57
+ font-size: clamp(18px, 2vw, 28px);
58
+ line-height: 1.05;
59
+ color: white;
60
+ `;
61
+ const Subtitle = styled.div `
62
+ font-size: 12px;
63
+ line-height: 1.5;
64
+ color: rgba(255, 255, 255, 0.66);
65
+ `;
66
+ const MetaRow = styled.div `
67
+ display: flex;
68
+ flex-wrap: wrap;
69
+ gap: 8px;
70
+ `;
71
+ const MetaPill = styled.span `
72
+ display: inline-flex;
73
+ align-items: center;
74
+ min-height: 24px;
75
+ padding: 0 9px;
76
+ border-radius: 999px;
77
+ border: 1px solid rgba(255, 255, 255, 0.18);
78
+ background: rgba(255, 255, 255, 0.08);
79
+ font-size: 10px;
80
+ letter-spacing: 0.14em;
81
+ text-transform: uppercase;
82
+ color: rgba(255, 255, 255, 0.64);
83
+ `;
84
+ const TopActions = styled.div `
85
+ display: flex;
86
+ flex-wrap: wrap;
87
+ justify-content: flex-end;
88
+ gap: 8px;
89
+ `;
90
+ const ActionButton = styled.button `
91
+ min-height: 34px;
92
+ padding: 0 12px;
93
+ border-radius: 999px;
94
+ border: 1px solid ${(props) => (props.$variant === 'primary' ? 'rgba(255, 255, 255, 0.62)' : 'rgba(255, 255, 255, 0.24)')};
95
+ background: ${(props) => (props.$variant === 'primary' ? 'rgba(255, 255, 255, 0.92)' : 'rgba(255, 255, 255, 0.08)')};
96
+ color: ${(props) => (props.$variant === 'primary' ? '#0d1117' : 'rgba(255, 255, 255, 0.86)')};
97
+ cursor: pointer;
98
+ font-size: 11px;
99
+ font-weight: 700;
100
+ letter-spacing: 0.06em;
101
+ text-transform: lowercase;
102
+
103
+ &:disabled {
104
+ opacity: 0.42;
105
+ cursor: not-allowed;
106
+ }
107
+ `;
108
+ const CloseButton = styled.button `
109
+ min-width: 34px;
110
+ min-height: 34px;
111
+ border: 0;
112
+ padding: 0 8px;
113
+ border-radius: 999px;
114
+ background: transparent;
115
+ color: rgba(255, 255, 255, 0.72);
116
+ cursor: pointer;
117
+ font-size: 26px;
118
+ line-height: 1;
119
+
120
+ &:hover {
121
+ color: white;
122
+ }
123
+ `;
124
+ const ShareRow = styled.div `
125
+ pointer-events: auto;
126
+ display: flex;
127
+ flex-wrap: wrap;
128
+ gap: 8px;
129
+ `;
130
+ const Stage = styled.div `
131
+ position: relative;
132
+ pointer-events: auto;
133
+ flex: 1;
134
+ min-height: 0;
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ `;
139
+ const Image = styled.img `
140
+ max-width: 100%;
141
+ max-height: 100%;
142
+ object-fit: contain;
143
+ display: block;
144
+ `;
145
+ const EmptyImage = styled.div `
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ min-height: 240px;
150
+ font-size: 11px;
151
+ letter-spacing: 0.18em;
152
+ text-transform: uppercase;
153
+ color: rgba(255, 255, 255, 0.48);
154
+ `;
155
+ const StatusText = styled.div `
156
+ pointer-events: auto;
157
+ align-self: flex-start;
158
+ border-radius: 999px;
159
+ border: 1px solid rgba(255, 255, 255, 0.14);
160
+ background: rgba(0, 0, 0, 0.46);
161
+ padding: 7px 10px;
162
+ font-size: 11px;
163
+ color: rgba(255, 255, 255, 0.68);
164
+ `;
165
+ const DetailsToggle = styled.button `
166
+ position: absolute;
167
+ right: 0;
168
+ top: 50%;
169
+ transform: translateY(-50%);
170
+ border-radius: 999px;
171
+ border: 1px solid rgba(255, 255, 255, 0.28);
172
+ background: rgba(0, 0, 0, 0.56);
173
+ color: rgba(255, 255, 255, 0.82);
174
+ min-height: 36px;
175
+ padding: 0 12px;
176
+ cursor: pointer;
177
+ font-size: 11px;
178
+ letter-spacing: 0.12em;
179
+ text-transform: uppercase;
180
+ `;
181
+ const DetailsDrawer = styled.aside `
182
+ pointer-events: auto;
183
+ position: absolute;
184
+ top: 0;
185
+ right: 0;
186
+ bottom: 0;
187
+ width: min(460px, 92vw);
188
+ overflow: auto;
189
+ border-left: 1px solid rgba(255, 255, 255, 0.14);
190
+ background: rgba(0, 0, 0, 0.88);
191
+ backdrop-filter: blur(18px);
192
+ padding: 18px;
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: 16px;
196
+ `;
197
+ const DrawerHeader = styled.div `
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: space-between;
201
+ gap: 12px;
202
+ `;
203
+ const DrawerTitle = styled.div `
204
+ font-size: 11px;
205
+ letter-spacing: 0.18em;
206
+ text-transform: uppercase;
207
+ color: rgba(255, 255, 255, 0.54);
208
+ `;
209
+ const DetailTabs = styled.div `
210
+ display: flex;
211
+ flex-wrap: wrap;
212
+ gap: 8px;
213
+ `;
214
+ const DetailTab = styled.button `
215
+ min-height: 30px;
216
+ padding: 0 10px;
217
+ border-radius: 999px;
218
+ border: 1px solid ${(props) => (props.$active ? 'rgba(255, 255, 255, 0.6)' : 'rgba(255, 255, 255, 0.18)')};
219
+ background: ${(props) => (props.$active ? 'rgba(255, 255, 255, 0.2)' : 'rgba(255, 255, 255, 0.06)')};
220
+ color: rgba(255, 255, 255, 0.78);
221
+ cursor: pointer;
222
+ font-size: 11px;
223
+ text-transform: lowercase;
224
+ `;
225
+ const DetailDescription = styled.div `
226
+ font-size: 12px;
227
+ line-height: 1.5;
228
+ color: rgba(255, 255, 255, 0.58);
229
+ `;
230
+ const DrawerSection = styled.div `
231
+ display: flex;
232
+ flex-direction: column;
233
+ gap: 10px;
234
+ `;
235
+ const TitleInput = styled.input `
236
+ width: 100%;
237
+ min-height: 36px;
238
+ border-radius: 999px;
239
+ border: 1px solid rgba(255, 255, 255, 0.18);
240
+ background: rgba(255, 255, 255, 0.06);
241
+ color: white;
242
+ padding: 0 12px;
243
+ font-size: 12px;
244
+ outline: none;
245
+ `;
4
246
  const statusLabel = (status) => {
5
247
  if (!status || status === 'idle')
6
248
  return null;
7
249
  if (status === 'sending' || status === 'saving')
8
- return 'working';
250
+ return 'working';
9
251
  if (status === 'sent' || status === 'saved')
10
252
  return 'done';
11
253
  return 'failed';
12
254
  };
13
- export default function FullscreenImage({ open, src, alt = 'image', title, subtitle, modeLabel, prompt, promptManifest, createdAt, publicUrl, copyUrl, onClose, primaryAction, secondaryAction, editableTitle, }) {
255
+ export default function FullscreenImage({ open, src, alt = 'image', title, subtitle, modeLabel, createdAt, publicUrl, copyUrl, onClose, primaryAction, secondaryAction, extraActions = [], editableTitle, detailPanels = [], statusText, }) {
14
256
  const [shareStatus, setShareStatus] = useState('idle');
15
- const manifestText = useMemo(() => {
16
- if (!promptManifest)
17
- return '';
18
- try {
19
- return JSON.stringify(promptManifest, null, 2);
20
- }
21
- catch {
22
- return '';
257
+ const [detailsOpen, setDetailsOpen] = useState(false);
258
+ const [activeDetailPanelId, setActiveDetailPanelId] = useState(null);
259
+ const resolvedDetailPanels = useMemo(() => detailPanels, [detailPanels]);
260
+ useEffect(() => {
261
+ if (!open) {
262
+ setDetailsOpen(false);
263
+ setActiveDetailPanelId(null);
264
+ return;
23
265
  }
24
- }, [promptManifest]);
25
- if (!open || !src)
266
+ const defaultPanel = resolvedDetailPanels.find((panel) => panel.defaultOpen) ?? resolvedDetailPanels[0];
267
+ setDetailsOpen(false);
268
+ setActiveDetailPanelId(defaultPanel?.id ?? null);
269
+ }, [open, resolvedDetailPanels]);
270
+ useEffect(() => {
271
+ if (!open)
272
+ return;
273
+ const onKeyDown = (event) => {
274
+ if (event.key !== 'Escape')
275
+ return;
276
+ if (detailsOpen) {
277
+ setDetailsOpen(false);
278
+ return;
279
+ }
280
+ onClose();
281
+ };
282
+ window.addEventListener('keydown', onKeyDown);
283
+ return () => window.removeEventListener('keydown', onKeyDown);
284
+ }, [detailsOpen, onClose, open]);
285
+ if (!open)
26
286
  return null;
27
287
  const copy = async (value) => {
28
288
  if (!value)
29
- return;
289
+ return false;
30
290
  try {
31
291
  await navigator.clipboard.writeText(value);
32
- }
33
- catch {
34
- // ignore clipboard failures
35
- }
36
- };
37
- const shareImage = async () => {
38
- if (!publicUrl)
39
- return;
40
- try {
41
- if (typeof navigator !== 'undefined' && typeof navigator.share === 'function') {
42
- await navigator.share({
43
- title: title || 'render image',
44
- text: subtitle || modeLabel || 'Composer render',
45
- url: publicUrl,
46
- });
47
- setShareStatus('idle');
48
- return;
49
- }
50
- await copy(publicUrl);
51
292
  setShareStatus('copied');
293
+ window.setTimeout(() => setShareStatus('idle'), 1500);
294
+ return true;
52
295
  }
53
296
  catch {
54
297
  setShareStatus('error');
298
+ window.setTimeout(() => setShareStatus('idle'), 1500);
299
+ return false;
55
300
  }
56
301
  };
57
- const renderAction = (action) => {
302
+ const openShareTarget = (url) => {
303
+ if (typeof window === 'undefined')
304
+ return;
305
+ window.open(url, '_blank', 'noopener,noreferrer');
306
+ };
307
+ const encodedShareTitle = encodeURIComponent(title || 'Treasury image');
308
+ const encodedShareSummary = encodeURIComponent(subtitle || modeLabel || 'Treasury Composer');
309
+ const encodedPublicUrl = encodeURIComponent(publicUrl || '');
310
+ const shareTargets = publicUrl
311
+ ? [
312
+ {
313
+ id: 'copy',
314
+ label: shareStatus === 'copied' ? 'link copied' : shareStatus === 'error' ? 'copy failed' : 'copy link',
315
+ onClick: () => void copy(publicUrl),
316
+ },
317
+ {
318
+ id: 'x',
319
+ label: 'post to x',
320
+ onClick: () => openShareTarget(`https://twitter.com/intent/tweet?url=${encodedPublicUrl}&text=${encodedShareTitle}`),
321
+ },
322
+ {
323
+ id: 'linkedin',
324
+ label: 'linkedin',
325
+ onClick: () => openShareTarget(`https://www.linkedin.com/sharing/share-offsite/?url=${encodedPublicUrl}`),
326
+ },
327
+ {
328
+ id: 'email',
329
+ label: 'email',
330
+ onClick: () => openShareTarget(`mailto:?subject=${encodedShareTitle}&body=${encodedShareSummary}%0A%0A${encodedPublicUrl}`),
331
+ },
332
+ ]
333
+ : [];
334
+ const renderAction = (action, key) => {
58
335
  if (!action)
59
336
  return null;
60
- const classes = action.variant === 'secondary'
61
- ? 'viewer-ui-kit-button viewer-ui-kit-button-secondary'
62
- : 'viewer-ui-kit-button viewer-ui-kit-button-primary';
63
- return (_jsx("button", { type: "button", onClick: action.onClick, disabled: action.disabled, className: `px-4 py-2 text-xs font-black lowercase tracking-[0.08em] ${classes} ${action.disabled ? 'opacity-60 cursor-not-allowed' : ''}`, children: action.label }));
337
+ return (_jsx(ActionButton, { type: "button", onClick: action.onClick, disabled: action.disabled || action.status === 'sending' || action.status === 'saving', "$variant": action.variant ?? 'primary', children: statusLabel(action.status) || action.label }, key));
64
338
  };
65
- return (_jsxs("div", { className: "fixed inset-0 z-[140] bg-black/80 backdrop-blur-sm", children: [_jsx("button", { type: "button", "aria-label": "Close image", className: "absolute inset-0 h-full w-full cursor-default", onClick: onClose }), _jsxs("div", { className: "absolute inset-6 z-[1] grid gap-6 lg:grid-cols-[minmax(0,1.5fr)_minmax(320px,420px)]", children: [_jsx("div", { className: "min-h-0 rounded-3xl border viewer-ui-kit-border viewer-ui-kit-surface shadow-2xl overflow-hidden flex items-center justify-center", children: _jsx("img", { src: src, alt: alt, className: "max-h-full max-w-full object-contain" }) }), _jsxs("div", { className: "min-h-0 rounded-3xl border viewer-ui-kit-border viewer-ui-kit-surface shadow-2xl p-5 overflow-y-auto", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-2xl font-black lowercase viewer-ui-kit-text-strong", children: title || 'untitled image' }), subtitle ? _jsx("div", { className: "mt-1 text-sm viewer-ui-kit-text-muted", children: subtitle }) : null, _jsxs("div", { className: "mt-2 flex flex-wrap gap-2 text-[10px] uppercase tracking-[0.18em] viewer-ui-kit-text-subtle", children: [modeLabel ? _jsx("span", { children: modeLabel }) : null, createdAt ? _jsx("span", { children: createdAt }) : null] })] }), _jsx("button", { type: "button", onClick: onClose, className: "viewer-ui-kit-text-muted viewer-ui-kit-text-hover text-sm uppercase tracking-[0.2em]", children: "close" })] }), editableTitle ? (_jsxs("div", { className: "mt-5 space-y-2", children: [_jsx("div", { className: "text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: "title" }), _jsx("input", { value: editableTitle.value, onChange: (event) => editableTitle.onChange(event.target.value), className: "w-full rounded-xl border viewer-ui-kit-border-subtle viewer-ui-kit-surface px-3 py-2 text-sm viewer-ui-kit-text" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("button", { type: "button", onClick: editableTitle.onSave, className: "px-3 py-2 text-xs font-black lowercase tracking-[0.08em] viewer-ui-kit-button viewer-ui-kit-button-secondary", children: "save title" }), statusLabel(editableTitle.status) ? _jsx("span", { className: "text-xs viewer-ui-kit-text-muted", children: statusLabel(editableTitle.status) }) : null] })] })) : null, _jsxs("div", { className: "mt-5 flex flex-wrap gap-2", children: [renderAction(primaryAction), renderAction(secondaryAction), publicUrl ? (_jsx("button", { type: "button", onClick: () => void shareImage(), className: "px-4 py-2 text-xs font-black lowercase tracking-[0.08em] viewer-ui-kit-button viewer-ui-kit-button-secondary", children: "share image" })) : null, publicUrl ? (_jsx("button", { type: "button", onClick: () => void copy(publicUrl), className: "px-4 py-2 text-xs font-black lowercase tracking-[0.08em] viewer-ui-kit-button viewer-ui-kit-button-secondary", children: "copy public link" })) : null, copyUrl && copyUrl !== publicUrl ? (_jsx("button", { type: "button", onClick: () => void copy(copyUrl), className: "px-4 py-2 text-xs font-black lowercase tracking-[0.08em] viewer-ui-kit-button viewer-ui-kit-button-secondary", children: "copy image link" })) : null] }), (primaryAction?.status || secondaryAction?.status || shareStatus !== 'idle') ? (_jsx("div", { className: "mt-2 text-xs viewer-ui-kit-text-muted", children: statusLabel(primaryAction?.status) ||
66
- statusLabel(secondaryAction?.status) ||
67
- (shareStatus === 'copied' ? 'link copied' : shareStatus === 'error' ? 'share failed' : null) })) : null, prompt ? (_jsxs("div", { className: "mt-6 space-y-2", children: [_jsx("div", { className: "text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: "prompt" }), _jsx("pre", { className: "whitespace-pre-wrap rounded-2xl border viewer-ui-kit-border viewer-ui-kit-surface p-3 text-[11px] viewer-ui-kit-text", children: prompt })] })) : null, manifestText ? (_jsxs("div", { className: "mt-6 space-y-2", children: [_jsx("div", { className: "text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: "manifest" }), _jsx("pre", { className: "whitespace-pre-wrap rounded-2xl border viewer-ui-kit-border viewer-ui-kit-surface p-3 text-[10px] font-mono viewer-ui-kit-text-muted", children: manifestText })] })) : null] })] })] }));
339
+ const activePanel = resolvedDetailPanels.find((panel) => panel.id === activeDetailPanelId) ?? resolvedDetailPanels[0] ?? null;
340
+ return (_jsxs(Overlay, { role: "dialog", "aria-modal": "true", children: [_jsx(BackdropButton, { type: "button", "aria-label": "Close image", onClick: onClose }), _jsxs(Shell, { children: [_jsxs(TopBar, { children: [_jsxs(HeaderText, { children: [_jsx(Title, { children: title || 'untitled image' }), subtitle ? _jsx(Subtitle, { children: subtitle }) : null, _jsxs(MetaRow, { children: [modeLabel ? _jsx(MetaPill, { children: modeLabel }) : null, createdAt ? _jsx(MetaPill, { children: createdAt }) : null] })] }), _jsxs(TopActions, { children: [renderAction(primaryAction, 'primary'), renderAction(secondaryAction, 'secondary'), extraActions.map((action, index) => renderAction(action, `extra-${index}-${action.label}-${action.variant ?? 'secondary'}`)), copyUrl && copyUrl !== publicUrl ? (_jsx(ActionButton, { type: "button", onClick: () => void copy(copyUrl), "$variant": "secondary", children: "copy image link" })) : null, resolvedDetailPanels.length > 0 ? (_jsx(ActionButton, { type: "button", onClick: () => setDetailsOpen((prev) => !prev), "$variant": "secondary", children: detailsOpen ? 'close details' : 'details' })) : null, _jsx(CloseButton, { type: "button", onClick: onClose, "aria-label": "close", children: "\u00D7" })] })] }), shareTargets.length > 0 ? (_jsx(ShareRow, { children: shareTargets.map((target) => (_jsx(ActionButton, { type: "button", onClick: target.onClick, "$variant": "secondary", children: target.label }, target.id))) })) : null, _jsxs(Stage, { children: [src ? (
341
+ // eslint-disable-next-line @next/next/no-img-element
342
+ _jsx(Image, { src: src, alt: alt })) : (_jsx(EmptyImage, { children: "image unavailable" })), resolvedDetailPanels.length > 0 ? (_jsx(DetailsToggle, { type: "button", onClick: () => setDetailsOpen((prev) => !prev), children: detailsOpen ? 'close details' : 'details' })) : null, detailsOpen ? (_jsxs(DetailsDrawer, { onClick: (event) => event.stopPropagation(), children: [_jsxs(DrawerHeader, { children: [_jsx(DrawerTitle, { children: "image details" }), _jsx(CloseButton, { type: "button", onClick: () => setDetailsOpen(false), "aria-label": "close details", children: "\u00D7" })] }), editableTitle ? (_jsxs(DrawerSection, { children: [_jsx(DrawerTitle, { children: "title" }), _jsx(TitleInput, { value: editableTitle.value, onChange: (event) => editableTitle.onChange(event.target.value) }), _jsx(TopActions, { children: _jsx(ActionButton, { type: "button", onClick: editableTitle.onSave, "$variant": "secondary", children: statusLabel(editableTitle.status) || 'save title' }) })] })) : null, _jsx(DetailTabs, { children: resolvedDetailPanels.map((panel) => (_jsx(DetailTab, { type: "button", onClick: () => setActiveDetailPanelId(panel.id), "$active": panel.id === activePanel?.id, children: panel.label }, panel.id))) }), activePanel ? (_jsxs(DrawerSection, { children: [activePanel.description ? _jsx(DetailDescription, { children: activePanel.description }) : null, activePanel.content] })) : null] })) : null] }), (statusText || primaryAction?.status || secondaryAction?.status || shareStatus !== 'idle') ? (_jsx(StatusText, { children: statusText ||
343
+ statusLabel(primaryAction?.status) ||
344
+ statusLabel(secondaryAction?.status) ||
345
+ (shareStatus === 'copied' ? 'link copied' : shareStatus === 'error' ? 'share failed' : null) })) : null] })] }));
68
346
  }
@@ -7,8 +7,9 @@ type GalleryThumbCardProps = {
7
7
  imageSrc: string;
8
8
  onClick?: () => void;
9
9
  actions?: React.ReactNode;
10
+ mediaOverlay?: React.ReactNode;
10
11
  className?: string;
11
12
  };
12
- export default function GalleryThumbCard({ title, author, modeLabel, metaLine, imageSrc, onClick, actions, className, }: GalleryThumbCardProps): import("react/jsx-runtime").JSX.Element;
13
+ export default function GalleryThumbCard({ title, author, modeLabel, metaLine, imageSrc, onClick, actions, mediaOverlay, className, }: GalleryThumbCardProps): import("react/jsx-runtime").JSX.Element;
13
14
  export {};
14
15
  //# sourceMappingURL=GalleryThumbCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GalleryThumbCard.d.ts","sourceRoot":"","sources":["../src/GalleryThumbCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,KAAK,EACL,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,2CA+BvB"}
1
+ {"version":3,"file":"GalleryThumbCard.d.ts","sourceRoot":"","sources":["../src/GalleryThumbCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAyIF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,KAAK,EACL,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,SAAS,GACV,EAAE,qBAAqB,2CA6DvB"}
@@ -1,12 +1,130 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- export default function GalleryThumbCard({ title, author, modeLabel, metaLine, imageSrc, onClick, actions, className, }) {
4
- return (_jsxs("div", { onClick: onClick, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
3
+ import styled from 'styled-components';
4
+ const Card = styled.div `
5
+ position: relative;
6
+ display: flex;
7
+ height: 100%;
8
+ flex-direction: column;
9
+ overflow: hidden;
10
+ border-radius: 22px;
11
+ border: 1px solid var(--viewer-ui-color-panel-border-subtle);
12
+ background: var(--viewer-ui-color-surface);
13
+ box-shadow: 0 8px 18px rgba(0, 0, 0, 0.14);
14
+ transition: box-shadow 200ms ease, border-color 200ms ease;
15
+ cursor: ${(props) => (props.$interactive ? 'pointer' : 'default')};
16
+
17
+ &:hover {
18
+ border-color: ${(props) => props.$interactive ? 'var(--viewer-ui-color-panel-border)' : 'var(--viewer-ui-color-panel-border-subtle)'};
19
+ box-shadow: ${(props) => props.$interactive ? '0 14px 30px rgba(15, 23, 42, 0.12)' : '0 8px 18px rgba(0, 0, 0, 0.14)'};
20
+ }
21
+ `;
22
+ const MediaFrame = styled.div `
23
+ position: relative;
24
+ aspect-ratio: 4 / 3;
25
+ overflow: hidden;
26
+ border-bottom: 1px solid var(--viewer-ui-color-panel-border-subtle);
27
+ background: var(--viewer-ui-color-surface);
28
+ `;
29
+ const MediaGlow = styled.div `
30
+ position: absolute;
31
+ inset: 0;
32
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 35%, rgba(15, 23, 42, 0.04));
33
+ pointer-events: none;
34
+ `;
35
+ const MediaInset = styled.div `
36
+ position: absolute;
37
+ inset: 0;
38
+ padding: 12px;
39
+ `;
40
+ const MediaInner = styled.div `
41
+ position: relative;
42
+ height: 100%;
43
+ overflow: hidden;
44
+ border-radius: 16px;
45
+ border: 1px solid var(--viewer-ui-color-panel-border-subtle);
46
+ background: var(--viewer-ui-color-surface);
47
+ `;
48
+ const MediaImage = styled.img `
49
+ display: block;
50
+ height: 100%;
51
+ width: 100%;
52
+ object-fit: cover;
53
+ `;
54
+ const MediaFallback = styled.div `
55
+ display: flex;
56
+ height: 100%;
57
+ width: 100%;
58
+ align-items: center;
59
+ justify-content: center;
60
+ padding: 0 16px;
61
+ text-align: center;
62
+ font-size: 11px;
63
+ color: var(--viewer-ui-color-text-muted);
64
+ `;
65
+ const MediaOverlay = styled.div `
66
+ position: absolute;
67
+ left: 18px;
68
+ right: 18px;
69
+ bottom: 18px;
70
+ display: flex;
71
+ justify-content: flex-start;
72
+ pointer-events: auto;
73
+ `;
74
+ const CardBody = styled.div `
75
+ display: flex;
76
+ flex: 1;
77
+ flex-direction: column;
78
+ gap: 12px;
79
+ padding: 16px;
80
+ `;
81
+ const HeaderBlock = styled.div `
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 8px;
85
+ `;
86
+ const Title = styled.div `
87
+ font-size: 14px;
88
+ font-weight: 600;
89
+ line-height: 1.4;
90
+ color: var(--viewer-ui-color-text);
91
+ `;
92
+ const MetaRow = styled.div `
93
+ display: flex;
94
+ flex-wrap: wrap;
95
+ align-items: center;
96
+ gap: 8px;
97
+ `;
98
+ const Author = styled.div `
99
+ font-size: 11px;
100
+ color: var(--viewer-ui-color-text-muted);
101
+ `;
102
+ const ModePill = styled.div `
103
+ border-radius: 999px;
104
+ border: 1px solid var(--viewer-ui-color-panel-border-subtle);
105
+ padding: 2px 8px;
106
+ font-size: 9px;
107
+ letter-spacing: 0.18em;
108
+ text-transform: uppercase;
109
+ color: var(--viewer-ui-color-text-subtle);
110
+ `;
111
+ const MetaLine = styled.div `
112
+ font-size: 10px;
113
+ line-height: 1.4;
114
+ color: var(--viewer-ui-color-text-muted);
115
+ `;
116
+ const ActionSlot = styled.div `
117
+ margin-top: auto;
118
+ border-top: 1px solid var(--viewer-ui-color-panel-border-subtle);
119
+ padding-top: 12px;
120
+ `;
121
+ export default function GalleryThumbCard({ title, author, modeLabel, metaLine, imageSrc, onClick, actions, mediaOverlay, className, }) {
122
+ return (_jsxs(Card, { onClick: onClick, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
5
123
  ? (event) => {
6
124
  if (event.key === 'Enter' || event.key === ' ') {
7
125
  event.preventDefault();
8
126
  onClick();
9
127
  }
10
128
  }
11
- : undefined, className: `relative break-inside-avoid overflow-hidden rounded-[22px] border viewer-ui-kit-border-subtle viewer-ui-kit-surface shadow-[0_8px_18px_rgba(0,0,0,0.14)] transition ${onClick ? 'cursor-pointer hover:border-[var(--viewer-ui-color-panel-border)]' : ''} ${className || ''}`, children: [_jsx("div", { className: "relative overflow-hidden rounded-[20px] border viewer-ui-kit-border-subtle viewer-ui-kit-surface", children: _jsx("img", { src: imageSrc, alt: title, className: "h-auto w-full object-cover", draggable: false }) }), _jsxs("div", { className: "p-4", children: [_jsx("div", { className: "text-[14px] viewer-ui-kit-text normal-case", children: title }), author ? _jsx("div", { className: "mt-1 text-[11px] viewer-ui-kit-text-muted normal-case", children: author }) : null, metaLine ? _jsx("div", { className: "mt-1 text-[10px] viewer-ui-kit-text-muted normal-case", children: metaLine }) : null, modeLabel ? _jsx("div", { className: "mt-2 text-[10px] uppercase tracking-[0.2em] viewer-ui-kit-text-subtle", children: modeLabel }) : null] }), actions ? _jsx("div", { className: "px-4 pb-4", children: actions }) : null] }));
129
+ : undefined, className: className, "$interactive": Boolean(onClick), children: [_jsxs(MediaFrame, { children: [_jsx(MediaGlow, {}), _jsx(MediaInset, { children: _jsx(MediaInner, { children: imageSrc ? (_jsx(MediaImage, { src: imageSrc, alt: title, draggable: false, loading: "lazy", decoding: "async" })) : (_jsx(MediaFallback, { children: "image unavailable" })) }) }), mediaOverlay ? (_jsx(MediaOverlay, { onClick: (event) => event.stopPropagation(), children: mediaOverlay })) : null] }), _jsxs(CardBody, { children: [_jsxs(HeaderBlock, { children: [_jsx(Title, { children: title }), author || modeLabel ? (_jsxs(MetaRow, { children: [author ? _jsx(Author, { children: author }) : null, modeLabel ? (_jsx(ModePill, { children: modeLabel })) : null] })) : null, metaLine ? _jsx(MetaLine, { children: metaLine }) : null] }), actions ? (_jsx(ActionSlot, { children: actions })) : null] })] }));
12
130
  }
@@ -0,0 +1,9 @@
1
+ import { type ComposerGalleryReferenceImage, type ComposerReferenceImageItem } from '@treasuryspatial/render-kit';
2
+ type ImageReferenceStripProps = {
3
+ references?: ComposerGalleryReferenceImage[] | null;
4
+ labelPrefix?: string;
5
+ onOpen?: (reference: ComposerReferenceImageItem) => void;
6
+ };
7
+ export default function ImageReferenceStrip({ references, labelPrefix, onOpen, }: ImageReferenceStripProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=ImageReferenceStrip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageReferenceStrip.d.ts","sourceRoot":"","sources":["../src/ImageReferenceStrip.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAChC,MAAM,6BAA6B,CAAC;AA2CrC,KAAK,wBAAwB,GAAG;IAC9B,UAAU,CAAC,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC1D,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAC1C,UAAU,EACV,WAAyB,EACzB,MAAM,GACP,EAAE,wBAAwB,kDAmB1B"}
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import styled from 'styled-components';
4
+ import { resolveComposerReferenceImageItems, } from '@treasuryspatial/render-kit';
5
+ const Strip = styled.div `
6
+ display: flex;
7
+ flex-wrap: wrap;
8
+ gap: 8px;
9
+ `;
10
+ const ThumbButton = styled.button `
11
+ width: 46px;
12
+ height: 46px;
13
+ border: 1px solid var(--brand-panel-border, rgba(15, 23, 42, 0.14));
14
+ border-radius: 10px;
15
+ padding: 0;
16
+ overflow: hidden;
17
+ background: var(--brand-background, #fff);
18
+ cursor: zoom-in;
19
+ position: relative;
20
+ `;
21
+ const Thumb = styled.img `
22
+ width: 100%;
23
+ height: 100%;
24
+ object-fit: cover;
25
+ display: block;
26
+ `;
27
+ const Badge = styled.span `
28
+ position: absolute;
29
+ left: 3px;
30
+ bottom: 3px;
31
+ max-width: calc(100% - 6px);
32
+ overflow: hidden;
33
+ text-overflow: ellipsis;
34
+ white-space: nowrap;
35
+ border-radius: 999px;
36
+ background: rgba(0, 0, 0, 0.62);
37
+ color: white;
38
+ padding: 1px 5px;
39
+ font-size: 8px;
40
+ line-height: 1.4;
41
+ `;
42
+ export default function ImageReferenceStrip({ references, labelPrefix = 'reference', onOpen, }) {
43
+ const items = resolveComposerReferenceImageItems(references);
44
+ if (items.length === 0)
45
+ return null;
46
+ return (_jsx(Strip, { "aria-label": `${labelPrefix} images`, children: items.map((reference, index) => (_jsxs(ThumbButton, { type: "button", onClick: () => onOpen?.(reference), title: `${labelPrefix} ${index + 1}: ${reference.name}`, children: [_jsx(Thumb, { src: reference.thumbUrl || reference.url, alt: reference.name }), _jsx(Badge, { children: index + 1 })] }, reference.id || `${reference.url}-${index}`))) }));
47
+ }
@@ -9,8 +9,7 @@ type Props = {
9
9
  open: boolean;
10
10
  entries: PromptDebugEntry[];
11
11
  onToggle: (open: boolean) => void;
12
- onOpenEditor?: (entry: PromptDebugEntry) => void;
13
12
  };
14
- export default function PromptDebugPanel({ open, entries, onToggle, onOpenEditor }: Props): import("react/jsx-runtime").JSX.Element;
13
+ export default function PromptDebugPanel({ open, entries, onToggle }: Props): import("react/jsx-runtime").JSX.Element;
15
14
  export {};
16
15
  //# sourceMappingURL=PromptDebugPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PromptDebugPanel.d.ts","sourceRoot":"","sources":["../src/PromptDebugPanel.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,KAAK,2CAiDxF"}
1
+ {"version":3,"file":"PromptDebugPanel.d.ts","sourceRoot":"","sources":["../src/PromptDebugPanel.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC,CAAC;AAmFF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA+B1E"}