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.
Files changed (104) hide show
  1. package/README.md +30 -14
  2. package/bin/create-next-imagicma.mjs +121 -23
  3. package/package.json +2 -1
  4. package/template/app/globals.css +331 -0
  5. package/template/app/layout.tsx +4 -6
  6. package/template/app/page.tsx +18 -40
  7. package/template/package.json +1 -1
  8. package/template/public/imagicma-picker-bridge.js +374 -0
  9. package/template-hono/.env.example +8 -0
  10. package/template-hono/AGENTS.md +39 -0
  11. package/template-hono/README.md +58 -0
  12. package/template-hono/client/index.html +13 -0
  13. package/template-hono/client/public/favicon.ico +0 -0
  14. package/template-hono/client/public/file.svg +1 -0
  15. package/template-hono/client/public/globe.svg +1 -0
  16. package/template-hono/client/public/imagicma-picker-bridge.js +374 -0
  17. package/template-hono/client/public/next.svg +1 -0
  18. package/template-hono/client/public/vercel.svg +1 -0
  19. package/template-hono/client/public/window.svg +1 -0
  20. package/template-hono/client/src/App.tsx +13 -0
  21. package/template-hono/client/src/components/ErrorBoundary.tsx +74 -0
  22. package/template-hono/client/src/components/HelloClient.tsx +69 -0
  23. package/template-hono/client/src/components/ui/accordion.tsx +58 -0
  24. package/template-hono/client/src/components/ui/alert-dialog.tsx +141 -0
  25. package/template-hono/client/src/components/ui/alert.tsx +61 -0
  26. package/template-hono/client/src/components/ui/aspect-ratio.tsx +7 -0
  27. package/template-hono/client/src/components/ui/avatar.tsx +51 -0
  28. package/template-hono/client/src/components/ui/badge.tsx +40 -0
  29. package/template-hono/client/src/components/ui/breadcrumb.tsx +117 -0
  30. package/template-hono/client/src/components/ui/button.tsx +64 -0
  31. package/template-hono/client/src/components/ui/calendar.tsx +72 -0
  32. package/template-hono/client/src/components/ui/card.tsx +87 -0
  33. package/template-hono/client/src/components/ui/carousel.tsx +262 -0
  34. package/template-hono/client/src/components/ui/chart.tsx +365 -0
  35. package/template-hono/client/src/components/ui/checkbox.tsx +30 -0
  36. package/template-hono/client/src/components/ui/collapsible.tsx +11 -0
  37. package/template-hono/client/src/components/ui/command.tsx +153 -0
  38. package/template-hono/client/src/components/ui/context-menu.tsx +200 -0
  39. package/template-hono/client/src/components/ui/dialog.tsx +122 -0
  40. package/template-hono/client/src/components/ui/drawer.tsx +118 -0
  41. package/template-hono/client/src/components/ui/dropdown-menu.tsx +200 -0
  42. package/template-hono/client/src/components/ui/form.tsx +178 -0
  43. package/template-hono/client/src/components/ui/hover-card.tsx +29 -0
  44. package/template-hono/client/src/components/ui/input-otp.tsx +71 -0
  45. package/template-hono/client/src/components/ui/input.tsx +25 -0
  46. package/template-hono/client/src/components/ui/label.tsx +26 -0
  47. package/template-hono/client/src/components/ui/menubar.tsx +256 -0
  48. package/template-hono/client/src/components/ui/navigation-menu.tsx +130 -0
  49. package/template-hono/client/src/components/ui/pagination.tsx +119 -0
  50. package/template-hono/client/src/components/ui/popover.tsx +31 -0
  51. package/template-hono/client/src/components/ui/progress.tsx +28 -0
  52. package/template-hono/client/src/components/ui/radio-group.tsx +44 -0
  53. package/template-hono/client/src/components/ui/resizable.tsx +45 -0
  54. package/template-hono/client/src/components/ui/scroll-area.tsx +48 -0
  55. package/template-hono/client/src/components/ui/select.tsx +160 -0
  56. package/template-hono/client/src/components/ui/separator.tsx +31 -0
  57. package/template-hono/client/src/components/ui/sheet.tsx +140 -0
  58. package/template-hono/client/src/components/ui/sidebar.tsx +732 -0
  59. package/template-hono/client/src/components/ui/skeleton.tsx +17 -0
  60. package/template-hono/client/src/components/ui/slider.tsx +28 -0
  61. package/template-hono/client/src/components/ui/switch.tsx +29 -0
  62. package/template-hono/client/src/components/ui/table.tsx +119 -0
  63. package/template-hono/client/src/components/ui/tabs.tsx +55 -0
  64. package/template-hono/client/src/components/ui/textarea.tsx +24 -0
  65. package/template-hono/client/src/components/ui/toast.tsx +129 -0
  66. package/template-hono/client/src/components/ui/toaster.tsx +35 -0
  67. package/template-hono/client/src/components/ui/toggle-group.tsx +61 -0
  68. package/template-hono/client/src/components/ui/toggle.tsx +45 -0
  69. package/template-hono/client/src/components/ui/tooltip.tsx +30 -0
  70. package/template-hono/client/src/globals.css +767 -0
  71. package/template-hono/client/src/hooks/use-greeting.ts +15 -0
  72. package/template-hono/client/src/hooks/use-mobile.ts +21 -0
  73. package/template-hono/client/src/hooks/use-toast.ts +194 -0
  74. package/template-hono/client/src/lib/queryClient.ts +59 -0
  75. package/template-hono/client/src/lib/theme/default-theme.ts +11 -0
  76. package/template-hono/client/src/lib/utils.ts +6 -0
  77. package/template-hono/client/src/main.tsx +24 -0
  78. package/template-hono/client/src/pages/HelloPage.tsx +22 -0
  79. package/template-hono/client/src/pages/HomePage.tsx +30 -0
  80. package/template-hono/client/src/providers.tsx +21 -0
  81. package/template-hono/drizzle.config.ts +50 -0
  82. package/template-hono/eslint.config.mjs +13 -0
  83. package/template-hono/gitignore +40 -0
  84. package/template-hono/package.json +83 -0
  85. package/template-hono/pnpm-lock.yaml +5176 -0
  86. package/template-hono/postcss.config.mjs +7 -0
  87. package/template-hono/process-compose.yaml +13 -0
  88. package/template-hono/scripts/imagicma-common.mjs +118 -0
  89. package/template-hono/scripts/imagicma-dev.mjs +29 -0
  90. package/template-hono/scripts/imagicma-guard.mjs +17 -0
  91. package/template-hono/scripts/imagicma-start.mjs +24 -0
  92. package/template-hono/server/app.ts +40 -0
  93. package/template-hono/server/db.ts +22 -0
  94. package/template-hono/server/dev-app.ts +5 -0
  95. package/template-hono/server/index.ts +94 -0
  96. package/template-hono/server/routes/greeting.ts +25 -0
  97. package/template-hono/server/storage.ts +39 -0
  98. package/template-hono/shared/routes.ts +13 -0
  99. package/template-hono/shared/schema.ts +17 -0
  100. package/template-hono/tailwind.config.mjs +94 -0
  101. package/template-hono/tsconfig.json +33 -0
  102. package/template-hono/tsconfig.server.json +15 -0
  103. package/template-hono/types/pg.d.ts +19 -0
  104. package/template-hono/vite.config.ts +126 -0
@@ -1,47 +1,25 @@
1
1
  export default function Home() {
2
2
  return (
3
- <div 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">
4
- <div className="absolute inset-0 bg-[radial-gradient(60%_60%_at_50%_40%,rgba(255,255,255,0.20),transparent_60%)]" />
5
- <main className="relative w-full max-w-xl rounded-3xl border border-white/15 bg-white/10 p-10 shadow-2xl backdrop-blur-xl">
6
- <div className="flex flex-col items-center text-center">
7
- <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-white/10">
8
- <svg
9
- viewBox="0 0 24 24"
10
- className="h-9 w-9 text-white/90"
11
- fill="none"
12
- aria-hidden="true"
13
- >
14
- <path
15
- d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Z"
16
- stroke="currentColor"
17
- strokeWidth="1.8"
18
- />
19
- <path
20
- d="M19.4 13.1a7.8 7.8 0 0 0 0-2.2l2-1.6a.7.7 0 0 0 .2-.9l-1.9-3.2a.7.7 0 0 0-.8-.3l-2.4 1a8.3 8.3 0 0 0-1.9-1.1l-.4-2.5a.7.7 0 0 0-.7-.6H8.5a.7.7 0 0 0-.7.6l-.4 2.5a8.3 8.3 0 0 0-1.9 1.1l-2.4-1a.7.7 0 0 0-.8.3L.4 8.4a.7.7 0 0 0 .2.9l2 1.6a7.8 7.8 0 0 0 0 2.2l-2 1.6a.7.7 0 0 0-.2.9l1.9 3.2c.2.3.5.4.8.3l2.4-1a8.3 8.3 0 0 0 1.9 1.1l.4 2.5c.1.3.3.6.7.6h3.7c.3 0 .6-.2.7-.6l.4-2.5a8.3 8.3 0 0 0 1.9-1.1l2.4 1c.3.1.6 0 .8-.3l1.9-3.2a.7.7 0 0 0-.2-.9l-2-1.6Z"
21
- stroke="currentColor"
22
- strokeWidth="1.2"
23
- strokeLinejoin="round"
24
- opacity="0.85"
25
- />
26
- </svg>
27
- </div>
3
+ <div className="relative min-h-screen overflow-hidden bg-gradient-to-br from-indigo-600 via-purple-600 to-sky-600 text-white">
4
+ <div aria-hidden className="pointer-events-none absolute inset-0 overflow-hidden">
5
+ <div className="absolute inset-0 bg-[radial-gradient(60%_60%_at_50%_40%,rgba(255,255,255,0.20),transparent_60%)]" />
6
+ </div>
28
7
 
29
- <h1 className="mt-6 text-4xl font-semibold tracking-tight">
30
- 项目构建中
31
- </h1>
32
- <p className="mt-4 max-w-md text-base leading-7 text-white/80">
33
- 我们正在努力为你构建预览服务,请稍后再试。
34
- </p>
8
+ <div aria-hidden className="aurora-frame">
9
+ <span className="aurora-edge top" />
10
+ <span className="aurora-edge right" />
11
+ <span className="aurora-edge bottom" />
12
+ <span className="aurora-edge left" />
13
+ <span className="aurora-edge top is-glow" />
14
+ <span className="aurora-edge right is-glow" />
15
+ <span className="aurora-edge bottom is-glow" />
16
+ <span className="aurora-edge left is-glow" />
17
+ </div>
35
18
 
36
- <div
37
- className="mt-10 flex items-center gap-3 text-sm text-white/70"
38
- role="status"
39
- aria-live="polite"
40
- >
41
- <span className="sr-only">加载中</span>
42
- <div className="h-5 w-5 rounded-full border-2 border-white/25 border-t-white/90 motion-reduce:animate-none animate-spin" />
43
- <span>准备中…</span>
44
- </div>
19
+ <main className="relative z-[1] flex min-h-screen items-center justify-center">
20
+ <div role="status" aria-live="polite">
21
+ <span className="sr-only">加载中</span>
22
+ <div className="h-10 w-10 rounded-full border-[3px] border-white/25 border-t-white/90 motion-reduce:animate-none animate-spin" />
45
23
  </div>
46
24
  </main>
47
25
  </div>
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev",
6
+ "dev": "next dev -p 5005",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "check": "tsc -p tsconfig.json --noEmit",
@@ -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,8 @@
1
+ # 复制为 .env.local 并填入真实值(.env.local 不会被提交)
2
+ #
3
+ # Postgres 连接串(Drizzle + pg 使用)
4
+ # 示例:
5
+ # DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
6
+ DATABASE_URL=""
7
+
8
+
@@ -0,0 +1,39 @@
1
+ # 项目 Agent 指南(Hono + Vite + React + Drizzle)
2
+
3
+ 默认使用中文沟通与输出(除非用户明确要求其他语言)。
4
+
5
+ ## 技术栈与约束
6
+
7
+ - 框架:Hono(Node runtime)+ Vite(单进程开发)
8
+ - 前端:React 19 + React Router + Tailwind v4 + shadcn/ui
9
+ - 请求层:React Query + fetch
10
+ - 契约:Zod(`shared/routes.ts`)
11
+ - 数据库:Postgres + Drizzle
12
+
13
+ ## 目录约定
14
+
15
+ - `client/index.html`:Vite 前端入口 HTML
16
+ - `client/public/`:前端静态资源
17
+ - `client/src/`:前端应用入口、页面、Provider、错误边界
18
+ - `client/src/components/ui/`:shadcn/ui 组件
19
+ - `client/src/hooks/`:客户端 hooks
20
+ - `client/src/lib/`:通用工具
21
+ - `server/`:Hono 入口、路由、存储、DB
22
+ - `shared/`:前后端共享 schema/契约
23
+
24
+ ## 常用命令
25
+
26
+ - `pnpm dev`:单进程开发(Vite + Hono dev middleware)
27
+ - `pnpm build`:构建前端并编译 server
28
+ - `pnpm start`:生产启动
29
+ - `pnpm check`:类型检查
30
+ - `pnpm lint`:ESLint
31
+ - `pnpm db:push`:同步数据库结构
32
+
33
+ ## 开发规则
34
+
35
+ - 服务端数据库逻辑放在 `server/`,不要泄漏到客户端。
36
+ - API 响应必须经过 `shared/routes.ts` 的 Zod schema 校验。
37
+ - 不提交 `.env.local`、数据库密钥。
38
+ - 优先复用 `client/src/components/ui` 与 `client/src/hooks`,避免重复造轮子。
39
+
@@ -0,0 +1,58 @@
1
+ # hono-app
2
+
3
+ 基于 **Hono + Vite + React** 的全栈模板,目标是保留原 `nextjs-app` 的前端体验,同时降低服务端运行开销。
4
+
5
+ ## 技术栈
6
+
7
+ - 前端:React 19、React Router、Tailwind CSS v4、shadcn/ui、React Query
8
+ - 后端:Hono(Node runtime)
9
+ - 数据层:Drizzle ORM + PostgreSQL (`pg`)
10
+ - 校验契约:Zod(`shared/routes.ts`)
11
+
12
+ ## 目录结构(重点)
13
+
14
+ - `client/index.html`:前端入口 HTML
15
+ - `client/public/`:前端静态资源
16
+ - `client/src/components/ui/`:shadcn/ui 组件
17
+ - `client/src/hooks/`:前端 hooks
18
+ - `client/src/lib/`:前端通用工具
19
+ - `server/`:Hono 后端入口与路由
20
+ - `shared/`:前后端共享契约与 schema
21
+
22
+ ## 开发
23
+
24
+ ```bash
25
+ pnpm install
26
+ pnpm dev
27
+ ```
28
+
29
+ 默认端口由 `/.imagicma/port.json` 锁定(模板默认 `5001`)。
30
+ 请勿直接执行 `vite` 启动,必须使用 `pnpm dev`。
31
+
32
+ ## 构建与运行
33
+
34
+ ```bash
35
+ pnpm build
36
+ pnpm start
37
+ ```
38
+
39
+ - `build` 产出:
40
+ - 前端:`dist/client`
41
+ - 后端:`dist/server`
42
+ - `start`:由受保护脚本启动服务(禁止直接 `node dist/server/index.js`),并由 Hono 承载 API + 静态资源 + SPA fallback。
43
+ - 生产端口同样由 `/.imagicma/port.json` 锁定,不接受 `PORT` 覆盖。
44
+
45
+ ## 数据库
46
+
47
+ 1. 复制 `.env.example` 为 `.env.local`
48
+ 2. 填写 `DATABASE_URL`
49
+ 3. 初始化结构:
50
+
51
+ ```bash
52
+ pnpm db:push
53
+ ```
54
+
55
+ ## 验证路径
56
+
57
+ - API:`GET /api/greeting`
58
+ - 页面:`/hello`
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN" data-theme-style="quadratic">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>hono-app</title>
7
+ <script src="/imagicma-picker-bridge.js"></script>
8
+ </head>
9
+ <body class="antialiased">
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>