@treasuryspatial/prompt-ui-kit 0.1.14 → 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 +251 -25
- 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 +108 -17
- 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 +7 -5
package/dist/FeedbackPanel.d.ts
CHANGED
|
@@ -3,7 +3,26 @@ type FeedbackPanelProps = {
|
|
|
3
3
|
page?: string;
|
|
4
4
|
title?: string;
|
|
5
5
|
description?: string;
|
|
6
|
+
roadmap?: FeedbackRoadmapConfig | null;
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
+
type FeedbackRoadmapItem = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
category?: string;
|
|
13
|
+
status?: string;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
sortOrder?: number;
|
|
16
|
+
rank?: number;
|
|
17
|
+
score?: number;
|
|
18
|
+
};
|
|
19
|
+
type FeedbackRoadmapConfig = {
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
title?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
rankingMode?: string;
|
|
24
|
+
items?: FeedbackRoadmapItem[];
|
|
25
|
+
};
|
|
26
|
+
export default function FeedbackPanel({ toolId, page, title, description, roadmap, }: FeedbackPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
8
27
|
export {};
|
|
9
28
|
//# sourceMappingURL=FeedbackPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackPanel.d.ts","sourceRoot":"","sources":["../src/FeedbackPanel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FeedbackPanel.d.ts","sourceRoot":"","sources":["../src/FeedbackPanel.tsx"],"names":[],"mappings":"AAKA,KAAK,kBAAkB,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACxC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC/B,CAAC;AAyIF,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACpC,MAAW,EACX,IAAS,EACT,KAAoC,EACpC,WAAwH,EACxH,OAAc,GACf,EAAE,kBAAkB,2CAuSpB"}
|
package/dist/FeedbackPanel.js
CHANGED
|
@@ -1,20 +1,180 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useState } from 'react';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import styled from 'styled-components';
|
|
4
5
|
const FEEDBACK_IMAGE_LIMIT = 3;
|
|
5
6
|
const FEEDBACK_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
6
|
-
|
|
7
|
+
const TELEMETRY_SESSION_STORAGE_KEY = 'telemetry:session-id';
|
|
8
|
+
const Root = styled.div.attrs({ className: 'viewer-ui-panel-sections' }) `
|
|
9
|
+
font-family: var(--viewer-ui-font-body, var(--ui-font-family, 'Aenik', 'Aeonik', system-ui, sans-serif));
|
|
10
|
+
`;
|
|
11
|
+
const Section = styled.section.attrs({ className: 'panel-section' }) ``;
|
|
12
|
+
const SectionHeader = styled.div.attrs({ className: 'panel-section-header' }) ``;
|
|
13
|
+
const SectionHeaderCopy = styled.div `
|
|
14
|
+
min-width: 0;
|
|
15
|
+
`;
|
|
16
|
+
const SectionFields = styled.div.attrs({ className: 'panel-section-fields' }) ``;
|
|
17
|
+
const Textarea = styled.textarea.attrs({ className: 'cfg-textarea' }) `
|
|
18
|
+
width: 100%;
|
|
19
|
+
min-height: 136px;
|
|
20
|
+
resize: vertical;
|
|
21
|
+
font-family: var(--viewer-ui-font-body, var(--ui-font-family, 'Aenik', 'Aeonik', system-ui, sans-serif));
|
|
22
|
+
|
|
23
|
+
&::placeholder {
|
|
24
|
+
font-family: var(--viewer-ui-font-body, var(--ui-font-family, 'Aenik', 'Aeonik', system-ui, sans-serif));
|
|
25
|
+
letter-spacing: 0;
|
|
26
|
+
color: color-mix(in srgb, var(--brand-text-secondary) 72%, white 28%);
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
const Note = styled.div.attrs({ className: 'panel-section-note' }) ``;
|
|
30
|
+
const DropZone = styled.div `
|
|
31
|
+
margin-top: 8px;
|
|
32
|
+
border-radius: 18px;
|
|
33
|
+
border: 1px dashed ${(props) => (props.$dragActive ? 'var(--brand-primary)' : 'var(--brand-panel-border)')};
|
|
34
|
+
background: ${(props) => props.$dragActive
|
|
35
|
+
? 'color-mix(in srgb, var(--brand-primary) 10%, var(--brand-background) 90%)'
|
|
36
|
+
: 'var(--brand-background)'};
|
|
37
|
+
padding: 18px 14px;
|
|
38
|
+
text-align: center;
|
|
39
|
+
transition: border-color 0.16s ease, background-color 0.16s ease;
|
|
40
|
+
`;
|
|
41
|
+
const DropButton = styled.button `
|
|
42
|
+
border: 0;
|
|
43
|
+
background: transparent;
|
|
44
|
+
color: var(--brand-text-primary);
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
font-family: inherit;
|
|
47
|
+
font-size: 11px;
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
letter-spacing: 0.08em;
|
|
50
|
+
text-transform: var(--viewer-ui-action-text-case, lowercase);
|
|
51
|
+
`;
|
|
52
|
+
const HiddenFileInput = styled.input `
|
|
53
|
+
position: absolute;
|
|
54
|
+
width: 1px;
|
|
55
|
+
height: 1px;
|
|
56
|
+
padding: 0;
|
|
57
|
+
margin: -1px;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
clip: rect(0 0 0 0);
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
border: 0;
|
|
62
|
+
`;
|
|
63
|
+
const FileList = styled.div.attrs({ className: 'panel-section-list' }) `
|
|
64
|
+
margin-top: 10px;
|
|
65
|
+
`;
|
|
66
|
+
const FileRow = styled.div.attrs({ className: 'panel-section-row' }) `
|
|
67
|
+
font-size: 11px;
|
|
68
|
+
color: var(--brand-text-secondary);
|
|
69
|
+
`;
|
|
70
|
+
const RemoveButton = styled.button `
|
|
71
|
+
border: 0;
|
|
72
|
+
background: transparent;
|
|
73
|
+
color: var(--brand-primary);
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
font-size: 11px;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
letter-spacing: 0.16em;
|
|
78
|
+
`;
|
|
79
|
+
const Status = styled.div `
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
color: ${(props) => props.$tone === 'success' ? 'var(--brand-text-primary)' : 'var(--viewer-ui-color-text-error, #b91c1c)'};
|
|
82
|
+
`;
|
|
83
|
+
const SubmitButton = styled.button.attrs({ className: 'cfg-button cfg-button--primary' }) `
|
|
84
|
+
width: 100%;
|
|
85
|
+
font-family: inherit;
|
|
86
|
+
`;
|
|
87
|
+
const RoadmapSection = styled.section.attrs({ className: 'panel-section' }) ``;
|
|
88
|
+
const RoadmapList = styled.div.attrs({ className: 'panel-section-stack' }) ``;
|
|
89
|
+
const RoadmapItem = styled.div.attrs({ className: 'panel-section-group' }) ``;
|
|
90
|
+
const RoadmapItemHeader = styled.div `
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: flex-start;
|
|
93
|
+
justify-content: space-between;
|
|
94
|
+
gap: 12px;
|
|
95
|
+
`;
|
|
96
|
+
const RoadmapItemTitle = styled.h4 ``;
|
|
97
|
+
const RoadmapItemDescription = styled.p ``;
|
|
98
|
+
const RoadmapMeta = styled.div.attrs({ className: 'panel-chip-row' }) ``;
|
|
99
|
+
const RoadmapTag = styled.span `
|
|
100
|
+
border-radius: 999px;
|
|
101
|
+
border: 1px solid var(--brand-panel-border);
|
|
102
|
+
padding: 3px 8px;
|
|
103
|
+
font-size: 9px;
|
|
104
|
+
line-height: 1;
|
|
105
|
+
color: var(--brand-text-secondary);
|
|
106
|
+
text-transform: uppercase;
|
|
107
|
+
letter-spacing: 0.1em;
|
|
108
|
+
`;
|
|
109
|
+
const VoteButton = styled.button.attrs({ className: 'cfg-button cfg-button--secondary cfg-button--compact' }) `
|
|
110
|
+
flex: 0 0 auto;
|
|
111
|
+
font-family: inherit;
|
|
112
|
+
`;
|
|
113
|
+
export default function FeedbackPanel({ toolId = '', page = '', title = 'design requests + feedback', description = 'Request new design types, plugins, tools, workflows, features, bug fixes, or visual/render improvements.', roadmap = null, }) {
|
|
7
114
|
const [message, setMessage] = useState('');
|
|
8
115
|
const [status, setStatus] = useState('idle');
|
|
9
116
|
const [error, setError] = useState('');
|
|
10
117
|
const [images, setImages] = useState([]);
|
|
11
118
|
const [imageError, setImageError] = useState('');
|
|
12
119
|
const [dragActive, setDragActive] = useState(false);
|
|
120
|
+
const [roadmapItems, setRoadmapItems] = useState([]);
|
|
121
|
+
const [roadmapStatus, setRoadmapStatus] = useState('idle');
|
|
122
|
+
const [roadmapError, setRoadmapError] = useState('');
|
|
123
|
+
const [votedFeatureIds, setVotedFeatureIds] = useState(() => new Set());
|
|
13
124
|
const inputRef = useRef(null);
|
|
125
|
+
const configuredRoadmapItems = (roadmap?.items ?? [])
|
|
126
|
+
.filter((item) => item && item.enabled !== false && item.id && item.name)
|
|
127
|
+
.sort((a, b) => {
|
|
128
|
+
const aOrder = Number.isFinite(Number(a.sortOrder)) ? Number(a.sortOrder) : 9999;
|
|
129
|
+
const bOrder = Number.isFinite(Number(b.sortOrder)) ? Number(b.sortOrder) : 9999;
|
|
130
|
+
return aOrder - bOrder || a.name.localeCompare(b.name);
|
|
131
|
+
});
|
|
132
|
+
const visibleRoadmapItems = roadmapItems.length > 0 ? roadmapItems : configuredRoadmapItems;
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (roadmap?.enabled === false || configuredRoadmapItems.length === 0) {
|
|
135
|
+
setRoadmapItems([]);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
let cancelled = false;
|
|
139
|
+
const loadRoadmap = async () => {
|
|
140
|
+
setRoadmapStatus('loading');
|
|
141
|
+
setRoadmapError('');
|
|
142
|
+
try {
|
|
143
|
+
const response = await fetch('/api/feedback/roadmap', { cache: 'no-store' });
|
|
144
|
+
const payload = await response.json().catch(() => null);
|
|
145
|
+
if (!response.ok || !payload?.success) {
|
|
146
|
+
throw new Error(payload?.error || 'Failed to load roadmap ranking');
|
|
147
|
+
}
|
|
148
|
+
if (cancelled)
|
|
149
|
+
return;
|
|
150
|
+
const rankedItems = Array.isArray(payload?.data?.items) ? payload.data.items : payload?.items;
|
|
151
|
+
if (Array.isArray(rankedItems) && rankedItems.length > 0) {
|
|
152
|
+
setRoadmapItems(rankedItems.filter((item) => item?.enabled !== false && item?.id && item?.name));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
setRoadmapItems([]);
|
|
156
|
+
}
|
|
157
|
+
setRoadmapStatus('idle');
|
|
158
|
+
}
|
|
159
|
+
catch (nextError) {
|
|
160
|
+
if (cancelled)
|
|
161
|
+
return;
|
|
162
|
+
setRoadmapItems([]);
|
|
163
|
+
setRoadmapError(nextError?.message || 'Failed to load roadmap ranking');
|
|
164
|
+
setRoadmapStatus('error');
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
void loadRoadmap();
|
|
168
|
+
return () => {
|
|
169
|
+
cancelled = true;
|
|
170
|
+
};
|
|
171
|
+
}, [roadmap?.enabled, configuredRoadmapItems.map((item) => item.id).join('|')]);
|
|
14
172
|
const addFiles = (fileList) => {
|
|
15
173
|
const incoming = Array.from(fileList);
|
|
16
174
|
const remaining = Math.max(0, FEEDBACK_IMAGE_LIMIT - images.length);
|
|
17
|
-
const accepted = incoming
|
|
175
|
+
const accepted = incoming
|
|
176
|
+
.filter((file) => file.type.startsWith('image/') && file.size <= FEEDBACK_IMAGE_MAX_BYTES)
|
|
177
|
+
.slice(0, remaining);
|
|
18
178
|
if (accepted.length === 0) {
|
|
19
179
|
setImageError(`Add up to ${FEEDBACK_IMAGE_LIMIT} images under ${Math.round(FEEDBACK_IMAGE_MAX_BYTES / 1024 / 1024)}MB each.`);
|
|
20
180
|
return;
|
|
@@ -30,6 +190,55 @@ export default function FeedbackPanel({ toolId = '', page = '', title = 'feedbac
|
|
|
30
190
|
]);
|
|
31
191
|
};
|
|
32
192
|
const removeFile = (id) => setImages((prev) => prev.filter((item) => item.id !== id));
|
|
193
|
+
const voteForRoadmapItem = async (featureId) => {
|
|
194
|
+
setRoadmapStatus('voting');
|
|
195
|
+
setRoadmapError('');
|
|
196
|
+
try {
|
|
197
|
+
const submissionId = (typeof crypto !== 'undefined' && 'randomUUID' in crypto && crypto.randomUUID()) ||
|
|
198
|
+
`${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
199
|
+
let telemetrySessionId = '';
|
|
200
|
+
try {
|
|
201
|
+
telemetrySessionId = window.sessionStorage.getItem(TELEMETRY_SESSION_STORAGE_KEY) || '';
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
telemetrySessionId = '';
|
|
205
|
+
}
|
|
206
|
+
const response = await fetch('/api/feedback/roadmap', {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
headers: {
|
|
209
|
+
'Content-Type': 'application/json',
|
|
210
|
+
'x-submission-id': submissionId,
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify({
|
|
213
|
+
featureId,
|
|
214
|
+
submissionId,
|
|
215
|
+
telemetrySessionId,
|
|
216
|
+
toolId,
|
|
217
|
+
page,
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
220
|
+
const payload = await response.json().catch(() => null);
|
|
221
|
+
if (!response.ok || !payload?.success) {
|
|
222
|
+
throw new Error(payload?.error || 'Failed to record roadmap vote');
|
|
223
|
+
}
|
|
224
|
+
setVotedFeatureIds((current) => {
|
|
225
|
+
const next = new Set(current);
|
|
226
|
+
next.add(featureId);
|
|
227
|
+
return next;
|
|
228
|
+
});
|
|
229
|
+
setRoadmapStatus('idle');
|
|
230
|
+
const reload = await fetch('/api/feedback/roadmap', { cache: 'no-store' });
|
|
231
|
+
const nextPayload = await reload.json().catch(() => null);
|
|
232
|
+
const rankedItems = Array.isArray(nextPayload?.data?.items) ? nextPayload.data.items : nextPayload?.items;
|
|
233
|
+
if (reload.ok && Array.isArray(rankedItems)) {
|
|
234
|
+
setRoadmapItems(rankedItems.filter((item) => item?.enabled !== false && item?.id && item?.name));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (nextError) {
|
|
238
|
+
setRoadmapStatus('error');
|
|
239
|
+
setRoadmapError(nextError?.message || 'Failed to record roadmap vote');
|
|
240
|
+
}
|
|
241
|
+
};
|
|
33
242
|
const submit = async () => {
|
|
34
243
|
const trimmed = message.trim();
|
|
35
244
|
if (!trimmed) {
|
|
@@ -41,13 +250,29 @@ export default function FeedbackPanel({ toolId = '', page = '', title = 'feedbac
|
|
|
41
250
|
setError('');
|
|
42
251
|
try {
|
|
43
252
|
const form = new FormData();
|
|
253
|
+
const submissionId = (typeof crypto !== 'undefined' && 'randomUUID' in crypto && crypto.randomUUID()) ||
|
|
254
|
+
`${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
44
255
|
form.set('message', trimmed);
|
|
256
|
+
form.set('submissionId', submissionId);
|
|
257
|
+
form.set('category', 'design-type-feature-feedback');
|
|
45
258
|
if (toolId)
|
|
46
259
|
form.set('toolId', toolId);
|
|
47
260
|
if (page)
|
|
48
261
|
form.set('page', page);
|
|
262
|
+
try {
|
|
263
|
+
const telemetrySessionId = window.sessionStorage.getItem(TELEMETRY_SESSION_STORAGE_KEY);
|
|
264
|
+
if (telemetrySessionId)
|
|
265
|
+
form.set('telemetrySessionId', telemetrySessionId);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Ignore storage failures; the server still writes feedback.
|
|
269
|
+
}
|
|
49
270
|
images.forEach((item) => form.append('images', item.file));
|
|
50
|
-
const response = await fetch('/api/feedback', {
|
|
271
|
+
const response = await fetch('/api/feedback', {
|
|
272
|
+
method: 'POST',
|
|
273
|
+
body: form,
|
|
274
|
+
headers: { 'x-submission-id': submissionId },
|
|
275
|
+
});
|
|
51
276
|
const payload = await response.json().catch(() => null);
|
|
52
277
|
if (!response.ok || !payload?.success)
|
|
53
278
|
throw new Error(payload?.error || 'Failed to send feedback');
|
|
@@ -60,26 +285,27 @@ export default function FeedbackPanel({ toolId = '', page = '', title = 'feedbac
|
|
|
60
285
|
setError(nextError?.message || 'Failed to send feedback');
|
|
61
286
|
}
|
|
62
287
|
};
|
|
63
|
-
return (_jsxs(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
}, rows: 6, placeholder: "Share feedback, feature requests, or issues.", className: "w-full rounded-xl border border-[var(--viewer-ui-color-button-secondary)] bg-[var(--viewer-ui-color-panel-bg)] text-[var(--viewer-ui-color-text-strong)] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--viewer-ui-color-focus)]" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "text-xs text-[var(--viewer-ui-color-text-muted)] normal-case", children: ["Add up to ", FEEDBACK_IMAGE_LIMIT, " screenshots (max ", Math.round(FEEDBACK_IMAGE_MAX_BYTES / 1024 / 1024), "MB each)."] }), _jsxs("div", { onDragOver: (event) => {
|
|
70
|
-
event.preventDefault();
|
|
71
|
-
setDragActive(true);
|
|
72
|
-
}, onDragLeave: () => setDragActive(false), onDrop: (event) => {
|
|
73
|
-
event.preventDefault();
|
|
74
|
-
setDragActive(false);
|
|
75
|
-
if (event.dataTransfer?.files?.length)
|
|
76
|
-
addFiles(event.dataTransfer.files);
|
|
77
|
-
}, className: `rounded-xl border border-dashed px-4 py-4 text-center text-sm transition ${dragActive
|
|
78
|
-
? 'border-[var(--viewer-ui-color-button-primary)] bg-[var(--viewer-ui-color-button-primary)]/10 text-[var(--viewer-ui-color-text-strong)]'
|
|
79
|
-
: 'border-[var(--viewer-ui-color-button-secondary)] text-[var(--viewer-ui-color-text-muted)]'}`, children: [_jsx("input", { ref: inputRef, type: "file", accept: "image/*", multiple: true, className: "hidden", onChange: (event) => {
|
|
80
|
-
if (event.target.files?.length) {
|
|
81
|
-
addFiles(event.target.files);
|
|
82
|
-
event.target.value = '';
|
|
288
|
+
return (_jsxs(Root, { children: [_jsxs(Section, { "data-panel-id": "feedback-request", children: [_jsx(SectionHeader, { children: _jsxs(SectionHeaderCopy, { children: [_jsx("h3", { children: title }), _jsx("p", { children: description })] }) }), _jsxs(SectionFields, { children: [_jsx(Textarea, { value: message, onChange: (event) => {
|
|
289
|
+
setMessage(event.target.value);
|
|
290
|
+
if (status !== 'idle') {
|
|
291
|
+
setStatus('idle');
|
|
292
|
+
setError('');
|
|
83
293
|
}
|
|
84
|
-
}
|
|
294
|
+
}, rows: 6, placeholder: "Ask for a new design type, plugin, workflow, feature, or fix. Include what you want to make and what output should change." }), _jsxs("div", { children: [_jsxs(Note, { children: ["Add up to ", FEEDBACK_IMAGE_LIMIT, " screenshots (max ", Math.round(FEEDBACK_IMAGE_MAX_BYTES / 1024 / 1024), "MB each)."] }), _jsxs(DropZone, { "$dragActive": dragActive, onDragOver: (event) => {
|
|
295
|
+
event.preventDefault();
|
|
296
|
+
setDragActive(true);
|
|
297
|
+
}, onDragLeave: () => setDragActive(false), onDrop: (event) => {
|
|
298
|
+
event.preventDefault();
|
|
299
|
+
setDragActive(false);
|
|
300
|
+
if (event.dataTransfer?.files?.length)
|
|
301
|
+
addFiles(event.dataTransfer.files);
|
|
302
|
+
}, children: [_jsx(HiddenFileInput, { ref: inputRef, type: "file", accept: "image/*", multiple: true, onChange: (event) => {
|
|
303
|
+
if (event.target.files?.length) {
|
|
304
|
+
addFiles(event.target.files);
|
|
305
|
+
event.target.value = '';
|
|
306
|
+
}
|
|
307
|
+
} }), _jsx(DropButton, { type: "button", onClick: () => inputRef.current?.click(), children: "drop screenshots here or click to upload" })] }), imageError ? _jsx(Status, { "$tone": "error", style: { marginTop: '8px' }, children: imageError }) : null, images.length > 0 ? (_jsx(FileList, { children: images.map((item) => (_jsxs(FileRow, { children: [_jsx("span", { className: "truncate", children: item.file.name }), _jsx(RemoveButton, { type: "button", onClick: () => removeFile(item.id), children: "remove" })] }, item.id))) })) : null] }), status === 'error' ? _jsx(Status, { "$tone": "error", children: error || 'Failed to send feedback.' }) : null, status === 'success' ? _jsx(Status, { "$tone": "success", children: "Thanks \u2014 we received your feedback." }) : null, _jsx(SubmitButton, { type: "button", onClick: () => void submit(), disabled: status === 'sending', children: status === 'sending' ? 'sending...' : 'send request' })] })] }), roadmap?.enabled !== false && visibleRoadmapItems.length > 0 ? (_jsxs(RoadmapSection, { "data-panel-id": "feedback-roadmap", children: [_jsx(SectionHeader, { children: _jsxs(SectionHeaderCopy, { children: [_jsx("h3", { children: roadmap?.title || 'roadmap priorities' }), roadmap?.description ? _jsx("p", { children: roadmap.description }) : null] }) }), roadmapStatus === 'error' && roadmapError ? _jsx(Status, { "$tone": "error", children: roadmapError }) : null, _jsx(RoadmapList, { children: visibleRoadmapItems.map((item) => {
|
|
308
|
+
const voted = votedFeatureIds.has(item.id);
|
|
309
|
+
return (_jsxs(RoadmapItem, { children: [_jsxs(RoadmapItemHeader, { children: [_jsxs("div", { children: [_jsx(RoadmapItemTitle, { children: item.name }), item.description ? _jsx(RoadmapItemDescription, { children: item.description }) : null] }), _jsx(VoteButton, { type: "button", onClick: () => void voteForRoadmapItem(item.id), disabled: roadmapStatus === 'voting' || voted, children: voted ? 'noted' : 'vote' })] }), item.category || item.status ? (_jsxs(RoadmapMeta, { children: [item.category ? _jsx(RoadmapTag, { children: item.category }) : null, item.status ? _jsx(RoadmapTag, { children: item.status }) : null] })) : null] }, item.id));
|
|
310
|
+
}) })] })) : null] }));
|
|
85
311
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
type ActionConfig = {
|
|
2
3
|
label: string;
|
|
3
4
|
onClick?: () => void;
|
|
@@ -11,6 +12,13 @@ type EditableTitleConfig = {
|
|
|
11
12
|
onSave: () => void;
|
|
12
13
|
status?: 'idle' | 'saving' | 'saved' | 'error';
|
|
13
14
|
};
|
|
15
|
+
export type FullscreenDetailPanelConfig = {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
content: ReactNode;
|
|
20
|
+
defaultOpen?: boolean;
|
|
21
|
+
};
|
|
14
22
|
type FullscreenImageProps = {
|
|
15
23
|
open: boolean;
|
|
16
24
|
src: string;
|
|
@@ -26,8 +34,11 @@ type FullscreenImageProps = {
|
|
|
26
34
|
onClose: () => void;
|
|
27
35
|
primaryAction?: ActionConfig | null;
|
|
28
36
|
secondaryAction?: ActionConfig | null;
|
|
37
|
+
extraActions?: ActionConfig[];
|
|
29
38
|
editableTitle?: EditableTitleConfig | null;
|
|
39
|
+
detailPanels?: FullscreenDetailPanelConfig[];
|
|
40
|
+
statusText?: string | null;
|
|
30
41
|
};
|
|
31
|
-
export default function FullscreenImage({ open, src, alt, title, subtitle, modeLabel,
|
|
42
|
+
export default function FullscreenImage({ open, src, alt, title, subtitle, modeLabel, createdAt, publicUrl, copyUrl, onClose, primaryAction, secondaryAction, extraActions, editableTitle, detailPanels, statusText, }: FullscreenImageProps): import("react/jsx-runtime").JSX.Element | null;
|
|
32
43
|
export {};
|
|
33
44
|
//# sourceMappingURL=FullscreenImage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullscreenImage.d.ts","sourceRoot":"","sources":["../src/FullscreenImage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FullscreenImage.d.ts","sourceRoot":"","sources":["../src/FullscreenImage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpE,OAAO,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CACnC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,SAAS,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,eAAe,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAoRF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,IAAI,EACJ,GAAG,EACH,GAAa,EACb,KAAK,EACL,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,OAAO,EACP,OAAO,EACP,aAAa,EACb,eAAe,EACf,YAAiB,EACjB,aAAa,EACb,YAAiB,EACjB,UAAU,GACX,EAAE,oBAAoB,kDAiNtB"}
|