@chuzi/shared 1.3.45 → 1.3.47

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.
@@ -77,6 +77,10 @@ interface ConstellationSceneEntry {
77
77
  id: string;
78
78
  /** Visual with final position already computed by the consumer. */
79
79
  visual: AtomVisualProps;
80
+ /** Title scene — arc starts here. */
81
+ isTitle?: boolean;
82
+ /** End scene — arc ends here. */
83
+ isEnd?: boolean;
80
84
  /** Scene label shown beneath the preview card. */
81
85
  label?: string;
82
86
  /** Static preview image (coverbox, poster frame, etc.). */
@@ -175,10 +179,12 @@ interface DistributeOptions {
175
179
  * sessions.
176
180
  */
177
181
  declare function distributeStars(count: number, options?: DistributeOptions): Vec3[];
178
- /** World-space gap between adjacent scenes along the story spine. */
179
- declare const COSMOS_SCENE_LEVEL_SPACING = 6;
182
+ /** World-space gap between adjacent tree levels along +Z (chronological depth). */
183
+ declare const COSMOS_SCENE_LEVEL_SPACING = 11;
180
184
  /** Branch spread multiplier for tree_graph x offsets. */
181
- declare const COSMOS_SCENE_BRANCH_SPACING = 1.1;
185
+ declare const COSMOS_SCENE_BRANCH_SPACING = 1.6;
186
+ /** Maximum distance between two connected scene stars (fallback spine only). */
187
+ declare const COSMOS_MAX_SCENE_EDGE_LENGTH = 14;
182
188
  /** Minimum empty space between constellation bounds, in scene spacings. */
183
189
  declare const COSMOS_CONSTELLATION_GAP_SCENES = 2;
184
190
  /** Halo around a lone title star before scenes load. */
@@ -192,6 +198,10 @@ interface ConstellationLayoutScene {
192
198
  order?: number;
193
199
  created_at?: string;
194
200
  }
201
+ interface ConstellationLayoutEdge {
202
+ source: string;
203
+ target: string;
204
+ }
195
205
  interface ConstellationLayoutNode {
196
206
  id: string;
197
207
  level: number;
@@ -200,6 +210,7 @@ interface ConstellationLayoutNode {
200
210
  }
201
211
  interface ConstellationLayoutGraph {
202
212
  nodes: ConstellationLayoutNode[];
213
+ edges?: ConstellationLayoutEdge[];
203
214
  meta?: {
204
215
  root_id?: string | null;
205
216
  };
@@ -215,7 +226,10 @@ interface ConstellationAnchorOptions {
215
226
  visualPadding?: number;
216
227
  }
217
228
  /**
218
- * Scene positions relative to the title star at the origin (constellation anchor).
229
+ * Scene positions relative to the title star at the constellation anchor.
230
+ * When a tree_graph is present, level maps to +Z depth (chronological "after")
231
+ * and x spreads branches laterally. Without a graph, scenes fall back to a
232
+ * linear spine along +Z.
219
233
  */
220
234
  declare function computeConstellationScenePositions(scenes: ConstellationLayoutScene[], graph: ConstellationLayoutGraph | undefined, anchor?: Vec3): Map<string, Vec3>;
221
235
  /** Bounding radius from the title anchor to the outermost scene plus visual padding. */
@@ -244,4 +258,4 @@ interface StarBillboardProps {
244
258
  */
245
259
  declare function StarBillboard({ label, appearance, previewImageUrl, previewContent, dimmed, focused, visible, controlsSlot, }: StarBillboardProps): react_jsx_runtime.JSX.Element | null;
246
260
 
247
- export { COSMOS_CONSTELLATION_GAP_SCENES, COSMOS_CONSTELLATION_VISUAL_PADDING, COSMOS_DEFAULT_FOOTPRINT_RADIUS, COSMOS_SCENE_BRANCH_SPACING, COSMOS_SCENE_LEVEL_SPACING, Constellation, type ConstellationAnchorInput, type ConstellationAnchorOptions, ConstellationAppearance, type ConstellationBounds, ConstellationEdge, type ConstellationEdgeEntry, type ConstellationEdgeProps, type ConstellationLayoutGraph, type ConstellationLayoutNode, type ConstellationLayoutScene, type ConstellationProps, type ConstellationSceneEntry, type ConstellationStoryOverlay, ConstellationTitle, type ConstellationTitleProps, CosmosSandbox, type CosmosSandboxProps, type DistributeOptions, Star, StarBillboard, type StarBillboardProps, type StarProps, type Vec3, World, type WorldProps, computeConstellationBounds, computeConstellationScenePositions, constellationFootprintRadius, distributeConstellationAnchors, distributeStars };
261
+ export { COSMOS_CONSTELLATION_GAP_SCENES, COSMOS_CONSTELLATION_VISUAL_PADDING, COSMOS_DEFAULT_FOOTPRINT_RADIUS, COSMOS_MAX_SCENE_EDGE_LENGTH, COSMOS_SCENE_BRANCH_SPACING, COSMOS_SCENE_LEVEL_SPACING, Constellation, type ConstellationAnchorInput, type ConstellationAnchorOptions, ConstellationAppearance, type ConstellationBounds, ConstellationEdge, type ConstellationEdgeEntry, type ConstellationEdgeProps, type ConstellationLayoutEdge, type ConstellationLayoutGraph, type ConstellationLayoutNode, type ConstellationLayoutScene, type ConstellationProps, type ConstellationSceneEntry, type ConstellationStoryOverlay, ConstellationTitle, type ConstellationTitleProps, CosmosSandbox, type CosmosSandboxProps, type DistributeOptions, Star, StarBillboard, type StarBillboardProps, type StarProps, type Vec3, World, type WorldProps, computeConstellationBounds, computeConstellationScenePositions, constellationFootprintRadius, distributeConstellationAnchors, distributeStars };
@@ -200,20 +200,30 @@ function ConstellationTitle({
200
200
  const centerX = svgWidth / 2;
201
201
  const arcY = svgHeight * 0.88;
202
202
  const arcPeak = svgHeight * 0.06;
203
- let arcStartX = svgWidth * 0.04;
204
- let arcEndX = svgWidth * 0.96;
203
+ const minArcSpan = Math.max(240, title.trim().length * 15);
204
+ let arcStartX = centerX - minArcSpan / 2;
205
+ let arcEndX = centerX + minArcSpan / 2;
205
206
  if (arcFrom && arcTo) {
206
207
  const [fromX] = arcFrom;
207
208
  const [toX] = arcTo;
208
209
  arcStartX = centerX + (fromX - bounds.center[0]) * WORLD_TO_SVG;
209
210
  arcEndX = centerX + (toX - bounds.center[0]) * WORLD_TO_SVG;
210
- arcStartX = Math.max(12, Math.min(arcStartX, svgWidth - 12));
211
- arcEndX = Math.max(12, Math.min(arcEndX, svgWidth - 12));
212
211
  if (arcStartX > arcEndX) {
213
212
  const swap = arcStartX;
214
213
  arcStartX = arcEndX;
215
214
  arcEndX = swap;
216
215
  }
216
+ if (arcEndX - arcStartX < minArcSpan) {
217
+ const mid = (arcStartX + arcEndX) / 2;
218
+ arcStartX = mid - minArcSpan / 2;
219
+ arcEndX = mid + minArcSpan / 2;
220
+ }
221
+ }
222
+ arcStartX = Math.max(12, Math.min(arcStartX, svgWidth - 12));
223
+ arcEndX = Math.max(12, Math.min(arcEndX, svgWidth - 12));
224
+ if (arcEndX - arcStartX < minArcSpan * 0.65) {
225
+ arcStartX = Math.max(12, centerX - minArcSpan / 2);
226
+ arcEndX = Math.min(svgWidth - 12, centerX + minArcSpan / 2);
217
227
  }
218
228
  const arcMidX = (arcStartX + arcEndX) / 2;
219
229
  const arcPath = `M ${arcStartX} ${arcY} Q ${arcMidX} ${arcPeak} ${arcEndX} ${arcY}`;
@@ -583,6 +593,8 @@ function Constellation({
583
593
  const bounds = computeConstellationBounds(
584
594
  scenes.map((s) => s.visual.position)
585
595
  );
596
+ const titleScene = scenes.find((s) => s.isTitle) ?? scenes[0];
597
+ const endScene = scenes.find((s) => s.isEnd) ?? scenes[scenes.length - 1];
586
598
  const resolvedEdges = [];
587
599
  for (const edge of edges) {
588
600
  const src = sceneMap.get(edge.source);
@@ -598,8 +610,8 @@ function Constellation({
598
610
  title: storyTitle,
599
611
  bounds,
600
612
  appearance,
601
- arcFrom: scenes[0]?.visual.position,
602
- arcTo: scenes[scenes.length - 1]?.visual.position,
613
+ arcFrom: titleScene?.visual.position,
614
+ arcTo: endScene?.visual.position,
603
615
  storyOverlay
604
616
  }
605
617
  ) : null,
@@ -728,11 +740,13 @@ function mulberry32(seed) {
728
740
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
729
741
  };
730
742
  }
731
- var COSMOS_SCENE_LEVEL_SPACING = 6;
732
- var COSMOS_SCENE_BRANCH_SPACING = 1.1;
743
+ var COSMOS_SCENE_LEVEL_SPACING = 11;
744
+ var COSMOS_SCENE_BRANCH_SPACING = 1.6;
745
+ var COSMOS_MAX_SCENE_EDGE_LENGTH = 14;
733
746
  var COSMOS_CONSTELLATION_GAP_SCENES = 2;
734
747
  var COSMOS_DEFAULT_FOOTPRINT_RADIUS = 10;
735
748
  var COSMOS_CONSTELLATION_VISUAL_PADDING = 6;
749
+ var GOLDEN_ANGLE = 2.399963229728653;
736
750
  function sortScenesForConstellationLayout(sceneList) {
737
751
  return [...sceneList].sort((a, b) => {
738
752
  if (a.is_title !== b.is_title) return a.is_title ? -1 : 1;
@@ -768,35 +782,122 @@ function orderScenesForConstellationLayout(sceneList, graph) {
768
782
  }
769
783
  return ordered;
770
784
  }
785
+ function hashSceneId(id, salt = "") {
786
+ let h = 0;
787
+ const key = id + salt;
788
+ for (let i = 0; i < key.length; i++) {
789
+ h = h * 31 + key.charCodeAt(i) >>> 0;
790
+ }
791
+ return h / 4294967296;
792
+ }
793
+ function clampEdgeLength(positions, sourceId, targetId, maxLength) {
794
+ const src = positions.get(sourceId);
795
+ const tgt = positions.get(targetId);
796
+ if (!src || !tgt) return;
797
+ const dx = tgt[0] - src[0];
798
+ const dy = tgt[1] - src[1];
799
+ const dz = tgt[2] - src[2];
800
+ const len = Math.hypot(dx, dy, dz);
801
+ if (len <= maxLength || len === 0) return;
802
+ const scale = maxLength / len;
803
+ positions.set(targetId, [
804
+ src[0] + dx * scale,
805
+ src[1] + dy * scale,
806
+ src[2] + dz * scale
807
+ ]);
808
+ }
809
+ function buildParentByChild(ordered, edges) {
810
+ const parentByChild = /* @__PURE__ */ new Map();
811
+ const orderIndex = new Map(ordered.map((scene, idx) => [scene.id, idx]));
812
+ const sortedEdges = [...edges].sort((a, b) => {
813
+ const aIdx = orderIndex.get(a.target) ?? Number.MAX_SAFE_INTEGER;
814
+ const bIdx = orderIndex.get(b.target) ?? Number.MAX_SAFE_INTEGER;
815
+ return aIdx - bIdx;
816
+ });
817
+ for (const edge of sortedEdges) {
818
+ if (!parentByChild.has(edge.target)) {
819
+ parentByChild.set(edge.target, edge.source);
820
+ }
821
+ }
822
+ return parentByChild;
823
+ }
824
+ function sceneLevelJitter(sceneId) {
825
+ return (hashSceneId(sceneId, "y") - 0.5) * 1.2;
826
+ }
771
827
  function computeConstellationScenePositions(scenes, graph, anchor = [0, 0, 0]) {
772
828
  const positions = /* @__PURE__ */ new Map();
773
829
  const ordered = orderScenesForConstellationLayout(scenes, graph);
774
- const count = ordered.length;
775
- if (!graph?.nodes?.length) {
776
- ordered.forEach((scene, idx) => {
777
- const along = count === 1 ? 0 : idx * COSMOS_SCENE_LEVEL_SPACING;
778
- positions.set(scene.id, [anchor[0] + along, anchor[1], anchor[2]]);
779
- });
780
- return positions;
781
- }
782
- const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
783
- const rootId = graph.meta?.root_id ?? graph.nodes[0]?.id;
784
- const rootNode = rootId ? nodeById.get(rootId) : graph.nodes[0];
785
- ordered.forEach((scene, idx) => {
786
- const node = nodeById.get(scene.id);
787
- if (node && rootNode) {
788
- const alongPath = node.level * COSMOS_SCENE_LEVEL_SPACING;
789
- const branchSpread = (node.x - rootNode.x) * COSMOS_SCENE_BRANCH_SPACING;
830
+ if (ordered.length === 0) return positions;
831
+ const root = ordered.find((scene) => scene.is_title) ?? ordered[0];
832
+ positions.set(root.id, [anchor[0], anchor[1], anchor[2]]);
833
+ const nodeById = new Map(
834
+ (graph?.nodes ?? []).map((node) => [node.id, node])
835
+ );
836
+ if (nodeById.size > 0) {
837
+ const rootNode = nodeById.get(root.id);
838
+ const rootX = rootNode?.x ?? 0;
839
+ for (const scene of ordered) {
840
+ if (scene.id === root.id) continue;
841
+ const node = nodeById.get(scene.id);
842
+ if (!node) continue;
790
843
  positions.set(scene.id, [
791
- anchor[0] + alongPath,
792
- anchor[1],
793
- anchor[2] + branchSpread
844
+ anchor[0] + (node.x - rootX) * COSMOS_SCENE_BRANCH_SPACING,
845
+ anchor[1] + sceneLevelJitter(scene.id),
846
+ anchor[2] + node.level * COSMOS_SCENE_LEVEL_SPACING
794
847
  ]);
795
- return;
796
848
  }
797
- const along = count === 1 ? 0 : idx * COSMOS_SCENE_LEVEL_SPACING;
798
- positions.set(scene.id, [anchor[0] + along, anchor[1], anchor[2]]);
799
- });
849
+ let fallbackLevel = 0;
850
+ for (const scene of ordered) {
851
+ if (positions.has(scene.id)) continue;
852
+ fallbackLevel += 1;
853
+ positions.set(scene.id, [
854
+ anchor[0],
855
+ anchor[1] + sceneLevelJitter(scene.id),
856
+ anchor[2] + fallbackLevel * COSMOS_SCENE_LEVEL_SPACING
857
+ ]);
858
+ }
859
+ return positions;
860
+ }
861
+ const edges = graph?.edges ?? [];
862
+ const parentByChild = edges.length > 0 ? buildParentByChild(ordered, edges) : /* @__PURE__ */ new Map();
863
+ let spineIndex = 0;
864
+ for (const scene of ordered) {
865
+ if (scene.id === root.id) continue;
866
+ let parentId = parentByChild.get(scene.id);
867
+ if (!parentId || !positions.has(parentId)) {
868
+ const prevScene = ordered[spineIndex] ?? root;
869
+ parentId = prevScene.id;
870
+ }
871
+ const parentPos = positions.get(parentId) ?? [anchor[0], anchor[1], anchor[2]];
872
+ const angle = spineIndex * GOLDEN_ANGLE + hashSceneId(scene.id) * 0.55;
873
+ const dist = COSMOS_MAX_SCENE_EDGE_LENGTH * (0.9 + hashSceneId(scene.id, "d") * 0.1);
874
+ const yJitter = sceneLevelJitter(scene.id);
875
+ positions.set(scene.id, [
876
+ parentPos[0] + Math.cos(angle) * dist,
877
+ parentPos[1] + yJitter,
878
+ parentPos[2] + Math.sin(angle) * dist
879
+ ]);
880
+ spineIndex += 1;
881
+ }
882
+ if (edges.length > 0) {
883
+ for (const edge of edges) {
884
+ clampEdgeLength(
885
+ positions,
886
+ edge.source,
887
+ edge.target,
888
+ COSMOS_MAX_SCENE_EDGE_LENGTH
889
+ );
890
+ }
891
+ } else {
892
+ for (let i = 1; i < ordered.length; i++) {
893
+ clampEdgeLength(
894
+ positions,
895
+ ordered[i - 1].id,
896
+ ordered[i].id,
897
+ COSMOS_MAX_SCENE_EDGE_LENGTH
898
+ );
899
+ }
900
+ }
800
901
  return positions;
801
902
  }
802
903
  function constellationFootprintRadius(localPositions, padding = COSMOS_CONSTELLATION_VISUAL_PADDING) {
@@ -863,6 +964,6 @@ function CosmosSandbox({
863
964
  )) });
864
965
  }
865
966
 
866
- export { COSMOS_CONSTELLATION_GAP_SCENES, COSMOS_CONSTELLATION_VISUAL_PADDING, COSMOS_DEFAULT_FOOTPRINT_RADIUS, COSMOS_SCENE_BRANCH_SPACING, COSMOS_SCENE_LEVEL_SPACING, Constellation, ConstellationEdge, ConstellationTitle, CosmosSandbox, DEFAULT_CONSTELLATION_APPEARANCE, Star, StarBillboard, World, computeConstellationBounds, computeConstellationScenePositions, constellationFootprintRadius, distributeConstellationAnchors, distributeStars, mergeConstellationAppearance };
967
+ export { COSMOS_CONSTELLATION_GAP_SCENES, COSMOS_CONSTELLATION_VISUAL_PADDING, COSMOS_DEFAULT_FOOTPRINT_RADIUS, COSMOS_MAX_SCENE_EDGE_LENGTH, COSMOS_SCENE_BRANCH_SPACING, COSMOS_SCENE_LEVEL_SPACING, Constellation, ConstellationEdge, ConstellationTitle, CosmosSandbox, DEFAULT_CONSTELLATION_APPEARANCE, Star, StarBillboard, World, computeConstellationBounds, computeConstellationScenePositions, constellationFootprintRadius, distributeConstellationAnchors, distributeStars, mergeConstellationAppearance };
867
968
  //# sourceMappingURL=index.js.map
868
969
  //# sourceMappingURL=index.js.map