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
package/template/app/page.tsx
CHANGED
|
@@ -1,47 +1,25 @@
|
|
|
1
1
|
export default function Home() {
|
|
2
2
|
return (
|
|
3
|
-
<div className="relative
|
|
4
|
-
<div className="absolute inset-0
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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>
|
package/template/package.json
CHANGED
|
@@ -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,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>
|
|
Binary file
|
|
@@ -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>
|