sa2kit 1.6.68 → 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
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ var drizzleOrm = require('drizzle-orm');
17
17
  require('crypto');
18
18
  require('bcryptjs');
19
19
  require('jsonwebtoken');
20
- var THREE3 = require('three');
20
+ var THREE2 = require('three');
21
21
 
22
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
23
23
 
@@ -41,7 +41,7 @@ function _interopNamespace(e) {
41
41
 
42
42
  var React42__namespace = /*#__PURE__*/_interopNamespace(React42);
43
43
  var Link__default = /*#__PURE__*/_interopDefault(Link);
44
- var THREE3__namespace = /*#__PURE__*/_interopNamespace(THREE3);
44
+ var THREE2__namespace = /*#__PURE__*/_interopNamespace(THREE2);
45
45
 
46
46
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
47
47
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -5864,7 +5864,7 @@ function createCircularSpriteTexture() {
5864
5864
  canvas.height = size;
5865
5865
  const ctx = canvas.getContext("2d");
5866
5866
  if (!ctx) {
5867
- return new THREE3__namespace.CanvasTexture(canvas);
5867
+ return new THREE2__namespace.CanvasTexture(canvas);
5868
5868
  }
5869
5869
  const center = size / 2;
5870
5870
  const gradient = ctx.createRadialGradient(center, center, 2, center, center, center);
@@ -5873,7 +5873,7 @@ function createCircularSpriteTexture() {
5873
5873
  gradient.addColorStop(1, "rgba(255,255,255,0)");
5874
5874
  ctx.fillStyle = gradient;
5875
5875
  ctx.fillRect(0, 0, size, size);
5876
- const texture = new THREE3__namespace.CanvasTexture(canvas);
5876
+ const texture = new THREE2__namespace.CanvasTexture(canvas);
5877
5877
  texture.needsUpdate = true;
5878
5878
  return texture;
5879
5879
  }
@@ -6080,11 +6080,11 @@ var FireworksEngine = class {
6080
6080
  this.maxActiveFireworks = init.options?.maxActiveFireworks ?? DEFAULT_MAX_ACTIVE_FIREWORKS;
6081
6081
  this.onError = init.options?.onError;
6082
6082
  this.onFpsReport = init.options?.onFpsReport;
6083
- this.scene = new THREE3__namespace.Scene();
6084
- this.scene.background = new THREE3__namespace.Color("#060816");
6085
- this.camera = new THREE3__namespace.PerspectiveCamera(50, 1, 0.1, 1e3);
6083
+ this.scene = new THREE2__namespace.Scene();
6084
+ this.scene.background = new THREE2__namespace.Color("#060816");
6085
+ this.camera = new THREE2__namespace.PerspectiveCamera(50, 1, 0.1, 1e3);
6086
6086
  this.camera.position.set(0, 0, 45);
6087
- this.renderer = new THREE3__namespace.WebGLRenderer({
6087
+ this.renderer = new THREE2__namespace.WebGLRenderer({
6088
6088
  canvas: this.canvas,
6089
6089
  alpha: true,
6090
6090
  antialias: true,
@@ -6129,7 +6129,7 @@ var FireworksEngine = class {
6129
6129
  const particles = [];
6130
6130
  const positions = new Float32Array(seeds.length * 3);
6131
6131
  const colors = new Float32Array(seeds.length * 3);
6132
- const colorHelper = new THREE3__namespace.Color();
6132
+ const colorHelper = new THREE2__namespace.Color();
6133
6133
  for (let i = 0; i < seeds.length; i += 1) {
6134
6134
  const seed = seeds[i];
6135
6135
  if (!seed) {
@@ -6161,19 +6161,19 @@ var FireworksEngine = class {
6161
6161
  this.releaseParticles(particles);
6162
6162
  return;
6163
6163
  }
6164
- const geometry = new THREE3__namespace.BufferGeometry();
6165
- geometry.setAttribute("position", new THREE3__namespace.BufferAttribute(positions, 3));
6166
- geometry.setAttribute("color", new THREE3__namespace.BufferAttribute(colors, 3));
6167
- const material = new THREE3__namespace.PointsMaterial({
6164
+ const geometry = new THREE2__namespace.BufferGeometry();
6165
+ geometry.setAttribute("position", new THREE2__namespace.BufferAttribute(positions, 3));
6166
+ geometry.setAttribute("color", new THREE2__namespace.BufferAttribute(colors, 3));
6167
+ const material = new THREE2__namespace.PointsMaterial({
6168
6168
  size: payload.kind === "miku" ? 0.42 : 0.36,
6169
6169
  vertexColors: true,
6170
6170
  map: this.spriteTexture,
6171
6171
  transparent: true,
6172
6172
  opacity: 1,
6173
6173
  depthWrite: false,
6174
- blending: THREE3__namespace.AdditiveBlending
6174
+ blending: THREE2__namespace.AdditiveBlending
6175
6175
  });
6176
- const points = new THREE3__namespace.Points(geometry, material);
6176
+ const points = new THREE2__namespace.Points(geometry, material);
6177
6177
  this.scene.add(points);
6178
6178
  const burst = {
6179
6179
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
@@ -7154,179 +7154,568 @@ function ScreenReceiverPanel(props) {
7154
7154
  function StatusItem({ label, value }) {
7155
7155
  return /* @__PURE__ */ React42__namespace.default.createElement("div", { className: "rounded-md border px-3 py-2" }, /* @__PURE__ */ React42__namespace.default.createElement("p", { className: "text-xs text-slate-500" }, label), /* @__PURE__ */ React42__namespace.default.createElement("p", { className: "text-sm font-medium" }, value));
7156
7156
  }
7157
- var createSnow = (count) => {
7158
- const positions = new Float32Array(count * 3);
7159
- const speeds = new Float32Array(count);
7160
- for (let i = 0; i < count; i++) {
7161
- const i3 = i * 3;
7162
- positions[i3] = (Math.random() - 0.5) * 16;
7163
- positions[i3 + 1] = Math.random() * 10 + 1;
7164
- positions[i3 + 2] = (Math.random() - 0.5) * 16;
7165
- speeds[i] = 4e-3 + Math.random() * 0.01;
7166
- }
7167
- return { positions, speeds };
7157
+
7158
+ // src/festivalCard/core/defaults.ts
7159
+ var DEFAULT_FESTIVAL_CARD_CONFIG = {
7160
+ id: "default-festival-card",
7161
+ name: "Holiday Card",
7162
+ theme: "winter",
7163
+ coverTitle: "Happy Holidays",
7164
+ coverSubtitle: "Warm wishes for you",
7165
+ background: {
7166
+ colorA: "#0c1a34",
7167
+ colorB: "#1f4f8a"
7168
+ },
7169
+ backgroundMusic: {
7170
+ src: "",
7171
+ loop: true,
7172
+ autoPlay: false,
7173
+ volume: 0.5
7174
+ },
7175
+ pages: [
7176
+ {
7177
+ id: "page-1",
7178
+ title: "\u5C01\u9762",
7179
+ background: { color: "#11284d" },
7180
+ elements: [
7181
+ {
7182
+ id: "p1-text-1",
7183
+ type: "text",
7184
+ x: 50,
7185
+ y: 20,
7186
+ content: "\u65B0\u5E74\u5FEB\u4E50",
7187
+ fontSize: 34,
7188
+ fontWeight: 700,
7189
+ align: "center",
7190
+ color: "#f8fafc"
7191
+ },
7192
+ {
7193
+ id: "p1-text-2",
7194
+ type: "text",
7195
+ x: 50,
7196
+ y: 36,
7197
+ content: "\u613F\u8FD9\u4E00\u5E74\u5E73\u5B89\u559C\u4E50",
7198
+ fontSize: 18,
7199
+ fontWeight: 500,
7200
+ align: "center",
7201
+ color: "#dbeafe"
7202
+ },
7203
+ {
7204
+ id: "p1-image-1",
7205
+ type: "image",
7206
+ x: 50,
7207
+ y: 68,
7208
+ width: 72,
7209
+ height: 42,
7210
+ src: "https://images.unsplash.com/photo-1512389142860-9c449e58a543?auto=format&fit=crop&w=1200&q=80",
7211
+ fit: "cover",
7212
+ borderRadius: 16,
7213
+ alt: "holiday"
7214
+ }
7215
+ ]
7216
+ },
7217
+ {
7218
+ id: "page-2",
7219
+ title: "\u795D\u798F",
7220
+ background: { color: "#1a3766" },
7221
+ elements: [
7222
+ {
7223
+ id: "p2-text-1",
7224
+ type: "text",
7225
+ x: 50,
7226
+ y: 28,
7227
+ content: "\u613F\u4F60\u65B0\u5C81\uFF1A",
7228
+ fontSize: 28,
7229
+ fontWeight: 700,
7230
+ align: "center",
7231
+ color: "#fef3c7"
7232
+ },
7233
+ {
7234
+ id: "p2-text-2",
7235
+ type: "text",
7236
+ x: 50,
7237
+ y: 42,
7238
+ content: "\u6240\u5F97\u7686\u6240\u671F\uFF0C\u6240\u5931\u4EA6\u65E0\u788D",
7239
+ fontSize: 18,
7240
+ fontWeight: 500,
7241
+ align: "center",
7242
+ color: "#f8fafc"
7243
+ },
7244
+ {
7245
+ id: "p2-text-3",
7246
+ type: "text",
7247
+ x: 50,
7248
+ y: 55,
7249
+ content: "\u613F\u4F60\u7684\u6BCF\u4E00\u6B65\u90FD\u8D70\u5411\u5149\u4EAE",
7250
+ fontSize: 16,
7251
+ fontWeight: 400,
7252
+ align: "center",
7253
+ color: "#dbeafe"
7254
+ }
7255
+ ]
7256
+ },
7257
+ {
7258
+ id: "page-3",
7259
+ title: "\u843D\u6B3E",
7260
+ background: { color: "#11284d" },
7261
+ elements: [
7262
+ {
7263
+ id: "p3-image-1",
7264
+ type: "image",
7265
+ x: 50,
7266
+ y: 34,
7267
+ width: 60,
7268
+ height: 42,
7269
+ src: "https://images.unsplash.com/photo-1456324504439-367cee3b3c32?auto=format&fit=crop&w=1200&q=80",
7270
+ fit: "cover",
7271
+ borderRadius: 14,
7272
+ alt: "gift"
7273
+ },
7274
+ {
7275
+ id: "p3-text-1",
7276
+ type: "text",
7277
+ x: 50,
7278
+ y: 72,
7279
+ content: "Best wishes, from SA2Kit",
7280
+ fontSize: 18,
7281
+ fontWeight: 600,
7282
+ align: "center",
7283
+ color: "#f8fafc"
7284
+ }
7285
+ ]
7286
+ }
7287
+ ]
7168
7288
  };
7169
- var FestivalCard3D = ({
7170
- title = "Happy Holidays",
7171
- subtitle = "Wishing you joy and peace",
7172
- className
7173
- }) => {
7174
- const mountRef = React42.useRef(null);
7289
+
7290
+ // src/festivalCard/core/normalize.ts
7291
+ var ensurePage = (page, index) => ({
7292
+ id: page.id || `page-${index + 1}`,
7293
+ title: page.title || `\u7B2C ${index + 1} \u9875`,
7294
+ elements: Array.isArray(page.elements) ? page.elements : [],
7295
+ background: page.background || {}
7296
+ });
7297
+ var normalizeFestivalCardConfig = (config) => {
7298
+ const pages = config?.pages && config.pages.length > 0 ? config.pages : DEFAULT_FESTIVAL_CARD_CONFIG.pages;
7299
+ return {
7300
+ ...DEFAULT_FESTIVAL_CARD_CONFIG,
7301
+ ...config,
7302
+ background: {
7303
+ ...DEFAULT_FESTIVAL_CARD_CONFIG.background,
7304
+ ...config?.background || {}
7305
+ },
7306
+ backgroundMusic: {
7307
+ ...DEFAULT_FESTIVAL_CARD_CONFIG.backgroundMusic,
7308
+ ...config?.backgroundMusic || {},
7309
+ src: config?.backgroundMusic?.src ?? DEFAULT_FESTIVAL_CARD_CONFIG.backgroundMusic?.src ?? ""
7310
+ },
7311
+ pages: pages.map(ensurePage)
7312
+ };
7313
+ };
7314
+ var resizeFestivalCardPages = (config, pageCount) => {
7315
+ const safeCount = Math.max(1, Math.min(12, Math.floor(pageCount || 1)));
7316
+ const nextPages = [...config.pages];
7317
+ while (nextPages.length < safeCount) {
7318
+ const idx = nextPages.length;
7319
+ nextPages.push({
7320
+ id: `page-${idx + 1}`,
7321
+ title: `\u7B2C ${idx + 1} \u9875`,
7322
+ elements: [],
7323
+ background: {}
7324
+ });
7325
+ }
7326
+ return {
7327
+ ...config,
7328
+ pages: nextPages.slice(0, safeCount)
7329
+ };
7330
+ };
7331
+ var useFestivalCardConfig = (options) => {
7332
+ const [config, setConfig] = React42.useState(
7333
+ () => normalizeFestivalCardConfig(options?.initialConfig || DEFAULT_FESTIVAL_CARD_CONFIG)
7334
+ );
7335
+ const [loading, setLoading] = React42.useState(Boolean(options?.fetchConfig));
7336
+ const [saving, setSaving] = React42.useState(false);
7175
7337
  React42.useEffect(() => {
7176
- const mount = mountRef.current;
7177
- if (!mount) return;
7178
- const scene = new THREE3__namespace.Scene();
7179
- scene.fog = new THREE3__namespace.Fog(528934, 12, 28);
7180
- const camera = new THREE3__namespace.PerspectiveCamera(50, mount.clientWidth / mount.clientHeight, 0.1, 100);
7181
- camera.position.set(0, 2.6, 7.5);
7182
- const renderer = new THREE3__namespace.WebGLRenderer({ antialias: true, alpha: true });
7183
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
7184
- renderer.setSize(mount.clientWidth, mount.clientHeight);
7185
- renderer.outputColorSpace = THREE3__namespace.SRGBColorSpace;
7186
- mount.appendChild(renderer.domElement);
7187
- const ambientLight = new THREE3__namespace.AmbientLight(9090303, 0.8);
7188
- scene.add(ambientLight);
7189
- const keyLight = new THREE3__namespace.DirectionalLight(16777215, 1.25);
7190
- keyLight.position.set(2, 5, 4);
7191
- scene.add(keyLight);
7192
- const fillLight = new THREE3__namespace.PointLight(8050687, 0.9, 24);
7193
- fillLight.position.set(-4, 3, -2);
7194
- scene.add(fillLight);
7195
- const floor = new THREE3__namespace.Mesh(
7196
- new THREE3__namespace.CircleGeometry(12, 48),
7197
- new THREE3__namespace.MeshStandardMaterial({ color: 1056826, roughness: 0.95, metalness: 0.05 })
7198
- );
7199
- floor.rotation.x = -Math.PI / 2;
7200
- floor.position.y = -1.2;
7201
- scene.add(floor);
7202
- const giftGroup = new THREE3__namespace.Group();
7203
- scene.add(giftGroup);
7204
- const box = new THREE3__namespace.Mesh(
7205
- new THREE3__namespace.BoxGeometry(2, 1.6, 2),
7206
- new THREE3__namespace.MeshStandardMaterial({ color: 14238053, roughness: 0.55, metalness: 0.2 })
7207
- );
7208
- giftGroup.add(box);
7209
- const ribbonMaterial = new THREE3__namespace.MeshStandardMaterial({ color: 16767354, roughness: 0.3, metalness: 0.5 });
7210
- const verticalRibbon = new THREE3__namespace.Mesh(new THREE3__namespace.BoxGeometry(0.24, 1.7, 2.02), ribbonMaterial);
7211
- const horizontalRibbon = new THREE3__namespace.Mesh(new THREE3__namespace.BoxGeometry(2.02, 1.7, 0.24), ribbonMaterial);
7212
- giftGroup.add(verticalRibbon);
7213
- giftGroup.add(horizontalRibbon);
7214
- const lid = new THREE3__namespace.Mesh(
7215
- new THREE3__namespace.BoxGeometry(2.15, 0.34, 2.15),
7216
- new THREE3__namespace.MeshStandardMaterial({ color: 13448278, roughness: 0.52, metalness: 0.2 })
7217
- );
7218
- lid.position.y = 0.98;
7219
- giftGroup.add(lid);
7220
- const bowLeft = new THREE3__namespace.Mesh(new THREE3__namespace.TorusGeometry(0.26, 0.08, 12, 32), ribbonMaterial);
7221
- bowLeft.rotation.set(Math.PI / 2, Math.PI / 6, 0);
7222
- bowLeft.position.set(-0.22, 1.14, 0);
7223
- const bowRight = bowLeft.clone();
7224
- bowRight.rotation.y = -Math.PI / 6;
7225
- bowRight.position.x = 0.22;
7226
- giftGroup.add(bowLeft, bowRight);
7227
- giftGroup.position.y = -0.15;
7228
- const { positions, speeds } = createSnow(260);
7229
- const snowGeometry = new THREE3__namespace.BufferGeometry();
7230
- snowGeometry.setAttribute("position", new THREE3__namespace.BufferAttribute(positions, 3));
7231
- const snow = new THREE3__namespace.Points(
7232
- snowGeometry,
7233
- new THREE3__namespace.PointsMaterial({
7234
- color: 15267071,
7235
- size: 0.08,
7236
- transparent: true,
7237
- opacity: 0.92,
7238
- depthWrite: false
7239
- })
7240
- );
7241
- scene.add(snow);
7242
- let rafId = 0;
7243
- const clock = new THREE3__namespace.Clock();
7244
- const animate = () => {
7245
- const elapsed = clock.getElapsedTime();
7246
- const pos = snowGeometry.attributes.position;
7247
- for (let i = 0; i < pos.count; i++) {
7248
- const speed = speeds[i] ?? 0.01;
7249
- const y = pos.getY(i) - speed;
7250
- pos.setY(i, y < -1.1 ? 10 + Math.random() * 2 : y);
7251
- pos.setX(i, pos.getX(i) + Math.sin(elapsed + i * 0.04) * 16e-4);
7252
- }
7253
- pos.needsUpdate = true;
7254
- giftGroup.rotation.y = elapsed * 0.35;
7255
- giftGroup.position.y = -0.15 + Math.sin(elapsed * 1.4) * 0.08;
7256
- lid.position.y = 0.98 + Math.sin(elapsed * 2.2) * 0.08;
7257
- renderer.render(scene, camera);
7258
- rafId = window.requestAnimationFrame(animate);
7259
- };
7260
- const handleResize = () => {
7261
- if (!mount) return;
7262
- const width = mount.clientWidth;
7263
- const height = mount.clientHeight;
7264
- camera.aspect = width / height;
7265
- camera.updateProjectionMatrix();
7266
- renderer.setSize(width, height);
7267
- };
7268
- window.addEventListener("resize", handleResize);
7269
- animate();
7338
+ if (!options?.fetchConfig) return;
7339
+ let active = true;
7340
+ void options.fetchConfig().then((value) => {
7341
+ if (!active) return;
7342
+ setConfig(normalizeFestivalCardConfig(value));
7343
+ }).finally(() => {
7344
+ if (active) setLoading(false);
7345
+ });
7270
7346
  return () => {
7271
- window.cancelAnimationFrame(rafId);
7272
- window.removeEventListener("resize", handleResize);
7273
- scene.traverse((obj) => {
7274
- const mesh = obj;
7275
- if (mesh.geometry) mesh.geometry.dispose();
7276
- const material = mesh.material;
7277
- if (Array.isArray(material)) material.forEach((m) => m.dispose());
7278
- else material?.dispose();
7279
- });
7280
- renderer.dispose();
7281
- mount.removeChild(renderer.domElement);
7347
+ active = false;
7282
7348
  };
7283
- }, []);
7349
+ }, [options]);
7350
+ const save = React42.useCallback(async () => {
7351
+ if (!options?.onSave) return;
7352
+ setSaving(true);
7353
+ try {
7354
+ await options.onSave(config);
7355
+ } finally {
7356
+ setSaving(false);
7357
+ }
7358
+ }, [config, options]);
7359
+ return React42.useMemo(
7360
+ () => ({
7361
+ config,
7362
+ setConfig,
7363
+ loading,
7364
+ saving,
7365
+ save
7366
+ }),
7367
+ [config, loading, save, saving]
7368
+ );
7369
+ };
7370
+ var elementStyle = (element) => ({
7371
+ position: "absolute",
7372
+ left: `${element.x}%`,
7373
+ top: `${element.y}%`,
7374
+ width: `${element.width ?? 70}%`,
7375
+ height: element.height ? `${element.height}%` : void 0,
7376
+ transform: "translate(-50%, -50%)"
7377
+ });
7378
+ var renderElement = (element) => {
7379
+ if (element.type === "text") {
7380
+ return /* @__PURE__ */ React42__namespace.default.createElement(
7381
+ "div",
7382
+ {
7383
+ key: element.id,
7384
+ style: {
7385
+ ...elementStyle(element),
7386
+ color: element.color || "#f8fafc",
7387
+ fontSize: element.fontSize || 18,
7388
+ fontWeight: element.fontWeight || 500,
7389
+ textAlign: element.align || "left",
7390
+ lineHeight: 1.45,
7391
+ whiteSpace: "pre-wrap"
7392
+ }
7393
+ },
7394
+ element.content
7395
+ );
7396
+ }
7397
+ return /* @__PURE__ */ React42__namespace.default.createElement(
7398
+ "img",
7399
+ {
7400
+ key: element.id,
7401
+ src: element.src,
7402
+ alt: element.alt || "festival-card-image",
7403
+ style: {
7404
+ ...elementStyle(element),
7405
+ objectFit: element.fit || "cover",
7406
+ borderRadius: element.borderRadius || 0,
7407
+ overflow: "hidden",
7408
+ boxShadow: "0 12px 30px rgba(2, 6, 23, 0.32)"
7409
+ }
7410
+ }
7411
+ );
7412
+ };
7413
+ var FestivalCardPageRenderer = ({ page }) => {
7284
7414
  return /* @__PURE__ */ React42__namespace.default.createElement(
7285
7415
  "div",
7286
7416
  {
7287
- className,
7288
7417
  style: {
7289
7418
  position: "relative",
7290
7419
  width: "100%",
7291
- minHeight: 420,
7292
- borderRadius: 20,
7420
+ height: "100%",
7293
7421
  overflow: "hidden",
7294
- background: "radial-gradient(circle at 20% 20%, #244d8c 0%, #0c1a34 45%, #060d1f 100%)"
7422
+ borderRadius: 16,
7423
+ backgroundColor: page.background?.color || "#0f172a",
7424
+ backgroundImage: page.background?.image ? `url(${page.background.image})` : void 0,
7425
+ backgroundSize: "cover",
7426
+ backgroundPosition: "center"
7427
+ }
7428
+ },
7429
+ page.elements.map(renderElement)
7430
+ );
7431
+ };
7432
+
7433
+ // src/festivalCard/components/FestivalCardBook3D.tsx
7434
+ var FestivalCardBook3D = ({ config, className }) => {
7435
+ const [currentPage, setCurrentPage] = React42.useState(0);
7436
+ const normalized = React42.useMemo(() => normalizeFestivalCardConfig(config), [config]);
7437
+ const pages = normalized.pages;
7438
+ const canPrev = currentPage > 0;
7439
+ const canNext = currentPage < pages.length - 1;
7440
+ return /* @__PURE__ */ React42__namespace.default.createElement("div", { className }, /* @__PURE__ */ React42__namespace.default.createElement(
7441
+ "div",
7442
+ {
7443
+ style: {
7444
+ width: "100%",
7445
+ minHeight: 560,
7446
+ borderRadius: 24,
7447
+ padding: 24,
7448
+ background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`,
7449
+ boxShadow: "0 26px 70px rgba(2, 6, 23, 0.45)"
7295
7450
  }
7296
7451
  },
7297
- /* @__PURE__ */ React42__namespace.default.createElement("div", { ref: mountRef, style: { position: "absolute", inset: 0 } }),
7298
7452
  /* @__PURE__ */ React42__namespace.default.createElement(
7299
7453
  "div",
7300
7454
  {
7301
7455
  style: {
7302
- position: "absolute",
7303
- inset: 0,
7304
- pointerEvents: "none",
7305
- 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%)"
7456
+ marginBottom: 14,
7457
+ color: "#f8fafc",
7458
+ fontSize: 14,
7459
+ opacity: 0.9,
7460
+ textAlign: "center"
7306
7461
  }
7307
- }
7462
+ },
7463
+ normalized.coverTitle || "Festival Card",
7464
+ " \xB7 \u7B2C ",
7465
+ currentPage + 1,
7466
+ " / ",
7467
+ pages.length,
7468
+ " \u9875"
7308
7469
  ),
7309
- /* @__PURE__ */ React42__namespace.default.createElement(
7470
+ /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { perspective: 1400, width: "100%", maxWidth: 920, margin: "0 auto" } }, /* @__PURE__ */ React42__namespace.default.createElement(
7310
7471
  "div",
7311
7472
  {
7312
7473
  style: {
7313
- position: "absolute",
7314
- left: 20,
7315
- right: 20,
7316
- bottom: 20,
7317
- zIndex: 2,
7318
- padding: "16px 18px",
7319
- borderRadius: 14,
7320
- backgroundColor: "rgba(8, 16, 35, 0.66)",
7321
- border: "1px solid rgba(255, 255, 255, 0.16)",
7322
- color: "#f8fafc"
7474
+ position: "relative",
7475
+ height: 460,
7476
+ transformStyle: "preserve-3d"
7323
7477
  }
7324
7478
  },
7325
- /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { fontSize: 26, fontWeight: 700, lineHeight: 1.2 } }, title),
7326
- /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { marginTop: 6, fontSize: 15, opacity: 0.92 } }, subtitle)
7327
- )
7328
- );
7479
+ pages.map((page, index) => {
7480
+ const isFlipped = index < currentPage;
7481
+ const zIndex = pages.length - index;
7482
+ return /* @__PURE__ */ React42__namespace.default.createElement(
7483
+ "div",
7484
+ {
7485
+ key: page.id,
7486
+ style: {
7487
+ position: "absolute",
7488
+ inset: 0,
7489
+ transformStyle: "preserve-3d",
7490
+ transformOrigin: "left center",
7491
+ transform: `rotateY(${isFlipped ? -170 : 0}deg)`,
7492
+ transition: "transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1)",
7493
+ zIndex
7494
+ }
7495
+ },
7496
+ /* @__PURE__ */ React42__namespace.default.createElement(
7497
+ "div",
7498
+ {
7499
+ style: {
7500
+ position: "absolute",
7501
+ inset: 0,
7502
+ backfaceVisibility: "hidden"
7503
+ }
7504
+ },
7505
+ /* @__PURE__ */ React42__namespace.default.createElement(FestivalCardPageRenderer, { page })
7506
+ ),
7507
+ /* @__PURE__ */ React42__namespace.default.createElement(
7508
+ "div",
7509
+ {
7510
+ style: {
7511
+ position: "absolute",
7512
+ inset: 0,
7513
+ transform: "rotateY(180deg)",
7514
+ backfaceVisibility: "hidden",
7515
+ borderRadius: 16,
7516
+ background: "#0f172a"
7517
+ }
7518
+ }
7519
+ )
7520
+ );
7521
+ })
7522
+ )),
7523
+ /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { display: "flex", justifyContent: "center", gap: 12, marginTop: 18 } }, /* @__PURE__ */ React42__namespace.default.createElement(
7524
+ "button",
7525
+ {
7526
+ type: "button",
7527
+ disabled: !canPrev,
7528
+ onClick: () => setCurrentPage((p) => Math.max(0, p - 1)),
7529
+ style: {
7530
+ border: "none",
7531
+ borderRadius: 999,
7532
+ padding: "9px 16px",
7533
+ fontSize: 14,
7534
+ cursor: canPrev ? "pointer" : "not-allowed",
7535
+ opacity: canPrev ? 1 : 0.4
7536
+ }
7537
+ },
7538
+ "\u4E0A\u4E00\u9875"
7539
+ ), /* @__PURE__ */ React42__namespace.default.createElement(
7540
+ "button",
7541
+ {
7542
+ type: "button",
7543
+ disabled: !canNext,
7544
+ onClick: () => setCurrentPage((p) => Math.min(pages.length - 1, p + 1)),
7545
+ style: {
7546
+ border: "none",
7547
+ borderRadius: 999,
7548
+ padding: "9px 16px",
7549
+ fontSize: 14,
7550
+ cursor: canNext ? "pointer" : "not-allowed",
7551
+ opacity: canNext ? 1 : 0.4
7552
+ }
7553
+ },
7554
+ "\u4E0B\u4E00\u9875"
7555
+ ))
7556
+ ), normalized.backgroundMusic?.src ? /* @__PURE__ */ React42__namespace.default.createElement(
7557
+ "audio",
7558
+ {
7559
+ src: normalized.backgroundMusic.src,
7560
+ autoPlay: normalized.backgroundMusic.autoPlay,
7561
+ loop: normalized.backgroundMusic.loop,
7562
+ controls: true,
7563
+ style: { width: "100%", marginTop: 10 }
7564
+ }
7565
+ ) : null);
7329
7566
  };
7567
+ var createTextElement = (pageIndex) => ({
7568
+ id: `text-${Date.now()}-${pageIndex}`,
7569
+ type: "text",
7570
+ x: 50,
7571
+ y: 50,
7572
+ content: "\u8BF7\u8F93\u5165\u6587\u5B57",
7573
+ fontSize: 18,
7574
+ fontWeight: 500,
7575
+ align: "center",
7576
+ color: "#ffffff"
7577
+ });
7578
+ var createImageElement = (pageIndex) => ({
7579
+ id: `image-${Date.now()}-${pageIndex}`,
7580
+ type: "image",
7581
+ x: 50,
7582
+ y: 50,
7583
+ width: 60,
7584
+ height: 40,
7585
+ src: "https://images.unsplash.com/photo-1482517967863-00e15c9b44be?auto=format&fit=crop&w=1200&q=80",
7586
+ fit: "cover",
7587
+ borderRadius: 12
7588
+ });
7589
+ var FestivalCardConfigEditor = ({ value, onChange }) => {
7590
+ const [activePageIndex, setActivePageIndex] = React42.useState(0);
7591
+ const page = value.pages[activePageIndex];
7592
+ const canEditPage = Boolean(page);
7593
+ const pageOptions = React42.useMemo(() => value.pages.map((_, index) => index), [value.pages]);
7594
+ const updateElement = (elementId, patch) => {
7595
+ onChange({
7596
+ ...value,
7597
+ pages: value.pages.map(
7598
+ (p, pIndex) => pIndex === activePageIndex ? {
7599
+ ...p,
7600
+ elements: p.elements.map((el) => el.id === elementId ? { ...el, ...patch } : el)
7601
+ } : p
7602
+ )
7603
+ });
7604
+ };
7605
+ return /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { borderRadius: 16, background: "#0f172a", color: "#e2e8f0", padding: 16 } }, /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { display: "grid", gap: 12 } }, /* @__PURE__ */ React42__namespace.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React42__namespace.default.createElement("span", null, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React42__namespace.default.createElement(
7606
+ "input",
7607
+ {
7608
+ type: "number",
7609
+ min: 1,
7610
+ max: 12,
7611
+ value: value.pages.length,
7612
+ onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value)))
7613
+ }
7614
+ )), /* @__PURE__ */ React42__namespace.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React42__namespace.default.createElement("span", null, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React42__namespace.default.createElement(
7615
+ "input",
7616
+ {
7617
+ type: "url",
7618
+ value: value.backgroundMusic?.src || "",
7619
+ onChange: (event) => onChange({
7620
+ ...value,
7621
+ backgroundMusic: {
7622
+ ...value.backgroundMusic,
7623
+ src: event.target.value
7624
+ }
7625
+ })
7626
+ }
7627
+ )), /* @__PURE__ */ React42__namespace.default.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React42__namespace.default.createElement("span", null, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React42__namespace.default.createElement("select", { value: activePageIndex, onChange: (event) => setActivePageIndex(Number(event.target.value)) }, pageOptions.map((index) => /* @__PURE__ */ React42__namespace.default.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))))), canEditPage ? /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { marginTop: 16 } }, /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { display: "flex", gap: 8, marginBottom: 12 } }, /* @__PURE__ */ React42__namespace.default.createElement(
7628
+ "button",
7629
+ {
7630
+ type: "button",
7631
+ onClick: () => onChange({
7632
+ ...value,
7633
+ pages: value.pages.map(
7634
+ (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createTextElement(index)] } : p
7635
+ )
7636
+ })
7637
+ },
7638
+ "+ \u6587\u5B57"
7639
+ ), /* @__PURE__ */ React42__namespace.default.createElement(
7640
+ "button",
7641
+ {
7642
+ type: "button",
7643
+ onClick: () => onChange({
7644
+ ...value,
7645
+ pages: value.pages.map(
7646
+ (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createImageElement(index)] } : p
7647
+ )
7648
+ })
7649
+ },
7650
+ "+ \u56FE\u7247"
7651
+ )), /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { display: "grid", gap: 10, maxHeight: 340, overflow: "auto" } }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React42__namespace.default.createElement("div", { key: element.id, style: { border: "1px solid #334155", borderRadius: 10, padding: 10 } }, /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { marginBottom: 8 } }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React42__namespace.default.createElement(
7652
+ "textarea",
7653
+ {
7654
+ value: element.content,
7655
+ onChange: (event) => updateElement(element.id, { content: event.target.value }),
7656
+ rows: 3,
7657
+ style: { width: "100%" }
7658
+ }
7659
+ ) : /* @__PURE__ */ React42__namespace.default.createElement(
7660
+ "input",
7661
+ {
7662
+ type: "url",
7663
+ value: element.src,
7664
+ onChange: (event) => updateElement(element.id, { src: event.target.value }),
7665
+ style: { width: "100%" }
7666
+ }
7667
+ ))))) : null);
7668
+ };
7669
+ var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
7670
+ const { config, setConfig, loading, save, saving } = useFestivalCardConfig({
7671
+ initialConfig: normalizeFestivalCardConfig(initialConfig),
7672
+ fetchConfig,
7673
+ onSave
7674
+ });
7675
+ if (loading) return /* @__PURE__ */ React42__namespace.default.createElement("div", null, "\u52A0\u8F7D\u4E2D...");
7676
+ return /* @__PURE__ */ React42__namespace.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 16 } }, /* @__PURE__ */ React42__namespace.default.createElement(FestivalCardBook3D, { config }), /* @__PURE__ */ React42__namespace.default.createElement("div", null, /* @__PURE__ */ React42__namespace.default.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React42__namespace.default.createElement(
7677
+ "button",
7678
+ {
7679
+ type: "button",
7680
+ onClick: () => void save(),
7681
+ disabled: saving,
7682
+ style: { marginTop: 12, width: "100%", padding: "10px 16px" }
7683
+ },
7684
+ saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"
7685
+ ) : null));
7686
+ };
7687
+
7688
+ // src/festivalCard/server/db.ts
7689
+ var dbAdapter = null;
7690
+ var getFestivalCardDb = () => dbAdapter;
7691
+
7692
+ // src/festivalCard/services/festivalCardService.ts
7693
+ var FestivalCardService = class {
7694
+ constructor(options) {
7695
+ this.db = options?.db || getFestivalCardDb();
7696
+ }
7697
+ async getConfig(cardId = "default-festival-card") {
7698
+ if (!this.db) return DEFAULT_FESTIVAL_CARD_CONFIG;
7699
+ const config = await this.db.getConfig(cardId);
7700
+ return normalizeFestivalCardConfig(config);
7701
+ }
7702
+ async saveConfig(cardId, config) {
7703
+ const normalized = normalizeFestivalCardConfig(config);
7704
+ if (!this.db) return normalized;
7705
+ await this.db.saveConfig(cardId, normalized);
7706
+ return normalized;
7707
+ }
7708
+ };
7709
+ var memoryStore = /* @__PURE__ */ new Map();
7710
+ var createInMemoryFestivalCardDb = () => ({
7711
+ getConfig(id) {
7712
+ return Promise.resolve(memoryStore.get(id) || null);
7713
+ },
7714
+ saveConfig(id, config) {
7715
+ memoryStore.set(id, config);
7716
+ return Promise.resolve();
7717
+ }
7718
+ });
7330
7719
 
7331
7720
  // src/storage/adapters/react-native-adapter.ts
7332
7721
  var AsyncStorage = null;
@@ -7715,6 +8104,7 @@ exports.ConsoleLoggerAdapter = ConsoleLoggerAdapter;
7715
8104
  exports.Contact = Contact_default;
7716
8105
  exports.DANMAKU_MAX_LENGTH = DANMAKU_MAX_LENGTH;
7717
8106
  exports.DANMAKU_TRACK_COUNT = DANMAKU_TRACK_COUNT;
8107
+ exports.DEFAULT_FESTIVAL_CARD_CONFIG = DEFAULT_FESTIVAL_CARD_CONFIG;
7718
8108
  exports.DEFAULT_MAX_ACTIVE_FIREWORKS = DEFAULT_MAX_ACTIVE_FIREWORKS;
7719
8109
  exports.DEFAULT_MAX_PARTICLES = DEFAULT_MAX_PARTICLES;
7720
8110
  exports.DEFAULT_OPENAI_BASE_URL = DEFAULT_OPENAI_BASE_URL;
@@ -7752,7 +8142,11 @@ exports.ExperimentCard = ExperimentCard;
7752
8142
  exports.ExperimentGrid = ExperimentGrid;
7753
8143
  exports.ExperimentItemGrid = ExperimentItemGrid;
7754
8144
  exports.FIREWORK_KIND_LABELS = FIREWORK_KIND_LABELS;
7755
- exports.FestivalCard3D = FestivalCard3D;
8145
+ exports.FestivalCardBook3D = FestivalCardBook3D;
8146
+ exports.FestivalCardConfigEditor = FestivalCardConfigEditor;
8147
+ exports.FestivalCardPageRenderer = FestivalCardPageRenderer;
8148
+ exports.FestivalCardService = FestivalCardService;
8149
+ exports.FestivalCardStudio = FestivalCardStudio;
7756
8150
  exports.FilterButtonGroup = FilterButtonGroup;
7757
8151
  exports.FireworksCanvas = FireworksCanvas;
7758
8152
  exports.FireworksControlPanel = FireworksControlPanel;
@@ -7836,6 +8230,7 @@ exports.buttonVariants = buttonVariants;
7836
8230
  exports.cn = cn;
7837
8231
  exports.createAiClient = createAiClient;
7838
8232
  exports.createChatSession = createChatSession;
8233
+ exports.createInMemoryFestivalCardDb = createInMemoryFestivalCardDb;
7839
8234
  exports.createLogger = createLogger;
7840
8235
  exports.createOpenAICompatibleProvider = createOpenAICompatibleProvider;
7841
8236
  exports.createSkillRegistry = createSkillRegistry;
@@ -7853,7 +8248,9 @@ exports.getCompletionStatusText = getCompletionStatusText;
7853
8248
  exports.getExperimentCounts = getExperimentCounts;
7854
8249
  exports.japaneseUtils = japaneseUtils;
7855
8250
  exports.logger = logger;
8251
+ exports.normalizeFestivalCardConfig = normalizeFestivalCardConfig;
7856
8252
  exports.normalizePromptVariables = normalizePromptVariables;
8253
+ exports.resizeFestivalCardPages = resizeFestivalCardPages;
7857
8254
  exports.resolveScreenReceiverSignalUrl = resolveScreenReceiverSignalUrl;
7858
8255
  exports.skillToToolDefinition = skillToToolDefinition;
7859
8256
  exports.sortExperiments = sortExperiments;
@@ -7863,6 +8260,7 @@ exports.useAsyncStorage = useAsyncStorage;
7863
8260
  exports.useBackgroundRemoval = useBackgroundRemoval;
7864
8261
  exports.useDanmakuController = useDanmakuController;
7865
8262
  exports.useElectronStorage = useElectronStorage;
8263
+ exports.useFestivalCardConfig = useFestivalCardConfig;
7866
8264
  exports.useFireworksEngine = useFireworksEngine;
7867
8265
  exports.useFireworksRealtime = useFireworksRealtime;
7868
8266
  exports.useLocalStorage = useLocalStorage;