@chuzi/shared 1.3.36 → 1.3.38

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.
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
  import { AtomVisualProps } from '../../index.js';
4
- import { C as ConstellationAppearance } from '../../../appearance-DFP3Ymge.js';
5
- export { D as DEFAULT_CONSTELLATION_APPEARANCE, m as mergeConstellationAppearance } from '../../../appearance-DFP3Ymge.js';
4
+ import { C as ConstellationAppearance } from '../../../appearance-CVHSA6Ql.js';
5
+ export { D as DEFAULT_CONSTELLATION_APPEARANCE, m as mergeConstellationAppearance } from '../../../appearance-CVHSA6Ql.js';
6
6
  import { StoryListItem } from '../../../types/index.js';
7
7
 
8
8
  interface WorldProps {
@@ -51,6 +51,8 @@ interface ConstellationSceneEntry {
51
51
  locked?: boolean;
52
52
  /** Highlight this star's billboard (focused traversal). */
53
53
  focused?: boolean;
54
+ /** Control strip slot — rendered beneath preview when focused. */
55
+ controlsSlot?: ReactNode;
54
56
  }
55
57
  interface ConstellationEdgeEntry {
56
58
  source: string;
@@ -65,6 +67,8 @@ interface ConstellationProps {
65
67
  /** Explicit story-flow edges (goto + choice). No implicit sequential links. */
66
68
  edges?: ConstellationEdgeEntry[];
67
69
  appearance?: Partial<ConstellationAppearance>;
70
+ /** When false, hide Html billboards and arc title (e.g. while editor is open). */
71
+ showOverlays?: boolean;
68
72
  onSceneSelect?: (index: number) => void;
69
73
  }
70
74
  /**
@@ -72,7 +76,7 @@ interface ConstellationProps {
72
76
  * luminous gradient edges. This is the cosmos realm's `Group` component —
73
77
  * identical rendering for own and others' stories.
74
78
  */
75
- declare function Constellation({ scenes, storyTitle, edges, appearance: appearanceOverrides, onSceneSelect, }: ConstellationProps): react_jsx_runtime.JSX.Element;
79
+ declare function Constellation({ scenes, storyTitle, edges, appearance: appearanceOverrides, showOverlays, onSceneSelect, }: ConstellationProps): react_jsx_runtime.JSX.Element;
76
80
 
77
81
  type ConstellationEdgeVariant = "solid" | "dotted";
78
82
  interface ConstellationEdgeProps {
@@ -142,11 +146,15 @@ interface ConstellationTitleProps {
142
146
  title: string;
143
147
  bounds: ConstellationBounds;
144
148
  appearance: ConstellationAppearance;
149
+ /** World position of the title (first) scene — arc starts here. */
150
+ arcFrom?: [number, number, number];
151
+ /** World position of the end scene — arc ends here. */
152
+ arcTo?: [number, number, number];
145
153
  }
146
154
  /**
147
- * Large ghostly film title draped across the constellation footprint.
155
+ * Vintage cartography-style ocean label arcing from title scene to end scene.
148
156
  */
149
- declare function ConstellationTitle({ title, bounds, appearance, }: ConstellationTitleProps): react_jsx_runtime.JSX.Element | null;
157
+ declare function ConstellationTitle({ title, bounds, appearance, arcFrom, arcTo, }: ConstellationTitleProps): react_jsx_runtime.JSX.Element | null;
150
158
 
151
159
  interface StarBillboardProps {
152
160
  label: string;
@@ -155,11 +163,15 @@ interface StarBillboardProps {
155
163
  previewContent?: ReactNode;
156
164
  dimmed?: boolean;
157
165
  focused?: boolean;
166
+ visible?: boolean;
167
+ /** Control strip rendered flush beneath the preview when focused. */
168
+ controlsSlot?: ReactNode;
158
169
  }
159
170
  /**
160
- * Floating preview card and scene label above a star. Label sits beneath
161
- * the preview image, offset from the star sphere.
171
+ * Floating preview card and scene label above a star. Unfocused previews
172
+ * are dimmed; labels stay prominent. Anchor sits at preview bottom-center
173
+ * so attached controls align with the card edge.
162
174
  */
163
- declare function StarBillboard({ label, appearance, previewImageUrl, previewContent, dimmed, focused, }: StarBillboardProps): react_jsx_runtime.JSX.Element;
175
+ declare function StarBillboard({ label, appearance, previewImageUrl, previewContent, dimmed, focused, visible, controlsSlot, }: StarBillboardProps): react_jsx_runtime.JSX.Element | null;
164
176
 
165
177
  export { Constellation, ConstellationAppearance, type ConstellationBounds, ConstellationEdge, type ConstellationEdgeEntry, type ConstellationEdgeProps, type ConstellationProps, type ConstellationSceneEntry, ConstellationTitle, type ConstellationTitleProps, CosmosSandbox, type CosmosSandboxProps, type DistributeOptions, Star, StarBillboard, type StarBillboardProps, type StarProps, type Vec3, World, type WorldProps, computeConstellationBounds, distributeStars };
@@ -1,7 +1,7 @@
1
1
  import { Canvas, useFrame } from '@react-three/fiber';
2
- import { Stars, OrbitControls, Line, Text, Html } from '@react-three/drei';
2
+ import { Stars, OrbitControls, Line, Billboard, Html } from '@react-three/drei';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
- import { useRef, useMemo } from 'react';
4
+ import { useRef, useMemo, useId } from 'react';
5
5
  import * as THREE from 'three';
6
6
 
7
7
  // src/realms/cosmos/components/World.tsx
@@ -76,17 +76,20 @@ function Star({ visual, onSelect, dimmed, locked }) {
76
76
 
77
77
  // src/realms/cosmos/appearance.ts
78
78
  var DEFAULT_CONSTELLATION_APPEARANCE = {
79
- titleFontSize: 2.8,
80
- titleOpacity: 0.14,
81
- titleColor: "#e8f0ff",
82
- titleLetterSpacing: 0.35,
83
- titleYOffset: 1.2,
79
+ titleFontSize: 34,
80
+ titleOpacity: 0.82,
81
+ titleColor: "#c8dce8",
82
+ titleLetterSpacing: 6,
83
+ titleYOffset: 8,
84
+ titleDistanceFactor: 12,
85
+ htmlZIndexRange: [8, 0],
84
86
  previewWidth: 148,
85
87
  previewHeight: 96,
86
88
  previewOffsetY: 2.4,
87
89
  labelFontSize: 13,
88
90
  labelLetterSpacing: 1.2,
89
- labelGap: 14,
91
+ labelGap: 10,
92
+ controlsGridHeight: 92,
90
93
  billboardDistanceFactor: 10,
91
94
  cameraDefaultOffset: [0, 4, 12],
92
95
  cameraTargetOffset: [0, 0, 0],
@@ -179,30 +182,96 @@ function computeConstellationBounds(positions) {
179
182
  spanZ: Math.max(4, maxZ - minZ + 3)
180
183
  };
181
184
  }
185
+ var WORLD_TO_SVG = 58;
182
186
  function ConstellationTitle({
183
187
  title,
184
188
  bounds,
185
- appearance
189
+ appearance,
190
+ arcFrom,
191
+ arcTo
186
192
  }) {
193
+ const gradientId = useId().replace(/:/g, "");
194
+ const pathId = `arc-${gradientId}`;
187
195
  if (!title.trim()) return null;
196
+ const span = Math.max(bounds.spanX, bounds.spanZ);
197
+ const svgWidth = Math.max(420, Math.round(span * WORLD_TO_SVG + 80));
198
+ const svgHeight = 110;
199
+ const centerX = svgWidth / 2;
200
+ const arcY = svgHeight * 0.88;
201
+ const arcPeak = svgHeight * 0.06;
202
+ let arcStartX = svgWidth * 0.04;
203
+ let arcEndX = svgWidth * 0.96;
204
+ if (arcFrom && arcTo) {
205
+ const [fromX] = arcFrom;
206
+ const [toX] = arcTo;
207
+ arcStartX = centerX + (fromX - bounds.center[0]) * WORLD_TO_SVG;
208
+ arcEndX = centerX + (toX - bounds.center[0]) * WORLD_TO_SVG;
209
+ arcStartX = Math.max(12, Math.min(arcStartX, svgWidth - 12));
210
+ arcEndX = Math.max(12, Math.min(arcEndX, svgWidth - 12));
211
+ if (arcStartX > arcEndX) {
212
+ const swap = arcStartX;
213
+ arcStartX = arcEndX;
214
+ arcEndX = swap;
215
+ }
216
+ }
217
+ const arcMidX = (arcStartX + arcEndX) / 2;
218
+ const arcPath = `M ${arcStartX} ${arcY} Q ${arcMidX} ${arcPeak} ${arcEndX} ${arcY}`;
188
219
  return /* @__PURE__ */ jsx(
189
- Text,
220
+ Billboard,
190
221
  {
191
222
  position: [
192
223
  bounds.center[0],
193
224
  bounds.center[1] + appearance.titleYOffset,
194
225
  bounds.center[2]
195
226
  ],
196
- rotation: [-Math.PI / 2, 0, 0],
197
- fontSize: appearance.titleFontSize,
198
- color: appearance.titleColor,
199
- fillOpacity: appearance.titleOpacity,
200
- anchorX: "center",
201
- anchorY: "middle",
202
- maxWidth: Math.max(bounds.spanX, bounds.spanZ) * 1.35,
203
- letterSpacing: appearance.titleLetterSpacing,
204
- textAlign: "center",
205
- children: title.toUpperCase()
227
+ children: /* @__PURE__ */ jsx(
228
+ Html,
229
+ {
230
+ center: true,
231
+ transform: true,
232
+ distanceFactor: appearance.titleDistanceFactor,
233
+ zIndexRange: appearance.htmlZIndexRange,
234
+ occlude: false,
235
+ style: { pointerEvents: "none", userSelect: "none" },
236
+ children: /* @__PURE__ */ jsxs(
237
+ "svg",
238
+ {
239
+ width: svgWidth,
240
+ height: svgHeight,
241
+ viewBox: `0 0 ${svgWidth} ${svgHeight}`,
242
+ style: { overflow: "visible", display: "block" },
243
+ "aria-hidden": true,
244
+ children: [
245
+ /* @__PURE__ */ jsxs("defs", { children: [
246
+ /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
247
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#8ab4c8" }),
248
+ /* @__PURE__ */ jsx("stop", { offset: "35%", stopColor: "#c8d8e4" }),
249
+ /* @__PURE__ */ jsx("stop", { offset: "65%", stopColor: "#e8dcc8" }),
250
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#8ab4c8" })
251
+ ] }),
252
+ /* @__PURE__ */ jsx("path", { id: pathId, d: arcPath, fill: "none" })
253
+ ] }),
254
+ /* @__PURE__ */ jsx(
255
+ "text",
256
+ {
257
+ fill: `url(#${gradientId})`,
258
+ fontFamily: "'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, serif",
259
+ fontSize: appearance.titleFontSize,
260
+ fontWeight: 700,
261
+ fontStyle: "italic",
262
+ letterSpacing: appearance.titleLetterSpacing,
263
+ opacity: appearance.titleOpacity,
264
+ stroke: "rgba(20,40,60,0.35)",
265
+ strokeWidth: 0.6,
266
+ paintOrder: "stroke fill",
267
+ children: /* @__PURE__ */ jsx("textPath", { href: `#${pathId}`, startOffset: "50%", textAnchor: "middle", children: title.toUpperCase() })
268
+ }
269
+ )
270
+ ]
271
+ }
272
+ )
273
+ }
274
+ )
206
275
  }
207
276
  );
208
277
  }
@@ -212,93 +281,130 @@ function StarBillboard({
212
281
  previewImageUrl,
213
282
  previewContent,
214
283
  dimmed,
215
- focused
284
+ focused,
285
+ visible = true,
286
+ controlsSlot
216
287
  }) {
217
- const borderColor = focused ? "rgba(126,184,255,0.65)" : "rgba(126,184,255,0.28)";
218
- const opacity = dimmed ? 0.45 : focused ? 1 : 0.88;
219
- return /* @__PURE__ */ jsx(
288
+ if (!visible) return null;
289
+ const borderColor = focused ? "rgba(126,184,255,0.75)" : "rgba(126,184,255,0.22)";
290
+ const previewOpacity = focused ? 1 : dimmed ? 0.22 : 0.34;
291
+ const previewFilter = focused ? "brightness(1.08) saturate(1.1)" : "brightness(0.72) saturate(0.65)";
292
+ const hasControls = !!(focused && controlsSlot);
293
+ return /* @__PURE__ */ jsxs(
220
294
  Html,
221
295
  {
222
296
  position: [0, appearance.previewOffsetY, 0],
223
297
  center: true,
298
+ transform: true,
224
299
  distanceFactor: appearance.billboardDistanceFactor,
300
+ zIndexRange: appearance.htmlZIndexRange,
301
+ occlude: false,
225
302
  style: {
226
303
  pointerEvents: "none",
227
- userSelect: "none",
228
- opacity,
229
- transition: "opacity 0.35s ease"
304
+ userSelect: "none"
230
305
  },
231
- children: /* @__PURE__ */ jsxs(
232
- "div",
233
- {
234
- style: {
235
- display: "flex",
236
- flexDirection: "column",
237
- alignItems: "center",
238
- gap: appearance.labelGap
239
- },
240
- children: [
241
- /* @__PURE__ */ jsx(
242
- "div",
243
- {
244
- style: {
245
- width: appearance.previewWidth,
246
- height: appearance.previewHeight,
247
- borderRadius: 8,
248
- overflow: "hidden",
249
- border: `1px solid ${borderColor}`,
250
- background: "rgba(4,7,13,0.92)",
251
- boxShadow: focused ? "0 0 24px rgba(126,184,255,0.35)" : "0 4px 20px rgba(0,0,0,0.55)",
252
- display: "flex",
253
- alignItems: "center",
254
- justifyContent: "center"
255
- },
256
- children: previewImageUrl ? /* @__PURE__ */ jsx(
257
- "img",
258
- {
259
- src: previewImageUrl,
260
- alt: "",
261
- style: {
262
- width: "100%",
263
- height: "100%",
264
- objectFit: "cover",
265
- display: "block"
306
+ children: [
307
+ /* @__PURE__ */ jsx("style", { children: `
308
+ @keyframes chuziPreviewFocusIn {
309
+ 0% { opacity: 0.4; transform: scale(0.9); filter: brightness(0.8) saturate(0.7); }
310
+ 55% { opacity: 1; transform: scale(1.05); filter: brightness(1.12) saturate(1.15); }
311
+ 100% { opacity: 1; transform: scale(1); filter: brightness(1.08) saturate(1.1); }
312
+ }
313
+ @keyframes chuziPreviewFocusOut {
314
+ 0% { opacity: 1; transform: scale(1); }
315
+ 100% { opacity: ${previewOpacity}; transform: scale(0.96); }
316
+ }
317
+ ` }),
318
+ /* @__PURE__ */ jsxs(
319
+ "div",
320
+ {
321
+ style: {
322
+ display: "flex",
323
+ flexDirection: "column",
324
+ alignItems: "center",
325
+ transform: `translateY(-${appearance.previewHeight / 2}px)`
326
+ },
327
+ children: [
328
+ /* @__PURE__ */ jsx(
329
+ "div",
330
+ {
331
+ style: {
332
+ width: appearance.previewWidth,
333
+ height: appearance.previewHeight,
334
+ borderRadius: hasControls ? "8px 8px 0 0" : 8,
335
+ overflow: "hidden",
336
+ border: `1px solid ${borderColor}`,
337
+ borderBottom: hasControls ? "none" : `1px solid ${borderColor}`,
338
+ background: "rgba(4,7,13,0.92)",
339
+ boxShadow: focused ? "0 0 28px rgba(126,184,255,0.45), 0 4px 24px rgba(0,0,0,0.5)" : "0 4px 20px rgba(0,0,0,0.55)",
340
+ display: "flex",
341
+ alignItems: "center",
342
+ justifyContent: "center",
343
+ opacity: previewOpacity,
344
+ filter: previewFilter,
345
+ transition: "border-color 0.35s ease, box-shadow 0.35s ease",
346
+ animation: focused ? "chuziPreviewFocusIn 0.48s cubic-bezier(0.34, 1.45, 0.64, 1) forwards" : "chuziPreviewFocusOut 0.32s ease forwards"
347
+ },
348
+ children: previewImageUrl ? /* @__PURE__ */ jsx(
349
+ "img",
350
+ {
351
+ src: previewImageUrl,
352
+ alt: "",
353
+ style: {
354
+ width: "100%",
355
+ height: "100%",
356
+ objectFit: "cover",
357
+ display: "block"
358
+ }
266
359
  }
267
- }
268
- ) : previewContent ?? /* @__PURE__ */ jsx(
269
- "div",
270
- {
271
- style: {
272
- width: "100%",
273
- height: "100%",
274
- background: "radial-gradient(ellipse at 30% 20%, rgba(80,120,200,0.2) 0%, rgba(10,14,30,0.95) 70%)"
360
+ ) : previewContent ?? /* @__PURE__ */ jsx(
361
+ "div",
362
+ {
363
+ style: {
364
+ width: "100%",
365
+ height: "100%",
366
+ background: "radial-gradient(ellipse at 30% 20%, rgba(80,120,200,0.2) 0%, rgba(10,14,30,0.95) 70%)"
367
+ }
275
368
  }
276
- }
277
- )
278
- }
279
- ),
280
- /* @__PURE__ */ jsx(
281
- "div",
282
- {
283
- style: {
284
- fontSize: appearance.labelFontSize,
285
- fontWeight: 700,
286
- letterSpacing: appearance.labelLetterSpacing,
287
- textTransform: "uppercase",
288
- color: focused ? "rgba(232,240,255,0.95)" : "rgba(232,240,255,0.72)",
289
- textAlign: "center",
290
- textShadow: "0 2px 12px rgba(0,0,0,0.95)",
291
- whiteSpace: "nowrap",
292
- maxWidth: appearance.previewWidth + 40,
293
- overflow: "hidden",
294
- textOverflow: "ellipsis"
369
+ )
295
370
  },
296
- children: label
297
- }
298
- )
299
- ]
300
- }
301
- )
371
+ focused ? "focused" : "unfocused"
372
+ ),
373
+ hasControls ? /* @__PURE__ */ jsx(
374
+ "div",
375
+ {
376
+ style: {
377
+ width: appearance.previewWidth,
378
+ pointerEvents: "auto",
379
+ flexShrink: 0
380
+ },
381
+ children: controlsSlot
382
+ }
383
+ ) : null,
384
+ /* @__PURE__ */ jsx(
385
+ "div",
386
+ {
387
+ style: {
388
+ marginTop: appearance.labelGap,
389
+ fontSize: appearance.labelFontSize,
390
+ fontWeight: 700,
391
+ letterSpacing: appearance.labelLetterSpacing,
392
+ textTransform: "uppercase",
393
+ color: "rgba(232,240,255,0.96)",
394
+ textAlign: "center",
395
+ textShadow: "0 0 18px rgba(0,0,0,0.95), 0 2px 12px rgba(0,0,0,0.95), 0 0 6px rgba(126,184,255,0.25)",
396
+ whiteSpace: "nowrap",
397
+ maxWidth: appearance.previewWidth + 48,
398
+ overflow: "hidden",
399
+ textOverflow: "ellipsis"
400
+ },
401
+ children: label
402
+ }
403
+ )
404
+ ]
405
+ }
406
+ )
407
+ ]
302
408
  }
303
409
  );
304
410
  }
@@ -307,6 +413,7 @@ function Constellation({
307
413
  storyTitle,
308
414
  edges = [],
309
415
  appearance: appearanceOverrides,
416
+ showOverlays = true,
310
417
  onSceneSelect
311
418
  }) {
312
419
  const appearance = mergeConstellationAppearance(appearanceOverrides);
@@ -323,12 +430,14 @@ function Constellation({
323
430
  }
324
431
  }
325
432
  return /* @__PURE__ */ jsxs("group", { children: [
326
- storyTitle && bounds ? /* @__PURE__ */ jsx(
433
+ showOverlays && storyTitle && bounds ? /* @__PURE__ */ jsx(
327
434
  ConstellationTitle,
328
435
  {
329
436
  title: storyTitle,
330
437
  bounds,
331
- appearance
438
+ appearance,
439
+ arcFrom: scenes[0]?.visual.position,
440
+ arcTo: scenes[scenes.length - 1]?.visual.position
332
441
  }
333
442
  ) : null,
334
443
  scenes.map((entry, i) => /* @__PURE__ */ jsxs("group", { position: entry.visual.position, children: [
@@ -341,7 +450,7 @@ function Constellation({
341
450
  onSelect: onSceneSelect ? () => onSceneSelect(i) : void 0
342
451
  }
343
452
  ),
344
- entry.label || entry.previewImageUrl || entry.previewContent ? /* @__PURE__ */ jsx(
453
+ showOverlays && (entry.label || entry.previewImageUrl || entry.previewContent) ? /* @__PURE__ */ jsx(
345
454
  StarBillboard,
346
455
  {
347
456
  label: entry.label ?? "",
@@ -349,7 +458,9 @@ function Constellation({
349
458
  previewImageUrl: entry.previewImageUrl,
350
459
  previewContent: entry.previewContent,
351
460
  dimmed: entry.dimmed,
352
- focused: entry.focused
461
+ focused: entry.focused,
462
+ controlsSlot: entry.controlsSlot,
463
+ visible: showOverlays
353
464
  }
354
465
  ) : null
355
466
  ] }, entry.id)),