sa2kit 1.6.69 → 1.6.72

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 (43) hide show
  1. package/dist/festivalCard/core/index.d.mts +1 -1
  2. package/dist/festivalCard/core/index.d.ts +1 -1
  3. package/dist/festivalCard/core/index.js +4 -2
  4. package/dist/festivalCard/core/index.js.map +1 -1
  5. package/dist/festivalCard/core/index.mjs +4 -2
  6. package/dist/festivalCard/core/index.mjs.map +1 -1
  7. package/dist/festivalCard/index.d.mts +3 -3
  8. package/dist/festivalCard/index.d.ts +3 -3
  9. package/dist/festivalCard/index.js +220 -119
  10. package/dist/festivalCard/index.js.map +1 -1
  11. package/dist/festivalCard/index.mjs +219 -120
  12. package/dist/festivalCard/index.mjs.map +1 -1
  13. package/dist/festivalCard/miniapp/index.d.mts +3 -3
  14. package/dist/festivalCard/miniapp/index.d.ts +3 -3
  15. package/dist/festivalCard/miniapp/index.js +51 -15
  16. package/dist/festivalCard/miniapp/index.js.map +1 -1
  17. package/dist/festivalCard/miniapp/index.mjs +51 -15
  18. package/dist/festivalCard/miniapp/index.mjs.map +1 -1
  19. package/dist/festivalCard/routes/index.d.mts +44 -0
  20. package/dist/festivalCard/routes/index.d.ts +44 -0
  21. package/dist/festivalCard/routes/index.js +268 -0
  22. package/dist/festivalCard/routes/index.js.map +1 -0
  23. package/dist/festivalCard/routes/index.mjs +263 -0
  24. package/dist/festivalCard/routes/index.mjs.map +1 -0
  25. package/dist/festivalCard/server/index.d.mts +1 -1
  26. package/dist/festivalCard/server/index.d.ts +1 -1
  27. package/dist/festivalCard/web/index.d.mts +18 -4
  28. package/dist/festivalCard/web/index.d.ts +18 -4
  29. package/dist/festivalCard/web/index.js +220 -119
  30. package/dist/festivalCard/web/index.js.map +1 -1
  31. package/dist/festivalCard/web/index.mjs +219 -120
  32. package/dist/festivalCard/web/index.mjs.map +1 -1
  33. package/dist/{festivalCardService-uSg0oNuD.d.mts → festivalCardService-CqBTVC6x.d.mts} +3 -1
  34. package/dist/{festivalCardService-C0fhTezx.d.ts → festivalCardService-iFKu0k9q.d.ts} +3 -1
  35. package/dist/index.d.mts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.js +220 -119
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.mjs +219 -120
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/{types-B-tjQTGi.d.mts → types-D2WetAPc.d.mts} +8 -1
  42. package/dist/{types-B-tjQTGi.d.ts → types-D2WetAPc.d.ts} +8 -1
  43. package/package.json +6 -1
@@ -61,7 +61,8 @@ var DEFAULT_FESTIVAL_CARD_CONFIG = {
61
61
  src: "https://images.unsplash.com/photo-1512389142860-9c449e58a543?auto=format&fit=crop&w=1200&q=80",
62
62
  fit: "cover",
63
63
  borderRadius: 16,
64
- alt: "holiday"
64
+ alt: "holiday",
65
+ isBackground: false
65
66
  }
66
67
  ]
67
68
  },
@@ -120,7 +121,8 @@ var DEFAULT_FESTIVAL_CARD_CONFIG = {
120
121
  src: "https://images.unsplash.com/photo-1456324504439-367cee3b3c32?auto=format&fit=crop&w=1200&q=80",
121
122
  fit: "cover",
122
123
  borderRadius: 14,
123
- alt: "gift"
124
+ alt: "gift",
125
+ isBackground: false
124
126
  },
125
127
  {
126
128
  id: "p3-text-1",
@@ -186,9 +188,10 @@ var useFestivalCardConfig = (options) => {
186
188
  const [loading, setLoading] = React3.useState(Boolean(options?.fetchConfig));
187
189
  const [saving, setSaving] = React3.useState(false);
188
190
  React3.useEffect(() => {
189
- if (!options?.fetchConfig) return;
191
+ const fetchConfig = options?.fetchConfig;
192
+ if (!fetchConfig) return;
190
193
  let active = true;
191
- void options.fetchConfig().then((value) => {
194
+ void fetchConfig().then((value) => {
192
195
  if (!active) return;
193
196
  setConfig(normalizeFestivalCardConfig(value));
194
197
  }).finally(() => {
@@ -197,16 +200,17 @@ var useFestivalCardConfig = (options) => {
197
200
  return () => {
198
201
  active = false;
199
202
  };
200
- }, [options]);
203
+ }, [options?.fetchConfig]);
201
204
  const save = React3.useCallback(async () => {
202
- if (!options?.onSave) return;
205
+ const onSave = options?.onSave;
206
+ if (!onSave) return;
203
207
  setSaving(true);
204
208
  try {
205
- await options.onSave(config);
209
+ await onSave(config);
206
210
  } finally {
207
211
  setSaving(false);
208
212
  }
209
- }, [config, options]);
213
+ }, [config, options?.onSave]);
210
214
  return React3.useMemo(
211
215
  () => ({
212
216
  config,
@@ -220,6 +224,7 @@ var useFestivalCardConfig = (options) => {
220
224
  };
221
225
  var elementStyle = (element) => ({
222
226
  position: "absolute",
227
+ zIndex: 2,
223
228
  left: `${element.x}%`,
224
229
  top: `${element.y}%`,
225
230
  width: `${element.width ?? 70}%`,
@@ -262,22 +267,23 @@ var renderElement = (element) => {
262
267
  );
263
268
  };
264
269
  var FestivalCardPageRenderer = ({ page }) => {
270
+ const backgroundElement = page.elements.find(
271
+ (element) => element.type === "image" && Boolean(element.isBackground)
272
+ );
273
+ const foregroundElements = page.elements.filter((element) => !(element.type === "image" && element.isBackground));
265
274
  return /* @__PURE__ */ React3__default.default.createElement(
266
275
  "div",
267
276
  {
277
+ className: "relative h-full w-full overflow-hidden rounded-2xl",
268
278
  style: {
269
- position: "relative",
270
- width: "100%",
271
- height: "100%",
272
- overflow: "hidden",
273
- borderRadius: 16,
274
279
  backgroundColor: page.background?.color || "#0f172a",
275
- backgroundImage: page.background?.image ? `url(${page.background.image})` : void 0,
280
+ backgroundImage: backgroundElement ? `url(${backgroundElement.src})` : page.background?.image ? `url(${page.background.image})` : void 0,
276
281
  backgroundSize: "cover",
277
282
  backgroundPosition: "center"
278
283
  }
279
284
  },
280
- page.elements.map(renderElement)
285
+ /* @__PURE__ */ React3__default.default.createElement("div", { className: "absolute inset-0 bg-slate-950/20" }),
286
+ foregroundElements.map(renderElement)
281
287
  );
282
288
  };
283
289
 
@@ -291,100 +297,31 @@ var FestivalCardBook3D = ({ config, className }) => {
291
297
  return /* @__PURE__ */ React3__default.default.createElement("div", { className }, /* @__PURE__ */ React3__default.default.createElement(
292
298
  "div",
293
299
  {
300
+ className: "w-full min-h-[560px] rounded-3xl p-6 shadow-2xl shadow-slate-900/50",
294
301
  style: {
295
- width: "100%",
296
- minHeight: 560,
297
- borderRadius: 24,
298
- padding: 24,
299
- background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`,
300
- boxShadow: "0 26px 70px rgba(2, 6, 23, 0.45)"
302
+ background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`
301
303
  }
302
304
  },
303
- /* @__PURE__ */ React3__default.default.createElement(
304
- "div",
305
- {
306
- style: {
307
- marginBottom: 14,
308
- color: "#f8fafc",
309
- fontSize: 14,
310
- opacity: 0.9,
311
- textAlign: "center"
312
- }
313
- },
314
- normalized.coverTitle || "Festival Card",
315
- " \xB7 \u7B2C ",
316
- currentPage + 1,
317
- " / ",
318
- pages.length,
319
- " \u9875"
320
- ),
321
- /* @__PURE__ */ React3__default.default.createElement("div", { style: { perspective: 1400, width: "100%", maxWidth: 920, margin: "0 auto" } }, /* @__PURE__ */ React3__default.default.createElement(
305
+ /* @__PURE__ */ React3__default.default.createElement("div", { className: "mx-auto w-full max-w-5xl text-center text-slate-100" }, /* @__PURE__ */ React3__default.default.createElement("h3", { className: "mb-4 text-lg font-semibold" }, normalized.coverTitle || "Festival Card")),
306
+ /* @__PURE__ */ React3__default.default.createElement("div", { className: "mx-auto w-full max-w-5xl" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "relative h-[460px]" }, pages.map((page, index) => /* @__PURE__ */ React3__default.default.createElement(
322
307
  "div",
323
308
  {
309
+ key: page.id,
310
+ className: "absolute inset-0 transition-opacity duration-500 ease-out",
324
311
  style: {
325
- position: "relative",
326
- height: 460,
327
- transformStyle: "preserve-3d"
312
+ opacity: index === currentPage ? 1 : 0,
313
+ pointerEvents: index === currentPage ? "auto" : "none"
328
314
  }
329
315
  },
330
- pages.map((page, index) => {
331
- const isFlipped = index < currentPage;
332
- const zIndex = pages.length - index;
333
- return /* @__PURE__ */ React3__default.default.createElement(
334
- "div",
335
- {
336
- key: page.id,
337
- style: {
338
- position: "absolute",
339
- inset: 0,
340
- transformStyle: "preserve-3d",
341
- transformOrigin: "left center",
342
- transform: `rotateY(${isFlipped ? -170 : 0}deg)`,
343
- transition: "transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1)",
344
- zIndex
345
- }
346
- },
347
- /* @__PURE__ */ React3__default.default.createElement(
348
- "div",
349
- {
350
- style: {
351
- position: "absolute",
352
- inset: 0,
353
- backfaceVisibility: "hidden"
354
- }
355
- },
356
- /* @__PURE__ */ React3__default.default.createElement(FestivalCardPageRenderer, { page })
357
- ),
358
- /* @__PURE__ */ React3__default.default.createElement(
359
- "div",
360
- {
361
- style: {
362
- position: "absolute",
363
- inset: 0,
364
- transform: "rotateY(180deg)",
365
- backfaceVisibility: "hidden",
366
- borderRadius: 16,
367
- background: "#0f172a"
368
- }
369
- }
370
- )
371
- );
372
- })
373
- )),
374
- /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "flex", justifyContent: "center", gap: 12, marginTop: 18 } }, /* @__PURE__ */ React3__default.default.createElement(
316
+ /* @__PURE__ */ React3__default.default.createElement(FestivalCardPageRenderer, { page })
317
+ )))),
318
+ /* @__PURE__ */ React3__default.default.createElement("div", { className: "mt-5 flex justify-center gap-3" }, /* @__PURE__ */ React3__default.default.createElement(
375
319
  "button",
376
320
  {
377
321
  type: "button",
378
322
  disabled: !canPrev,
379
323
  onClick: () => setCurrentPage((p) => Math.max(0, p - 1)),
380
- style: {
381
- border: "none",
382
- borderRadius: 999,
383
- padding: "9px 16px",
384
- fontSize: 14,
385
- cursor: canPrev ? "pointer" : "not-allowed",
386
- opacity: canPrev ? 1 : 0.4
387
- }
324
+ className: "rounded-full bg-white px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
388
325
  },
389
326
  "\u4E0A\u4E00\u9875"
390
327
  ), /* @__PURE__ */ React3__default.default.createElement(
@@ -393,14 +330,7 @@ var FestivalCardBook3D = ({ config, className }) => {
393
330
  type: "button",
394
331
  disabled: !canNext,
395
332
  onClick: () => setCurrentPage((p) => Math.min(pages.length - 1, p + 1)),
396
- style: {
397
- border: "none",
398
- borderRadius: 999,
399
- padding: "9px 16px",
400
- fontSize: 14,
401
- cursor: canNext ? "pointer" : "not-allowed",
402
- opacity: canNext ? 1 : 0.4
403
- }
333
+ className: "rounded-full bg-sky-300 px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
404
334
  },
405
335
  "\u4E0B\u4E00\u9875"
406
336
  ))
@@ -411,7 +341,7 @@ var FestivalCardBook3D = ({ config, className }) => {
411
341
  autoPlay: normalized.backgroundMusic.autoPlay,
412
342
  loop: normalized.backgroundMusic.loop,
413
343
  controls: true,
414
- style: { width: "100%", marginTop: 10 }
344
+ className: "mt-3 w-full"
415
345
  }
416
346
  ) : null);
417
347
  };
@@ -453,16 +383,17 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
453
383
  )
454
384
  });
455
385
  };
456
- return /* @__PURE__ */ React3__default.default.createElement("div", { style: { borderRadius: 16, background: "#0f172a", color: "#e2e8f0", padding: 16 } }, /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "grid", gap: 12 } }, /* @__PURE__ */ React3__default.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3__default.default.createElement("span", null, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React3__default.default.createElement(
386
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "rounded-2xl border border-slate-200 bg-white p-4 text-slate-900 shadow-sm" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React3__default.default.createElement(
457
387
  "input",
458
388
  {
459
389
  type: "number",
460
390
  min: 1,
461
391
  max: 12,
462
392
  value: value.pages.length,
463
- onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value)))
393
+ onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value))),
394
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
464
395
  }
465
- )), /* @__PURE__ */ React3__default.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3__default.default.createElement("span", null, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React3__default.default.createElement(
396
+ )), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React3__default.default.createElement(
466
397
  "input",
467
398
  {
468
399
  type: "url",
@@ -473,9 +404,18 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
473
404
  ...value.backgroundMusic,
474
405
  src: event.target.value
475
406
  }
476
- })
407
+ }),
408
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
477
409
  }
478
- )), /* @__PURE__ */ React3__default.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3__default.default.createElement("span", null, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React3__default.default.createElement("select", { value: activePageIndex, onChange: (event) => setActivePageIndex(Number(event.target.value)) }, pageOptions.map((index) => /* @__PURE__ */ React3__default.default.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))))), canEditPage ? /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginTop: 16 } }, /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "flex", gap: 8, marginBottom: 12 } }, /* @__PURE__ */ React3__default.default.createElement(
410
+ )), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React3__default.default.createElement(
411
+ "select",
412
+ {
413
+ value: activePageIndex,
414
+ onChange: (event) => setActivePageIndex(Number(event.target.value)),
415
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
416
+ },
417
+ pageOptions.map((index) => /* @__PURE__ */ React3__default.default.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))
418
+ ))), canEditPage ? /* @__PURE__ */ React3__default.default.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
479
419
  "button",
480
420
  {
481
421
  type: "button",
@@ -484,7 +424,8 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
484
424
  pages: value.pages.map(
485
425
  (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createTextElement(index)] } : p
486
426
  )
487
- })
427
+ }),
428
+ className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white"
488
429
  },
489
430
  "+ \u6587\u5B57"
490
431
  ), /* @__PURE__ */ React3__default.default.createElement(
@@ -496,27 +437,38 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
496
437
  pages: value.pages.map(
497
438
  (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createImageElement(index)] } : p
498
439
  )
499
- })
440
+ }),
441
+ className: "rounded-lg bg-sky-600 px-3 py-2 text-sm font-medium text-white"
500
442
  },
501
443
  "+ \u56FE\u7247"
502
- )), /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "grid", gap: 10, maxHeight: 340, overflow: "auto" } }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React3__default.default.createElement("div", { key: element.id, style: { border: "1px solid #334155", borderRadius: 10, padding: 10 } }, /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginBottom: 8 } }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React3__default.default.createElement(
444
+ )), /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid max-h-[340px] gap-2.5 overflow-auto pr-1" }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React3__default.default.createElement("div", { key: element.id, className: "rounded-xl border border-slate-200 bg-slate-50 p-3" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "mb-2 text-xs font-semibold tracking-wide text-slate-500" }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React3__default.default.createElement(
503
445
  "textarea",
504
446
  {
505
447
  value: element.content,
506
448
  onChange: (event) => updateElement(element.id, { content: event.target.value }),
507
449
  rows: 3,
508
- style: { width: "100%" }
450
+ className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
509
451
  }
510
- ) : /* @__PURE__ */ React3__default.default.createElement(
452
+ ) : /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
511
453
  "input",
512
454
  {
513
455
  type: "url",
514
456
  value: element.src,
515
457
  onChange: (event) => updateElement(element.id, { src: event.target.value }),
516
- style: { width: "100%" }
458
+ className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
459
+ }
460
+ ), /* @__PURE__ */ React3__default.default.createElement("label", { className: "inline-flex items-center gap-2 text-sm text-slate-700" }, /* @__PURE__ */ React3__default.default.createElement(
461
+ "input",
462
+ {
463
+ type: "checkbox",
464
+ checked: Boolean(element.isBackground),
465
+ onChange: (event) => updateElement(element.id, { isBackground: event.target.checked }),
466
+ className: "h-4 w-4 rounded border-slate-300 text-sky-600"
517
467
  }
518
- ))))) : null);
468
+ ), "\u4F5C\u4E3A\u672C\u9875\u80CC\u666F\u56FE")))))) : null);
519
469
  };
470
+
471
+ // src/festivalCard/components/FestivalCardStudio.tsx
520
472
  var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
521
473
  const { config, setConfig, loading, save, saving } = useFestivalCardConfig({
522
474
  initialConfig: normalizeFestivalCardConfig(initialConfig),
@@ -524,18 +476,135 @@ var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
524
476
  onSave
525
477
  });
526
478
  if (loading) return /* @__PURE__ */ React3__default.default.createElement("div", null, "\u52A0\u8F7D\u4E2D...");
527
- return /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 16 } }, /* @__PURE__ */ React3__default.default.createElement(FestivalCardBook3D, { config }), /* @__PURE__ */ React3__default.default.createElement("div", null, /* @__PURE__ */ React3__default.default.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React3__default.default.createElement(
479
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-4 lg:grid-cols-[1.45fr_1fr]" }, /* @__PURE__ */ React3__default.default.createElement(FestivalCardBook3D, { config, className: "h-full" }), /* @__PURE__ */ React3__default.default.createElement("div", null, /* @__PURE__ */ React3__default.default.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React3__default.default.createElement(
528
480
  "button",
529
481
  {
530
482
  type: "button",
531
483
  onClick: () => void save(),
532
484
  disabled: saving,
533
- style: { marginTop: 12, width: "100%", padding: "10px 16px" }
485
+ className: "mt-3 w-full rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-medium text-white disabled:opacity-60"
534
486
  },
535
487
  saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"
536
488
  ) : null));
537
489
  };
538
490
 
491
+ // src/festivalCard/components/FestivalCardConfigPage.tsx
492
+ var FestivalCardConfigPage = ({
493
+ apiBase = "/api/festivalCard",
494
+ cardId,
495
+ mainPagePath = "/festivalCard"
496
+ }) => {
497
+ const [list, setList] = React3.useState([]);
498
+ const [selectedId, setSelectedId] = React3.useState(cardId || "default-festival-card");
499
+ const parseListResponse2 = (data) => {
500
+ if (!data || typeof data !== "object") return [];
501
+ const payload = data.data;
502
+ if (!Array.isArray(payload)) return [];
503
+ return payload.filter((item) => Boolean(item && typeof item === "object" && typeof item.id === "string")).map((item) => ({ id: item.id, name: item.name }));
504
+ };
505
+ const parseConfigResponse2 = (data) => {
506
+ if (!data || typeof data !== "object") return normalizeFestivalCardConfig();
507
+ const payload = data.data;
508
+ if (!payload || typeof payload !== "object") return normalizeFestivalCardConfig();
509
+ return normalizeFestivalCardConfig(payload);
510
+ };
511
+ const reloadList = React3.useCallback(async () => {
512
+ const response = await fetch(apiBase, { cache: "no-store" });
513
+ const data = await response.json();
514
+ setList(parseListResponse2(data));
515
+ }, [apiBase]);
516
+ React3.useEffect(() => {
517
+ void reloadList();
518
+ }, [reloadList]);
519
+ const fetchConfig = async () => {
520
+ const response = await fetch(`${apiBase}/${encodeURIComponent(selectedId)}`, { cache: "no-store" });
521
+ const data = await response.json();
522
+ return parseConfigResponse2(data);
523
+ };
524
+ const saveConfig = async (config) => {
525
+ await fetch(`${apiBase}/${encodeURIComponent(selectedId)}`, {
526
+ method: "PUT",
527
+ headers: { "Content-Type": "application/json" },
528
+ body: JSON.stringify({ config })
529
+ });
530
+ await reloadList();
531
+ };
532
+ const createNew = async () => {
533
+ const name = window.prompt("\u8BF7\u8F93\u5165\u65B0\u5361\u7247\u540D\u79F0");
534
+ if (!name) return;
535
+ const id = `festival-${Date.now()}`;
536
+ const config = normalizeFestivalCardConfig({
537
+ id,
538
+ name
539
+ });
540
+ await fetch(`${apiBase}/${encodeURIComponent(id)}`, {
541
+ method: "PUT",
542
+ headers: { "Content-Type": "application/json" },
543
+ body: JSON.stringify({ config })
544
+ });
545
+ setSelectedId(id);
546
+ await reloadList();
547
+ };
548
+ const mainLink = React3.useMemo(() => `${mainPagePath}?cardId=${encodeURIComponent(selectedId)}`, [mainPagePath, selectedId]);
549
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
550
+ "select",
551
+ {
552
+ value: selectedId,
553
+ onChange: (event) => setSelectedId(event.target.value),
554
+ className: "min-w-[200px] rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
555
+ },
556
+ list.map((item) => /* @__PURE__ */ React3__default.default.createElement("option", { key: item.id, value: item.id }, item.name || item.id))
557
+ ), /* @__PURE__ */ React3__default.default.createElement("button", { type: "button", onClick: () => void createNew(), className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white" }, "\u65B0\u5EFA\u5361\u7247"), /* @__PURE__ */ React3__default.default.createElement("a", { href: mainLink, className: "rounded-lg border border-sky-200 bg-sky-50 px-3 py-2 text-sm text-sky-700" }, "\u6253\u5F00\u4E3B\u9875\u9762")), /* @__PURE__ */ React3__default.default.createElement(FestivalCardStudio, { fetchConfig, onSave: saveConfig }));
558
+ };
559
+ var isSummary = (value) => {
560
+ if (!value || typeof value !== "object") return false;
561
+ return typeof value.id === "string";
562
+ };
563
+ var parseListResponse = (data) => {
564
+ if (!data || typeof data !== "object") return [];
565
+ const payload = data.data;
566
+ if (!Array.isArray(payload)) return [];
567
+ return payload.filter(isSummary).map((item) => ({ id: item.id, name: item.name }));
568
+ };
569
+ var parseConfigResponse = (data) => {
570
+ if (!data || typeof data !== "object") return null;
571
+ const payload = data.data;
572
+ if (!payload || typeof payload !== "object") return null;
573
+ if (!Array.isArray(payload.pages)) return null;
574
+ return payload;
575
+ };
576
+ var FestivalCardManagedPage = ({
577
+ apiBase = "/api/festivalCard",
578
+ cardId,
579
+ configPagePath = "/festivalCard/config"
580
+ }) => {
581
+ const [currentCardId, setCurrentCardId] = React3.useState(cardId || "");
582
+ const [config, setConfig] = React3.useState(null);
583
+ const [loading, setLoading] = React3.useState(true);
584
+ React3.useEffect(() => {
585
+ const fetchList = async () => {
586
+ const response = await fetch(apiBase, { cache: "no-store" });
587
+ const data = await response.json();
588
+ const items = parseListResponse(data);
589
+ const first = items[0];
590
+ if (!currentCardId && first) {
591
+ setCurrentCardId(first.id);
592
+ }
593
+ };
594
+ void fetchList();
595
+ }, [apiBase, currentCardId]);
596
+ React3.useEffect(() => {
597
+ if (!currentCardId) return;
598
+ setLoading(true);
599
+ void fetch(`${apiBase}/${encodeURIComponent(currentCardId)}`, { cache: "no-store" }).then((res) => res.json()).then((data) => setConfig(parseConfigResponse(data))).finally(() => setLoading(false));
600
+ }, [apiBase, currentCardId]);
601
+ const configLink = React3.useMemo(() => {
602
+ if (!currentCardId) return configPagePath;
603
+ return `${configPagePath}?cardId=${encodeURIComponent(currentCardId)}`;
604
+ }, [configPagePath, currentCardId]);
605
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "flex justify-end" }, /* @__PURE__ */ React3__default.default.createElement("a", { href: configLink, className: "rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 text-sm text-sky-300" }, "\u8FDB\u5165\u914D\u7F6E\u9875")), /* @__PURE__ */ React3__default.default.createElement("div", null, loading || !config ? /* @__PURE__ */ React3__default.default.createElement("div", { className: "py-12 text-center text-slate-400" }, "\u52A0\u8F7D\u4E2D...") : /* @__PURE__ */ React3__default.default.createElement(FestivalCardBook3D, { config })));
606
+ };
607
+
539
608
  // src/festivalCard/server/db.ts
540
609
  var dbAdapter = null;
541
610
  var getFestivalCardDb = () => dbAdapter;
@@ -545,6 +614,16 @@ var FestivalCardService = class {
545
614
  constructor(options) {
546
615
  this.db = options?.db || getFestivalCardDb();
547
616
  }
617
+ async listConfigs() {
618
+ if (!this.db) {
619
+ return [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
620
+ }
621
+ if (this.db.listConfigs) {
622
+ const list = await this.db.listConfigs();
623
+ return list.length > 0 ? list : [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
624
+ }
625
+ return [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
626
+ }
548
627
  async getConfig(cardId = "default-festival-card") {
549
628
  if (!this.db) return DEFAULT_FESTIVAL_CARD_CONFIG;
550
629
  const config = await this.db.getConfig(cardId);
@@ -556,21 +635,43 @@ var FestivalCardService = class {
556
635
  await this.db.saveConfig(cardId, normalized);
557
636
  return normalized;
558
637
  }
638
+ async deleteConfig(cardId) {
639
+ if (!this.db?.deleteConfig) return;
640
+ await this.db.deleteConfig(cardId);
641
+ }
559
642
  };
560
643
  var memoryStore = /* @__PURE__ */ new Map();
644
+ var defaultId = DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card";
645
+ if (!memoryStore.has(defaultId)) {
646
+ memoryStore.set(defaultId, DEFAULT_FESTIVAL_CARD_CONFIG);
647
+ }
561
648
  var createInMemoryFestivalCardDb = () => ({
649
+ listConfigs() {
650
+ return Promise.resolve(
651
+ Array.from(memoryStore.entries()).map(([id, config]) => ({
652
+ id,
653
+ name: config.name || id
654
+ }))
655
+ );
656
+ },
562
657
  getConfig(id) {
563
658
  return Promise.resolve(memoryStore.get(id) || null);
564
659
  },
565
660
  saveConfig(id, config) {
566
661
  memoryStore.set(id, config);
567
662
  return Promise.resolve();
663
+ },
664
+ deleteConfig(id) {
665
+ memoryStore.delete(id);
666
+ return Promise.resolve();
568
667
  }
569
668
  });
570
669
 
571
670
  exports.DEFAULT_FESTIVAL_CARD_CONFIG = DEFAULT_FESTIVAL_CARD_CONFIG;
572
671
  exports.FestivalCardBook3D = FestivalCardBook3D;
573
672
  exports.FestivalCardConfigEditor = FestivalCardConfigEditor;
673
+ exports.FestivalCardConfigPage = FestivalCardConfigPage;
674
+ exports.FestivalCardManagedPage = FestivalCardManagedPage;
574
675
  exports.FestivalCardPageRenderer = FestivalCardPageRenderer;
575
676
  exports.FestivalCardService = FestivalCardService;
576
677
  exports.FestivalCardStudio = FestivalCardStudio;