create-next-imagicma 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -14
- package/bin/create-next-imagicma.mjs +121 -23
- package/package.json +2 -1
- package/template/app/globals.css +331 -0
- package/template/app/layout.tsx +4 -6
- package/template/app/page.tsx +18 -40
- package/template/package.json +1 -1
- package/template/public/imagicma-picker-bridge.js +374 -0
- package/template-hono/.env.example +8 -0
- package/template-hono/AGENTS.md +39 -0
- package/template-hono/README.md +58 -0
- package/template-hono/client/index.html +13 -0
- package/template-hono/client/public/favicon.ico +0 -0
- package/template-hono/client/public/file.svg +1 -0
- package/template-hono/client/public/globe.svg +1 -0
- package/template-hono/client/public/imagicma-picker-bridge.js +374 -0
- package/template-hono/client/public/next.svg +1 -0
- package/template-hono/client/public/vercel.svg +1 -0
- package/template-hono/client/public/window.svg +1 -0
- package/template-hono/client/src/App.tsx +13 -0
- package/template-hono/client/src/components/ErrorBoundary.tsx +74 -0
- package/template-hono/client/src/components/HelloClient.tsx +69 -0
- package/template-hono/client/src/components/ui/accordion.tsx +58 -0
- package/template-hono/client/src/components/ui/alert-dialog.tsx +141 -0
- package/template-hono/client/src/components/ui/alert.tsx +61 -0
- package/template-hono/client/src/components/ui/aspect-ratio.tsx +7 -0
- package/template-hono/client/src/components/ui/avatar.tsx +51 -0
- package/template-hono/client/src/components/ui/badge.tsx +40 -0
- package/template-hono/client/src/components/ui/breadcrumb.tsx +117 -0
- package/template-hono/client/src/components/ui/button.tsx +64 -0
- package/template-hono/client/src/components/ui/calendar.tsx +72 -0
- package/template-hono/client/src/components/ui/card.tsx +87 -0
- package/template-hono/client/src/components/ui/carousel.tsx +262 -0
- package/template-hono/client/src/components/ui/chart.tsx +365 -0
- package/template-hono/client/src/components/ui/checkbox.tsx +30 -0
- package/template-hono/client/src/components/ui/collapsible.tsx +11 -0
- package/template-hono/client/src/components/ui/command.tsx +153 -0
- package/template-hono/client/src/components/ui/context-menu.tsx +200 -0
- package/template-hono/client/src/components/ui/dialog.tsx +122 -0
- package/template-hono/client/src/components/ui/drawer.tsx +118 -0
- package/template-hono/client/src/components/ui/dropdown-menu.tsx +200 -0
- package/template-hono/client/src/components/ui/form.tsx +178 -0
- package/template-hono/client/src/components/ui/hover-card.tsx +29 -0
- package/template-hono/client/src/components/ui/input-otp.tsx +71 -0
- package/template-hono/client/src/components/ui/input.tsx +25 -0
- package/template-hono/client/src/components/ui/label.tsx +26 -0
- package/template-hono/client/src/components/ui/menubar.tsx +256 -0
- package/template-hono/client/src/components/ui/navigation-menu.tsx +130 -0
- package/template-hono/client/src/components/ui/pagination.tsx +119 -0
- package/template-hono/client/src/components/ui/popover.tsx +31 -0
- package/template-hono/client/src/components/ui/progress.tsx +28 -0
- package/template-hono/client/src/components/ui/radio-group.tsx +44 -0
- package/template-hono/client/src/components/ui/resizable.tsx +45 -0
- package/template-hono/client/src/components/ui/scroll-area.tsx +48 -0
- package/template-hono/client/src/components/ui/select.tsx +160 -0
- package/template-hono/client/src/components/ui/separator.tsx +31 -0
- package/template-hono/client/src/components/ui/sheet.tsx +140 -0
- package/template-hono/client/src/components/ui/sidebar.tsx +732 -0
- package/template-hono/client/src/components/ui/skeleton.tsx +17 -0
- package/template-hono/client/src/components/ui/slider.tsx +28 -0
- package/template-hono/client/src/components/ui/switch.tsx +29 -0
- package/template-hono/client/src/components/ui/table.tsx +119 -0
- package/template-hono/client/src/components/ui/tabs.tsx +55 -0
- package/template-hono/client/src/components/ui/textarea.tsx +24 -0
- package/template-hono/client/src/components/ui/toast.tsx +129 -0
- package/template-hono/client/src/components/ui/toaster.tsx +35 -0
- package/template-hono/client/src/components/ui/toggle-group.tsx +61 -0
- package/template-hono/client/src/components/ui/toggle.tsx +45 -0
- package/template-hono/client/src/components/ui/tooltip.tsx +30 -0
- package/template-hono/client/src/globals.css +767 -0
- package/template-hono/client/src/hooks/use-greeting.ts +15 -0
- package/template-hono/client/src/hooks/use-mobile.ts +21 -0
- package/template-hono/client/src/hooks/use-toast.ts +194 -0
- package/template-hono/client/src/lib/queryClient.ts +59 -0
- package/template-hono/client/src/lib/theme/default-theme.ts +11 -0
- package/template-hono/client/src/lib/utils.ts +6 -0
- package/template-hono/client/src/main.tsx +24 -0
- package/template-hono/client/src/pages/HelloPage.tsx +22 -0
- package/template-hono/client/src/pages/HomePage.tsx +30 -0
- package/template-hono/client/src/providers.tsx +21 -0
- package/template-hono/drizzle.config.ts +50 -0
- package/template-hono/eslint.config.mjs +13 -0
- package/template-hono/gitignore +40 -0
- package/template-hono/package.json +83 -0
- package/template-hono/pnpm-lock.yaml +5176 -0
- package/template-hono/postcss.config.mjs +7 -0
- package/template-hono/process-compose.yaml +13 -0
- package/template-hono/scripts/imagicma-common.mjs +118 -0
- package/template-hono/scripts/imagicma-dev.mjs +29 -0
- package/template-hono/scripts/imagicma-guard.mjs +17 -0
- package/template-hono/scripts/imagicma-start.mjs +24 -0
- package/template-hono/server/app.ts +40 -0
- package/template-hono/server/db.ts +22 -0
- package/template-hono/server/dev-app.ts +5 -0
- package/template-hono/server/index.ts +94 -0
- package/template-hono/server/routes/greeting.ts +25 -0
- package/template-hono/server/storage.ts +39 -0
- package/template-hono/shared/routes.ts +13 -0
- package/template-hono/shared/schema.ts +17 -0
- package/template-hono/tailwind.config.mjs +94 -0
- package/template-hono/tsconfig.json +33 -0
- package/template-hono/tsconfig.server.json +15 -0
- package/template-hono/types/pg.d.ts +19 -0
- package/template-hono/vite.config.ts +126 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var CHANNEL = 'imagicma.preview-picker';
|
|
3
|
+
var VERSION = 1;
|
|
4
|
+
var MAX_TEXT_LENGTH = 240;
|
|
5
|
+
var MAX_SELECTOR_LENGTH = 512;
|
|
6
|
+
var PROD_PARENT_ORIGIN = 'https://www.imagicma.cn';
|
|
7
|
+
var LOCAL_PARENT_RE = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i;
|
|
8
|
+
var LOCAL_IMAGICMA_PARENT_RE = /^https?:\/\/([a-z0-9-]+\.)?local\.imagicma\.cn(:\d+)?$/i;
|
|
9
|
+
|
|
10
|
+
var state = {
|
|
11
|
+
active: false,
|
|
12
|
+
sessionId: '',
|
|
13
|
+
nonce: '',
|
|
14
|
+
parentOrigin: '',
|
|
15
|
+
hoverEl: null,
|
|
16
|
+
overlayEl: null,
|
|
17
|
+
rafId: 0,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
var listenersBound = false;
|
|
21
|
+
|
|
22
|
+
function isAllowedParentOrigin(origin) {
|
|
23
|
+
if (!origin) return false;
|
|
24
|
+
return origin === PROD_PARENT_ORIGIN || LOCAL_PARENT_RE.test(origin) || LOCAL_IMAGICMA_PARENT_RE.test(origin);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return typeof value === 'object' && value !== null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function trimText(value) {
|
|
32
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function truncateText(value, max) {
|
|
36
|
+
if (!value) return '';
|
|
37
|
+
return value.length > max ? value.slice(0, max) : value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function safePostMessage(type, payload) {
|
|
41
|
+
if (!state.parentOrigin) return;
|
|
42
|
+
if (window.parent === window) return;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
window.parent.postMessage(
|
|
46
|
+
{
|
|
47
|
+
channel: CHANNEL,
|
|
48
|
+
version: VERSION,
|
|
49
|
+
type: type,
|
|
50
|
+
sessionId: state.sessionId,
|
|
51
|
+
nonce: state.nonce,
|
|
52
|
+
payload: payload,
|
|
53
|
+
},
|
|
54
|
+
state.parentOrigin,
|
|
55
|
+
);
|
|
56
|
+
} catch (_error) {
|
|
57
|
+
// Ignore postMessage failures.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ensureOverlay() {
|
|
62
|
+
if (state.overlayEl && document.body.contains(state.overlayEl)) return state.overlayEl;
|
|
63
|
+
var overlay = document.createElement('div');
|
|
64
|
+
overlay.setAttribute('data-imagicma-picker-overlay', 'true');
|
|
65
|
+
overlay.style.position = 'fixed';
|
|
66
|
+
overlay.style.left = '0';
|
|
67
|
+
overlay.style.top = '0';
|
|
68
|
+
overlay.style.width = '0';
|
|
69
|
+
overlay.style.height = '0';
|
|
70
|
+
overlay.style.zIndex = '2147483647';
|
|
71
|
+
overlay.style.pointerEvents = 'none';
|
|
72
|
+
overlay.style.border = '2px solid #1d4ed8';
|
|
73
|
+
overlay.style.background = 'rgba(29, 78, 216, 0.10)';
|
|
74
|
+
overlay.style.boxSizing = 'border-box';
|
|
75
|
+
overlay.style.display = 'none';
|
|
76
|
+
document.body.appendChild(overlay);
|
|
77
|
+
state.overlayEl = overlay;
|
|
78
|
+
return overlay;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function hideOverlay() {
|
|
82
|
+
if (!state.overlayEl) return;
|
|
83
|
+
state.overlayEl.style.display = 'none';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function updateOverlay() {
|
|
87
|
+
if (!state.active || !state.hoverEl) {
|
|
88
|
+
hideOverlay();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
var rect = state.hoverEl.getBoundingClientRect();
|
|
93
|
+
var overlay = ensureOverlay();
|
|
94
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
95
|
+
overlay.style.display = 'none';
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
overlay.style.display = 'block';
|
|
100
|
+
overlay.style.left = rect.left + 'px';
|
|
101
|
+
overlay.style.top = rect.top + 'px';
|
|
102
|
+
overlay.style.width = rect.width + 'px';
|
|
103
|
+
overlay.style.height = rect.height + 'px';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function queueOverlayUpdate() {
|
|
107
|
+
if (state.rafId) return;
|
|
108
|
+
state.rafId = window.requestAnimationFrame(function () {
|
|
109
|
+
state.rafId = 0;
|
|
110
|
+
updateOverlay();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isValidElementTarget(target) {
|
|
115
|
+
if (!(target instanceof Element)) return false;
|
|
116
|
+
if (state.overlayEl && state.overlayEl.contains(target)) return false;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function cssEscapeIdent(value) {
|
|
121
|
+
if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape === 'function') {
|
|
122
|
+
return window.CSS.escape(value);
|
|
123
|
+
}
|
|
124
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function selectorByClassChain(element) {
|
|
128
|
+
var tokens = [];
|
|
129
|
+
if (element.classList && element.classList.length > 0) {
|
|
130
|
+
for (var i = 0; i < element.classList.length && i < 3; i += 1) {
|
|
131
|
+
var cls = trimText(element.classList[i]);
|
|
132
|
+
if (cls) tokens.push('.' + cssEscapeIdent(cls));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return tokens.length > 0 ? element.tagName.toLowerCase() + tokens.join('') : '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function selectorByPath(element) {
|
|
139
|
+
var parts = [];
|
|
140
|
+
var current = element;
|
|
141
|
+
var depth = 0;
|
|
142
|
+
while (current && current.nodeType === 1 && depth < 6) {
|
|
143
|
+
var part = current.tagName.toLowerCase();
|
|
144
|
+
if (current.id) {
|
|
145
|
+
part += '#' + cssEscapeIdent(current.id);
|
|
146
|
+
parts.unshift(part);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
var classSelector = selectorByClassChain(current);
|
|
151
|
+
if (classSelector) {
|
|
152
|
+
part = classSelector;
|
|
153
|
+
} else if (current.parentElement) {
|
|
154
|
+
var siblings = current.parentElement.children;
|
|
155
|
+
var sameTagIndex = 0;
|
|
156
|
+
var sameTagCount = 0;
|
|
157
|
+
for (var i = 0; i < siblings.length; i += 1) {
|
|
158
|
+
var sibling = siblings[i];
|
|
159
|
+
if (sibling.tagName === current.tagName) {
|
|
160
|
+
sameTagCount += 1;
|
|
161
|
+
if (sibling === current) {
|
|
162
|
+
sameTagIndex = sameTagCount;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (sameTagCount > 1 && sameTagIndex > 0) {
|
|
167
|
+
part += ':nth-of-type(' + sameTagIndex + ')';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
parts.unshift(part);
|
|
172
|
+
current = current.parentElement;
|
|
173
|
+
depth += 1;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return parts.join(' > ');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getElementSelector(element) {
|
|
180
|
+
if (element.id) {
|
|
181
|
+
return '#' + cssEscapeIdent(element.id);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
var testId = element.getAttribute('data-testid');
|
|
185
|
+
if (testId) {
|
|
186
|
+
return '[data-testid="' + testId.replace(/"/g, '\\"') + '"]';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var ariaLabel = element.getAttribute('aria-label');
|
|
190
|
+
if (ariaLabel) {
|
|
191
|
+
return element.tagName.toLowerCase() + '[aria-label="' + ariaLabel.replace(/"/g, '\\"') + '"]';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
var role = element.getAttribute('role');
|
|
195
|
+
if (role) {
|
|
196
|
+
return element.tagName.toLowerCase() + '[role="' + role.replace(/"/g, '\\"') + '"]';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
var byClass = selectorByClassChain(element);
|
|
200
|
+
if (byClass) return byClass;
|
|
201
|
+
|
|
202
|
+
return selectorByPath(element) || element.tagName.toLowerCase();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function buildSelectionPayload(element) {
|
|
206
|
+
var rect = element.getBoundingClientRect();
|
|
207
|
+
var selector = truncateText(getElementSelector(element), MAX_SELECTOR_LENGTH);
|
|
208
|
+
var textSource = trimText(element.innerText || element.textContent || '');
|
|
209
|
+
var attributes = {};
|
|
210
|
+
|
|
211
|
+
function putAttr(name, value) {
|
|
212
|
+
if (!name || typeof value !== 'string') return;
|
|
213
|
+
var key = trimText(name).toLowerCase();
|
|
214
|
+
var val = trimText(value);
|
|
215
|
+
if (!key || !val) return;
|
|
216
|
+
attributes[key] = truncateText(val, 240);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
putAttr('id', element.id || '');
|
|
220
|
+
putAttr('class', element.className || '');
|
|
221
|
+
putAttr('role', element.getAttribute('role') || '');
|
|
222
|
+
putAttr('name', element.getAttribute('name') || '');
|
|
223
|
+
putAttr('type', element.getAttribute('type') || '');
|
|
224
|
+
putAttr('title', element.getAttribute('title') || '');
|
|
225
|
+
putAttr('aria-label', element.getAttribute('aria-label') || '');
|
|
226
|
+
putAttr('aria-labelledby', element.getAttribute('aria-labelledby') || '');
|
|
227
|
+
putAttr('aria-describedby', element.getAttribute('aria-describedby') || '');
|
|
228
|
+
putAttr('placeholder', element.getAttribute('placeholder') || '');
|
|
229
|
+
putAttr('href', element.getAttribute('href') || '');
|
|
230
|
+
putAttr('src', element.getAttribute('src') || '');
|
|
231
|
+
putAttr('alt', element.getAttribute('alt') || '');
|
|
232
|
+
|
|
233
|
+
if (element.attributes && element.attributes.length > 0) {
|
|
234
|
+
for (var i = 0; i < element.attributes.length; i += 1) {
|
|
235
|
+
var attr = element.attributes[i];
|
|
236
|
+
if (!attr) continue;
|
|
237
|
+
var attrName = trimText(attr.name || '').toLowerCase();
|
|
238
|
+
if (!attrName) continue;
|
|
239
|
+
if (attrName.indexOf('data-') === 0 || attrName.indexOf('aria-') === 0) {
|
|
240
|
+
putAttr(attrName, attr.value || '');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
pageUrl: window.location.href,
|
|
247
|
+
selector: selector,
|
|
248
|
+
tagName: element.tagName.toLowerCase(),
|
|
249
|
+
textSnippet: truncateText(textSource, MAX_TEXT_LENGTH),
|
|
250
|
+
attributes: attributes,
|
|
251
|
+
rect: {
|
|
252
|
+
x: Number(rect.left.toFixed(2)),
|
|
253
|
+
y: Number(rect.top.toFixed(2)),
|
|
254
|
+
width: Number(rect.width.toFixed(2)),
|
|
255
|
+
height: Number(rect.height.toFixed(2)),
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function handleMouseMove(event) {
|
|
261
|
+
if (!state.active) return;
|
|
262
|
+
var target = event.target;
|
|
263
|
+
if (!isValidElementTarget(target)) return;
|
|
264
|
+
state.hoverEl = target;
|
|
265
|
+
queueOverlayUpdate();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function handleScrollOrResize() {
|
|
269
|
+
if (!state.active) return;
|
|
270
|
+
queueOverlayUpdate();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function handleClickCapture(event) {
|
|
274
|
+
if (!state.active) return;
|
|
275
|
+
var target = event.target;
|
|
276
|
+
if (!isValidElementTarget(target)) return;
|
|
277
|
+
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
event.stopPropagation();
|
|
280
|
+
if (typeof event.stopImmediatePropagation === 'function') {
|
|
281
|
+
event.stopImmediatePropagation();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
var payload = buildSelectionPayload(target);
|
|
286
|
+
safePostMessage('IMAGICMA_PICKER_SELECTED', payload);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
safePostMessage('IMAGICMA_PICKER_ERROR', {
|
|
289
|
+
message: error instanceof Error ? error.message : 'Failed to build selected element payload',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function bindInteractionListeners() {
|
|
295
|
+
if (listenersBound) return;
|
|
296
|
+
listenersBound = true;
|
|
297
|
+
document.addEventListener('mousemove', handleMouseMove, true);
|
|
298
|
+
document.addEventListener('click', handleClickCapture, true);
|
|
299
|
+
window.addEventListener('scroll', handleScrollOrResize, true);
|
|
300
|
+
window.addEventListener('resize', handleScrollOrResize, true);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function unbindInteractionListeners() {
|
|
304
|
+
if (!listenersBound) return;
|
|
305
|
+
listenersBound = false;
|
|
306
|
+
document.removeEventListener('mousemove', handleMouseMove, true);
|
|
307
|
+
document.removeEventListener('click', handleClickCapture, true);
|
|
308
|
+
window.removeEventListener('scroll', handleScrollOrResize, true);
|
|
309
|
+
window.removeEventListener('resize', handleScrollOrResize, true);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function activatePicking() {
|
|
313
|
+
state.active = true;
|
|
314
|
+
state.hoverEl = null;
|
|
315
|
+
bindInteractionListeners();
|
|
316
|
+
document.documentElement.style.cursor = 'crosshair';
|
|
317
|
+
document.body.style.cursor = 'crosshair';
|
|
318
|
+
safePostMessage('IMAGICMA_PICKER_READY', {
|
|
319
|
+
pageUrl: window.location.href,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function deactivatePicking() {
|
|
324
|
+
state.active = false;
|
|
325
|
+
state.hoverEl = null;
|
|
326
|
+
if (state.rafId) {
|
|
327
|
+
window.cancelAnimationFrame(state.rafId);
|
|
328
|
+
state.rafId = 0;
|
|
329
|
+
}
|
|
330
|
+
hideOverlay();
|
|
331
|
+
unbindInteractionListeners();
|
|
332
|
+
document.documentElement.style.cursor = '';
|
|
333
|
+
document.body.style.cursor = '';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function isValidMessage(data) {
|
|
337
|
+
if (!isRecord(data)) return false;
|
|
338
|
+
if (data.channel !== CHANNEL) return false;
|
|
339
|
+
if (data.version !== VERSION) return false;
|
|
340
|
+
if (typeof data.type !== 'string') return false;
|
|
341
|
+
if (typeof data.sessionId !== 'string' || data.sessionId.length === 0) return false;
|
|
342
|
+
if (typeof data.nonce !== 'string' || data.nonce.length === 0) return false;
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function handleParentMessage(event) {
|
|
347
|
+
if (!isAllowedParentOrigin(event.origin)) return;
|
|
348
|
+
if (!isValidMessage(event.data)) return;
|
|
349
|
+
|
|
350
|
+
var data = event.data;
|
|
351
|
+
state.parentOrigin = event.origin;
|
|
352
|
+
state.sessionId = data.sessionId;
|
|
353
|
+
state.nonce = data.nonce;
|
|
354
|
+
|
|
355
|
+
if (data.type === 'IMAGICMA_PICKER_PING') {
|
|
356
|
+
safePostMessage('IMAGICMA_PICKER_READY', {
|
|
357
|
+
pageUrl: window.location.href,
|
|
358
|
+
});
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (data.type === 'IMAGICMA_PICKER_START') {
|
|
363
|
+
activatePicking();
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (data.type === 'IMAGICMA_PICKER_STOP') {
|
|
368
|
+
deactivatePicking();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
window.addEventListener('message', handleParentMessage);
|
|
373
|
+
window.addEventListener('beforeunload', deactivatePicking);
|
|
374
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Navigate, Route, Routes } from "react-router-dom";
|
|
2
|
+
import { HelloPage } from "./pages/HelloPage";
|
|
3
|
+
import { HomePage } from "./pages/HomePage";
|
|
4
|
+
|
|
5
|
+
export function App() {
|
|
6
|
+
return (
|
|
7
|
+
<Routes>
|
|
8
|
+
<Route path="/" element={<HomePage />} />
|
|
9
|
+
<Route path="/hello" element={<HelloPage />} />
|
|
10
|
+
<Route path="*" element={<Navigate to="/" replace />} />
|
|
11
|
+
</Routes>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
type AppErrorBoundaryState = {
|
|
4
|
+
error: Error | null;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class AppErrorBoundary extends React.Component<
|
|
8
|
+
{ children: React.ReactNode },
|
|
9
|
+
AppErrorBoundaryState
|
|
10
|
+
> {
|
|
11
|
+
state: AppErrorBoundaryState = { error: null };
|
|
12
|
+
|
|
13
|
+
static getDerivedStateFromError(error: Error): AppErrorBoundaryState {
|
|
14
|
+
return { error };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
componentDidCatch(error: Error) {
|
|
18
|
+
console.error(error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private reset = () => {
|
|
22
|
+
this.setState({ error: null });
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
const { error } = this.state;
|
|
27
|
+
if (!error) {
|
|
28
|
+
return this.props.children;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isDev = import.meta.env.DEV;
|
|
32
|
+
const message = isDev ? (error.message || "发生未知错误").trim() : "";
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
className="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-indigo-600 via-purple-600 to-sky-600 px-6 py-16 text-white"
|
|
37
|
+
role="alert"
|
|
38
|
+
>
|
|
39
|
+
<div className="absolute inset-0 bg-[radial-gradient(60%_60%_at_50%_40%,rgba(255,255,255,0.20),transparent_60%)]" />
|
|
40
|
+
<main className="relative w-full max-w-xl rounded-3xl border border-white/15 bg-white/10 p-10 shadow-2xl backdrop-blur-xl">
|
|
41
|
+
<div className="flex flex-col items-center text-center">
|
|
42
|
+
<h1 className="text-3xl font-semibold tracking-tight">预览暂时不可用</h1>
|
|
43
|
+
<p className="mt-4 max-w-md text-base leading-7 text-white/80">
|
|
44
|
+
检测到错误。修复后可点击“重试”恢复,或直接刷新页面。
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
{isDev && message ? (
|
|
48
|
+
<pre className="mt-6 w-full max-h-40 overflow-auto rounded-2xl border border-white/10 bg-black/25 p-4 text-left text-sm leading-6 text-white/85 shadow-inner">
|
|
49
|
+
<code>{message}</code>
|
|
50
|
+
</pre>
|
|
51
|
+
) : null}
|
|
52
|
+
|
|
53
|
+
<div className="mt-8 flex w-full flex-col gap-3 sm:flex-row sm:justify-center">
|
|
54
|
+
<button
|
|
55
|
+
type="button"
|
|
56
|
+
className="inline-flex h-11 w-full items-center justify-center rounded-full bg-white px-5 text-sm font-medium text-black transition-colors hover:bg-white/90 sm:w-auto"
|
|
57
|
+
onClick={this.reset}
|
|
58
|
+
>
|
|
59
|
+
重试
|
|
60
|
+
</button>
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
className="inline-flex h-11 w-full items-center justify-center rounded-full border border-white/20 bg-white/10 px-5 text-sm font-medium text-white transition-colors hover:bg-white/15 sm:w-auto"
|
|
64
|
+
onClick={() => window.location.reload()}
|
|
65
|
+
>
|
|
66
|
+
刷新页面
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</main>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
import { useGreeting } from "@/hooks/use-greeting";
|
|
6
|
+
import { toast } from "@/hooks/use-toast";
|
|
7
|
+
|
|
8
|
+
export function HelloClient() {
|
|
9
|
+
const { data, isLoading, error, refetch, isFetching } = useGreeting();
|
|
10
|
+
|
|
11
|
+
const status = isLoading ? "loading" : error ? "error" : "ready";
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Card>
|
|
15
|
+
<CardHeader>
|
|
16
|
+
<div className="flex items-center justify-between gap-4">
|
|
17
|
+
<div className="space-y-1">
|
|
18
|
+
<CardTitle>GET /api/greeting</CardTitle>
|
|
19
|
+
<CardDescription>响应会被 Zod 校验,并展示到 UI 中。</CardDescription>
|
|
20
|
+
</div>
|
|
21
|
+
<Badge variant={status === "error" ? "destructive" : "secondary"}>{status}</Badge>
|
|
22
|
+
</div>
|
|
23
|
+
</CardHeader>
|
|
24
|
+
<CardContent className="space-y-4">
|
|
25
|
+
<div className="rounded-lg border bg-card p-4">
|
|
26
|
+
{isLoading ? (
|
|
27
|
+
<p className="text-sm text-muted-foreground">加载中…</p>
|
|
28
|
+
) : error ? (
|
|
29
|
+
<p className="text-sm text-destructive">
|
|
30
|
+
{error instanceof Error ? error.message : "请求失败"}
|
|
31
|
+
</p>
|
|
32
|
+
) : (
|
|
33
|
+
<p className="text-lg leading-7">“{data?.message}”</p>
|
|
34
|
+
)}
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div className="flex flex-wrap gap-2">
|
|
38
|
+
<Button type="button" onClick={() => refetch()} disabled={isFetching}>
|
|
39
|
+
{isFetching ? "刷新中…" : "刷新"}
|
|
40
|
+
</Button>
|
|
41
|
+
<Button
|
|
42
|
+
type="button"
|
|
43
|
+
variant="outline"
|
|
44
|
+
onClick={() =>
|
|
45
|
+
toast({
|
|
46
|
+
title: "Toast 正常工作",
|
|
47
|
+
description: "如果你能看到我,说明 shadcn/toast 已接入。",
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
触发 Toast
|
|
52
|
+
</Button>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<EnvHint />
|
|
56
|
+
</CardContent>
|
|
57
|
+
</Card>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function EnvHint() {
|
|
62
|
+
return (
|
|
63
|
+
<div className="text-sm text-muted-foreground">
|
|
64
|
+
<p>
|
|
65
|
+
若 API 报错请检查:是否已在 <code>.env.local</code> 设置 <code>DATABASE_URL</code>,并执行 <code>npm run db:push</code>。
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
|
5
|
+
import { ChevronDown } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
const Accordion = AccordionPrimitive.Root
|
|
10
|
+
|
|
11
|
+
const AccordionItem = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<AccordionPrimitive.Item
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn("border-b", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
))
|
|
21
|
+
AccordionItem.displayName = "AccordionItem"
|
|
22
|
+
|
|
23
|
+
const AccordionTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
26
|
+
>(({ className, children, ...props }, ref) => (
|
|
27
|
+
<AccordionPrimitive.Header className="flex">
|
|
28
|
+
<AccordionPrimitive.Trigger
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
38
|
+
</AccordionPrimitive.Trigger>
|
|
39
|
+
</AccordionPrimitive.Header>
|
|
40
|
+
))
|
|
41
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
|
42
|
+
|
|
43
|
+
const AccordionContent = React.forwardRef<
|
|
44
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
45
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
46
|
+
>(({ className, children, ...props }, ref) => (
|
|
47
|
+
<AccordionPrimitive.Content
|
|
48
|
+
ref={ref}
|
|
49
|
+
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
|
53
|
+
</AccordionPrimitive.Content>
|
|
54
|
+
))
|
|
55
|
+
|
|
56
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
|
57
|
+
|
|
58
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|