worldorbit 2.5.16 → 2.6.0

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 (35) hide show
  1. package/README.md +81 -15
  2. package/dist/browser/core/dist/index.js +1228 -110
  3. package/dist/browser/editor/dist/index.js +1896 -180
  4. package/dist/browser/markdown/dist/index.js +1071 -99
  5. package/dist/browser/viewer/dist/index.js +1127 -113
  6. package/dist/unpkg/core/dist/index.js +1228 -110
  7. package/dist/unpkg/editor/dist/index.js +1896 -180
  8. package/dist/unpkg/markdown/dist/index.js +1071 -99
  9. package/dist/unpkg/viewer/dist/index.js +1127 -113
  10. package/dist/unpkg/worldorbit-core.min.js +12 -12
  11. package/dist/unpkg/worldorbit-editor.min.js +295 -203
  12. package/dist/unpkg/worldorbit-markdown.min.js +66 -58
  13. package/dist/unpkg/worldorbit-viewer.min.js +84 -76
  14. package/dist/unpkg/worldorbit.js +1304 -124
  15. package/dist/unpkg/worldorbit.min.js +88 -80
  16. package/package.json +1 -1
  17. package/packages/core/dist/atlas-edit.js +75 -1
  18. package/packages/core/dist/atlas-validate.js +211 -8
  19. package/packages/core/dist/draft-parse.js +401 -22
  20. package/packages/core/dist/draft.d.ts +5 -2
  21. package/packages/core/dist/draft.js +103 -8
  22. package/packages/core/dist/format.js +99 -6
  23. package/packages/core/dist/load.js +9 -2
  24. package/packages/core/dist/normalize.js +1 -0
  25. package/packages/core/dist/scene.js +400 -64
  26. package/packages/core/dist/types.d.ts +60 -4
  27. package/packages/editor/dist/editor.js +702 -65
  28. package/packages/editor/dist/types.d.ts +3 -1
  29. package/packages/viewer/dist/atlas-state.js +11 -2
  30. package/packages/viewer/dist/atlas-viewer.js +19 -7
  31. package/packages/viewer/dist/render.js +31 -2
  32. package/packages/viewer/dist/theme.js +1 -0
  33. package/packages/viewer/dist/tooltip.js +9 -0
  34. package/packages/viewer/dist/types.d.ts +12 -2
  35. package/packages/viewer/dist/viewer.js +28 -1
@@ -1,4 +1,4 @@
1
- import type { AtlasDocumentPath, AtlasResolvedDiagnostic, WorldOrbitAtlasDocument, WorldOrbitAtlasSystem, WorldOrbitAtlasViewpoint, WorldOrbitObject } from "@worldorbit/core";
1
+ import type { AtlasDocumentPath, AtlasResolvedDiagnostic, WorldOrbitAtlasDocument, WorldOrbitEvent, WorldOrbitAtlasSystem, WorldOrbitAtlasViewpoint, WorldOrbitObject } from "@worldorbit/core";
2
2
  export interface WorldOrbitEditorSelection {
3
3
  path: AtlasDocumentPath | null;
4
4
  }
@@ -37,6 +37,7 @@ export interface WorldOrbitEditor {
37
37
  undo(): boolean;
38
38
  redo(): boolean;
39
39
  addObject(type?: WorldOrbitObject["type"]): string;
40
+ addEvent(): string;
40
41
  addViewpoint(): string;
41
42
  addAnnotation(): string;
42
43
  addMetadata(key?: string, value?: string): string;
@@ -49,5 +50,6 @@ export interface WorldOrbitEditorFormState {
49
50
  selection: WorldOrbitEditorSelection | null;
50
51
  system: WorldOrbitAtlasSystem | null;
51
52
  viewpoints: WorldOrbitAtlasViewpoint[];
53
+ events: WorldOrbitEvent[];
52
54
  objects: WorldOrbitObject[];
53
55
  }
@@ -62,14 +62,17 @@ export function searchSceneObjects(scene, query, limit = 12) {
62
62
  }
63
63
  export function createAtlasStateSnapshot(viewerState, renderOptions, filter, viewpointId) {
64
64
  return {
65
- version: "2.0",
65
+ version: "2.5",
66
66
  viewpointId,
67
+ activeEventId: renderOptions.activeEventId ?? null,
67
68
  viewerState: { ...viewerState },
68
69
  renderOptions: {
69
70
  preset: renderOptions.preset,
70
71
  projection: renderOptions.projection,
72
+ camera: renderOptions.camera ? { ...renderOptions.camera } : null,
71
73
  layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
72
74
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
75
+ activeEventId: renderOptions.activeEventId ?? null,
73
76
  },
74
77
  filter: normalizeViewerFilter(filter),
75
78
  };
@@ -80,8 +83,9 @@ export function serializeViewerAtlasState(state) {
80
83
  export function deserializeViewerAtlasState(serialized) {
81
84
  const raw = JSON.parse(decodeURIComponent(serialized));
82
85
  return {
83
- version: "2.0",
86
+ version: raw.version === "2.0" ? "2.0" : "2.5",
84
87
  viewpointId: raw.viewpointId ?? null,
88
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
85
89
  viewerState: {
86
90
  scale: raw.viewerState?.scale ?? 1,
87
91
  rotationDeg: raw.viewerState?.rotationDeg ?? 0,
@@ -92,10 +96,12 @@ export function deserializeViewerAtlasState(serialized) {
92
96
  renderOptions: {
93
97
  preset: raw.renderOptions?.preset,
94
98
  projection: raw.renderOptions?.projection,
99
+ camera: raw.renderOptions?.camera ? { ...raw.renderOptions.camera } : null,
95
100
  layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : undefined,
96
101
  scaleModel: raw.renderOptions?.scaleModel
97
102
  ? { ...raw.renderOptions.scaleModel }
98
103
  : undefined,
104
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
99
105
  },
100
106
  filter: normalizeViewerFilter(raw.filter ?? null),
101
107
  };
@@ -113,10 +119,12 @@ export function createViewerBookmark(name, label, atlasState) {
113
119
  viewerState: { ...atlasState.viewerState },
114
120
  renderOptions: {
115
121
  ...atlasState.renderOptions,
122
+ camera: atlasState.renderOptions.camera ? { ...atlasState.renderOptions.camera } : null,
116
123
  layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : undefined,
117
124
  scaleModel: atlasState.renderOptions.scaleModel
118
125
  ? { ...atlasState.renderOptions.scaleModel }
119
126
  : undefined,
127
+ activeEventId: atlasState.renderOptions.activeEventId ?? null,
120
128
  },
121
129
  filter: atlasState.filter ? { ...atlasState.filter } : null,
122
130
  },
@@ -134,6 +142,7 @@ export function sceneViewpointToLayerOptions(viewpoint) {
134
142
  background: viewpoint.layers.background,
135
143
  guides: viewpoint.layers.guides,
136
144
  relations: viewpoint.layers.relations,
145
+ events: viewpoint.layers.events,
137
146
  orbits: viewpoint.layers["orbits-front"] === undefined && viewpoint.layers["orbits-back"] === undefined
138
147
  ? undefined
139
148
  : viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
@@ -326,6 +326,8 @@ export function createAtlasViewer(container, options) {
326
326
  }
327
327
  function buildInspectorSnapshot() {
328
328
  const activeViewer = requireViewer();
329
+ const scene = activeViewer.getScene();
330
+ const camera = scene.camera;
329
331
  return {
330
332
  selection: activeViewer.getSelectionDetails(),
331
333
  activeViewpoint: activeViewer.getActiveViewpoint(),
@@ -333,13 +335,23 @@ export function createAtlasViewer(container, options) {
333
335
  atlasState: activeViewer.getAtlasState(),
334
336
  visibleObjectIds: activeViewer.getVisibleObjects().map((object) => object.objectId),
335
337
  scene: {
336
- title: activeViewer.getScene().title,
337
- projection: activeViewer.getScene().projection,
338
- renderPreset: activeViewer.getScene().renderPreset,
339
- groupCount: activeViewer.getScene().groups.length,
340
- semanticGroupCount: activeViewer.getScene().semanticGroups.length,
341
- relationCount: activeViewer.getScene().relations.length,
342
- viewpointCount: activeViewer.getScene().viewpoints.length,
338
+ title: scene.title,
339
+ projection: scene.projection,
340
+ renderProjection: scene.renderProjection,
341
+ camera: camera
342
+ ? {
343
+ azimuth: camera.azimuth,
344
+ elevation: camera.elevation,
345
+ roll: camera.roll,
346
+ distance: camera.distance,
347
+ }
348
+ : null,
349
+ renderPreset: scene.renderPreset,
350
+ groupCount: scene.groups.length,
351
+ semanticGroupCount: scene.semanticGroups.length,
352
+ relationCount: scene.relations.length,
353
+ eventCount: scene.events.length,
354
+ viewpointCount: scene.viewpoints.length,
343
355
  },
344
356
  };
345
357
  }
@@ -39,6 +39,12 @@ export function renderSceneToSvg(scene, options = {}) {
39
39
  .map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`)
40
40
  .join("")
41
41
  : "";
42
+ const eventMarkup = layers.events
43
+ ? scene.events
44
+ .filter((event) => !event.hidden)
45
+ .map((event) => renderSceneEventOverlay(scene, event, visibleObjectIds, theme))
46
+ .join("")
47
+ : "";
42
48
  const objectMarkup = layers.objects
43
49
  ? visibleObjects
44
50
  .map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme))
@@ -86,6 +92,9 @@ export function renderSceneToSvg(scene, options = {}) {
86
92
  .wo-orbit-front { opacity: 0.9; }
87
93
  .wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
88
94
  .wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
95
+ .wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
96
+ .wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
97
+ .wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
89
98
  .wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
90
99
  .wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
91
100
  .wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
@@ -120,14 +129,34 @@ export function renderSceneToSvg(scene, options = {}) {
120
129
  ${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
121
130
  ${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
122
131
  ${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
132
+ ${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
123
133
  ${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
124
- ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
125
- ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
134
+ ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
135
+ ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
126
136
  </g>
127
137
  </g>
128
138
  </g>
129
139
  </svg>`;
130
140
  }
141
+ function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
142
+ const participants = event.objectIds
143
+ .filter((objectId) => visibleObjectIds.has(objectId))
144
+ .map((objectId) => scene.objects.find((object) => object.objectId === objectId && !object.hidden))
145
+ .filter(Boolean);
146
+ if (participants.length === 0) {
147
+ return "";
148
+ }
149
+ const stroke = event.event.color || theme.accent;
150
+ const label = event.event.label || event.event.id;
151
+ const lineMarkup = participants
152
+ .map((object) => `<line class="wo-event-line" x1="${event.x}" y1="${event.y}" x2="${object.x}" y2="${object.y}" stroke="${escapeAttribute(stroke)}" data-event-id="${escapeAttribute(event.eventId)}" data-object-id="${escapeAttribute(object.objectId)}" />`)
153
+ .join("");
154
+ return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
155
+ ${lineMarkup}
156
+ <circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
157
+ <text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
158
+ </g>`;
159
+ }
131
160
  export function renderDocumentToSvg(document, options = {}) {
132
161
  return renderSceneToSvg(renderDocumentToScene(document, options), options);
133
162
  }
@@ -2,6 +2,7 @@ const DEFAULT_LAYERS = {
2
2
  background: true,
3
3
  guides: true,
4
4
  relations: true,
5
+ events: true,
5
6
  orbits: true,
6
7
  objects: true,
7
8
  labels: true,
@@ -111,6 +111,15 @@ function buildTooltipFields(details) {
111
111
  value: `${details.object.resonance.targetObjectId} ${details.object.resonance.ratio}`,
112
112
  });
113
113
  }
114
+ if (details.relatedEvents.length > 0) {
115
+ fields.set("events", {
116
+ key: "events",
117
+ label: "Events",
118
+ value: details.relatedEvents
119
+ .map((event) => event.event.label || event.event.id)
120
+ .join(", "),
121
+ });
122
+ }
114
123
  if (placement?.mode === "at") {
115
124
  fields.set("placement", {
116
125
  key: "placement",
@@ -1,4 +1,4 @@
1
- import type { CoordinatePoint, RenderOrbitVisual, RenderPresetName, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, ViewProjection, WorldOrbitObject, WorldOrbitDocument } from "@worldorbit/core";
1
+ import type { CoordinatePoint, RenderOrbitVisual, RenderProjectionFallback, RenderPresetName, RenderSceneEvent, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, ViewProjection, WorldOrbitObject, WorldOrbitDocument, WorldOrbitViewCamera } from "@worldorbit/core";
2
2
  export type WorldOrbitThemeName = "atlas" | "nightglass" | "ember";
3
3
  export type WorldOrbitEmbedMode = "static" | "interactive";
4
4
  export type TooltipMode = "hover" | "pinned" | "disabled";
@@ -29,6 +29,7 @@ export interface ViewerLayerOptions {
29
29
  background?: boolean;
30
30
  guides?: boolean;
31
31
  relations?: boolean;
32
+ events?: boolean;
32
33
  orbits?: boolean;
33
34
  objects?: boolean;
34
35
  labels?: boolean;
@@ -79,6 +80,7 @@ export interface ViewerObjectDetails {
79
80
  orbit: RenderOrbitVisual | null;
80
81
  relatedOrbits: RenderOrbitVisual[];
81
82
  relations: RenderScene["relations"];
83
+ relatedEvents: RenderSceneEvent[];
82
84
  parent: RenderSceneObject | null;
83
85
  children: RenderSceneObject[];
84
86
  ancestors: RenderSceneObject[];
@@ -102,14 +104,17 @@ export interface ViewerTooltipDetails {
102
104
  details: ViewerObjectDetails;
103
105
  }
104
106
  export interface ViewerAtlasState {
105
- version: "2.0";
107
+ version: "2.0" | "2.5";
106
108
  viewpointId: string | null;
109
+ activeEventId?: string | null;
107
110
  viewerState: ViewerState;
108
111
  renderOptions: {
109
112
  preset?: RenderPresetName;
110
113
  projection?: "document" | ViewProjection;
114
+ camera?: WorldOrbitViewCamera | null;
111
115
  layers?: ViewerLayerOptions;
112
116
  scaleModel?: Partial<RenderScaleModel>;
117
+ activeEventId?: string | null;
113
118
  };
114
119
  filter: ViewerFilter | null;
115
120
  }
@@ -135,10 +140,13 @@ export interface AtlasInspectorSnapshot {
135
140
  scene: {
136
141
  title: string;
137
142
  projection: ViewProjection;
143
+ renderProjection: RenderProjectionFallback;
144
+ camera: WorldOrbitViewCamera | null;
138
145
  renderPreset: RenderPresetName | null;
139
146
  groupCount: number;
140
147
  semanticGroupCount: number;
141
148
  relationCount: number;
149
+ eventCount: number;
142
150
  viewpointCount: number;
143
151
  };
144
152
  }
@@ -189,6 +197,8 @@ export interface WorldOrbitViewer {
189
197
  listViewpoints(): RenderSceneViewpoint[];
190
198
  getActiveViewpoint(): RenderSceneViewpoint | null;
191
199
  goToViewpoint(id: string): boolean;
200
+ getActiveEventId(): string | null;
201
+ setActiveEvent(id: string | null): void;
192
202
  search(query: string, limit?: number): ViewerSearchResult[];
193
203
  getFilter(): ViewerFilter | null;
194
204
  setFilter(filter: ViewerFilter | null): void;
@@ -43,6 +43,7 @@ export function createInteractiveViewer(container, options) {
43
43
  padding: options.padding,
44
44
  preset: options.preset,
45
45
  projection: options.projection,
46
+ camera: options.camera ? { ...options.camera } : null,
46
47
  scaleModel: options.scaleModel ? { ...options.scaleModel } : undefined,
47
48
  theme: options.theme,
48
49
  layers: options.layers,
@@ -333,6 +334,12 @@ export function createInteractiveViewer(container, options) {
333
334
  if (currentInput.kind !== "scene" && viewpoint.projection !== scene.projection) {
334
335
  nextRenderOptions.projection = viewpoint.projection;
335
336
  }
337
+ if (viewpoint.camera) {
338
+ nextRenderOptions.camera = { ...viewpoint.camera };
339
+ }
340
+ else if (renderOptions.camera) {
341
+ nextRenderOptions.camera = null;
342
+ }
336
343
  if (viewpointLayers) {
337
344
  nextRenderOptions.layers = viewpointLayers;
338
345
  }
@@ -355,6 +362,12 @@ export function createInteractiveViewer(container, options) {
355
362
  emitAtlasStateChange();
356
363
  return true;
357
364
  },
365
+ getActiveEventId() {
366
+ return renderOptions.activeEventId ?? null;
367
+ },
368
+ setActiveEvent(id) {
369
+ api.setRenderOptions({ activeEventId: id });
370
+ },
358
371
  search(query, limit = 12) {
359
372
  return searchSceneObjects(scene, query, limit);
360
373
  },
@@ -653,6 +666,9 @@ export function createInteractiveViewer(container, options) {
653
666
  relations: scene.relations.filter((relation) => !relation.hidden &&
654
667
  (relation.fromObjectId === renderObject.objectId ||
655
668
  relation.toObjectId === renderObject.objectId)),
669
+ relatedEvents: scene.events.filter((event) => !event.hidden &&
670
+ (event.targetObjectId === renderObject.objectId ||
671
+ event.objectIds.includes(renderObject.objectId))),
656
672
  parent: getObjectById(renderObject.parentId),
657
673
  children: renderObject.childIds.map((childId) => getObjectById(childId)).filter(Boolean),
658
674
  ancestors: renderObject.ancestorIds
@@ -983,18 +999,27 @@ function renderSceneFromInput(input, renderOptions) {
983
999
  function cloneRenderOptions(renderOptions) {
984
1000
  return {
985
1001
  ...renderOptions,
1002
+ camera: renderOptions.camera ? { ...renderOptions.camera } : null,
986
1003
  filter: renderOptions.filter ? { ...renderOptions.filter } : undefined,
987
1004
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
988
1005
  layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
989
1006
  theme: renderOptions.theme && typeof renderOptions.theme === "object"
990
1007
  ? { ...renderOptions.theme }
991
1008
  : renderOptions.theme,
1009
+ activeEventId: renderOptions.activeEventId ?? null,
992
1010
  };
993
1011
  }
994
1012
  function mergeRenderOptions(current, next) {
995
1013
  return {
996
1014
  ...current,
997
1015
  ...next,
1016
+ camera: next.camera !== undefined
1017
+ ? next.camera
1018
+ ? { ...next.camera }
1019
+ : null
1020
+ : current.camera
1021
+ ? { ...current.camera }
1022
+ : null,
998
1023
  filter: next.filter !== undefined
999
1024
  ? normalizeViewerFilter(next.filter)
1000
1025
  : current.filter
@@ -1027,7 +1052,9 @@ function hasSceneAffectingRenderOptions(options) {
1027
1052
  options.padding !== undefined ||
1028
1053
  options.preset !== undefined ||
1029
1054
  options.projection !== undefined ||
1030
- options.scaleModel !== undefined);
1055
+ options.camera !== undefined ||
1056
+ options.scaleModel !== undefined ||
1057
+ options.activeEventId !== undefined);
1031
1058
  }
1032
1059
  function resolveSourceRenderOptions(loaded, renderOptions) {
1033
1060
  const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;