@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.
- package/dist/FeedbackPanel.d.ts +20 -1
- package/dist/FeedbackPanel.d.ts.map +1 -1
- package/dist/FeedbackPanel.js +204 -95
- package/dist/FullscreenImage.d.ts +12 -1
- package/dist/FullscreenImage.d.ts.map +1 -1
- package/dist/FullscreenImage.js +319 -41
- package/dist/GalleryThumbCard.d.ts +2 -1
- package/dist/GalleryThumbCard.d.ts.map +1 -1
- package/dist/GalleryThumbCard.js +121 -3
- package/dist/ImageReferenceStrip.d.ts +9 -0
- package/dist/ImageReferenceStrip.d.ts.map +1 -0
- package/dist/ImageReferenceStrip.js +47 -0
- package/dist/PromptDebugPanel.d.ts +1 -2
- package/dist/PromptDebugPanel.d.ts.map +1 -1
- package/dist/PromptDebugPanel.js +74 -2
- package/dist/RenderHistoryPanel.d.ts +6 -1
- package/dist/RenderHistoryPanel.d.ts.map +1 -1
- package/dist/RenderHistoryPanel.js +55 -55
- package/dist/RenderImageStage.d.ts +10 -0
- package/dist/RenderImageStage.d.ts.map +1 -0
- package/dist/RenderImageStage.js +90 -0
- package/dist/RenderLightboxModal.d.ts +4 -2
- package/dist/RenderLightboxModal.d.ts.map +1 -1
- package/dist/RenderLightboxModal.js +54 -18
- package/dist/RenderPanel.d.ts +17 -16
- package/dist/RenderPanel.d.ts.map +1 -1
- package/dist/RenderPanel.js +178 -204
- package/dist/createRenderHistoryActions.d.ts +11 -0
- package/dist/createRenderHistoryActions.d.ts.map +1 -0
- package/dist/createRenderHistoryActions.js +63 -0
- package/dist/imageDetailPanels.d.ts +14 -0
- package/dist/imageDetailPanels.d.ts.map +1 -0
- package/dist/imageDetailPanels.js +128 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/useRenderImageFlow.d.ts +2 -2
- package/dist/useRenderImageFlow.d.ts.map +1 -1
- package/dist/useRenderImageFlow.js +1 -1
- package/dist/useRenderLightbox.d.ts +14 -2
- package/dist/useRenderLightbox.d.ts.map +1 -1
- package/dist/useRenderLightbox.js +333 -34
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Dispatch, type SetStateAction } from 'react';
|
|
2
|
-
import type { RenderHistoryEntry } from './RenderHistoryPanel';
|
|
3
|
-
import { type RenderLightboxState, type UseRenderLightboxOptions } from './useRenderLightbox';
|
|
2
|
+
import type { RenderHistoryEntry } from './RenderHistoryPanel.js';
|
|
3
|
+
import { type RenderLightboxState, type UseRenderLightboxOptions } from './useRenderLightbox.js';
|
|
4
4
|
export type RenderImageFlowEntry = Omit<RenderHistoryEntry, 'id'> & {
|
|
5
5
|
id?: string;
|
|
6
6
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRenderImageFlow.d.ts","sourceRoot":"","sources":["../src/useRenderImageFlow.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"useRenderImageFlow.d.ts","sourceRoot":"","sources":["../src/useRenderImageFlow.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAqB,KAAK,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAIpH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,GAAG;IAClE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,wBAAwB,GAAG;IACjE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,gBAAgB,EAAE,QAAQ,CAAC,cAAc,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;IACjE,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,mBAAmB,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,kBAAkB,CAAC;IACzE,QAAQ,EAAE,mBAAmB,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAS,yBAA8B,KACtC,oBAyCF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback, useState } from 'react';
|
|
3
|
-
import { useRenderLightbox } from './useRenderLightbox';
|
|
3
|
+
import { useRenderLightbox } from './useRenderLightbox.js';
|
|
4
4
|
const createHistoryId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5
5
|
export const useRenderImageFlow = (options = {}) => {
|
|
6
6
|
const { onImageRegistered, ...lightboxOptions } = options;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComposerGallerySnapshot } from '@treasuryspatial/render-kit';
|
|
2
|
+
import type { RenderHistoryEntry } from './RenderHistoryPanel.js';
|
|
2
3
|
export type RenderLightboxStatus = 'idle' | 'sending' | 'sent' | 'error';
|
|
3
4
|
export type UseRenderLightboxOptions = {
|
|
4
5
|
shareEndpoint?: string;
|
|
@@ -10,17 +11,28 @@ export type UseRenderLightboxOptions = {
|
|
|
10
11
|
isAdminUser?: boolean;
|
|
11
12
|
user?: string | null;
|
|
12
13
|
name?: string | null;
|
|
14
|
+
surfaceId?: string | null;
|
|
15
|
+
mode?: string | null;
|
|
16
|
+
promptPackId?: string | null;
|
|
17
|
+
onRestartFromSnapshot?: (entry: RenderHistoryEntry, snapshot: ComposerGallerySnapshot) => void;
|
|
13
18
|
};
|
|
14
19
|
export type RenderLightboxState = {
|
|
15
20
|
entry: RenderHistoryEntry | null;
|
|
16
21
|
src: string;
|
|
17
22
|
alt: string;
|
|
18
23
|
status: RenderLightboxStatus;
|
|
24
|
+
statusText: string;
|
|
19
25
|
shortId: string | null;
|
|
20
26
|
publicUrl: string;
|
|
21
27
|
open: (entry: RenderHistoryEntry | null, src: string, alt: string) => void;
|
|
22
28
|
close: () => void;
|
|
23
|
-
|
|
29
|
+
shareImage: (entry?: RenderHistoryEntry | null, src?: string) => Promise<void>;
|
|
30
|
+
copyPrompt: (entry?: RenderHistoryEntry | null) => Promise<void>;
|
|
31
|
+
copyPublicLink: (entry?: RenderHistoryEntry | null, src?: string) => Promise<void>;
|
|
32
|
+
copyImageLink: (entry?: RenderHistoryEntry | null, src?: string) => Promise<void>;
|
|
33
|
+
downloadImage: (entry?: RenderHistoryEntry | null, src?: string) => void;
|
|
34
|
+
restartFromSnapshot: (entry?: RenderHistoryEntry | null) => Promise<void>;
|
|
35
|
+
submitToGallery: (entry?: RenderHistoryEntry | null, src?: string) => Promise<void>;
|
|
24
36
|
};
|
|
25
37
|
export declare const useRenderLightbox: (options?: UseRenderLightboxOptions) => RenderLightboxState;
|
|
26
38
|
//# sourceMappingURL=useRenderLightbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRenderLightbox.d.ts","sourceRoot":"","sources":["../src/useRenderLightbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useRenderLightbox.d.ts","sourceRoot":"","sources":["../src/useRenderLightbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,40 +1,196 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { buildImageActionRecord, buildComposerImageApiUrl, isRestartableGallerySnapshot, resolveComposerEffectivePrompt, resolveComposerImageLinks, } from '@treasuryspatial/render-kit';
|
|
3
4
|
export const useRenderLightbox = (options = {}) => {
|
|
4
|
-
const { shareEndpoint = '/api/share/shortlink', galleryEndpoint = '/api/gallery/submit', encodeShareToken, resolveTitle, buildShareUrl, trackTelemetry, isAdminUser, user, name, } = options;
|
|
5
|
+
const { shareEndpoint = '/api/share/shortlink', galleryEndpoint = '/api/gallery/submit', encodeShareToken, resolveTitle, buildShareUrl, trackTelemetry, isAdminUser, user, name, surfaceId, mode, promptPackId, onRestartFromSnapshot, } = options;
|
|
5
6
|
const [entry, setEntry] = useState(null);
|
|
6
7
|
const [src, setSrc] = useState('');
|
|
7
8
|
const [alt, setAlt] = useState('');
|
|
8
9
|
const [shortId, setShortId] = useState(null);
|
|
9
10
|
const [status, setStatus] = useState('idle');
|
|
11
|
+
const [statusText, setStatusText] = useState('');
|
|
12
|
+
const toAbsoluteUrl = useCallback((value) => {
|
|
13
|
+
if (!value)
|
|
14
|
+
return '';
|
|
15
|
+
if (/^(https?:|data:|blob:)/i.test(value))
|
|
16
|
+
return value;
|
|
17
|
+
if (value.startsWith('/') && typeof window !== 'undefined') {
|
|
18
|
+
return `${window.location.origin}${value}`;
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}, []);
|
|
22
|
+
const getOrigin = useCallback(() => (typeof window !== 'undefined' ? window.location.origin : ''), []);
|
|
23
|
+
const isShareLikeUrl = useCallback((value) => {
|
|
24
|
+
if (!value)
|
|
25
|
+
return false;
|
|
26
|
+
if (/^(data:|blob:)/i.test(value))
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = new URL(value, typeof window !== 'undefined' ? window.location.origin : 'https://treasury.space');
|
|
30
|
+
return (parsed.pathname.startsWith('/i/') ||
|
|
31
|
+
parsed.pathname.startsWith('/g/') ||
|
|
32
|
+
parsed.pathname.startsWith('/gallery/') ||
|
|
33
|
+
parsed.pathname.startsWith('/composer/gallery/'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
const resolvePublicUrl = useCallback((targetEntry, targetSrc = src) => {
|
|
40
|
+
const targetShortId = (targetEntry?.id === entry?.id ? shortId : null) ||
|
|
41
|
+
targetEntry?.shortId ||
|
|
42
|
+
targetEntry?.snapshot?.shortId ||
|
|
43
|
+
null;
|
|
44
|
+
const links = resolveComposerImageLinks({
|
|
45
|
+
storage: targetEntry?.storage ?? targetEntry?.snapshot?.storage ?? null,
|
|
46
|
+
shortId: targetShortId,
|
|
47
|
+
origin: getOrigin(),
|
|
48
|
+
fallbackImageUrl: targetSrc,
|
|
49
|
+
});
|
|
50
|
+
if (links.pageUrl)
|
|
51
|
+
return links.pageUrl;
|
|
52
|
+
if (!targetEntry?.storage?.key) {
|
|
53
|
+
return targetSrc && /^https?:/i.test(targetSrc) ? targetSrc : '';
|
|
54
|
+
}
|
|
55
|
+
if (buildShareUrl)
|
|
56
|
+
return buildShareUrl(targetEntry.storage.key, targetShortId);
|
|
57
|
+
if (encodeShareToken) {
|
|
58
|
+
return `${getOrigin()}/i/${encodeShareToken(targetEntry.storage.key)}`;
|
|
59
|
+
}
|
|
60
|
+
return targetSrc && /^https?:/i.test(targetSrc) ? targetSrc : '';
|
|
61
|
+
}, [buildShareUrl, encodeShareToken, entry?.id, getOrigin, shortId, src]);
|
|
62
|
+
const resolveAssetUrl = useCallback((targetEntry, targetSrc = src) => {
|
|
63
|
+
const links = resolveComposerImageLinks({
|
|
64
|
+
storage: targetEntry?.storage ?? targetEntry?.snapshot?.storage ?? null,
|
|
65
|
+
shortId: targetEntry?.shortId || targetEntry?.snapshot?.shortId || (targetEntry?.id === entry?.id ? shortId : null),
|
|
66
|
+
origin: getOrigin(),
|
|
67
|
+
fallbackImageUrl: targetSrc,
|
|
68
|
+
});
|
|
69
|
+
if (links.directImageUrl)
|
|
70
|
+
return links.directImageUrl;
|
|
71
|
+
const canonicalStorageUrl = buildComposerImageApiUrl(targetEntry?.storage);
|
|
72
|
+
if (canonicalStorageUrl) {
|
|
73
|
+
return toAbsoluteUrl(canonicalStorageUrl);
|
|
74
|
+
}
|
|
75
|
+
const storageUrl = String(targetEntry?.storage?.url || '').trim();
|
|
76
|
+
if (storageUrl && !isShareLikeUrl(storageUrl)) {
|
|
77
|
+
if (/^https?:/i.test(storageUrl) || /^data:/i.test(storageUrl) || /^blob:/i.test(storageUrl)) {
|
|
78
|
+
return storageUrl;
|
|
79
|
+
}
|
|
80
|
+
if (storageUrl.startsWith('/')) {
|
|
81
|
+
return toAbsoluteUrl(storageUrl);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (targetEntry?.storage?.key && typeof window !== 'undefined') {
|
|
85
|
+
return `${window.location.origin}/api/images/image?key=${encodeURIComponent(targetEntry.storage.key)}`;
|
|
86
|
+
}
|
|
87
|
+
if (targetSrc && (/^https?:/i.test(targetSrc) || /^data:/i.test(targetSrc) || /^blob:/i.test(targetSrc))) {
|
|
88
|
+
return targetSrc;
|
|
89
|
+
}
|
|
90
|
+
return '';
|
|
91
|
+
}, [entry?.id, getOrigin, isShareLikeUrl, shortId, src, toAbsoluteUrl]);
|
|
92
|
+
const publicUrl = useMemo(() => resolvePublicUrl(entry, src), [entry, resolvePublicUrl, src]);
|
|
93
|
+
const buildEntrySnapshot = useCallback((targetEntry) => {
|
|
94
|
+
if (targetEntry.snapshot)
|
|
95
|
+
return targetEntry.snapshot;
|
|
96
|
+
return {
|
|
97
|
+
id: targetEntry.snapshotId || targetEntry.id,
|
|
98
|
+
operationId: targetEntry.operationId || null,
|
|
99
|
+
interactionId: targetEntry.interactionId || null,
|
|
100
|
+
requestKind: targetEntry.requestKind || (targetEntry.kind === 'capture' ? 'render' : 'adapt'),
|
|
101
|
+
modeId: targetEntry.modeId || mode || null,
|
|
102
|
+
promptPackId: targetEntry.promptPackId || promptPackId || null,
|
|
103
|
+
title: targetEntry.title || null,
|
|
104
|
+
prompt: targetEntry.prompt || null,
|
|
105
|
+
fullPrompt: targetEntry.fullPrompt || null,
|
|
106
|
+
manifest: targetEntry.manifest ?? null,
|
|
107
|
+
galleryStatus: targetEntry.galleryStatus || null,
|
|
108
|
+
referenceImages: targetEntry.referenceImages ?? null,
|
|
109
|
+
baseImage: targetEntry.baseImage ?? null,
|
|
110
|
+
storage: targetEntry.storage
|
|
111
|
+
? {
|
|
112
|
+
bucket: targetEntry.storage.bucket,
|
|
113
|
+
key: targetEntry.storage.key,
|
|
114
|
+
metaKey: targetEntry.storage.metaKey ?? null,
|
|
115
|
+
url: targetEntry.storage.url ?? null,
|
|
116
|
+
}
|
|
117
|
+
: null,
|
|
118
|
+
};
|
|
119
|
+
}, [mode, promptPackId]);
|
|
120
|
+
const ensureShortlink = useCallback(async (targetEntry) => {
|
|
121
|
+
if (!targetEntry?.storage?.key || !shareEndpoint)
|
|
122
|
+
return targetEntry?.shortId || targetEntry?.snapshot?.shortId || '';
|
|
123
|
+
const existing = targetEntry.shortId || targetEntry.snapshot?.shortId || (targetEntry.id === entry?.id ? shortId : null);
|
|
124
|
+
if (existing)
|
|
125
|
+
return existing;
|
|
126
|
+
const response = await fetch(shareEndpoint, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'Content-Type': 'application/json' },
|
|
129
|
+
body: JSON.stringify({
|
|
130
|
+
key: targetEntry.storage.key,
|
|
131
|
+
metaKey: targetEntry.storage.metaKey ?? null,
|
|
132
|
+
title: resolveTitle?.(targetEntry) ?? targetEntry.title ?? '',
|
|
133
|
+
user: user ?? '',
|
|
134
|
+
name: name ?? '',
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
const data = await response.json().catch(() => null);
|
|
138
|
+
const nextShortId = typeof data?.shortId === 'string' ? data.shortId : '';
|
|
139
|
+
if (nextShortId && targetEntry.id === entry?.id)
|
|
140
|
+
setShortId(nextShortId);
|
|
141
|
+
return nextShortId;
|
|
142
|
+
}, [entry?.id, name, resolveTitle, shareEndpoint, shortId, user]);
|
|
143
|
+
const buildTelemetryPayload = useCallback((payload = {}, targetEntry = entry, targetPublicUrl = publicUrl, imageActionKind, imageActionStatus) => ({
|
|
144
|
+
surfaceId: surfaceId ?? undefined,
|
|
145
|
+
mode: mode ?? undefined,
|
|
146
|
+
promptPackId: promptPackId ?? undefined,
|
|
147
|
+
entryId: targetEntry?.id,
|
|
148
|
+
snapshotId: targetEntry?.snapshotId || targetEntry?.snapshot?.id,
|
|
149
|
+
operationId: targetEntry?.operationId || targetEntry?.snapshot?.operationId,
|
|
150
|
+
requestKind: targetEntry?.requestKind || targetEntry?.snapshot?.requestKind,
|
|
151
|
+
galleryStatus: targetEntry?.galleryStatus || targetEntry?.snapshot?.galleryStatus,
|
|
152
|
+
title: targetEntry?.title,
|
|
153
|
+
storageKey: targetEntry?.storage?.key,
|
|
154
|
+
storageUrl: targetEntry?.storage?.url,
|
|
155
|
+
publicUrl: targetPublicUrl || undefined,
|
|
156
|
+
imageAction: targetEntry
|
|
157
|
+
? buildImageActionRecord({
|
|
158
|
+
snapshot: targetEntry.snapshot ?? null,
|
|
159
|
+
imageRecord: targetEntry,
|
|
160
|
+
kind: imageActionKind,
|
|
161
|
+
status: imageActionStatus,
|
|
162
|
+
publicUrl: targetPublicUrl || undefined,
|
|
163
|
+
})
|
|
164
|
+
: undefined,
|
|
165
|
+
...payload,
|
|
166
|
+
}), [entry, mode, promptPackId, publicUrl, surfaceId]);
|
|
167
|
+
const copyText = useCallback(async (value, eventName, targetEntry = entry, targetPublicUrl) => {
|
|
168
|
+
if (!value)
|
|
169
|
+
return;
|
|
170
|
+
try {
|
|
171
|
+
await navigator.clipboard.writeText(value);
|
|
172
|
+
trackTelemetry?.(eventName, buildTelemetryPayload(targetPublicUrl ? { publicUrl: targetPublicUrl } : {}, targetEntry, targetPublicUrl, eventName === 'snapshot_copy_prompt' ? 'prompt.copied' : 'image.shared', 'success'));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
trackTelemetry?.(`${eventName}_error`, buildTelemetryPayload(targetPublicUrl ? { publicUrl: targetPublicUrl } : {}, targetEntry, targetPublicUrl, eventName === 'snapshot_copy_prompt' ? 'prompt.copied' : 'image.shared', 'error'));
|
|
176
|
+
}
|
|
177
|
+
}, [buildTelemetryPayload, entry, trackTelemetry]);
|
|
10
178
|
const open = useCallback((next, nextSrc, nextAlt) => {
|
|
11
179
|
setEntry(next);
|
|
12
180
|
setSrc(nextSrc);
|
|
13
181
|
setAlt(nextAlt);
|
|
14
182
|
setStatus('idle');
|
|
15
|
-
|
|
183
|
+
setStatusText('');
|
|
184
|
+
trackTelemetry?.('gallery_open', buildTelemetryPayload({}, next, resolvePublicUrl(next, nextSrc), 'image.opened', 'success'));
|
|
185
|
+
}, [buildTelemetryPayload, resolvePublicUrl, trackTelemetry]);
|
|
16
186
|
const close = useCallback(() => {
|
|
187
|
+
trackTelemetry?.('image_lightbox_close', buildTelemetryPayload({}, entry, publicUrl, 'image.closed', 'success'));
|
|
17
188
|
setEntry(null);
|
|
18
189
|
setSrc('');
|
|
19
190
|
setAlt('');
|
|
20
191
|
setStatus('idle');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!entry?.storage?.key) {
|
|
24
|
-
return src && /^https?:/i.test(src) ? src : '';
|
|
25
|
-
}
|
|
26
|
-
if (buildShareUrl)
|
|
27
|
-
return buildShareUrl(entry.storage.key, shortId);
|
|
28
|
-
if (typeof window === 'undefined')
|
|
29
|
-
return '';
|
|
30
|
-
if (shortId) {
|
|
31
|
-
return `${window.location.origin}/i/${shortId}`;
|
|
32
|
-
}
|
|
33
|
-
if (encodeShareToken) {
|
|
34
|
-
return `${window.location.origin}/i/${encodeShareToken(entry.storage.key)}`;
|
|
35
|
-
}
|
|
36
|
-
return src && /^https?:/i.test(src) ? src : '';
|
|
37
|
-
}, [buildShareUrl, encodeShareToken, entry?.storage?.key, shortId, src]);
|
|
192
|
+
setStatusText('');
|
|
193
|
+
}, [buildTelemetryPayload, entry, publicUrl, trackTelemetry]);
|
|
38
194
|
useEffect(() => {
|
|
39
195
|
if (!entry?.storage?.key) {
|
|
40
196
|
setShortId(null);
|
|
@@ -72,57 +228,200 @@ export const useRenderLightbox = (options = {}) => {
|
|
|
72
228
|
cancelled = true;
|
|
73
229
|
};
|
|
74
230
|
}, [entry?.storage?.key, entry?.storage?.metaKey, entry?.title, name, resolveTitle, shareEndpoint, user]);
|
|
75
|
-
const submitToGallery = useCallback(async () => {
|
|
76
|
-
if (!
|
|
231
|
+
const submitToGallery = useCallback(async (targetEntry = entry, targetSrc = src) => {
|
|
232
|
+
if (!targetEntry?.storage?.bucket || !targetEntry?.storage?.key) {
|
|
77
233
|
setStatus('error');
|
|
234
|
+
setStatusText('Image storage is missing. Gallery submission is blocked until this render has a real stored asset.');
|
|
78
235
|
return;
|
|
79
236
|
}
|
|
80
237
|
setStatus('sending');
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
238
|
+
setStatusText(Boolean(isAdminUser) ? 'Publishing to gallery…' : 'Proposing to gallery…');
|
|
239
|
+
const title = resolveTitle?.(targetEntry) ?? targetEntry.title ?? '';
|
|
240
|
+
const s3Url = `s3://${targetEntry.storage.bucket}/${targetEntry.storage.key}`;
|
|
241
|
+
const fullPrompt = resolveComposerEffectivePrompt({
|
|
242
|
+
promptCompilation: targetEntry.promptCompilation ?? targetEntry.snapshot?.promptCompilation ?? null,
|
|
243
|
+
fullPrompt: targetEntry.fullPrompt ?? targetEntry.snapshot?.fullPrompt ?? null,
|
|
244
|
+
prompt: targetEntry.prompt ?? targetEntry.snapshot?.prompt ?? null,
|
|
245
|
+
});
|
|
246
|
+
const targetPublicUrl = resolvePublicUrl(targetEntry, targetSrc);
|
|
84
247
|
try {
|
|
85
248
|
const response = await fetch(galleryEndpoint, {
|
|
86
249
|
method: 'POST',
|
|
87
250
|
headers: { 'Content-Type': 'application/json' },
|
|
88
251
|
body: JSON.stringify({
|
|
89
252
|
s3Url,
|
|
90
|
-
bucket:
|
|
91
|
-
key:
|
|
92
|
-
metaKey:
|
|
253
|
+
bucket: targetEntry.storage.bucket,
|
|
254
|
+
key: targetEntry.storage.key,
|
|
255
|
+
metaKey: targetEntry.storage.metaKey,
|
|
256
|
+
snapshotId: targetEntry.snapshotId || targetEntry.snapshot?.id || null,
|
|
93
257
|
prompt: fullPrompt,
|
|
94
258
|
title,
|
|
259
|
+
promptPackId: targetEntry.promptPackId || promptPackId || null,
|
|
260
|
+
mode: targetEntry.modeId || mode || null,
|
|
261
|
+
toolId: targetEntry.snapshot?.toolId || null,
|
|
262
|
+
operationId: targetEntry.operationId || targetEntry.snapshot?.operationId || null,
|
|
95
263
|
direct: Boolean(isAdminUser),
|
|
96
264
|
}),
|
|
97
265
|
});
|
|
98
|
-
|
|
99
|
-
|
|
266
|
+
const data = await response.json().catch(() => null);
|
|
267
|
+
if (!response.ok || !data?.success) {
|
|
268
|
+
throw new Error(data?.error || 'gallery submission failed');
|
|
269
|
+
}
|
|
270
|
+
if (isAdminUser && data?.published !== true) {
|
|
271
|
+
throw new Error(`gallery submission was not published (${String(data?.status || 'unknown')})`);
|
|
100
272
|
}
|
|
101
273
|
trackTelemetry?.('gallery_submission', {
|
|
102
274
|
s3Url,
|
|
103
|
-
bucket:
|
|
104
|
-
key:
|
|
105
|
-
metaKey:
|
|
275
|
+
bucket: targetEntry.storage.bucket,
|
|
276
|
+
key: targetEntry.storage.key,
|
|
277
|
+
metaKey: targetEntry.storage.metaKey,
|
|
106
278
|
prompt: fullPrompt,
|
|
107
279
|
title,
|
|
108
280
|
direct: Boolean(isAdminUser),
|
|
109
|
-
publicUrl,
|
|
281
|
+
publicUrl: targetPublicUrl,
|
|
282
|
+
imageAction: buildImageActionRecord({
|
|
283
|
+
snapshot: targetEntry.snapshot ?? null,
|
|
284
|
+
imageRecord: targetEntry,
|
|
285
|
+
kind: isAdminUser ? 'gallery.published' : 'gallery.submitted',
|
|
286
|
+
status: 'success',
|
|
287
|
+
publicUrl: targetPublicUrl,
|
|
288
|
+
}),
|
|
110
289
|
});
|
|
290
|
+
trackTelemetry?.(isAdminUser ? 'gallery_publish' : 'gallery_submit', buildTelemetryPayload({ direct: Boolean(isAdminUser) }, targetEntry, targetPublicUrl, isAdminUser ? 'gallery.published' : 'gallery.submitted', 'success'));
|
|
111
291
|
setStatus('sent');
|
|
292
|
+
setStatusText(data?.status === 'published' || isAdminUser
|
|
293
|
+
? 'Image published to gallery.'
|
|
294
|
+
: 'Image proposed to gallery for review.');
|
|
295
|
+
setEntry((prev) => {
|
|
296
|
+
if (!prev || prev.id !== targetEntry.id)
|
|
297
|
+
return prev;
|
|
298
|
+
const nextStatus = data?.status || (isAdminUser ? 'published' : 'pending');
|
|
299
|
+
return {
|
|
300
|
+
...prev,
|
|
301
|
+
galleryStatus: nextStatus,
|
|
302
|
+
snapshot: prev.snapshot
|
|
303
|
+
? {
|
|
304
|
+
...prev.snapshot,
|
|
305
|
+
galleryStatus: nextStatus,
|
|
306
|
+
}
|
|
307
|
+
: prev.snapshot,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
trackTelemetry?.(isAdminUser ? 'gallery_publish_error' : 'gallery_submit_error', buildTelemetryPayload({}, targetEntry, targetPublicUrl, isAdminUser ? 'gallery.published' : 'gallery.submitted', 'error'));
|
|
313
|
+
setStatus('error');
|
|
314
|
+
setStatusText(error?.message || 'Gallery submission failed.');
|
|
315
|
+
}
|
|
316
|
+
}, [buildTelemetryPayload, entry, galleryEndpoint, isAdminUser, mode, promptPackId, resolvePublicUrl, resolveTitle, src, trackTelemetry]);
|
|
317
|
+
const shareImage = useCallback(async (targetEntry = entry, targetSrc = src) => {
|
|
318
|
+
const targetPublicUrl = resolvePublicUrl(targetEntry, targetSrc);
|
|
319
|
+
if (!targetPublicUrl)
|
|
320
|
+
return;
|
|
321
|
+
try {
|
|
322
|
+
if (typeof navigator !== 'undefined' && typeof navigator.share === 'function') {
|
|
323
|
+
await navigator.share({
|
|
324
|
+
title: targetEntry?.title || 'render image',
|
|
325
|
+
text: mode || 'Composer render',
|
|
326
|
+
url: targetPublicUrl,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
await navigator.clipboard.writeText(targetPublicUrl);
|
|
331
|
+
}
|
|
332
|
+
trackTelemetry?.('snapshot_share', buildTelemetryPayload({}, targetEntry, targetPublicUrl, 'image.shared', 'success'));
|
|
112
333
|
}
|
|
113
334
|
catch {
|
|
335
|
+
trackTelemetry?.('snapshot_share_error', buildTelemetryPayload({}, targetEntry, targetPublicUrl, 'image.shared', 'error'));
|
|
336
|
+
}
|
|
337
|
+
}, [buildTelemetryPayload, entry, mode, resolvePublicUrl, src, trackTelemetry]);
|
|
338
|
+
const copyPrompt = useCallback(async (targetEntry = entry) => {
|
|
339
|
+
const targetPublicUrl = resolvePublicUrl(targetEntry);
|
|
340
|
+
await copyText(resolveComposerEffectivePrompt({
|
|
341
|
+
promptCompilation: targetEntry?.promptCompilation ?? targetEntry?.snapshot?.promptCompilation ?? null,
|
|
342
|
+
fullPrompt: targetEntry?.fullPrompt ?? targetEntry?.snapshot?.fullPrompt ?? null,
|
|
343
|
+
prompt: targetEntry?.prompt ?? targetEntry?.snapshot?.prompt ?? null,
|
|
344
|
+
}), 'snapshot_copy_prompt', targetEntry, targetPublicUrl);
|
|
345
|
+
}, [copyText, entry, resolvePublicUrl]);
|
|
346
|
+
const copyPublicLink = useCallback(async (targetEntry = entry, targetSrc = src) => {
|
|
347
|
+
const ensuredShortId = await ensureShortlink(targetEntry);
|
|
348
|
+
const targetPublicUrl = ensuredShortId
|
|
349
|
+
? resolveComposerImageLinks({
|
|
350
|
+
storage: targetEntry?.storage ?? targetEntry?.snapshot?.storage ?? null,
|
|
351
|
+
shortId: ensuredShortId,
|
|
352
|
+
origin: getOrigin(),
|
|
353
|
+
fallbackImageUrl: targetSrc,
|
|
354
|
+
}).pageUrl
|
|
355
|
+
: resolvePublicUrl(targetEntry, targetSrc);
|
|
356
|
+
await copyText(targetPublicUrl, 'snapshot_copy_public_link', targetEntry, targetPublicUrl);
|
|
357
|
+
}, [copyText, ensureShortlink, entry, getOrigin, resolvePublicUrl, src]);
|
|
358
|
+
const copyImageLink = useCallback(async (targetEntry = entry, targetSrc = src) => {
|
|
359
|
+
const ensuredShortId = await ensureShortlink(targetEntry);
|
|
360
|
+
const imageUrl = ensuredShortId
|
|
361
|
+
? resolveComposerImageLinks({
|
|
362
|
+
storage: targetEntry?.storage ?? targetEntry?.snapshot?.storage ?? null,
|
|
363
|
+
shortId: ensuredShortId,
|
|
364
|
+
origin: getOrigin(),
|
|
365
|
+
fallbackImageUrl: targetSrc,
|
|
366
|
+
}).directImageUrl
|
|
367
|
+
: resolveAssetUrl(targetEntry, targetSrc);
|
|
368
|
+
await copyText(imageUrl, 'snapshot_copy_image_link', targetEntry);
|
|
369
|
+
}, [copyText, ensureShortlink, entry, getOrigin, resolveAssetUrl, src]);
|
|
370
|
+
const downloadImage = useCallback((targetEntry = entry, targetSrc = src) => {
|
|
371
|
+
const targetPublicUrl = resolvePublicUrl(targetEntry, targetSrc);
|
|
372
|
+
const href = resolveAssetUrl(targetEntry, targetSrc);
|
|
373
|
+
if (!href || typeof document === 'undefined')
|
|
374
|
+
return;
|
|
375
|
+
const link = document.createElement('a');
|
|
376
|
+
link.href = href;
|
|
377
|
+
link.download = `${(targetEntry?.title || 'render-image').replace(/\s+/g, '-').toLowerCase()}.png`;
|
|
378
|
+
document.body.appendChild(link);
|
|
379
|
+
link.click();
|
|
380
|
+
link.remove();
|
|
381
|
+
trackTelemetry?.('snapshot_download', buildTelemetryPayload({}, targetEntry, targetPublicUrl, 'image.downloaded', 'success'));
|
|
382
|
+
}, [buildTelemetryPayload, entry, resolveAssetUrl, resolvePublicUrl, src, trackTelemetry]);
|
|
383
|
+
const restartFromSnapshot = useCallback(async (targetEntry = entry) => {
|
|
384
|
+
if (!targetEntry)
|
|
385
|
+
return;
|
|
386
|
+
const targetPublicUrl = resolvePublicUrl(targetEntry);
|
|
387
|
+
let snapshot = buildEntrySnapshot(targetEntry);
|
|
388
|
+
if (!isRestartableGallerySnapshot(snapshot) && targetEntry.storage?.metaKey) {
|
|
389
|
+
try {
|
|
390
|
+
const response = await fetch(`/api/images/meta?key=${encodeURIComponent(targetEntry.storage.metaKey)}`);
|
|
391
|
+
const data = await response.json().catch(() => null);
|
|
392
|
+
if (response.ok && data?.success && data?.meta?.snapshot) {
|
|
393
|
+
snapshot = data.meta.snapshot;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
// Restart failure is reported below through status and telemetry.
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (!isRestartableGallerySnapshot(snapshot)) {
|
|
114
401
|
setStatus('error');
|
|
402
|
+
setStatusText('Saved snapshot is incomplete. Restart is blocked until this image has full durable snapshot metadata.');
|
|
403
|
+
trackTelemetry?.('snapshot_restart_error', buildTelemetryPayload({}, targetEntry, targetPublicUrl, 'snapshot.restarted', 'error'));
|
|
404
|
+
return;
|
|
115
405
|
}
|
|
116
|
-
|
|
406
|
+
onRestartFromSnapshot?.(targetEntry, snapshot);
|
|
407
|
+
trackTelemetry?.('snapshot_restart', buildTelemetryPayload({}, targetEntry, targetPublicUrl, 'snapshot.restarted', 'success'));
|
|
408
|
+
}, [buildEntrySnapshot, buildTelemetryPayload, entry, onRestartFromSnapshot, resolvePublicUrl, trackTelemetry]);
|
|
117
409
|
return {
|
|
118
410
|
entry,
|
|
119
411
|
src,
|
|
120
412
|
alt,
|
|
121
413
|
status,
|
|
414
|
+
statusText,
|
|
122
415
|
shortId,
|
|
123
416
|
publicUrl,
|
|
124
417
|
open,
|
|
125
418
|
close,
|
|
419
|
+
shareImage,
|
|
420
|
+
copyPrompt,
|
|
421
|
+
copyPublicLink,
|
|
422
|
+
copyImageLink,
|
|
423
|
+
downloadImage,
|
|
424
|
+
restartFromSnapshot,
|
|
126
425
|
submitToGallery,
|
|
127
426
|
};
|
|
128
427
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treasuryspatial/prompt-ui-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@treasuryspatial/render-kit": "^0.1.
|
|
21
|
+
"@treasuryspatial/render-kit": "^0.1.23",
|
|
22
22
|
"styled-components": "^6.1.19"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|