sa2kit 1.6.67 → 1.6.69

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 (41) hide show
  1. package/dist/festivalCard/core/index.d.mts +8 -0
  2. package/dist/festivalCard/core/index.d.ts +8 -0
  3. package/dist/festivalCard/core/index.js +181 -0
  4. package/dist/festivalCard/core/index.js.map +1 -0
  5. package/dist/festivalCard/core/index.mjs +177 -0
  6. package/dist/festivalCard/core/index.mjs.map +1 -0
  7. package/dist/festivalCard/index.d.mts +5 -10
  8. package/dist/festivalCard/index.d.ts +5 -10
  9. package/dist/festivalCard/index.js +549 -173
  10. package/dist/festivalCard/index.js.map +1 -1
  11. package/dist/festivalCard/index.mjs +539 -153
  12. package/dist/festivalCard/index.mjs.map +1 -1
  13. package/dist/festivalCard/miniapp/index.d.mts +12 -0
  14. package/dist/festivalCard/miniapp/index.d.ts +12 -0
  15. package/dist/festivalCard/miniapp/index.js +341 -0
  16. package/dist/festivalCard/miniapp/index.js.map +1 -0
  17. package/dist/festivalCard/miniapp/index.mjs +329 -0
  18. package/dist/festivalCard/miniapp/index.mjs.map +1 -0
  19. package/dist/festivalCard/server/index.d.mts +6 -0
  20. package/dist/festivalCard/server/index.d.ts +6 -0
  21. package/dist/festivalCard/server/index.js +13 -0
  22. package/dist/festivalCard/server/index.js.map +1 -0
  23. package/dist/festivalCard/server/index.mjs +10 -0
  24. package/dist/festivalCard/server/index.mjs.map +1 -0
  25. package/dist/festivalCard/web/index.d.mts +31 -0
  26. package/dist/festivalCard/web/index.d.ts +31 -0
  27. package/dist/festivalCard/web/index.js +582 -0
  28. package/dist/festivalCard/web/index.js.map +1 -0
  29. package/dist/festivalCard/web/index.mjs +567 -0
  30. package/dist/festivalCard/web/index.mjs.map +1 -0
  31. package/dist/festivalCardService-C0fhTezx.d.ts +25 -0
  32. package/dist/festivalCardService-uSg0oNuD.d.mts +25 -0
  33. package/dist/index.d.mts +4 -1
  34. package/dist/index.d.ts +4 -1
  35. package/dist/index.js +562 -164
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +552 -163
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/types-B-tjQTGi.d.mts +62 -0
  40. package/dist/types-B-tjQTGi.d.ts +62 -0
  41. package/package.json +21 -1
@@ -1,181 +1,567 @@
1
- import React, { useRef, useEffect } from 'react';
2
- import * as THREE from 'three';
1
+ import React3, { useState, useEffect, useCallback, useMemo } from 'react';
3
2
 
4
- // src/festivalCard/components/FestivalCard3D.tsx
5
- var createSnow = (count) => {
6
- const positions = new Float32Array(count * 3);
7
- const speeds = new Float32Array(count);
8
- for (let i = 0; i < count; i++) {
9
- const i3 = i * 3;
10
- positions[i3] = (Math.random() - 0.5) * 16;
11
- positions[i3 + 1] = Math.random() * 10 + 1;
12
- positions[i3 + 2] = (Math.random() - 0.5) * 16;
13
- speeds[i] = 4e-3 + Math.random() * 0.01;
3
+ // src/festivalCard/core/defaults.ts
4
+ var DEFAULT_FESTIVAL_CARD_CONFIG = {
5
+ id: "default-festival-card",
6
+ name: "Holiday Card",
7
+ theme: "winter",
8
+ coverTitle: "Happy Holidays",
9
+ coverSubtitle: "Warm wishes for you",
10
+ background: {
11
+ colorA: "#0c1a34",
12
+ colorB: "#1f4f8a"
13
+ },
14
+ backgroundMusic: {
15
+ src: "",
16
+ loop: true,
17
+ autoPlay: false,
18
+ volume: 0.5
19
+ },
20
+ pages: [
21
+ {
22
+ id: "page-1",
23
+ title: "\u5C01\u9762",
24
+ background: { color: "#11284d" },
25
+ elements: [
26
+ {
27
+ id: "p1-text-1",
28
+ type: "text",
29
+ x: 50,
30
+ y: 20,
31
+ content: "\u65B0\u5E74\u5FEB\u4E50",
32
+ fontSize: 34,
33
+ fontWeight: 700,
34
+ align: "center",
35
+ color: "#f8fafc"
36
+ },
37
+ {
38
+ id: "p1-text-2",
39
+ type: "text",
40
+ x: 50,
41
+ y: 36,
42
+ content: "\u613F\u8FD9\u4E00\u5E74\u5E73\u5B89\u559C\u4E50",
43
+ fontSize: 18,
44
+ fontWeight: 500,
45
+ align: "center",
46
+ color: "#dbeafe"
47
+ },
48
+ {
49
+ id: "p1-image-1",
50
+ type: "image",
51
+ x: 50,
52
+ y: 68,
53
+ width: 72,
54
+ height: 42,
55
+ src: "https://images.unsplash.com/photo-1512389142860-9c449e58a543?auto=format&fit=crop&w=1200&q=80",
56
+ fit: "cover",
57
+ borderRadius: 16,
58
+ alt: "holiday"
59
+ }
60
+ ]
61
+ },
62
+ {
63
+ id: "page-2",
64
+ title: "\u795D\u798F",
65
+ background: { color: "#1a3766" },
66
+ elements: [
67
+ {
68
+ id: "p2-text-1",
69
+ type: "text",
70
+ x: 50,
71
+ y: 28,
72
+ content: "\u613F\u4F60\u65B0\u5C81\uFF1A",
73
+ fontSize: 28,
74
+ fontWeight: 700,
75
+ align: "center",
76
+ color: "#fef3c7"
77
+ },
78
+ {
79
+ id: "p2-text-2",
80
+ type: "text",
81
+ x: 50,
82
+ y: 42,
83
+ content: "\u6240\u5F97\u7686\u6240\u671F\uFF0C\u6240\u5931\u4EA6\u65E0\u788D",
84
+ fontSize: 18,
85
+ fontWeight: 500,
86
+ align: "center",
87
+ color: "#f8fafc"
88
+ },
89
+ {
90
+ id: "p2-text-3",
91
+ type: "text",
92
+ x: 50,
93
+ y: 55,
94
+ content: "\u613F\u4F60\u7684\u6BCF\u4E00\u6B65\u90FD\u8D70\u5411\u5149\u4EAE",
95
+ fontSize: 16,
96
+ fontWeight: 400,
97
+ align: "center",
98
+ color: "#dbeafe"
99
+ }
100
+ ]
101
+ },
102
+ {
103
+ id: "page-3",
104
+ title: "\u843D\u6B3E",
105
+ background: { color: "#11284d" },
106
+ elements: [
107
+ {
108
+ id: "p3-image-1",
109
+ type: "image",
110
+ x: 50,
111
+ y: 34,
112
+ width: 60,
113
+ height: 42,
114
+ src: "https://images.unsplash.com/photo-1456324504439-367cee3b3c32?auto=format&fit=crop&w=1200&q=80",
115
+ fit: "cover",
116
+ borderRadius: 14,
117
+ alt: "gift"
118
+ },
119
+ {
120
+ id: "p3-text-1",
121
+ type: "text",
122
+ x: 50,
123
+ y: 72,
124
+ content: "Best wishes, from SA2Kit",
125
+ fontSize: 18,
126
+ fontWeight: 600,
127
+ align: "center",
128
+ color: "#f8fafc"
129
+ }
130
+ ]
131
+ }
132
+ ]
133
+ };
134
+
135
+ // src/festivalCard/core/normalize.ts
136
+ var ensurePage = (page, index) => ({
137
+ id: page.id || `page-${index + 1}`,
138
+ title: page.title || `\u7B2C ${index + 1} \u9875`,
139
+ elements: Array.isArray(page.elements) ? page.elements : [],
140
+ background: page.background || {}
141
+ });
142
+ var normalizeFestivalCardConfig = (config) => {
143
+ const pages = config?.pages && config.pages.length > 0 ? config.pages : DEFAULT_FESTIVAL_CARD_CONFIG.pages;
144
+ return {
145
+ ...DEFAULT_FESTIVAL_CARD_CONFIG,
146
+ ...config,
147
+ background: {
148
+ ...DEFAULT_FESTIVAL_CARD_CONFIG.background,
149
+ ...config?.background || {}
150
+ },
151
+ backgroundMusic: {
152
+ ...DEFAULT_FESTIVAL_CARD_CONFIG.backgroundMusic,
153
+ ...config?.backgroundMusic || {},
154
+ src: config?.backgroundMusic?.src ?? DEFAULT_FESTIVAL_CARD_CONFIG.backgroundMusic?.src ?? ""
155
+ },
156
+ pages: pages.map(ensurePage)
157
+ };
158
+ };
159
+ var resizeFestivalCardPages = (config, pageCount) => {
160
+ const safeCount = Math.max(1, Math.min(12, Math.floor(pageCount || 1)));
161
+ const nextPages = [...config.pages];
162
+ while (nextPages.length < safeCount) {
163
+ const idx = nextPages.length;
164
+ nextPages.push({
165
+ id: `page-${idx + 1}`,
166
+ title: `\u7B2C ${idx + 1} \u9875`,
167
+ elements: [],
168
+ background: {}
169
+ });
14
170
  }
15
- return { positions, speeds };
171
+ return {
172
+ ...config,
173
+ pages: nextPages.slice(0, safeCount)
174
+ };
16
175
  };
17
- var FestivalCard3D = ({
18
- title = "Happy Holidays",
19
- subtitle = "Wishing you joy and peace",
20
- className
21
- }) => {
22
- const mountRef = useRef(null);
176
+ var useFestivalCardConfig = (options) => {
177
+ const [config, setConfig] = useState(
178
+ () => normalizeFestivalCardConfig(options?.initialConfig || DEFAULT_FESTIVAL_CARD_CONFIG)
179
+ );
180
+ const [loading, setLoading] = useState(Boolean(options?.fetchConfig));
181
+ const [saving, setSaving] = useState(false);
23
182
  useEffect(() => {
24
- const mount = mountRef.current;
25
- if (!mount) return;
26
- const scene = new THREE.Scene();
27
- scene.fog = new THREE.Fog(528934, 12, 28);
28
- const camera = new THREE.PerspectiveCamera(50, mount.clientWidth / mount.clientHeight, 0.1, 100);
29
- camera.position.set(0, 2.6, 7.5);
30
- const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
31
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
32
- renderer.setSize(mount.clientWidth, mount.clientHeight);
33
- renderer.outputColorSpace = THREE.SRGBColorSpace;
34
- mount.appendChild(renderer.domElement);
35
- const ambientLight = new THREE.AmbientLight(9090303, 0.8);
36
- scene.add(ambientLight);
37
- const keyLight = new THREE.DirectionalLight(16777215, 1.25);
38
- keyLight.position.set(2, 5, 4);
39
- scene.add(keyLight);
40
- const fillLight = new THREE.PointLight(8050687, 0.9, 24);
41
- fillLight.position.set(-4, 3, -2);
42
- scene.add(fillLight);
43
- const floor = new THREE.Mesh(
44
- new THREE.CircleGeometry(12, 48),
45
- new THREE.MeshStandardMaterial({ color: 1056826, roughness: 0.95, metalness: 0.05 })
46
- );
47
- floor.rotation.x = -Math.PI / 2;
48
- floor.position.y = -1.2;
49
- scene.add(floor);
50
- const giftGroup = new THREE.Group();
51
- scene.add(giftGroup);
52
- const box = new THREE.Mesh(
53
- new THREE.BoxGeometry(2, 1.6, 2),
54
- new THREE.MeshStandardMaterial({ color: 14238053, roughness: 0.55, metalness: 0.2 })
55
- );
56
- giftGroup.add(box);
57
- const ribbonMaterial = new THREE.MeshStandardMaterial({ color: 16767354, roughness: 0.3, metalness: 0.5 });
58
- const verticalRibbon = new THREE.Mesh(new THREE.BoxGeometry(0.24, 1.7, 2.02), ribbonMaterial);
59
- const horizontalRibbon = new THREE.Mesh(new THREE.BoxGeometry(2.02, 1.7, 0.24), ribbonMaterial);
60
- giftGroup.add(verticalRibbon);
61
- giftGroup.add(horizontalRibbon);
62
- const lid = new THREE.Mesh(
63
- new THREE.BoxGeometry(2.15, 0.34, 2.15),
64
- new THREE.MeshStandardMaterial({ color: 13448278, roughness: 0.52, metalness: 0.2 })
65
- );
66
- lid.position.y = 0.98;
67
- giftGroup.add(lid);
68
- const bowLeft = new THREE.Mesh(new THREE.TorusGeometry(0.26, 0.08, 12, 32), ribbonMaterial);
69
- bowLeft.rotation.set(Math.PI / 2, Math.PI / 6, 0);
70
- bowLeft.position.set(-0.22, 1.14, 0);
71
- const bowRight = bowLeft.clone();
72
- bowRight.rotation.y = -Math.PI / 6;
73
- bowRight.position.x = 0.22;
74
- giftGroup.add(bowLeft, bowRight);
75
- giftGroup.position.y = -0.15;
76
- const { positions, speeds } = createSnow(260);
77
- const snowGeometry = new THREE.BufferGeometry();
78
- snowGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
79
- const snow = new THREE.Points(
80
- snowGeometry,
81
- new THREE.PointsMaterial({
82
- color: 15267071,
83
- size: 0.08,
84
- transparent: true,
85
- opacity: 0.92,
86
- depthWrite: false
87
- })
88
- );
89
- scene.add(snow);
90
- let rafId = 0;
91
- const clock = new THREE.Clock();
92
- const animate = () => {
93
- const elapsed = clock.getElapsedTime();
94
- const pos = snowGeometry.attributes.position;
95
- for (let i = 0; i < pos.count; i++) {
96
- const speed = speeds[i] ?? 0.01;
97
- const y = pos.getY(i) - speed;
98
- pos.setY(i, y < -1.1 ? 10 + Math.random() * 2 : y);
99
- pos.setX(i, pos.getX(i) + Math.sin(elapsed + i * 0.04) * 16e-4);
100
- }
101
- pos.needsUpdate = true;
102
- giftGroup.rotation.y = elapsed * 0.35;
103
- giftGroup.position.y = -0.15 + Math.sin(elapsed * 1.4) * 0.08;
104
- lid.position.y = 0.98 + Math.sin(elapsed * 2.2) * 0.08;
105
- renderer.render(scene, camera);
106
- rafId = window.requestAnimationFrame(animate);
107
- };
108
- const handleResize = () => {
109
- if (!mount) return;
110
- const width = mount.clientWidth;
111
- const height = mount.clientHeight;
112
- camera.aspect = width / height;
113
- camera.updateProjectionMatrix();
114
- renderer.setSize(width, height);
115
- };
116
- window.addEventListener("resize", handleResize);
117
- animate();
183
+ if (!options?.fetchConfig) return;
184
+ let active = true;
185
+ void options.fetchConfig().then((value) => {
186
+ if (!active) return;
187
+ setConfig(normalizeFestivalCardConfig(value));
188
+ }).finally(() => {
189
+ if (active) setLoading(false);
190
+ });
118
191
  return () => {
119
- window.cancelAnimationFrame(rafId);
120
- window.removeEventListener("resize", handleResize);
121
- scene.traverse((obj) => {
122
- const mesh = obj;
123
- if (mesh.geometry) mesh.geometry.dispose();
124
- const material = mesh.material;
125
- if (Array.isArray(material)) material.forEach((m) => m.dispose());
126
- else material?.dispose();
127
- });
128
- renderer.dispose();
129
- mount.removeChild(renderer.domElement);
192
+ active = false;
130
193
  };
131
- }, []);
132
- return /* @__PURE__ */ React.createElement(
194
+ }, [options]);
195
+ const save = useCallback(async () => {
196
+ if (!options?.onSave) return;
197
+ setSaving(true);
198
+ try {
199
+ await options.onSave(config);
200
+ } finally {
201
+ setSaving(false);
202
+ }
203
+ }, [config, options]);
204
+ return useMemo(
205
+ () => ({
206
+ config,
207
+ setConfig,
208
+ loading,
209
+ saving,
210
+ save
211
+ }),
212
+ [config, loading, save, saving]
213
+ );
214
+ };
215
+ var elementStyle = (element) => ({
216
+ position: "absolute",
217
+ left: `${element.x}%`,
218
+ top: `${element.y}%`,
219
+ width: `${element.width ?? 70}%`,
220
+ height: element.height ? `${element.height}%` : void 0,
221
+ transform: "translate(-50%, -50%)"
222
+ });
223
+ var renderElement = (element) => {
224
+ if (element.type === "text") {
225
+ return /* @__PURE__ */ React3.createElement(
226
+ "div",
227
+ {
228
+ key: element.id,
229
+ style: {
230
+ ...elementStyle(element),
231
+ color: element.color || "#f8fafc",
232
+ fontSize: element.fontSize || 18,
233
+ fontWeight: element.fontWeight || 500,
234
+ textAlign: element.align || "left",
235
+ lineHeight: 1.45,
236
+ whiteSpace: "pre-wrap"
237
+ }
238
+ },
239
+ element.content
240
+ );
241
+ }
242
+ return /* @__PURE__ */ React3.createElement(
243
+ "img",
244
+ {
245
+ key: element.id,
246
+ src: element.src,
247
+ alt: element.alt || "festival-card-image",
248
+ style: {
249
+ ...elementStyle(element),
250
+ objectFit: element.fit || "cover",
251
+ borderRadius: element.borderRadius || 0,
252
+ overflow: "hidden",
253
+ boxShadow: "0 12px 30px rgba(2, 6, 23, 0.32)"
254
+ }
255
+ }
256
+ );
257
+ };
258
+ var FestivalCardPageRenderer = ({ page }) => {
259
+ return /* @__PURE__ */ React3.createElement(
133
260
  "div",
134
261
  {
135
- className,
136
262
  style: {
137
263
  position: "relative",
138
264
  width: "100%",
139
- minHeight: 420,
140
- borderRadius: 20,
265
+ height: "100%",
141
266
  overflow: "hidden",
142
- background: "radial-gradient(circle at 20% 20%, #244d8c 0%, #0c1a34 45%, #060d1f 100%)"
267
+ borderRadius: 16,
268
+ backgroundColor: page.background?.color || "#0f172a",
269
+ backgroundImage: page.background?.image ? `url(${page.background.image})` : void 0,
270
+ backgroundSize: "cover",
271
+ backgroundPosition: "center"
272
+ }
273
+ },
274
+ page.elements.map(renderElement)
275
+ );
276
+ };
277
+
278
+ // src/festivalCard/components/FestivalCardBook3D.tsx
279
+ var FestivalCardBook3D = ({ config, className }) => {
280
+ const [currentPage, setCurrentPage] = useState(0);
281
+ const normalized = useMemo(() => normalizeFestivalCardConfig(config), [config]);
282
+ const pages = normalized.pages;
283
+ const canPrev = currentPage > 0;
284
+ const canNext = currentPage < pages.length - 1;
285
+ return /* @__PURE__ */ React3.createElement("div", { className }, /* @__PURE__ */ React3.createElement(
286
+ "div",
287
+ {
288
+ style: {
289
+ width: "100%",
290
+ minHeight: 560,
291
+ borderRadius: 24,
292
+ padding: 24,
293
+ background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`,
294
+ boxShadow: "0 26px 70px rgba(2, 6, 23, 0.45)"
143
295
  }
144
296
  },
145
- /* @__PURE__ */ React.createElement("div", { ref: mountRef, style: { position: "absolute", inset: 0 } }),
146
- /* @__PURE__ */ React.createElement(
297
+ /* @__PURE__ */ React3.createElement(
147
298
  "div",
148
299
  {
149
300
  style: {
150
- position: "absolute",
151
- inset: 0,
152
- pointerEvents: "none",
153
- background: "linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.04) 35%, rgba(4,8,20,0.36) 100%)"
301
+ marginBottom: 14,
302
+ color: "#f8fafc",
303
+ fontSize: 14,
304
+ opacity: 0.9,
305
+ textAlign: "center"
154
306
  }
155
- }
307
+ },
308
+ normalized.coverTitle || "Festival Card",
309
+ " \xB7 \u7B2C ",
310
+ currentPage + 1,
311
+ " / ",
312
+ pages.length,
313
+ " \u9875"
156
314
  ),
157
- /* @__PURE__ */ React.createElement(
315
+ /* @__PURE__ */ React3.createElement("div", { style: { perspective: 1400, width: "100%", maxWidth: 920, margin: "0 auto" } }, /* @__PURE__ */ React3.createElement(
158
316
  "div",
159
317
  {
160
318
  style: {
161
- position: "absolute",
162
- left: 20,
163
- right: 20,
164
- bottom: 20,
165
- zIndex: 2,
166
- padding: "16px 18px",
167
- borderRadius: 14,
168
- backgroundColor: "rgba(8, 16, 35, 0.66)",
169
- border: "1px solid rgba(255, 255, 255, 0.16)",
170
- color: "#f8fafc"
319
+ position: "relative",
320
+ height: 460,
321
+ transformStyle: "preserve-3d"
171
322
  }
172
323
  },
173
- /* @__PURE__ */ React.createElement("div", { style: { fontSize: 26, fontWeight: 700, lineHeight: 1.2 } }, title),
174
- /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6, fontSize: 15, opacity: 0.92 } }, subtitle)
175
- )
176
- );
324
+ pages.map((page, index) => {
325
+ const isFlipped = index < currentPage;
326
+ const zIndex = pages.length - index;
327
+ return /* @__PURE__ */ React3.createElement(
328
+ "div",
329
+ {
330
+ key: page.id,
331
+ style: {
332
+ position: "absolute",
333
+ inset: 0,
334
+ transformStyle: "preserve-3d",
335
+ transformOrigin: "left center",
336
+ transform: `rotateY(${isFlipped ? -170 : 0}deg)`,
337
+ transition: "transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1)",
338
+ zIndex
339
+ }
340
+ },
341
+ /* @__PURE__ */ React3.createElement(
342
+ "div",
343
+ {
344
+ style: {
345
+ position: "absolute",
346
+ inset: 0,
347
+ backfaceVisibility: "hidden"
348
+ }
349
+ },
350
+ /* @__PURE__ */ React3.createElement(FestivalCardPageRenderer, { page })
351
+ ),
352
+ /* @__PURE__ */ React3.createElement(
353
+ "div",
354
+ {
355
+ style: {
356
+ position: "absolute",
357
+ inset: 0,
358
+ transform: "rotateY(180deg)",
359
+ backfaceVisibility: "hidden",
360
+ borderRadius: 16,
361
+ background: "#0f172a"
362
+ }
363
+ }
364
+ )
365
+ );
366
+ })
367
+ )),
368
+ /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", justifyContent: "center", gap: 12, marginTop: 18 } }, /* @__PURE__ */ React3.createElement(
369
+ "button",
370
+ {
371
+ type: "button",
372
+ disabled: !canPrev,
373
+ onClick: () => setCurrentPage((p) => Math.max(0, p - 1)),
374
+ style: {
375
+ border: "none",
376
+ borderRadius: 999,
377
+ padding: "9px 16px",
378
+ fontSize: 14,
379
+ cursor: canPrev ? "pointer" : "not-allowed",
380
+ opacity: canPrev ? 1 : 0.4
381
+ }
382
+ },
383
+ "\u4E0A\u4E00\u9875"
384
+ ), /* @__PURE__ */ React3.createElement(
385
+ "button",
386
+ {
387
+ type: "button",
388
+ disabled: !canNext,
389
+ onClick: () => setCurrentPage((p) => Math.min(pages.length - 1, p + 1)),
390
+ style: {
391
+ border: "none",
392
+ borderRadius: 999,
393
+ padding: "9px 16px",
394
+ fontSize: 14,
395
+ cursor: canNext ? "pointer" : "not-allowed",
396
+ opacity: canNext ? 1 : 0.4
397
+ }
398
+ },
399
+ "\u4E0B\u4E00\u9875"
400
+ ))
401
+ ), normalized.backgroundMusic?.src ? /* @__PURE__ */ React3.createElement(
402
+ "audio",
403
+ {
404
+ src: normalized.backgroundMusic.src,
405
+ autoPlay: normalized.backgroundMusic.autoPlay,
406
+ loop: normalized.backgroundMusic.loop,
407
+ controls: true,
408
+ style: { width: "100%", marginTop: 10 }
409
+ }
410
+ ) : null);
411
+ };
412
+ var createTextElement = (pageIndex) => ({
413
+ id: `text-${Date.now()}-${pageIndex}`,
414
+ type: "text",
415
+ x: 50,
416
+ y: 50,
417
+ content: "\u8BF7\u8F93\u5165\u6587\u5B57",
418
+ fontSize: 18,
419
+ fontWeight: 500,
420
+ align: "center",
421
+ color: "#ffffff"
422
+ });
423
+ var createImageElement = (pageIndex) => ({
424
+ id: `image-${Date.now()}-${pageIndex}`,
425
+ type: "image",
426
+ x: 50,
427
+ y: 50,
428
+ width: 60,
429
+ height: 40,
430
+ src: "https://images.unsplash.com/photo-1482517967863-00e15c9b44be?auto=format&fit=crop&w=1200&q=80",
431
+ fit: "cover",
432
+ borderRadius: 12
433
+ });
434
+ var FestivalCardConfigEditor = ({ value, onChange }) => {
435
+ const [activePageIndex, setActivePageIndex] = useState(0);
436
+ const page = value.pages[activePageIndex];
437
+ const canEditPage = Boolean(page);
438
+ const pageOptions = useMemo(() => value.pages.map((_, index) => index), [value.pages]);
439
+ const updateElement = (elementId, patch) => {
440
+ onChange({
441
+ ...value,
442
+ pages: value.pages.map(
443
+ (p, pIndex) => pIndex === activePageIndex ? {
444
+ ...p,
445
+ elements: p.elements.map((el) => el.id === elementId ? { ...el, ...patch } : el)
446
+ } : p
447
+ )
448
+ });
449
+ };
450
+ return /* @__PURE__ */ React3.createElement("div", { style: { borderRadius: 16, background: "#0f172a", color: "#e2e8f0", padding: 16 } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gap: 12 } }, /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React3.createElement(
451
+ "input",
452
+ {
453
+ type: "number",
454
+ min: 1,
455
+ max: 12,
456
+ value: value.pages.length,
457
+ onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value)))
458
+ }
459
+ )), /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React3.createElement(
460
+ "input",
461
+ {
462
+ type: "url",
463
+ value: value.backgroundMusic?.src || "",
464
+ onChange: (event) => onChange({
465
+ ...value,
466
+ backgroundMusic: {
467
+ ...value.backgroundMusic,
468
+ src: event.target.value
469
+ }
470
+ })
471
+ }
472
+ )), /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React3.createElement("select", { value: activePageIndex, onChange: (event) => setActivePageIndex(Number(event.target.value)) }, pageOptions.map((index) => /* @__PURE__ */ React3.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))))), canEditPage ? /* @__PURE__ */ React3.createElement("div", { style: { marginTop: 16 } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", gap: 8, marginBottom: 12 } }, /* @__PURE__ */ React3.createElement(
473
+ "button",
474
+ {
475
+ type: "button",
476
+ onClick: () => onChange({
477
+ ...value,
478
+ pages: value.pages.map(
479
+ (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createTextElement(index)] } : p
480
+ )
481
+ })
482
+ },
483
+ "+ \u6587\u5B57"
484
+ ), /* @__PURE__ */ React3.createElement(
485
+ "button",
486
+ {
487
+ type: "button",
488
+ onClick: () => onChange({
489
+ ...value,
490
+ pages: value.pages.map(
491
+ (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createImageElement(index)] } : p
492
+ )
493
+ })
494
+ },
495
+ "+ \u56FE\u7247"
496
+ )), /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gap: 10, maxHeight: 340, overflow: "auto" } }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React3.createElement("div", { key: element.id, style: { border: "1px solid #334155", borderRadius: 10, padding: 10 } }, /* @__PURE__ */ React3.createElement("div", { style: { marginBottom: 8 } }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React3.createElement(
497
+ "textarea",
498
+ {
499
+ value: element.content,
500
+ onChange: (event) => updateElement(element.id, { content: event.target.value }),
501
+ rows: 3,
502
+ style: { width: "100%" }
503
+ }
504
+ ) : /* @__PURE__ */ React3.createElement(
505
+ "input",
506
+ {
507
+ type: "url",
508
+ value: element.src,
509
+ onChange: (event) => updateElement(element.id, { src: event.target.value }),
510
+ style: { width: "100%" }
511
+ }
512
+ ))))) : null);
513
+ };
514
+ var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
515
+ const { config, setConfig, loading, save, saving } = useFestivalCardConfig({
516
+ initialConfig: normalizeFestivalCardConfig(initialConfig),
517
+ fetchConfig,
518
+ onSave
519
+ });
520
+ if (loading) return /* @__PURE__ */ React3.createElement("div", null, "\u52A0\u8F7D\u4E2D...");
521
+ return /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 16 } }, /* @__PURE__ */ React3.createElement(FestivalCardBook3D, { config }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React3.createElement(
522
+ "button",
523
+ {
524
+ type: "button",
525
+ onClick: () => void save(),
526
+ disabled: saving,
527
+ style: { marginTop: 12, width: "100%", padding: "10px 16px" }
528
+ },
529
+ saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"
530
+ ) : null));
177
531
  };
178
532
 
179
- export { FestivalCard3D };
533
+ // src/festivalCard/server/db.ts
534
+ var dbAdapter = null;
535
+ var getFestivalCardDb = () => dbAdapter;
536
+
537
+ // src/festivalCard/services/festivalCardService.ts
538
+ var FestivalCardService = class {
539
+ constructor(options) {
540
+ this.db = options?.db || getFestivalCardDb();
541
+ }
542
+ async getConfig(cardId = "default-festival-card") {
543
+ if (!this.db) return DEFAULT_FESTIVAL_CARD_CONFIG;
544
+ const config = await this.db.getConfig(cardId);
545
+ return normalizeFestivalCardConfig(config);
546
+ }
547
+ async saveConfig(cardId, config) {
548
+ const normalized = normalizeFestivalCardConfig(config);
549
+ if (!this.db) return normalized;
550
+ await this.db.saveConfig(cardId, normalized);
551
+ return normalized;
552
+ }
553
+ };
554
+ var memoryStore = /* @__PURE__ */ new Map();
555
+ var createInMemoryFestivalCardDb = () => ({
556
+ getConfig(id) {
557
+ return Promise.resolve(memoryStore.get(id) || null);
558
+ },
559
+ saveConfig(id, config) {
560
+ memoryStore.set(id, config);
561
+ return Promise.resolve();
562
+ }
563
+ });
564
+
565
+ export { DEFAULT_FESTIVAL_CARD_CONFIG, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, createInMemoryFestivalCardDb, normalizeFestivalCardConfig, resizeFestivalCardPages, useFestivalCardConfig };
180
566
  //# sourceMappingURL=index.mjs.map
181
567
  //# sourceMappingURL=index.mjs.map