worldorbit 3.0.5 → 3.0.6

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.
@@ -39546,6 +39546,7 @@ void main() {
39546
39546
  let cameraRoot = null;
39547
39547
  let runtime3d = null;
39548
39548
  let minimapRoot = null;
39549
+ let labelRoot = null;
39549
39550
  let tooltipRoot = null;
39550
39551
  let suppressClick = false;
39551
39552
  let activePointerId = null;
@@ -39570,7 +39571,7 @@ void main() {
39570
39571
  if (previousTabIndex === null) {
39571
39572
  container.tabIndex = 0;
39572
39573
  }
39573
- installViewerTooltipStyles();
39574
+ installViewerOverlayStyles();
39574
39575
  container.classList.add("wo-viewer-container");
39575
39576
  container.style.touchAction = behavior.touch ? "none" : previousTouchAction;
39576
39577
  if (!container.style.position) {
@@ -40120,6 +40121,8 @@ void main() {
40120
40121
  stopAnimationLoop();
40121
40122
  runtime3d?.destroy();
40122
40123
  runtime3d = null;
40124
+ labelRoot?.remove();
40125
+ labelRoot = null;
40123
40126
  tooltipRoot?.remove();
40124
40127
  tooltipRoot = null;
40125
40128
  minimapRoot?.remove();
@@ -40150,6 +40153,7 @@ void main() {
40150
40153
  svgElement = null;
40151
40154
  cameraRoot = null;
40152
40155
  minimapRoot = null;
40156
+ labelRoot = null;
40153
40157
  tooltipRoot = null;
40154
40158
  if (is3DView()) {
40155
40159
  spatialScene = spatialScene ?? renderSpatialSceneFromInput(currentInput, renderOptions, providedSpatialScene);
@@ -40168,6 +40172,10 @@ void main() {
40168
40172
  minimapRoot.dataset.worldorbitMinimapRoot = "true";
40169
40173
  container.append(minimapRoot);
40170
40174
  }
40175
+ labelRoot = document.createElement("div");
40176
+ labelRoot.className = "wo-viewer-label-root";
40177
+ labelRoot.dataset.worldorbitLabelRoot = "true";
40178
+ container.append(labelRoot);
40171
40179
  if (behavior.tooltipMode !== "disabled") {
40172
40180
  tooltipRoot = document.createElement("div");
40173
40181
  tooltipRoot.className = "wo-viewer-tooltip-root";
@@ -40179,6 +40187,7 @@ void main() {
40179
40187
  if (!is3DView() && (!svgElement || !cameraRoot)) {
40180
40188
  throw new Error("Interactive viewer could not locate the rendered SVG camera root.");
40181
40189
  }
40190
+ suppressStaticLabelLayers();
40182
40191
  state = resetView ? is3DView() ? { ...DEFAULT_VIEWER_STATE } : fitViewerState(scene, { ...DEFAULT_VIEWER_STATE }, constraints) : sanitizeState(state);
40183
40192
  applySelection(state.selectedObjectId && getObjectById(state.selectedObjectId) ? state.selectedObjectId : null, false);
40184
40193
  applyHover(hoveredObjectId && getObjectById(hoveredObjectId) ? hoveredObjectId : null, false);
@@ -40213,19 +40222,24 @@ void main() {
40213
40222
  return;
40214
40223
  }
40215
40224
  cameraRoot.setAttribute("transform", composeViewerTransform(scene, state));
40225
+ updateScreenLabels();
40216
40226
  updateMinimap();
40217
40227
  updateTooltip();
40218
40228
  }
40219
40229
  function applySelection(objectId, emitCallback = true) {
40220
40230
  if (!is3DView() && state.selectedObjectId) {
40221
- container.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)?.classList.remove("wo-object-selected");
40231
+ for (const element of container.querySelectorAll(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)) {
40232
+ element.classList.remove("wo-object-selected");
40233
+ }
40222
40234
  }
40223
40235
  state = {
40224
40236
  ...state,
40225
40237
  selectedObjectId: objectId && getObjectById(objectId) ? objectId : null
40226
40238
  };
40227
40239
  if (!is3DView() && state.selectedObjectId) {
40228
- container.querySelector(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)?.classList.add("wo-object-selected");
40240
+ for (const element of container.querySelectorAll(`[data-object-id="${cssEscape(state.selectedObjectId)}"]`)) {
40241
+ element.classList.add("wo-object-selected");
40242
+ }
40229
40243
  }
40230
40244
  syncAtlasHighlights();
40231
40245
  updateTooltip();
@@ -40558,14 +40572,17 @@ void main() {
40558
40572
  };
40559
40573
  }
40560
40574
  function project2DTooltipPoint(renderObject) {
40561
- if (!svgElement) {
40562
- return null;
40563
- }
40564
40575
  const anchor = {
40565
40576
  x: renderObject.anchorX ?? renderObject.x,
40566
40577
  y: renderObject.anchorY ?? renderObject.y - Math.max(renderObject.visualRadius, renderObject.radius)
40567
40578
  };
40568
- const viewportPoint = projectWorldPoint(anchor);
40579
+ return project2DScenePointToContainer(anchor);
40580
+ }
40581
+ function project2DScenePointToContainer(point) {
40582
+ if (!svgElement) {
40583
+ return null;
40584
+ }
40585
+ const viewportPoint = projectWorldPoint(point);
40569
40586
  const svgRect = svgElement.getBoundingClientRect();
40570
40587
  const containerRect = container.getBoundingClientRect();
40571
40588
  return {
@@ -40664,9 +40681,145 @@ void main() {
40664
40681
  state,
40665
40682
  timeSeconds: animationState.timeSeconds
40666
40683
  });
40684
+ updateScreenLabels();
40667
40685
  updateMinimap();
40668
40686
  updateTooltip();
40669
40687
  }
40688
+ function suppressStaticLabelLayers() {
40689
+ if (is3DView()) {
40690
+ return;
40691
+ }
40692
+ container.querySelector('[data-layer-id="labels"]')?.setAttribute("display", "none");
40693
+ for (const element of container.querySelectorAll(".wo-event-label")) {
40694
+ element.setAttribute("display", "none");
40695
+ }
40696
+ }
40697
+ function updateScreenLabels() {
40698
+ if (!labelRoot) {
40699
+ return;
40700
+ }
40701
+ const descriptors = buildScreenLabelDescriptors();
40702
+ labelRoot.replaceChildren(...descriptors.map((descriptor) => createScreenLabelElement(descriptor)));
40703
+ labelRoot.hidden = descriptors.length === 0;
40704
+ }
40705
+ function buildScreenLabelDescriptors() {
40706
+ const descriptors = [];
40707
+ const visibleObjectIds = getVisibleObjectIds();
40708
+ if (layerEnabled("labels")) {
40709
+ for (const label of scene.labels) {
40710
+ if (label.hidden || !visibleObjectIds.has(label.objectId)) {
40711
+ continue;
40712
+ }
40713
+ const point = is3DView() ? runtime3d?.projectObjectToContainer(label.objectId) ?? null : project2DScenePointToContainer({ x: label.x, y: label.y });
40714
+ if (!point) {
40715
+ continue;
40716
+ }
40717
+ descriptors.push({
40718
+ key: `object:${label.renderId}`,
40719
+ kind: "object",
40720
+ point: is3DView() ? { x: point.x, y: point.y - 18 } : point,
40721
+ textAnchor: label.textAnchor,
40722
+ objectId: label.objectId,
40723
+ primaryText: label.label,
40724
+ secondaryText: label.secondaryLabel,
40725
+ secondaryOffset: Math.max(label.secondaryY - label.y, 12)
40726
+ });
40727
+ }
40728
+ }
40729
+ if (!is3DView() && layerEnabled("events")) {
40730
+ for (const event of scene.events) {
40731
+ if (event.hidden || !isEventVisible(event, visibleObjectIds)) {
40732
+ continue;
40733
+ }
40734
+ const point = project2DScenePointToContainer({ x: event.x, y: event.y - 10 });
40735
+ if (!point) {
40736
+ continue;
40737
+ }
40738
+ descriptors.push({
40739
+ key: `event:${event.renderId}`,
40740
+ kind: "event",
40741
+ point,
40742
+ textAnchor: "middle",
40743
+ primaryText: event.event.label || event.event.id
40744
+ });
40745
+ }
40746
+ }
40747
+ return descriptors;
40748
+ }
40749
+ function isEventVisible(event, visibleObjectIds) {
40750
+ return event.objectIds.some((objectId) => visibleObjectIds.has(objectId));
40751
+ }
40752
+ function createScreenLabelElement(descriptor) {
40753
+ const element = document.createElement("div");
40754
+ element.className = `wo-viewer-label wo-viewer-label-${descriptor.kind}`;
40755
+ element.dataset.worldorbitScreenLabel = "true";
40756
+ element.dataset.labelKey = descriptor.key;
40757
+ element.dataset.anchor = descriptor.textAnchor;
40758
+ element.style.left = `${descriptor.point.x}px`;
40759
+ element.style.top = `${descriptor.point.y}px`;
40760
+ if (descriptor.objectId) {
40761
+ element.dataset.objectId = descriptor.objectId;
40762
+ for (const className of resolveScreenLabelClasses(descriptor.objectId)) {
40763
+ element.classList.add(className);
40764
+ }
40765
+ }
40766
+ const primary = document.createElement("span");
40767
+ primary.className = "wo-viewer-label-primary";
40768
+ if (descriptor.kind === "object") {
40769
+ primary.style.fontSize = `${14 * scene.scaleModel.labelMultiplier}px`;
40770
+ }
40771
+ primary.textContent = descriptor.primaryText;
40772
+ element.append(primary);
40773
+ if (descriptor.secondaryText) {
40774
+ const secondary = document.createElement("span");
40775
+ secondary.className = "wo-viewer-label-secondary";
40776
+ secondary.style.fontSize = `${11 * scene.scaleModel.labelMultiplier}px`;
40777
+ secondary.style.marginTop = `${Math.max(descriptor.secondaryOffset ?? 12, 10) - 10}px`;
40778
+ secondary.textContent = descriptor.secondaryText;
40779
+ element.append(secondary);
40780
+ }
40781
+ return element;
40782
+ }
40783
+ function layerEnabled(id) {
40784
+ return renderOptions.layers?.[id] !== false;
40785
+ }
40786
+ function resolveScreenLabelClasses(objectId) {
40787
+ const classes = [];
40788
+ const selectedDetails = buildObjectDetails(state.selectedObjectId);
40789
+ const hoveredDetails = buildObjectDetails(hoveredObjectId);
40790
+ if (state.selectedObjectId === objectId) {
40791
+ classes.push("wo-object-selected");
40792
+ }
40793
+ if (selectedDetails) {
40794
+ const selectedChain = /* @__PURE__ */ new Set([
40795
+ selectedDetails.objectId,
40796
+ ...selectedDetails.renderObject.childIds,
40797
+ ...selectedDetails.renderObject.ancestorIds
40798
+ ]);
40799
+ const selectedAncestors = new Set(selectedDetails.ancestors.map((ancestor) => ancestor.objectId));
40800
+ if (selectedChain.has(objectId)) {
40801
+ classes.push("wo-chain-selected");
40802
+ }
40803
+ if (selectedAncestors.has(objectId)) {
40804
+ classes.push("wo-ancestor-selected");
40805
+ }
40806
+ }
40807
+ if (hoveredDetails) {
40808
+ const hoveredChain = /* @__PURE__ */ new Set([
40809
+ hoveredDetails.objectId,
40810
+ ...hoveredDetails.renderObject.childIds,
40811
+ ...hoveredDetails.renderObject.ancestorIds
40812
+ ]);
40813
+ const hoveredAncestors = new Set(hoveredDetails.ancestors.map((ancestor) => ancestor.objectId));
40814
+ if (hoveredChain.has(objectId)) {
40815
+ classes.push("wo-chain-hover");
40816
+ }
40817
+ if (hoveredAncestors.has(objectId)) {
40818
+ classes.push("wo-ancestor-hover");
40819
+ }
40820
+ }
40821
+ return classes;
40822
+ }
40670
40823
  function create3DFocusState(objectId) {
40671
40824
  const target = spatialScene?.focusTargets.find((entry) => entry.objectId === objectId);
40672
40825
  if (!target) {
@@ -40918,7 +41071,7 @@ void main() {
40918
41071
  }
40919
41072
  return value.replace(/["\\]/g, "\\$&");
40920
41073
  }
40921
- function installViewerTooltipStyles() {
41074
+ function installViewerOverlayStyles() {
40922
41075
  if (typeof document === "undefined" || document.getElementById(TOOLTIP_STYLE_ID)) {
40923
41076
  return;
40924
41077
  }
@@ -40953,6 +41106,56 @@ void main() {
40953
41106
  height: 100%;
40954
41107
  min-height: 320px;
40955
41108
  }
41109
+ .wo-viewer-label-root {
41110
+ position: absolute;
41111
+ inset: 0;
41112
+ z-index: 8;
41113
+ pointer-events: none;
41114
+ overflow: hidden;
41115
+ }
41116
+ .wo-viewer-label {
41117
+ position: absolute;
41118
+ display: grid;
41119
+ gap: 2px;
41120
+ color: #edf6ff;
41121
+ font-family: "Segoe UI Variable", "Segoe UI", sans-serif;
41122
+ line-height: 1.15;
41123
+ text-shadow: 0 1px 2px rgba(7, 16, 25, 0.65), 0 0 18px rgba(7, 16, 25, 0.18);
41124
+ white-space: nowrap;
41125
+ }
41126
+ .wo-viewer-label[data-anchor="middle"] { transform: translate(-50%, 0); }
41127
+ .wo-viewer-label[data-anchor="end"] { transform: translate(-100%, 0); }
41128
+ .wo-viewer-label-primary {
41129
+ font-size: 14px;
41130
+ font-weight: 600;
41131
+ letter-spacing: 0.02em;
41132
+ }
41133
+ .wo-viewer-label-secondary {
41134
+ font-size: 11px;
41135
+ font-weight: 500;
41136
+ color: rgba(237, 246, 255, 0.72);
41137
+ }
41138
+ .wo-viewer-label-event {
41139
+ color: #ffce8a;
41140
+ text-transform: uppercase;
41141
+ letter-spacing: 0.04em;
41142
+ }
41143
+ .wo-viewer-label-event .wo-viewer-label-primary {
41144
+ font-size: 10px;
41145
+ font-weight: 700;
41146
+ }
41147
+ .wo-viewer-label.wo-object-selected .wo-viewer-label-primary,
41148
+ .wo-viewer-label.wo-chain-selected .wo-viewer-label-primary,
41149
+ .wo-viewer-label.wo-chain-hover .wo-viewer-label-primary {
41150
+ color: #ffce8a;
41151
+ }
41152
+ .wo-viewer-label.wo-object-selected .wo-viewer-label-secondary {
41153
+ color: #8fcaff;
41154
+ }
41155
+ .wo-viewer-label.wo-ancestor-selected .wo-viewer-label-primary,
41156
+ .wo-viewer-label.wo-ancestor-hover .wo-viewer-label-primary {
41157
+ opacity: 0.82;
41158
+ }
40956
41159
  .wo-viewer-tooltip-root {
40957
41160
  position: absolute;
40958
41161
  z-index: 12;
@@ -41087,6 +41290,7 @@ void main() {
41087
41290
  }
41088
41291
  function mountWorldOrbitEmbeds(root = document, options = {}) {
41089
41292
  const viewers = /* @__PURE__ */ new Map();
41293
+ const cleanupCallbacks = [];
41090
41294
  const elements = [...root.querySelectorAll(EMBED_SELECTOR)];
41091
41295
  for (const element of elements) {
41092
41296
  const payload = deserializePayloadFromElement(element);
@@ -41100,14 +41304,16 @@ void main() {
41100
41304
  const initialSelectionObjectId = options.viewer?.initialSelectionObjectId ?? payload.options?.initialSelectionObjectId;
41101
41305
  const minimap = options.viewer?.minimap ?? payload.options?.minimap;
41102
41306
  const viewMode = options.viewer?.viewMode ?? payload.options?.viewMode ?? embedModeToViewMode(mode);
41307
+ const measureViewport = () => resolveEmbedViewport(element, payload.scene, options);
41103
41308
  if (mode === "interactive-2d" || mode === "interactive-3d") {
41104
41309
  try {
41310
+ const viewport = measureViewport();
41105
41311
  const viewer = createInteractiveViewer(element, {
41106
41312
  ...options.viewer,
41107
41313
  scene: payload.scene,
41108
41314
  spatialScene: payload.spatialScene,
41109
- width: options.width ?? payload.scene.width,
41110
- height: options.height ?? payload.scene.height,
41315
+ width: viewport.width,
41316
+ height: viewport.height,
41111
41317
  padding: options.padding ?? payload.scene.padding,
41112
41318
  preset,
41113
41319
  theme,
@@ -41123,6 +41329,13 @@ void main() {
41123
41329
  viewer.setAtlasState(payload.options.atlasState);
41124
41330
  }
41125
41331
  viewers.set(element, viewer);
41332
+ cleanupCallbacks.push(bindEmbedResize(element, () => {
41333
+ const nextViewport = measureViewport();
41334
+ viewer.setRenderOptions({
41335
+ width: nextViewport.width,
41336
+ height: nextViewport.height
41337
+ });
41338
+ }));
41126
41339
  options.onMount?.(viewer, element);
41127
41340
  } catch (error2) {
41128
41341
  if (error2 instanceof WorldOrbit3DUnavailableError && mode === "interactive-3d") {
@@ -41133,17 +41346,22 @@ void main() {
41133
41346
  }
41134
41347
  }
41135
41348
  } else {
41136
- element.innerHTML = renderSceneToSvg(payload.scene, {
41137
- width: options.width ?? payload.scene.width,
41138
- height: options.height ?? payload.scene.height,
41139
- padding: options.padding ?? payload.scene.padding,
41140
- preset,
41141
- theme,
41142
- layers,
41143
- filter: initialFilter,
41144
- selectedObjectId: initialSelectionObjectId ?? null,
41145
- subtitle
41146
- });
41349
+ const renderStaticEmbed = () => {
41350
+ const viewport = measureViewport();
41351
+ element.innerHTML = renderSceneToSvg(payload.scene, {
41352
+ width: viewport.width,
41353
+ height: viewport.height,
41354
+ padding: options.padding ?? payload.scene.padding,
41355
+ preset,
41356
+ theme,
41357
+ layers,
41358
+ filter: initialFilter,
41359
+ selectedObjectId: initialSelectionObjectId ?? null,
41360
+ subtitle
41361
+ });
41362
+ };
41363
+ renderStaticEmbed();
41364
+ cleanupCallbacks.push(bindEmbedResize(element, renderStaticEmbed));
41147
41365
  options.onMount?.(null, element);
41148
41366
  }
41149
41367
  element.dataset.worldorbitMounted = "true";
@@ -41151,14 +41369,65 @@ void main() {
41151
41369
  return {
41152
41370
  viewers: [...viewers.values()],
41153
41371
  destroy() {
41372
+ for (const cleanup of cleanupCallbacks) {
41373
+ cleanup();
41374
+ }
41154
41375
  for (const [element, viewer] of viewers.entries()) {
41155
41376
  viewer.destroy();
41156
41377
  element.removeAttribute("data-worldorbit-mounted");
41157
41378
  }
41379
+ for (const element of elements) {
41380
+ element.removeAttribute("data-worldorbit-mounted");
41381
+ }
41158
41382
  viewers.clear();
41159
41383
  }
41160
41384
  };
41161
41385
  }
41386
+ function resolveEmbedViewport(element, scene, options) {
41387
+ const rect = element.getBoundingClientRect();
41388
+ const width = sanitizeViewportDimension(options.width) ?? sanitizeViewportDimension(element.clientWidth) ?? sanitizeViewportDimension(rect.width) ?? scene.width;
41389
+ const explicitHeight = sanitizeViewportDimension(options.height) ?? sanitizeViewportDimension(element.clientHeight) ?? sanitizeViewportDimension(rect.height);
41390
+ const fallbackHeight = Math.max(Math.round(width * (scene.height / Math.max(scene.width, 1))), Math.min(scene.height, 240));
41391
+ return {
41392
+ width,
41393
+ height: explicitHeight ?? fallbackHeight
41394
+ };
41395
+ }
41396
+ function sanitizeViewportDimension(value) {
41397
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : null;
41398
+ }
41399
+ function bindEmbedResize(element, callback) {
41400
+ let lastWidth = -1;
41401
+ let lastHeight = -1;
41402
+ const run = () => {
41403
+ const rect = element.getBoundingClientRect();
41404
+ const nextWidth = Math.round(Math.max(element.clientWidth || rect.width, 0));
41405
+ const nextHeight = Math.round(Math.max(element.clientHeight || rect.height, 0));
41406
+ if (nextWidth === lastWidth && nextHeight === lastHeight) {
41407
+ return;
41408
+ }
41409
+ lastWidth = nextWidth;
41410
+ lastHeight = nextHeight;
41411
+ callback();
41412
+ };
41413
+ run();
41414
+ if (typeof ResizeObserver !== "undefined") {
41415
+ const observer = new ResizeObserver(() => {
41416
+ run();
41417
+ });
41418
+ observer.observe(element);
41419
+ return () => {
41420
+ observer.disconnect();
41421
+ };
41422
+ }
41423
+ const handleWindowResize = () => {
41424
+ run();
41425
+ };
41426
+ window.addEventListener("resize", handleWindowResize);
41427
+ return () => {
41428
+ window.removeEventListener("resize", handleWindowResize);
41429
+ };
41430
+ }
41162
41431
  function deserializePayloadFromElement(element) {
41163
41432
  const serialized = element.dataset.worldorbitPayload;
41164
41433
  if (!serialized) {