deck.gl 9.2.0-beta.2 → 9.2.0-beta.4

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.
package/dist/dist.dev.js CHANGED
@@ -1907,7 +1907,7 @@ or create a device with the 'debug: true' prop.`;
1907
1907
  VERSION = (
1908
1908
  // Version detection using build plugin
1909
1909
  // @ts-expect-error no-undef
1910
- true ? "9.2.0" : "running from source"
1910
+ true ? "9.2.2" : "running from source"
1911
1911
  );
1912
1912
  spector;
1913
1913
  preregisteredAdapters = /* @__PURE__ */ new Map();
@@ -9603,7 +9603,7 @@ ${source4}`;
9603
9603
  * @param gl
9604
9604
  * @returns
9605
9605
  */
9606
- async attach(gl) {
9606
+ async attach(gl, props = {}) {
9607
9607
  const { WebGLDevice: WebGLDevice2 } = await Promise.resolve().then(() => (init_webgl_device(), webgl_device_exports));
9608
9608
  if (gl instanceof WebGLDevice2) {
9609
9609
  return gl;
@@ -9614,9 +9614,11 @@ ${source4}`;
9614
9614
  if (!isWebGL(gl)) {
9615
9615
  throw new Error("Invalid WebGL2RenderingContext");
9616
9616
  }
9617
+ const createCanvasContext = props.createCanvasContext === true ? {} : props.createCanvasContext;
9617
9618
  return new WebGLDevice2({
9619
+ ...props,
9618
9620
  _handle: gl,
9619
- createCanvasContext: { canvas: gl.canvas, autoResize: false }
9621
+ createCanvasContext: { canvas: gl.canvas, autoResize: false, ...createCanvasContext }
9620
9622
  });
9621
9623
  }
9622
9624
  async create(props = {}) {
@@ -43265,7 +43267,7 @@ void main() {
43265
43267
  if (props.gl instanceof WebGLRenderingContext) {
43266
43268
  log_default.error("WebGL1 context not supported.")();
43267
43269
  }
43268
- deviceOrPromise = webgl2Adapter.attach(props.gl);
43270
+ deviceOrPromise = webgl2Adapter.attach(props.gl, this.props.deviceProps);
43269
43271
  }
43270
43272
  if (!deviceOrPromise) {
43271
43273
  deviceOrPromise = this._createDevice(props);
@@ -43331,7 +43333,7 @@ void main() {
43331
43333
  this.eventManager?.destroy();
43332
43334
  this.canvas = null;
43333
43335
  }
43334
- log_default.log(`recreating animation loop for new device! id=${props.device.id}`);
43336
+ log_default.log(`recreating animation loop for new device! id=${props.device.id}`)();
43335
43337
  this.animationLoop = this._createAnimationLoop(props.device, props);
43336
43338
  this.animationLoop.start();
43337
43339
  }
@@ -44531,7 +44533,7 @@ void main() {
44531
44533
  }
44532
44534
  // Use generic value
44533
44535
  // Returns true if successful
44534
- setConstantValue(value) {
44536
+ setConstantValue(context, value) {
44535
44537
  const isWebGPU = this.device.type === "webgpu";
44536
44538
  if (isWebGPU || value === void 0 || typeof value === "function") {
44537
44539
  if (isWebGPU && typeof value !== "function") {
@@ -44542,7 +44544,8 @@ void main() {
44542
44544
  }
44543
44545
  return false;
44544
44546
  }
44545
- const hasChanged = this.setData({ constant: true, value });
44547
+ const transformedValue = this.settings.transform && context ? this.settings.transform.call(context, value) : value;
44548
+ const hasChanged = this.setData({ constant: true, value: transformedValue });
44546
44549
  if (hasChanged) {
44547
44550
  this.setNeedsRedraw();
44548
44551
  }
@@ -45571,7 +45574,7 @@ void main(void) {
45571
45574
  typeof accessorName === "string" ? buffers[accessorName] : void 0,
45572
45575
  data.startIndices
45573
45576
  )) {
45574
- } else if (typeof accessorName === "string" && !buffers[accessorName] && attribute.setConstantValue(props[accessorName])) {
45577
+ } else if (typeof accessorName === "string" && !buffers[accessorName] && attribute.setConstantValue(context, props[accessorName])) {
45575
45578
  } else if (attribute.needsUpdate()) {
45576
45579
  updated = true;
45577
45580
  this._updateAttribute({
@@ -45687,7 +45690,7 @@ void main(void) {
45687
45690
  const { attribute, numInstances } = opts;
45688
45691
  debug(TRACE_ATTRIBUTE_UPDATE_START, attribute);
45689
45692
  if (attribute.constant) {
45690
- attribute.setConstantValue(attribute.value);
45693
+ attribute.setConstantValue(opts.context, attribute.value);
45691
45694
  return;
45692
45695
  }
45693
45696
  if (attribute.allocate(numInstances)) {
@@ -50017,6 +50020,7 @@ void main(void) {
50017
50020
  var uniformBlock9 = `uniform iconUniforms {
50018
50021
  float sizeScale;
50019
50022
  vec2 iconsTextureDim;
50023
+ float sizeBasis;
50020
50024
  float sizeMinPixels;
50021
50025
  float sizeMaxPixels;
50022
50026
  bool billboard;
@@ -50031,6 +50035,7 @@ void main(void) {
50031
50035
  uniformTypes: {
50032
50036
  sizeScale: "f32",
50033
50037
  iconsTextureDim: "vec2<f32>",
50038
+ sizeBasis: "f32",
50034
50039
  sizeMinPixels: "f32",
50035
50040
  sizeMaxPixels: "f32",
50036
50041
  billboard: "f32",
@@ -50084,8 +50089,9 @@ void main(void) {
50084
50089
  icon.sizeMinPixels, icon.sizeMaxPixels
50085
50090
  );
50086
50091
 
50087
- // scale icon height to match instanceSize
50088
- float instanceScale = iconSize.y == 0.0 ? 0.0 : sizePixels / iconSize.y;
50092
+ // Choose correct constraint based on the 'sizeBasis' value (0.0 = width, 1.0 = height)
50093
+ float iconConstraint = icon.sizeBasis == 0.0 ? iconSize.x : iconSize.y;
50094
+ float instanceScale = iconConstraint == 0.0 ? 0.0 : sizePixels / iconConstraint;
50089
50095
 
50090
50096
  // scale and rotate vertex in "pixel" value and convert back to fraction in clipspace
50091
50097
  vec2 pixelOffset = positions / 2.0 * iconSize + instanceOffsets;
@@ -50099,7 +50105,6 @@ void main(void) {
50099
50105
  vec3 offset = vec3(pixelOffset, 0.0);
50100
50106
  DECKGL_FILTER_SIZE(offset, geometry);
50101
50107
  gl_Position.xy += project_pixel_size_to_clipspace(offset.xy);
50102
-
50103
50108
  } else {
50104
50109
  vec3 offset_common = vec3(project_pixel_size(pixelOffset), 0.0);
50105
50110
  DECKGL_FILTER_SIZE(offset_common, geometry);
@@ -50448,6 +50453,7 @@ void main(void) {
50448
50453
  sizeScale: { type: "number", value: 1, min: 0 },
50449
50454
  billboard: true,
50450
50455
  sizeUnits: "pixels",
50456
+ sizeBasis: "height",
50451
50457
  sizeMinPixels: { type: "number", min: 0, value: 0 },
50452
50458
  // min point radius in pixels
50453
50459
  sizeMaxPixels: { type: "number", min: 0, value: Number.MAX_SAFE_INTEGER },
@@ -50566,7 +50572,7 @@ void main(void) {
50566
50572
  this.state.iconManager.finalize();
50567
50573
  }
50568
50574
  draw({ uniforms }) {
50569
- const { sizeScale, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff } = this.props;
50575
+ const { sizeScale, sizeBasis, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff } = this.props;
50570
50576
  const { iconManager } = this.state;
50571
50577
  const iconsTexture = iconManager.getTexture();
50572
50578
  if (iconsTexture) {
@@ -50576,6 +50582,7 @@ void main(void) {
50576
50582
  iconsTextureDim: [iconsTexture.width, iconsTexture.height],
50577
50583
  sizeUnits: UNIT[sizeUnits],
50578
50584
  sizeScale,
50585
+ sizeBasis: sizeBasis === "height" ? 1 : 0,
50579
50586
  sizeMinPixels,
50580
50587
  sizeMaxPixels,
50581
50588
  billboard,
@@ -56287,12 +56294,21 @@ void main(void) {
56287
56294
  featureIds: lines.featureIds
56288
56295
  };
56289
56296
  layerProps.lines._pathType = "open";
56297
+ const vertexCount = polygons.positions.value.length / polygons.positions.size;
56298
+ const vertexValid = Array(vertexCount).fill(1);
56299
+ for (const index of polygons.primitivePolygonIndices.value) {
56300
+ vertexValid[index - 1] = 0;
56301
+ }
56290
56302
  layerProps.polygons.data = {
56291
56303
  length: polygons.polygonIndices.value.length - 1,
56292
56304
  startIndices: polygons.polygonIndices.value,
56293
56305
  attributes: {
56294
56306
  ...polygons.attributes,
56295
56307
  getPolygon: polygons.positions,
56308
+ instanceVertexValid: {
56309
+ size: 1,
56310
+ value: new Uint16Array(vertexValid)
56311
+ },
56296
56312
  pickingColors: {
56297
56313
  size: 4,
56298
56314
  value: customPickingColors.polygons
@@ -94190,7 +94206,9 @@ if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_US
94190
94206
  return { ...parameters2, depthCompare: "always" };
94191
94207
  }
94192
94208
  getShaderModuleProps(layer, effects, otherShaderModuleProps) {
94209
+ const base = super.getShaderModuleProps(layer, effects, otherShaderModuleProps);
94193
94210
  return {
94211
+ ...base,
94194
94212
  terrain: {
94195
94213
  project: otherShaderModuleProps.project
94196
94214
  }
@@ -102180,6 +102198,12 @@ void main() {
102180
102198
  constructor(props) {
102181
102199
  this._handleStyleChange = () => {
102182
102200
  resolveLayers(this._map, this._deck, this._props.layers, this._props.layers);
102201
+ if (!this._map)
102202
+ return;
102203
+ const projection = getProjection(this._map);
102204
+ if (projection && !this._props.views) {
102205
+ this._deck?.setProps({ views: getDefaultView(this._map) });
102206
+ }
102183
102207
  };
102184
102208
  this._updateContainerSize = () => {
102185
102209
  if (this._map && this._container) {
@@ -102265,16 +102289,24 @@ void main() {
102265
102289
  return;
102266
102290
  }
102267
102291
  };
102268
- const { interleaved = false, ...otherProps } = props;
102292
+ const { interleaved = false } = props;
102269
102293
  this._interleaved = interleaved;
102270
- this._props = otherProps;
102294
+ this._props = this.filterProps(props);
102295
+ }
102296
+ /** Filter out props to pass to Deck **/
102297
+ filterProps(props) {
102298
+ const { interleaved = false, useDevicePixels, ...deckProps } = props;
102299
+ if (!interleaved && useDevicePixels !== void 0) {
102300
+ deckProps.useDevicePixels = useDevicePixels;
102301
+ }
102302
+ return deckProps;
102271
102303
  }
102272
102304
  /** Update (partial) props of the underlying Deck instance. */
102273
102305
  setProps(props) {
102274
102306
  if (this._interleaved && props.layers) {
102275
102307
  resolveLayers(this._map, this._deck, this._props.layers, props.layers);
102276
102308
  }
102277
- Object.assign(this._props, props);
102309
+ Object.assign(this._props, this.filterProps(props));
102278
102310
  if (this._deck && this._map) {
102279
102311
  this._deck.setProps({
102280
102312
  ...this._props,
@@ -102334,7 +102366,11 @@ void main() {
102334
102366
  gl,
102335
102367
  deck: new Deck({
102336
102368
  ...this._props,
102337
- gl
102369
+ gl,
102370
+ parameters: { ...getDefaultParameters(map6, false), ...this._props.parameters },
102371
+ deviceProps: {
102372
+ createCanvasContext: { autoResize: true }
102373
+ }
102338
102374
  })
102339
102375
  });
102340
102376
  map6.on("styledata", this._handleStyleChange);
@@ -102741,21 +102777,21 @@ void main() {
102741
102777
 
102742
102778
  // ../widgets/src/lib/components/button-group.tsx
102743
102779
  var ButtonGroup = (props) => {
102744
- const { children, orientation } = props;
102780
+ const { children, orientation = "horizontal" } = props;
102745
102781
  return /* @__PURE__ */ u4("div", { className: `deck-widget-button-group ${orientation}`, children });
102746
102782
  };
102747
102783
 
102748
102784
  // ../widgets/src/lib/components/grouped-icon-button.tsx
102749
102785
  var GroupedIconButton = (props) => {
102750
- const { className, label, onClick } = props;
102786
+ const { className = "", label, onClick, children } = props;
102751
102787
  return /* @__PURE__ */ u4(
102752
102788
  "button",
102753
102789
  {
102754
- className: `deck-widget-icon-button ${className || ""}`,
102790
+ className: `deck-widget-icon-button ${className}`,
102755
102791
  type: "button",
102756
102792
  onClick,
102757
102793
  title: label,
102758
- children: /* @__PURE__ */ u4("div", { className: "deck-widget-icon" })
102794
+ children: children ? children : /* @__PURE__ */ u4("div", { className: "deck-widget-icon" })
102759
102795
  }
102760
102796
  );
102761
102797
  };
@@ -102766,10 +102802,8 @@ void main() {
102766
102802
  super(props, _ZoomWidget.defaultProps);
102767
102803
  this.className = "deck-widget-zoom";
102768
102804
  this.placement = "top-left";
102769
- this.viewId = null;
102770
102805
  this.viewports = {};
102771
- this.viewId = props.viewId ?? this.viewId;
102772
- this.placement = props.placement ?? this.placement;
102806
+ this.setProps(this.props);
102773
102807
  }
102774
102808
  setProps(props) {
102775
102809
  this.placement = props.placement ?? this.placement;
@@ -102808,7 +102842,9 @@ void main() {
102808
102842
  };
102809
102843
  if (this.props.transitionDuration > 0) {
102810
102844
  nextViewState.transitionDuration = this.props.transitionDuration;
102811
- nextViewState.transitionInterpolator = "latitude" in nextViewState ? new FlyToInterpolator() : new LinearInterpolator();
102845
+ nextViewState.transitionInterpolator = "latitude" in nextViewState ? new FlyToInterpolator() : new LinearInterpolator({
102846
+ transitionProps: ["zoom"]
102847
+ });
102812
102848
  }
102813
102849
  this.setViewState(viewId, nextViewState);
102814
102850
  }
@@ -102836,12 +102872,12 @@ void main() {
102836
102872
  transitionDuration: 200,
102837
102873
  zoomInLabel: "Zoom In",
102838
102874
  zoomOutLabel: "Zoom Out",
102839
- viewId: void 0
102875
+ viewId: null
102840
102876
  };
102841
102877
 
102842
102878
  // ../widgets/src/lib/components/icon-button.tsx
102843
102879
  var IconButton = (props) => {
102844
- const { className, label, onClick, children } = props;
102880
+ const { className = "", label, onClick, children } = props;
102845
102881
  return /* @__PURE__ */ u4("div", { className: "deck-widget-button", children: /* @__PURE__ */ u4(
102846
102882
  "button",
102847
102883
  {
@@ -102860,10 +102896,11 @@ void main() {
102860
102896
  super(props, _ResetViewWidget.defaultProps);
102861
102897
  this.className = "deck-widget-reset-view";
102862
102898
  this.placement = "top-left";
102863
- this.placement = props.placement ?? this.placement;
102899
+ this.setProps(this.props);
102864
102900
  }
102865
102901
  setProps(props) {
102866
102902
  this.placement = props.placement ?? this.placement;
102903
+ this.viewId = props.viewId ?? this.viewId;
102867
102904
  super.setProps(props);
102868
102905
  }
102869
102906
  onRenderHTML(rootElement) {
@@ -102884,9 +102921,9 @@ void main() {
102884
102921
  this.setViewState(initialViewState);
102885
102922
  }
102886
102923
  setViewState(viewState) {
102887
- const viewId = this.props.viewId || viewState?.id || "default-view";
102924
+ const viewId = this.props.viewId || "default-view";
102888
102925
  const nextViewState = {
102889
- ...viewState
102926
+ ...viewId !== "default-view" ? viewState?.[viewId] : viewState
102890
102927
  // only works for geospatial?
102891
102928
  // transitionDuration: this.props.transitionDuration,
102892
102929
  // transitionInterpolator: new FlyToInterpolator()
@@ -102901,95 +102938,7 @@ void main() {
102901
102938
  placement: "top-left",
102902
102939
  label: "Reset View",
102903
102940
  initialViewState: void 0,
102904
- viewId: void 0
102905
- };
102906
-
102907
- // ../widgets/src/compass-widget.tsx
102908
- var _CompassWidget = class extends Widget {
102909
- constructor(props = {}) {
102910
- super(props, _CompassWidget.defaultProps);
102911
- this.className = "deck-widget-compass";
102912
- this.placement = "top-left";
102913
- this.viewId = null;
102914
- this.viewports = {};
102915
- this.setProps(this.props);
102916
- }
102917
- setProps(props) {
102918
- this.placement = props.placement ?? this.placement;
102919
- this.viewId = props.viewId ?? this.viewId;
102920
- super.setProps(props);
102921
- }
102922
- onRenderHTML(rootElement) {
102923
- const viewId = this.viewId || Object.values(this.viewports)[0]?.id || "default-view";
102924
- const widgetViewport = this.viewports[viewId];
102925
- const [rz, rx] = this.getRotation(widgetViewport);
102926
- const ui = /* @__PURE__ */ u4("div", { className: "deck-widget-button", style: { perspective: 100 }, children: /* @__PURE__ */ u4(
102927
- "button",
102928
- {
102929
- type: "button",
102930
- onClick: () => {
102931
- for (const viewport of Object.values(this.viewports)) {
102932
- this.handleCompassReset(viewport);
102933
- }
102934
- },
102935
- title: this.props.label,
102936
- style: { transform: `rotateX(${rx}deg)` },
102937
- children: /* @__PURE__ */ u4("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: /* @__PURE__ */ u4("g", { transform: `rotate(${rz},13,13)`, children: [
102938
- /* @__PURE__ */ u4(
102939
- "path",
102940
- {
102941
- d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z",
102942
- fill: "var(--icon-compass-north-color, rgb(240, 92, 68))"
102943
- }
102944
- ),
102945
- /* @__PURE__ */ u4(
102946
- "path",
102947
- {
102948
- d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z",
102949
- fill: "var(--icon-compass-south-color, rgb(204, 204, 204))"
102950
- }
102951
- )
102952
- ] }) })
102953
- }
102954
- ) });
102955
- B3(ui, rootElement);
102956
- }
102957
- onViewportChange(viewport) {
102958
- if (!viewport.equals(this.viewports[viewport.id])) {
102959
- this.viewports[viewport.id] = viewport;
102960
- this.updateHTML();
102961
- }
102962
- }
102963
- getRotation(viewport) {
102964
- if (viewport instanceof WebMercatorViewport2) {
102965
- return [-viewport.bearing, viewport.pitch];
102966
- } else if (viewport instanceof GlobeViewport) {
102967
- return [0, Math.max(-80, Math.min(80, viewport.latitude))];
102968
- }
102969
- return [0, 0];
102970
- }
102971
- handleCompassReset(viewport) {
102972
- const viewId = this.viewId || viewport.id || "default-view";
102973
- if (viewport instanceof WebMercatorViewport2) {
102974
- const nextViewState = {
102975
- ...viewport,
102976
- bearing: 0,
102977
- ...this.getRotation(viewport)[0] === 0 ? { pitch: 0 } : {},
102978
- transitionDuration: this.props.transitionDuration,
102979
- transitionInterpolator: new FlyToInterpolator()
102980
- };
102981
- this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
102982
- }
102983
- }
102984
- };
102985
- var CompassWidget = _CompassWidget;
102986
- CompassWidget.defaultProps = {
102987
- ...Widget.defaultProps,
102988
- id: "compass",
102989
- placement: "top-left",
102990
- viewId: null,
102991
- label: "Reset Compass",
102992
- transitionDuration: 200
102941
+ viewId: null
102993
102942
  };
102994
102943
 
102995
102944
  // ../widgets/src/gimbal-widget.tsx
@@ -102998,7 +102947,7 @@ void main() {
102998
102947
  super(props, _GimbalWidget.defaultProps);
102999
102948
  this.className = "deck-widget-gimbal";
103000
102949
  this.placement = "top-left";
103001
- this.viewId = null;
102950
+ this.viewports = {};
103002
102951
  this.setProps(this.props);
103003
102952
  }
103004
102953
  setProps(props) {
@@ -103007,13 +102956,17 @@ void main() {
103007
102956
  super.setProps(props);
103008
102957
  }
103009
102958
  onRenderHTML(rootElement) {
103010
- const { rotationOrbit, rotationX } = this.getNormalizedRotation();
102959
+ const viewId = this.viewId || Object.values(this.viewports)[0]?.id || "default-view";
102960
+ const widgetViewport = this.viewports[viewId];
102961
+ const { rotationOrbit, rotationX } = this.getNormalizedRotation(widgetViewport);
103011
102962
  const ui = /* @__PURE__ */ u4("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: "auto" }, children: /* @__PURE__ */ u4(
103012
102963
  "button",
103013
102964
  {
103014
102965
  type: "button",
103015
102966
  onClick: () => {
103016
- this.resetOrbitView();
102967
+ for (const viewport of Object.values(this.viewports)) {
102968
+ this.resetOrbitView(viewport);
102969
+ }
103017
102970
  },
103018
102971
  title: this.props.label,
103019
102972
  style: { position: "relative", width: 26, height: 26 },
@@ -103076,10 +103029,11 @@ void main() {
103076
103029
  B3(ui, rootElement);
103077
103030
  }
103078
103031
  onViewportChange(viewport) {
103032
+ this.viewports[viewport.id] = viewport;
103079
103033
  this.updateHTML();
103080
103034
  }
103081
- resetOrbitView() {
103082
- const viewId = this.getViewId();
103035
+ resetOrbitView(viewport) {
103036
+ const viewId = this.getViewId(viewport);
103083
103037
  const viewState = this.getViewState(viewId);
103084
103038
  if ("rotationOrbit" in viewState || "rotationX" in viewState) {
103085
103039
  const nextViewState = {
@@ -103094,8 +103048,8 @@ void main() {
103094
103048
  this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
103095
103049
  }
103096
103050
  }
103097
- getNormalizedRotation() {
103098
- const viewState = this.getViewState(this.getViewId());
103051
+ getNormalizedRotation(viewport) {
103052
+ const viewState = this.getViewState(this.getViewId(viewport));
103099
103053
  const [rz, rx] = this.getRotation(viewState);
103100
103054
  const rotationOrbit = normalizeAndClampAngle(rz);
103101
103055
  const rotationX = normalizeAndClampAngle(rx);
@@ -103108,8 +103062,8 @@ void main() {
103108
103062
  return [0, 0];
103109
103063
  }
103110
103064
  // Move to Widget/WidgetManager?
103111
- getViewId() {
103112
- const viewId = this.viewId || "OrbitView";
103065
+ getViewId(viewport) {
103066
+ const viewId = this.viewId || viewport?.id || "OrbitView";
103113
103067
  return viewId;
103114
103068
  }
103115
103069
  getViewState(viewId) {
@@ -103130,7 +103084,7 @@ void main() {
103130
103084
  ...Widget.defaultProps,
103131
103085
  id: "gimbal",
103132
103086
  placement: "top-left",
103133
- viewId: void 0,
103087
+ viewId: null,
103134
103088
  label: "Gimbal",
103135
103089
  strokeWidth: 1.5,
103136
103090
  transitionDuration: 200
@@ -103149,6 +103103,93 @@ void main() {
103149
103103
  return normalized;
103150
103104
  }
103151
103105
 
103106
+ // ../widgets/src/compass-widget.tsx
103107
+ var _CompassWidget = class extends Widget {
103108
+ constructor(props = {}) {
103109
+ super(props, _CompassWidget.defaultProps);
103110
+ this.className = "deck-widget-compass";
103111
+ this.placement = "top-left";
103112
+ this.viewports = {};
103113
+ this.setProps(this.props);
103114
+ }
103115
+ setProps(props) {
103116
+ this.placement = props.placement ?? this.placement;
103117
+ this.viewId = props.viewId ?? this.viewId;
103118
+ super.setProps(props);
103119
+ }
103120
+ onRenderHTML(rootElement) {
103121
+ const viewId = this.viewId || Object.values(this.viewports)[0]?.id || "default-view";
103122
+ const widgetViewport = this.viewports[viewId];
103123
+ const [rz, rx] = this.getRotation(widgetViewport);
103124
+ const ui = /* @__PURE__ */ u4("div", { className: "deck-widget-button", style: { perspective: 100 }, children: /* @__PURE__ */ u4(
103125
+ "button",
103126
+ {
103127
+ type: "button",
103128
+ onClick: () => {
103129
+ for (const viewport of Object.values(this.viewports)) {
103130
+ this.handleCompassReset(viewport);
103131
+ }
103132
+ },
103133
+ title: this.props.label,
103134
+ style: { transform: `rotateX(${rx}deg)` },
103135
+ children: /* @__PURE__ */ u4("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: /* @__PURE__ */ u4("g", { transform: `rotate(${rz},13,13)`, children: [
103136
+ /* @__PURE__ */ u4(
103137
+ "path",
103138
+ {
103139
+ d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z",
103140
+ fill: "var(--icon-compass-north-color, rgb(240, 92, 68))"
103141
+ }
103142
+ ),
103143
+ /* @__PURE__ */ u4(
103144
+ "path",
103145
+ {
103146
+ d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z",
103147
+ fill: "var(--icon-compass-south-color, rgb(204, 204, 204))"
103148
+ }
103149
+ )
103150
+ ] }) })
103151
+ }
103152
+ ) });
103153
+ B3(ui, rootElement);
103154
+ }
103155
+ onViewportChange(viewport) {
103156
+ if (!viewport.equals(this.viewports[viewport.id])) {
103157
+ this.viewports[viewport.id] = viewport;
103158
+ this.updateHTML();
103159
+ }
103160
+ }
103161
+ getRotation(viewport) {
103162
+ if (viewport instanceof WebMercatorViewport2) {
103163
+ return [-viewport.bearing, viewport.pitch];
103164
+ } else if (viewport instanceof GlobeViewport) {
103165
+ return [0, Math.max(-80, Math.min(80, viewport.latitude))];
103166
+ }
103167
+ return [0, 0];
103168
+ }
103169
+ handleCompassReset(viewport) {
103170
+ const viewId = this.viewId || viewport.id || "default-view";
103171
+ if (viewport instanceof WebMercatorViewport2) {
103172
+ const nextViewState = {
103173
+ ...viewport,
103174
+ bearing: 0,
103175
+ ...this.getRotation(viewport)[0] === 0 ? { pitch: 0 } : {},
103176
+ transitionDuration: this.props.transitionDuration,
103177
+ transitionInterpolator: new FlyToInterpolator()
103178
+ };
103179
+ this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
103180
+ }
103181
+ }
103182
+ };
103183
+ var CompassWidget = _CompassWidget;
103184
+ CompassWidget.defaultProps = {
103185
+ ...Widget.defaultProps,
103186
+ id: "compass",
103187
+ placement: "top-left",
103188
+ viewId: null,
103189
+ label: "Reset Compass",
103190
+ transitionDuration: 200
103191
+ };
103192
+
103152
103193
  // ../widgets/src/scale-widget.tsx
103153
103194
  var _ScaleWidget = class extends Widget {
103154
103195
  constructor(props = {}) {
@@ -103161,10 +103202,11 @@ void main() {
103161
103202
  this.scaleValue = 0;
103162
103203
  // The formatted distance label (e.g. "200 m" or "1.0 km")
103163
103204
  this.scaleText = "";
103164
- this.placement = props.placement ?? this.placement;
103205
+ this.setProps(this.props);
103165
103206
  }
103166
103207
  setProps(props) {
103167
103208
  this.placement = props.placement ?? this.placement;
103209
+ this.viewId = props.viewId ?? this.viewId;
103168
103210
  super.setProps(props);
103169
103211
  }
103170
103212
  onRenderHTML(rootElement) {
@@ -103255,7 +103297,7 @@ void main() {
103255
103297
  id: "scale",
103256
103298
  placement: "bottom-left",
103257
103299
  label: "Scale",
103258
- onCapture: void 0
103300
+ viewId: null
103259
103301
  };
103260
103302
  function getMetersPerPixel(latitude, zoom) {
103261
103303
  const earthCircumference = 40075016686e-3;
@@ -103749,17 +103791,16 @@ void main() {
103749
103791
  }
103750
103792
  };
103751
103793
  this.viewports = {};
103752
- this.placement = this.props.placement ?? this.placement;
103753
103794
  this.setProps(this.props);
103754
- this.geocoder = getGeocoder(this.props);
103755
103795
  }
103756
103796
  setProps(props) {
103757
- super.setProps(props);
103758
103797
  this.placement = props.placement ?? this.placement;
103798
+ this.viewId = props.viewId ?? this.viewId;
103759
103799
  this.geocoder = getGeocoder(this.props);
103760
103800
  if (this.geocoder.requiresApiKey && !this.props.apiKey) {
103761
103801
  throw new Error(`API key is required for the ${this.geocoder.name} geocoder`);
103762
103802
  }
103803
+ super.setProps(props);
103763
103804
  }
103764
103805
  onRenderHTML(rootElement) {
103765
103806
  const menuItems = this.props._geolocation ? [CURRENT_LOCATION2, ...this.geocodeHistory.addressHistory] : [...this.geocodeHistory.addressHistory];
@@ -103834,7 +103875,7 @@ void main() {
103834
103875
  GeocoderWidget.defaultProps = {
103835
103876
  ...Widget.defaultProps,
103836
103877
  id: "geocoder",
103837
- viewId: void 0,
103878
+ viewId: null,
103838
103879
  placement: "top-left",
103839
103880
  label: "Geocoder",
103840
103881
  transitionDuration: 200,
@@ -103870,7 +103911,7 @@ void main() {
103870
103911
  this.className = "deck-widget-fullscreen";
103871
103912
  this.placement = "top-left";
103872
103913
  this.fullscreen = false;
103873
- this.placement = props.placement ?? this.placement;
103914
+ this.setProps(this.props);
103874
103915
  }
103875
103916
  onAdd() {
103876
103917
  document.addEventListener("fullscreenchange", this.onFullscreenChange.bind(this));
@@ -103883,7 +103924,9 @@ void main() {
103883
103924
  /* @__PURE__ */ u4(
103884
103925
  IconButton,
103885
103926
  {
103886
- onClick: this.handleClick.bind(this),
103927
+ onClick: () => {
103928
+ this.handleClick().catch((err) => log_default.error(err)());
103929
+ },
103887
103930
  label: this.fullscreen ? this.props.exitLabel : this.props.enterLabel,
103888
103931
  className: this.fullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter"
103889
103932
  }
@@ -103893,6 +103936,7 @@ void main() {
103893
103936
  }
103894
103937
  setProps(props) {
103895
103938
  this.placement = props.placement ?? this.placement;
103939
+ this.viewId = props.viewId ?? this.viewId;
103896
103940
  super.setProps(props);
103897
103941
  }
103898
103942
  getContainer() {
@@ -103938,322 +103982,318 @@ void main() {
103938
103982
  ...Widget.defaultProps,
103939
103983
  id: "fullscreen",
103940
103984
  placement: "top-left",
103985
+ viewId: null,
103941
103986
  enterLabel: "Enter Fullscreen",
103942
103987
  exitLabel: "Exit Fullscreen",
103943
103988
  container: void 0
103944
103989
  };
103945
103990
 
103946
- // ../widgets/src/screenshot-widget.tsx
103947
- var _ScreenshotWidget = class extends Widget {
103948
- constructor(props = {}) {
103949
- super(props, _ScreenshotWidget.defaultProps);
103950
- this.className = "deck-widget-screenshot";
103951
- this.placement = "top-left";
103952
- this.placement = props.placement ?? this.placement;
103991
+ // ../widgets/src/splitter-widget.tsx
103992
+ var _SplitterWidget = class extends Widget {
103993
+ constructor(props) {
103994
+ super(props, _SplitterWidget.defaultProps);
103995
+ this.className = "deck-widget-splitter";
103996
+ this.placement = "fill";
103953
103997
  }
103954
103998
  setProps(props) {
103955
- this.placement = props.placement ?? this.placement;
103956
103999
  super.setProps(props);
103957
104000
  }
103958
104001
  onRenderHTML(rootElement) {
104002
+ rootElement.style.position = "absolute";
104003
+ rootElement.style.top = "0";
104004
+ rootElement.style.left = "0";
104005
+ rootElement.style.width = "100%";
104006
+ rootElement.style.height = "100%";
104007
+ rootElement.style.margin = "0px";
103959
104008
  B3(
103960
104009
  /* @__PURE__ */ u4(
103961
- IconButton,
104010
+ Splitter,
103962
104011
  {
103963
- className: "deck-widget-camera",
103964
- label: this.props.label,
103965
- onClick: this.handleClick.bind(this)
104012
+ orientation: this.props.orientation,
104013
+ initialSplit: this.props.initialSplit,
104014
+ onChange: this.props.onChange,
104015
+ onDragStart: this.props.onDragStart,
104016
+ onDragEnd: this.props.onDragEnd
103966
104017
  }
103967
104018
  ),
103968
104019
  rootElement
103969
104020
  );
103970
104021
  }
103971
- handleClick() {
103972
- if (this.props.onCapture) {
103973
- this.props.onCapture(this);
103974
- return;
103975
- }
103976
- const dataURL = this.captureScreenToDataURL(this.props.imageFormat);
103977
- if (dataURL) {
103978
- this.downloadDataURL(dataURL, this.props.filename);
103979
- }
103980
- }
103981
- /** @note only captures canvas contents, not HTML DOM or CSS styles */
103982
- captureScreenToDataURL(imageFormat) {
103983
- const canvas2 = this.deck?.getCanvas();
103984
- return canvas2?.toDataURL(imageFormat);
103985
- }
103986
- /** Download a data URL */
103987
- downloadDataURL(dataURL, filename2) {
103988
- const link = document.createElement("a");
103989
- link.href = dataURL;
103990
- link.download = filename2;
103991
- link.click();
103992
- }
103993
104022
  };
103994
- var ScreenshotWidget = _ScreenshotWidget;
103995
- ScreenshotWidget.defaultProps = {
104023
+ var SplitterWidget = _SplitterWidget;
104024
+ SplitterWidget.defaultProps = {
103996
104025
  ...Widget.defaultProps,
103997
- id: "screenshot",
103998
- placement: "top-left",
103999
- label: "Screenshot",
104000
- filename: "screenshot.png",
104001
- imageFormat: "image/png",
104002
- onCapture: void 0
104026
+ id: "splitter-widget",
104027
+ viewId1: "",
104028
+ viewId2: "",
104029
+ orientation: "vertical",
104030
+ initialSplit: 0.5,
104031
+ onChange: () => {
104032
+ },
104033
+ onDragStart: () => {
104034
+ },
104035
+ onDragEnd: () => {
104036
+ }
104003
104037
  };
104038
+ function Splitter({
104039
+ orientation,
104040
+ initialSplit,
104041
+ onChange,
104042
+ onDragStart,
104043
+ onDragEnd
104044
+ }) {
104045
+ const [split, setSplit] = h3(initialSplit);
104046
+ const dragging = A4(false);
104047
+ const containerRef = A4(null);
104048
+ const handleDragStart = (event) => {
104049
+ dragging.current = true;
104050
+ onDragStart?.();
104051
+ document.addEventListener("mousemove", handleDragging);
104052
+ document.addEventListener("mouseup", handleDragEnd);
104053
+ event.preventDefault();
104054
+ };
104055
+ const handleDragging = (event) => {
104056
+ if (!dragging.current || !containerRef.current)
104057
+ return;
104058
+ const rect = containerRef.current.getBoundingClientRect();
104059
+ let newSplit;
104060
+ if (orientation === "vertical") {
104061
+ newSplit = (event.clientX - rect.left) / rect.width;
104062
+ } else {
104063
+ newSplit = (event.clientY - rect.top) / rect.height;
104064
+ }
104065
+ newSplit = Math.min(Math.max(newSplit, 0.05), 0.95);
104066
+ setSplit(newSplit);
104067
+ onChange?.(newSplit);
104068
+ };
104069
+ const handleDragEnd = (event) => {
104070
+ if (!dragging.current)
104071
+ return;
104072
+ dragging.current = false;
104073
+ onDragEnd?.();
104074
+ document.removeEventListener("mousemove", handleDragging);
104075
+ document.removeEventListener("mouseup", handleDragEnd);
104076
+ };
104077
+ const splitterStyle = orientation === "vertical" ? {
104078
+ position: "absolute",
104079
+ top: 0,
104080
+ bottom: 0,
104081
+ left: `${split * 100}%`,
104082
+ width: "4px",
104083
+ cursor: "col-resize",
104084
+ background: "#ccc",
104085
+ zIndex: 10,
104086
+ pointerEvents: "auto",
104087
+ boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
104088
+ } : {
104089
+ position: "absolute",
104090
+ left: 0,
104091
+ right: 0,
104092
+ top: `${split * 100}%`,
104093
+ height: "4px",
104094
+ cursor: "row-resize",
104095
+ background: "#ccc",
104096
+ zIndex: 10,
104097
+ pointerEvents: "auto",
104098
+ boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
104099
+ };
104100
+ const containerStyle = {
104101
+ position: "absolute",
104102
+ top: 0,
104103
+ left: 0,
104104
+ right: 0,
104105
+ bottom: 0
104106
+ };
104107
+ return /* @__PURE__ */ u4("div", { ref: containerRef, style: containerStyle, children: /* @__PURE__ */ u4(
104108
+ "div",
104109
+ {
104110
+ style: splitterStyle,
104111
+ onMouseDown: handleDragStart
104112
+ }
104113
+ ) });
104114
+ }
104004
104115
 
104005
- // ../widgets/src/loading-widget.tsx
104006
- var _LoadingWidget = class extends Widget {
104116
+ // ../widgets/src/lib/components/icon-menu.tsx
104117
+ function IconMenu(props) {
104118
+ const [menuOpen, setMenuOpen] = h3(false);
104119
+ const containerRef = A4(null);
104120
+ const handleClickOutside = (event) => {
104121
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
104122
+ setMenuOpen(false);
104123
+ }
104124
+ };
104125
+ y4(() => {
104126
+ document.addEventListener("mousedown", handleClickOutside);
104127
+ return () => {
104128
+ document.removeEventListener("mousedown", handleClickOutside);
104129
+ };
104130
+ }, [containerRef]);
104131
+ const [selectedItem, setSelectedItem] = h3(props.initialItem);
104132
+ const handleSelectItem = (item) => {
104133
+ setSelectedItem(item);
104134
+ setMenuOpen(false);
104135
+ props.onItemSelected(item);
104136
+ };
104137
+ const handleButtonClick = () => setMenuOpen(!menuOpen);
104138
+ const selectedMenuItem = props.menuItems.find((item) => item.value === selectedItem);
104139
+ const label = props.label || selectedMenuItem?.label || "";
104140
+ const icon = props.icon || selectedMenuItem?.icon;
104141
+ return /* @__PURE__ */ u4("div", { style: { position: "relative", display: "inline-block" }, ref: containerRef, children: [
104142
+ /* @__PURE__ */ u4(IconButton, { className: props.className, label, onClick: handleButtonClick, children: icon }),
104143
+ menuOpen && /* @__PURE__ */ u4("div", { className: "deck-widget-icon-menu", children: /* @__PURE__ */ u4(ButtonGroup, { orientation: "vertical", children: props.menuItems.map((item) => /* @__PURE__ */ u4(
104144
+ GroupedIconButton,
104145
+ {
104146
+ label: item.label,
104147
+ onClick: () => handleSelectItem(item.value),
104148
+ children: item.icon
104149
+ },
104150
+ item.value
104151
+ )) }) })
104152
+ ] });
104153
+ }
104154
+
104155
+ // ../widgets/src/view-selector-widget.tsx
104156
+ var _ViewSelectorWidget = class extends Widget {
104007
104157
  constructor(props = {}) {
104008
- super(props, _LoadingWidget.defaultProps);
104009
- this.className = "deck-widget-loading";
104158
+ super(props, _ViewSelectorWidget.defaultProps);
104159
+ this.className = "deck-widget-view-selector";
104010
104160
  this.placement = "top-left";
104011
- this.loading = true;
104012
- this.placement = props.placement ?? this.placement;
104161
+ this.handleSelectMode = (viewMode) => {
104162
+ this.viewMode = viewMode;
104163
+ this.updateHTML();
104164
+ this.props.onViewModeChange(viewMode);
104165
+ };
104166
+ this.viewMode = this.props.initialViewMode;
104167
+ this.setProps(this.props);
104013
104168
  }
104014
104169
  setProps(props) {
104015
104170
  this.placement = props.placement ?? this.placement;
104171
+ this.viewId = props.viewId ?? this.viewId;
104016
104172
  super.setProps(props);
104017
104173
  }
104018
104174
  onRenderHTML(rootElement) {
104019
104175
  B3(
104020
- // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
104021
- this.loading && /* @__PURE__ */ u4(
104022
- IconButton,
104176
+ /* @__PURE__ */ u4(
104177
+ IconMenu,
104023
104178
  {
104024
- className: "deck-widget-spinner-icon",
104025
- label: this.props.label,
104026
- onClick: this.handleClick.bind(this)
104179
+ className: "deck-widget-view-selector",
104180
+ menuItems: MENU_ITEMS.map((item) => ({
104181
+ ...item,
104182
+ icon: item.icon()
104183
+ })),
104184
+ initialItem: this.props.initialViewMode,
104185
+ onItemSelected: this.handleSelectMode
104027
104186
  }
104028
104187
  ),
104029
104188
  rootElement
104030
104189
  );
104031
104190
  }
104032
- onRedraw({ layers }) {
104033
- const loading = !layers.some((layer) => !layer.isLoaded);
104034
- if (loading !== this.loading) {
104035
- this.loading = loading;
104036
- this.updateHTML();
104037
- }
104038
- }
104039
- // TODO(ibgreen) - this should not be a button, see above.
104040
- handleClick() {
104041
- }
104042
104191
  };
104043
- var LoadingWidget = _LoadingWidget;
104044
- LoadingWidget.defaultProps = {
104192
+ var ViewSelectorWidget = _ViewSelectorWidget;
104193
+ ViewSelectorWidget.defaultProps = {
104045
104194
  ...Widget.defaultProps,
104046
- id: "loading",
104195
+ id: "view-selector",
104047
104196
  placement: "top-left",
104048
- label: "Loading layer data"
104197
+ viewId: null,
104198
+ label: "Split View",
104199
+ initialViewMode: "single",
104200
+ onViewModeChange: () => {
104201
+ }
104202
+ };
104203
+ var ICON_STYLE = { width: "24px", height: "24px" };
104204
+ var ICONS = {
104205
+ single: () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: /* @__PURE__ */ u4(
104206
+ "rect",
104207
+ {
104208
+ x: "4",
104209
+ y: "4",
104210
+ width: "16",
104211
+ height: "16",
104212
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
104213
+ fill: "none",
104214
+ strokeWidth: "2"
104215
+ }
104216
+ ) }),
104217
+ "split-horizontal": () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
104218
+ /* @__PURE__ */ u4(
104219
+ "rect",
104220
+ {
104221
+ x: "4",
104222
+ y: "4",
104223
+ width: "16",
104224
+ height: "7",
104225
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
104226
+ fill: "none",
104227
+ strokeWidth: "2"
104228
+ }
104229
+ ),
104230
+ /* @__PURE__ */ u4(
104231
+ "rect",
104232
+ {
104233
+ x: "4",
104234
+ y: "13",
104235
+ width: "16",
104236
+ height: "7",
104237
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
104238
+ fill: "none",
104239
+ strokeWidth: "2"
104240
+ }
104241
+ )
104242
+ ] }),
104243
+ "split-vertical": () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
104244
+ /* @__PURE__ */ u4(
104245
+ "rect",
104246
+ {
104247
+ x: "4",
104248
+ y: "4",
104249
+ width: "7",
104250
+ height: "16",
104251
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
104252
+ fill: "none",
104253
+ strokeWidth: "2"
104254
+ }
104255
+ ),
104256
+ /* @__PURE__ */ u4(
104257
+ "rect",
104258
+ {
104259
+ x: "13",
104260
+ y: "4",
104261
+ width: "7",
104262
+ height: "16",
104263
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
104264
+ fill: "none",
104265
+ strokeWidth: "2"
104266
+ }
104267
+ )
104268
+ ] })
104049
104269
  };
104270
+ var MENU_ITEMS = [
104271
+ { value: "single", icon: ICONS.single, label: "Single View" },
104272
+ { value: "split-horizontal", icon: ICONS["split-horizontal"], label: "Split Horizontal" },
104273
+ { value: "split-vertical", icon: ICONS["split-vertical"], label: "Split Vertical" }
104274
+ ];
104050
104275
 
104051
- // ../widgets/src/fps-widget.tsx
104052
- var _FpsWidget = class extends Widget {
104053
- constructor(props = {}) {
104054
- super(props, _FpsWidget.defaultProps);
104055
- this.className = "deck-widget-fps";
104056
- this.placement = "top-left";
104057
- this._lastFps = -1;
104058
- this.placement = props.placement ?? this.placement;
104276
+ // ../widgets/src/info-widget.tsx
104277
+ var _InfoWidget = class extends Widget {
104278
+ constructor(props) {
104279
+ super(props, _InfoWidget.defaultProps);
104280
+ this.className = "deck-widget-info";
104281
+ this.placement = "fill";
104282
+ this.setProps(this.props);
104059
104283
  }
104060
104284
  setProps(props) {
104061
- if (props.placement) {
104062
- this.placement = props.placement;
104063
- }
104285
+ this.viewId = props.viewId ?? this.viewId;
104064
104286
  super.setProps(props);
104065
104287
  }
104066
- onAdd({}) {
104067
- this._lastFps = this._getFps();
104068
- this.updateHTML();
104288
+ onCreateRootElement() {
104289
+ const element = super.onCreateRootElement();
104290
+ const style = { margin: "0px", top: "0px", left: "0px", position: "absolute" };
104291
+ Object.entries(style).forEach(([key, value]) => element.style.setProperty(key, value));
104292
+ return element;
104069
104293
  }
104070
- onRenderHTML(rootElement) {
104071
- const fps = this._getFps();
104072
- rootElement.innerText = `FPS:
104073
- ${fps}`;
104074
- rootElement.style.backgroundColor = "white";
104075
- rootElement.style.fontFamily = "monospace";
104076
- rootElement.style.fontSize = "8px";
104077
- rootElement.style.fontWeight = "700";
104078
- }
104079
- onRedraw({}) {
104080
- const fps = this._getFps();
104081
- if (fps !== this._lastFps) {
104082
- this._lastFps = fps;
104083
- this.updateHTML();
104084
- }
104085
- }
104086
- _getFps() {
104087
- return Math.round(this.deck.metrics.fps ?? 0);
104088
- }
104089
- };
104090
- var FpsWidget = _FpsWidget;
104091
- FpsWidget.defaultProps = {
104092
- ...Widget.defaultProps,
104093
- id: "fps",
104094
- placement: "top-left"
104095
- };
104096
-
104097
- // ../widgets/src/themes.ts
104098
- var LightTheme = {
104099
- "--widget-margin": "12px",
104100
- "--button-size": "28px",
104101
- "--button-corner-radius": "8px",
104102
- "--button-background": "#fff",
104103
- "--button-stroke": "rgba(255, 255, 255, 0.3)",
104104
- "--button-inner-stroke": "unset",
104105
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
104106
- "--button-backdrop-filter": "unset",
104107
- "--button-icon-idle": "rgba(97, 97, 102, 1)",
104108
- "--button-icon-hover": "rgba(24, 24, 26, 1)",
104109
- "--icon-compass-north-color": "rgb(240, 92, 68)",
104110
- "--icon-compass-south-color": "rgb(204, 204, 204)"
104111
- };
104112
- var DarkTheme = {
104113
- "--widget-margin": "12px",
104114
- "--button-size": "28px",
104115
- "--button-corner-radius": "8px",
104116
- "--button-background": "rgba(18, 18, 20, 1)",
104117
- "--button-stroke": "rgba(18, 18, 20, 0.30)",
104118
- "--button-inner-stroke": "unset",
104119
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
104120
- "--button-backdrop-filter": "unset",
104121
- "--button-icon-idle": "rgba(158, 157, 168, 1)",
104122
- "--button-icon-hover": "rgba(215, 214, 229, 1)",
104123
- "--icon-compass-north-color": "rgb(240, 92, 68)",
104124
- "--icon-compass-south-color": "rgb(200, 199, 209)"
104125
- };
104126
- var LightGlassTheme = {
104127
- "--widget-margin": "12px",
104128
- "--button-size": "28px",
104129
- "--button-corner-radius": "8px",
104130
- "--button-background": "rgba(255, 255, 255, 0.6)",
104131
- "--button-stroke": "rgba(255, 255, 255, 0.3)",
104132
- "--button-inner-stroke": "1px solid rgba(255, 255, 255, 0.6)",
104133
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
104134
- "--button-backdrop-filter": "blur(4px)",
104135
- "--button-icon-idle": "rgba(97, 97, 102, 1)",
104136
- "--button-icon-hover": "rgba(24, 24, 26, 1)",
104137
- "--icon-compass-north-color": "rgb(240, 92, 68)",
104138
- "--icon-compass-south-color": "rgb(204, 204, 204)"
104139
- };
104140
- var DarkGlassTheme = {
104141
- "--widget-margin": "12px",
104142
- "--button-size": "28px",
104143
- "--button-corner-radius": "8px",
104144
- "--button-background": "rgba(18, 18, 20, 0.75)",
104145
- "--button-stroke": "rgba(18, 18, 20, 0.30)",
104146
- "--button-inner-stroke": "1px solid rgba(18, 18, 20, 0.75)",
104147
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
104148
- "--button-backdrop-filter": "blur(4px)",
104149
- "--button-icon-idle": "rgba(158, 157, 168, 1)",
104150
- "--button-icon-hover": "rgba(215, 214, 229, 1)",
104151
- "--icon-compass-north-color": "rgb(240, 92, 68)",
104152
- "--icon-compass-south-color": "rgb(200, 199, 209)"
104153
- };
104154
-
104155
- // ../widgets/src/theme-widget.tsx
104156
- var _ThemeWidget = class extends Widget {
104157
- constructor(props = {}) {
104158
- super(props, _ThemeWidget.defaultProps);
104159
- this.className = "deck-widget-theme";
104160
- this.placement = "top-left";
104161
- this.themeMode = "dark";
104162
- this.placement = props.placement ?? this.placement;
104163
- this.themeMode = this._getInitialThemeMode();
104164
- }
104165
- // eslint-disable-next-line complexity
104166
- setProps(props) {
104167
- const { lightModeTheme, darkModeTheme } = this.props;
104168
- this.placement = props.placement ?? this.placement;
104169
- super.setProps(props);
104170
- switch (this.themeMode) {
104171
- case "light":
104172
- if (props.lightModeTheme && !deepEqual2(props.lightModeTheme, lightModeTheme, 1)) {
104173
- this._setThemeMode("light");
104174
- }
104175
- break;
104176
- case "dark":
104177
- if (props.darkModeTheme && !deepEqual2(props.darkModeTheme, darkModeTheme, 1)) {
104178
- this._setThemeMode("dark");
104179
- }
104180
- break;
104181
- }
104182
- }
104183
- onRenderHTML(rootElement) {
104184
- const { lightModeLabel, darkModeLabel } = this.props;
104185
- B3(
104186
- /* @__PURE__ */ u4(
104187
- IconButton,
104188
- {
104189
- onClick: this._handleClick.bind(this),
104190
- label: this.themeMode === "dark" ? darkModeLabel : lightModeLabel,
104191
- className: this.themeMode === "dark" ? "deck-widget-moon" : "deck-widget-sun"
104192
- }
104193
- ),
104194
- rootElement
104195
- );
104196
- }
104197
- onAdd() {
104198
- this._setThemeMode(this.themeMode);
104199
- }
104200
- _handleClick() {
104201
- const newThemeMode = this.themeMode === "dark" ? "light" : "dark";
104202
- this._setThemeMode(newThemeMode);
104203
- }
104204
- _setThemeMode(themeMode) {
104205
- this.themeMode = themeMode;
104206
- const container = this.rootElement?.closest(".deck-widget-container");
104207
- if (container) {
104208
- const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
104209
- applyStyles(container, themeStyle);
104210
- const label = this.themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
104211
- log_default.log(1, `Switched theme to ${label}`, themeStyle);
104212
- this.updateHTML();
104213
- }
104214
- }
104215
- /** Read browser preference */
104216
- _getInitialThemeMode() {
104217
- const { initialThemeMode } = this.props;
104218
- return initialThemeMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : initialThemeMode;
104219
- }
104220
- };
104221
- var ThemeWidget = _ThemeWidget;
104222
- ThemeWidget.defaultProps = {
104223
- ...Widget.defaultProps,
104224
- id: "theme",
104225
- placement: "top-left",
104226
- lightModeLabel: "Light Mode",
104227
- lightModeTheme: LightGlassTheme,
104228
- darkModeLabel: "Dark Mode",
104229
- darkModeTheme: DarkGlassTheme,
104230
- initialThemeMode: "auto"
104231
- };
104232
-
104233
- // ../widgets/src/info-widget.tsx
104234
- var _InfoWidget = class extends Widget {
104235
- constructor(props) {
104236
- super(props, _InfoWidget.defaultProps);
104237
- this.className = "deck-widget-info";
104238
- this.placement = "fill";
104239
- this.viewId = null;
104240
- props.position = props.position || [0, 0];
104241
- props.text = props.text || "";
104242
- props.visible = props.visible || false;
104243
- props.minOffset = props.minOffset ?? 0;
104244
- }
104245
- setProps(props) {
104246
- super.setProps(props);
104247
- }
104248
- onCreateRootElement() {
104249
- const element = super.onCreateRootElement();
104250
- const style = { margin: "0px", top: "0px", left: "0px", position: "absolute" };
104251
- Object.entries(style).forEach(([key, value]) => element.style.setProperty(key, value));
104252
- return element;
104253
- }
104254
- onViewportChange(viewport) {
104255
- this.viewport = viewport;
104256
- this.updateHTML();
104294
+ onViewportChange(viewport) {
104295
+ this.viewport = viewport;
104296
+ this.updateHTML();
104257
104297
  }
104258
104298
  onHover(info) {
104259
104299
  if (this.props.mode === "hover" && this.props.getTooltip) {
@@ -104261,7 +104301,7 @@ ${fps}`;
104261
104301
  this.setProps({
104262
104302
  visible: tooltip !== null,
104263
104303
  ...tooltip,
104264
- style: { zIndex: 1, ...tooltip?.style }
104304
+ style: { zIndex: "1", ...tooltip?.style }
104265
104305
  });
104266
104306
  }
104267
104307
  }
@@ -104374,10 +104414,10 @@ ${fps}`;
104374
104414
  var InfoWidget = _InfoWidget;
104375
104415
  InfoWidget.defaultProps = {
104376
104416
  ...Widget.defaultProps,
104417
+ id: "info",
104377
104418
  position: [0, 0],
104378
104419
  text: "",
104379
104420
  visible: false,
104380
- // Set default minOffset if not provided
104381
104421
  minOffset: 0,
104382
104422
  viewId: null,
104383
104423
  mode: "hover",
@@ -104385,136 +104425,6 @@ ${fps}`;
104385
104425
  onClick: void 0
104386
104426
  };
104387
104427
 
104388
- // ../widgets/src/stats-widget.tsx
104389
- init_dist4();
104390
- var RIGHT_ARROW = "\u25B6";
104391
- var DOWN_ARROW = "\u2B07";
104392
- var DEFAULT_COUNT_FORMATTER = (stat) => `${stat.name}: ${stat.count}`;
104393
- function formatTime2(time) {
104394
- return time < 1e3 ? `${time.toFixed(2)}ms` : `${(time / 1e3).toFixed(2)}s`;
104395
- }
104396
- function formatMemory(bytes) {
104397
- const mb = bytes / 1e6;
104398
- return `${mb.toFixed(1)} MB`;
104399
- }
104400
- var DEFAULT_FORMATTERS = {
104401
- count: DEFAULT_COUNT_FORMATTER,
104402
- averageTime: (stat) => `${stat.name}: ${formatTime2(stat.getAverageTime())}`,
104403
- totalTime: (stat) => `${stat.name}: ${formatTime2(stat.time)}`,
104404
- fps: (stat) => `${stat.name}: ${Math.round(stat.getHz())}fps`,
104405
- memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
104406
- };
104407
- var _StatsWidget = class extends Widget {
104408
- constructor(props = {}) {
104409
- super(props, _StatsWidget.defaultProps);
104410
- this.className = "deck-widget-stats";
104411
- this.placement = "top-left";
104412
- this._counter = 0;
104413
- this.collapsed = true;
104414
- this._toggleCollapsed = () => {
104415
- this.collapsed = !this.collapsed;
104416
- this.updateHTML();
104417
- };
104418
- this._formatters = { ...DEFAULT_FORMATTERS };
104419
- this.setProps(props);
104420
- this._resetOnUpdate = { ...this.props.resetOnUpdate };
104421
- this._stats = this.props.stats;
104422
- }
104423
- setProps(props) {
104424
- super.setProps(props);
104425
- this._stats = this._getStats();
104426
- if (props.formatters) {
104427
- for (const name13 in props.formatters) {
104428
- const f6 = props.formatters[name13];
104429
- this._formatters[name13] = typeof f6 === "string" ? DEFAULT_FORMATTERS[f6] || DEFAULT_COUNT_FORMATTER : f6;
104430
- }
104431
- }
104432
- if (props.resetOnUpdate) {
104433
- this._resetOnUpdate = { ...props.resetOnUpdate };
104434
- }
104435
- }
104436
- onAdd() {
104437
- this._stats = this._getStats();
104438
- this.updateHTML();
104439
- }
104440
- onRenderHTML(rootElement) {
104441
- const stats2 = this._stats;
104442
- const collapsed = this.collapsed;
104443
- const title = this.props.title || stats2?.id || "Stats";
104444
- const items = [];
104445
- if (!collapsed && stats2) {
104446
- stats2.forEach((stat) => {
104447
- const lines = this._getLines(stat);
104448
- if (this._resetOnUpdate && this._resetOnUpdate[stat.name]) {
104449
- stat.reset();
104450
- }
104451
- lines.forEach((line, i7) => {
104452
- items.push(
104453
- /* @__PURE__ */ u4("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i7}`)
104454
- );
104455
- });
104456
- });
104457
- }
104458
- B3(
104459
- /* @__PURE__ */ u4("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [
104460
- /* @__PURE__ */ u4(
104461
- "div",
104462
- {
104463
- className: "deck-widget-stats-header",
104464
- style: { cursor: "pointer", pointerEvents: "auto" },
104465
- onClick: this._toggleCollapsed,
104466
- children: [
104467
- collapsed ? RIGHT_ARROW : DOWN_ARROW,
104468
- " ",
104469
- title
104470
- ]
104471
- }
104472
- ),
104473
- !collapsed && /* @__PURE__ */ u4("div", { className: "deck-widget-stats-content deck-widget-common", children: items })
104474
- ] }),
104475
- rootElement
104476
- );
104477
- }
104478
- onRedraw() {
104479
- const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
104480
- if (this._counter++ % framesPerUpdate === 0) {
104481
- console.log("Redrawing stats widget");
104482
- this.updateHTML();
104483
- }
104484
- }
104485
- _getStats() {
104486
- switch (this.props.type) {
104487
- case "deck":
104488
- return this.deck?.stats;
104489
- case "luma":
104490
- return Array.from(luma.stats.stats.values())[0];
104491
- case "device":
104492
- const device = this.deck?.device;
104493
- const stats2 = device?.statsManager.stats.values();
104494
- return stats2 ? Array.from(stats2)[0] : void 0;
104495
- case "custom":
104496
- return this.props.stats;
104497
- default:
104498
- throw new Error(`Unknown stats type: ${this.props.type}`);
104499
- }
104500
- }
104501
- _getLines(stat) {
104502
- const formatter = this._formatters[stat.name] || this._formatters[stat.type || ""] || DEFAULT_COUNT_FORMATTER;
104503
- return formatter(stat).split("\n");
104504
- }
104505
- };
104506
- var StatsWidget = _StatsWidget;
104507
- StatsWidget.defaultProps = {
104508
- ...Widget.defaultProps,
104509
- type: "deck",
104510
- stats: void 0,
104511
- title: "Stats",
104512
- framesPerUpdate: 1,
104513
- formatters: {},
104514
- resetOnUpdate: {},
104515
- id: "stats"
104516
- };
104517
-
104518
104428
  // ../widgets/src/lib/components/simple-menu.tsx
104519
104429
  var MENU_STYLE = {
104520
104430
  position: "absolute",
@@ -104523,7 +104433,7 @@ ${fps}`;
104523
104433
  background: "white",
104524
104434
  border: "1px solid #ccc",
104525
104435
  borderRadius: "4px",
104526
- marginTop: "4px",
104436
+ marginTop: "var(--menu-gap, 4px)",
104527
104437
  zIndex: 100
104528
104438
  };
104529
104439
  var MENU_ITEM_STYLE = {
@@ -104562,7 +104472,7 @@ ${fps}`;
104562
104472
  this.placement = "fill";
104563
104473
  this.pickInfo = null;
104564
104474
  this.pickInfo = null;
104565
- this.setProps(props);
104475
+ this.setProps(this.props);
104566
104476
  }
104567
104477
  onAdd({ deck }) {
104568
104478
  const element = document.createElement("div");
@@ -104628,131 +104538,6 @@ ${fps}`;
104628
104538
  onMenuItemSelected: (key, pickInfo) => console.log("Context menu item selected:", key, pickInfo)
104629
104539
  };
104630
104540
 
104631
- // ../widgets/src/splitter-widget.tsx
104632
- var _SplitterWidget = class extends Widget {
104633
- constructor(props) {
104634
- super(props, _SplitterWidget.defaultProps);
104635
- this.className = "deck-widget-splitter";
104636
- this.placement = "fill";
104637
- }
104638
- setProps(props) {
104639
- super.setProps(props);
104640
- }
104641
- onRenderHTML(rootElement) {
104642
- rootElement.style.position = "absolute";
104643
- rootElement.style.top = "0";
104644
- rootElement.style.left = "0";
104645
- rootElement.style.width = "100%";
104646
- rootElement.style.height = "100%";
104647
- rootElement.style.margin = "0px";
104648
- B3(
104649
- /* @__PURE__ */ u4(
104650
- Splitter,
104651
- {
104652
- orientation: this.props.orientation,
104653
- initialSplit: this.props.initialSplit,
104654
- onChange: this.props.onChange,
104655
- onDragStart: this.props.onDragStart,
104656
- onDragEnd: this.props.onDragEnd
104657
- }
104658
- ),
104659
- rootElement
104660
- );
104661
- }
104662
- };
104663
- var SplitterWidget = _SplitterWidget;
104664
- SplitterWidget.defaultProps = {
104665
- ...Widget.defaultProps,
104666
- id: "splitter-widget",
104667
- viewId1: "",
104668
- viewId2: "",
104669
- orientation: "vertical",
104670
- initialSplit: 0.5,
104671
- onChange: () => {
104672
- },
104673
- onDragStart: () => {
104674
- },
104675
- onDragEnd: () => {
104676
- }
104677
- };
104678
- function Splitter({
104679
- orientation,
104680
- initialSplit,
104681
- onChange,
104682
- onDragStart,
104683
- onDragEnd
104684
- }) {
104685
- const [split, setSplit] = h3(initialSplit);
104686
- const dragging = A4(false);
104687
- const containerRef = A4(null);
104688
- const handleDragStart = (event) => {
104689
- dragging.current = true;
104690
- onDragStart?.();
104691
- document.addEventListener("mousemove", handleDragging);
104692
- document.addEventListener("mouseup", handleDragEnd);
104693
- event.preventDefault();
104694
- };
104695
- const handleDragging = (event) => {
104696
- if (!dragging.current || !containerRef.current)
104697
- return;
104698
- const rect = containerRef.current.getBoundingClientRect();
104699
- let newSplit;
104700
- if (orientation === "vertical") {
104701
- newSplit = (event.clientX - rect.left) / rect.width;
104702
- } else {
104703
- newSplit = (event.clientY - rect.top) / rect.height;
104704
- }
104705
- newSplit = Math.min(Math.max(newSplit, 0.05), 0.95);
104706
- setSplit(newSplit);
104707
- onChange?.(newSplit);
104708
- };
104709
- const handleDragEnd = (event) => {
104710
- if (!dragging.current)
104711
- return;
104712
- dragging.current = false;
104713
- onDragEnd?.();
104714
- document.removeEventListener("mousemove", handleDragging);
104715
- document.removeEventListener("mouseup", handleDragEnd);
104716
- };
104717
- const splitterStyle = orientation === "vertical" ? {
104718
- position: "absolute",
104719
- top: 0,
104720
- bottom: 0,
104721
- left: `${split * 100}%`,
104722
- width: "4px",
104723
- cursor: "col-resize",
104724
- background: "#ccc",
104725
- zIndex: 10,
104726
- pointerEvents: "auto",
104727
- boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
104728
- } : {
104729
- position: "absolute",
104730
- left: 0,
104731
- right: 0,
104732
- top: `${split * 100}%`,
104733
- height: "4px",
104734
- cursor: "row-resize",
104735
- background: "#ccc",
104736
- zIndex: 10,
104737
- pointerEvents: "auto",
104738
- boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
104739
- };
104740
- const containerStyle = {
104741
- position: "absolute",
104742
- top: 0,
104743
- left: 0,
104744
- right: 0,
104745
- bottom: 0
104746
- };
104747
- return /* @__PURE__ */ u4("div", { ref: containerRef, style: containerStyle, children: /* @__PURE__ */ u4(
104748
- "div",
104749
- {
104750
- style: splitterStyle,
104751
- onMouseDown: handleDragStart
104752
- }
104753
- ) });
104754
- }
104755
-
104756
104541
  // ../widgets/src/timeline-widget.tsx
104757
104542
  var _TimelineWidget = class extends Widget {
104758
104543
  constructor(props = {}) {
@@ -104790,9 +104575,11 @@ ${fps}`;
104790
104575
  }
104791
104576
  };
104792
104577
  this.currentTime = this.props.initialTime ?? this.props.timeRange[0];
104578
+ this.setProps(this.props);
104793
104579
  }
104794
104580
  setProps(props) {
104795
- this.placement = props.placement || this.placement;
104581
+ this.placement = props.placement ?? this.placement;
104582
+ this.viewId = props.viewId ?? this.viewId;
104796
104583
  super.setProps(props);
104797
104584
  }
104798
104585
  onAdd() {
@@ -104805,16 +104592,7 @@ ${fps}`;
104805
104592
  onRenderHTML(rootElement) {
104806
104593
  B3(
104807
104594
  /* @__PURE__ */ u4("div", { style: { display: "flex", alignItems: "center", pointerEvents: "auto" }, children: [
104808
- /* @__PURE__ */ u4(
104809
- "button",
104810
- {
104811
- type: "button",
104812
- className: "timeline-play-pause",
104813
- title: this.playing ? "Pause" : "Play",
104814
- onClick: this.handlePlayPause,
104815
- children: this.playing ? "\u23F8" : "\u25B6"
104816
- }
104817
- ),
104595
+ /* @__PURE__ */ u4(IconButton, { label: this.playing ? "Pause" : "Play", onClick: this.handlePlayPause, children: /* @__PURE__ */ u4("div", { className: "text", children: this.playing ? "\u23F8" : "\u25B6" }) }),
104818
104596
  /* @__PURE__ */ u4(
104819
104597
  "input",
104820
104598
  {
@@ -104850,6 +104628,7 @@ ${fps}`;
104850
104628
  ...Widget.defaultProps,
104851
104629
  id: "timeline",
104852
104630
  placement: "bottom-left",
104631
+ viewId: null,
104853
104632
  timeRange: [0, 100],
104854
104633
  step: 1,
104855
104634
  initialTime: void 0,
@@ -104858,123 +104637,445 @@ ${fps}`;
104858
104637
  playInterval: 1e3
104859
104638
  };
104860
104639
 
104861
- // ../widgets/src/lib/components/icon-menu.tsx
104862
- var MENU_STYLE2 = {
104863
- position: "absolute",
104864
- top: "100%",
104865
- left: 0,
104866
- background: "white",
104867
- border: "1px solid #ccc",
104868
- borderRadius: "4px",
104869
- marginTop: "4px",
104870
- zIndex: 100
104640
+ // ../widgets/src/screenshot-widget.tsx
104641
+ var _ScreenshotWidget = class extends Widget {
104642
+ constructor(props = {}) {
104643
+ super(props, _ScreenshotWidget.defaultProps);
104644
+ this.className = "deck-widget-screenshot";
104645
+ this.placement = "top-left";
104646
+ this.setProps(this.props);
104647
+ }
104648
+ setProps(props) {
104649
+ this.placement = props.placement ?? this.placement;
104650
+ this.viewId = props.viewId ?? this.viewId;
104651
+ super.setProps(props);
104652
+ }
104653
+ onRenderHTML(rootElement) {
104654
+ B3(
104655
+ /* @__PURE__ */ u4(
104656
+ IconButton,
104657
+ {
104658
+ className: "deck-widget-camera",
104659
+ label: this.props.label,
104660
+ onClick: this.handleClick.bind(this)
104661
+ }
104662
+ ),
104663
+ rootElement
104664
+ );
104665
+ }
104666
+ handleClick() {
104667
+ if (this.props.onCapture) {
104668
+ this.props.onCapture(this);
104669
+ return;
104670
+ }
104671
+ const dataURL = this.captureScreenToDataURL(this.props.imageFormat);
104672
+ if (dataURL) {
104673
+ this.downloadDataURL(dataURL, this.props.filename);
104674
+ }
104675
+ }
104676
+ /** @note only captures canvas contents, not HTML DOM or CSS styles */
104677
+ captureScreenToDataURL(imageFormat) {
104678
+ const canvas2 = this.deck?.getCanvas();
104679
+ return canvas2?.toDataURL(imageFormat);
104680
+ }
104681
+ /** Download a data URL */
104682
+ downloadDataURL(dataURL, filename2) {
104683
+ const link = document.createElement("a");
104684
+ link.href = dataURL;
104685
+ link.download = filename2;
104686
+ link.click();
104687
+ }
104871
104688
  };
104872
- var MENU_ITEM_STYLE2 = {
104873
- background: "white",
104874
- border: "none",
104875
- padding: "4px",
104876
- cursor: "pointer",
104877
- pointerEvents: "auto"
104689
+ var ScreenshotWidget = _ScreenshotWidget;
104690
+ ScreenshotWidget.defaultProps = {
104691
+ ...Widget.defaultProps,
104692
+ id: "screenshot",
104693
+ placement: "top-left",
104694
+ viewId: null,
104695
+ label: "Screenshot",
104696
+ filename: "screenshot.png",
104697
+ imageFormat: "image/png",
104698
+ onCapture: void 0
104878
104699
  };
104879
- function IconMenu(props) {
104880
- const [menuOpen, setMenuOpen] = h3(false);
104881
- const containerRef = A4(null);
104882
- const handleClickOutside = (event) => {
104883
- if (containerRef.current && !containerRef.current.contains(event.target)) {
104884
- setMenuOpen(false);
104885
- }
104886
- };
104887
- y4(() => {
104888
- document.addEventListener("mousedown", handleClickOutside);
104889
- return () => {
104890
- document.removeEventListener("mousedown", handleClickOutside);
104891
- };
104892
- }, [containerRef]);
104893
- const [selectedItem, setSelectedItem] = h3(props.initialItem);
104894
- const handleSelectItem = (item) => {
104895
- setSelectedItem(item);
104896
- setMenuOpen(false);
104897
- props.onItemSelected(item);
104898
- };
104899
- const handleButtonClick = () => setMenuOpen(!menuOpen);
104900
- const selectedMenuItem = props.menuItems.find((item) => item.value === selectedItem);
104901
- const label = props.label || selectedMenuItem?.label || "";
104902
- const Icon = props.icon || selectedMenuItem?.icon;
104903
- return /* @__PURE__ */ u4("div", { style: { position: "relative", display: "inline-block" }, ref: containerRef, children: [
104904
- /* @__PURE__ */ u4(IconButton, { className: props.className, label, onClick: handleButtonClick, children: /* @__PURE__ */ u4(Icon, {}) }),
104905
- menuOpen && /* @__PURE__ */ u4("div", { style: MENU_STYLE2, children: /* @__PURE__ */ u4("div", { style: { display: "flex", flexDirection: "column" }, children: props.menuItems.map((item) => /* @__PURE__ */ u4(
104906
- "button",
104907
- {
104908
- style: MENU_ITEM_STYLE2,
104909
- title: item.label,
104910
- onClick: () => handleSelectItem(item.value),
104911
- children: item.icon
104912
- },
104913
- item.value
104914
- )) }) })
104915
- ] });
104916
- }
104917
104700
 
104918
- // ../widgets/src/view-selector-widget.tsx
104919
- var _ViewSelectorWidget = class extends Widget {
104701
+ // ../widgets/src/themes.ts
104702
+ var LightTheme = {
104703
+ "--widget-margin": "12px",
104704
+ "--button-size": "28px",
104705
+ "--button-corner-radius": "8px",
104706
+ "--button-background": "#fff",
104707
+ "--button-stroke": "rgba(255, 255, 255, 0.3)",
104708
+ "--button-inner-stroke": "unset",
104709
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
104710
+ "--button-backdrop-filter": "unset",
104711
+ "--button-icon-idle": "rgba(97, 97, 102, 1)",
104712
+ "--button-icon-hover": "rgba(24, 24, 26, 1)",
104713
+ "--button-text": "rgb(24, 24, 26, 1)",
104714
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
104715
+ "--icon-compass-south-color": "rgb(204, 204, 204)",
104716
+ "--menu-gap": "4px"
104717
+ };
104718
+ var DarkTheme = {
104719
+ "--widget-margin": "12px",
104720
+ "--button-size": "28px",
104721
+ "--button-corner-radius": "8px",
104722
+ "--button-background": "rgba(18, 18, 20, 1)",
104723
+ "--button-stroke": "rgba(18, 18, 20, 0.30)",
104724
+ "--button-inner-stroke": "unset",
104725
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
104726
+ "--button-backdrop-filter": "unset",
104727
+ "--button-icon-idle": "rgba(158, 157, 168, 1)",
104728
+ "--button-icon-hover": "rgba(215, 214, 229, 1)",
104729
+ "--button-text": "rgb(215, 214, 229, 1)",
104730
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
104731
+ "--icon-compass-south-color": "rgb(200, 199, 209)",
104732
+ "--menu-gap": "4px"
104733
+ };
104734
+ var LightGlassTheme = {
104735
+ "--widget-margin": "12px",
104736
+ "--button-size": "28px",
104737
+ "--button-corner-radius": "8px",
104738
+ "--button-background": "rgba(255, 255, 255, 0.6)",
104739
+ "--button-stroke": "rgba(255, 255, 255, 0.3)",
104740
+ "--button-inner-stroke": "1px solid rgba(255, 255, 255, 0.6)",
104741
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
104742
+ "--button-backdrop-filter": "blur(4px)",
104743
+ "--button-icon-idle": "rgba(97, 97, 102, 1)",
104744
+ "--button-icon-hover": "rgba(24, 24, 26, 1)",
104745
+ "--button-text": "rgb(24, 24, 26, 1)",
104746
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
104747
+ "--icon-compass-south-color": "rgb(204, 204, 204)",
104748
+ "--menu-gap": "4px"
104749
+ };
104750
+ var DarkGlassTheme = {
104751
+ "--widget-margin": "12px",
104752
+ "--button-size": "28px",
104753
+ "--button-corner-radius": "8px",
104754
+ "--button-background": "rgba(18, 18, 20, 0.75)",
104755
+ "--button-stroke": "rgba(18, 18, 20, 0.30)",
104756
+ "--button-inner-stroke": "1px solid rgba(18, 18, 20, 0.75)",
104757
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
104758
+ "--button-backdrop-filter": "blur(4px)",
104759
+ "--button-icon-idle": "rgba(158, 157, 168, 1)",
104760
+ "--button-icon-hover": "rgba(215, 214, 229, 1)",
104761
+ "--button-text": "rgb(215, 214, 229, 1)",
104762
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
104763
+ "--icon-compass-south-color": "rgb(200, 199, 209)",
104764
+ "--menu-gap": "4px"
104765
+ };
104766
+
104767
+ // ../widgets/src/theme-widget.tsx
104768
+ var _ThemeWidget = class extends Widget {
104920
104769
  constructor(props = {}) {
104921
- super(props, _ViewSelectorWidget.defaultProps);
104922
- this.className = "deck-widget-view-selector";
104770
+ super(props, _ThemeWidget.defaultProps);
104771
+ this.className = "deck-widget-theme";
104923
104772
  this.placement = "top-left";
104924
- this.handleSelectMode = (viewMode) => {
104925
- this.viewMode = viewMode;
104926
- this.updateHTML();
104927
- };
104928
- this.placement = this.props.placement;
104929
- this.viewMode = this.props.initialViewMode;
104773
+ this.themeMode = "dark";
104774
+ this.themeMode = this._getInitialThemeMode();
104775
+ this.setProps(this.props);
104930
104776
  }
104777
+ // eslint-disable-next-line complexity
104931
104778
  setProps(props) {
104932
- super.setProps(props);
104779
+ const { lightModeTheme, darkModeTheme } = this.props;
104933
104780
  this.placement = props.placement ?? this.placement;
104781
+ this.viewId = props.viewId ?? this.viewId;
104782
+ super.setProps(props);
104783
+ switch (this.themeMode) {
104784
+ case "light":
104785
+ if (props.lightModeTheme && !deepEqual2(props.lightModeTheme, lightModeTheme, 1)) {
104786
+ this._setThemeMode("light");
104787
+ }
104788
+ break;
104789
+ case "dark":
104790
+ if (props.darkModeTheme && !deepEqual2(props.darkModeTheme, darkModeTheme, 1)) {
104791
+ this._setThemeMode("dark");
104792
+ }
104793
+ break;
104794
+ default:
104795
+ log_default.warn(`Invalid theme mode ${this.themeMode}`)();
104796
+ }
104934
104797
  }
104935
104798
  onRenderHTML(rootElement) {
104799
+ const { lightModeLabel, darkModeLabel } = this.props;
104936
104800
  B3(
104937
104801
  /* @__PURE__ */ u4(
104938
- IconMenu,
104802
+ IconButton,
104939
104803
  {
104940
- className: "deck-widget-view-selector",
104941
- menuItems: MENU_ITEMS,
104942
- initialItem: this.props.initialViewMode,
104943
- onItemSelected: this.handleSelectMode
104804
+ onClick: this._handleClick.bind(this),
104805
+ label: this.themeMode === "dark" ? darkModeLabel : lightModeLabel,
104806
+ className: this.themeMode === "dark" ? "deck-widget-moon" : "deck-widget-sun"
104944
104807
  }
104945
104808
  ),
104946
104809
  rootElement
104947
104810
  );
104948
104811
  }
104812
+ onAdd() {
104813
+ this._setThemeMode(this.themeMode);
104814
+ }
104815
+ _handleClick() {
104816
+ const newThemeMode = this.themeMode === "dark" ? "light" : "dark";
104817
+ this._setThemeMode(newThemeMode);
104818
+ }
104819
+ _setThemeMode(themeMode) {
104820
+ this.themeMode = themeMode;
104821
+ const container = this.rootElement?.closest(".deck-widget-container");
104822
+ if (container) {
104823
+ const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
104824
+ applyStyles(container, themeStyle);
104825
+ const label = this.themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
104826
+ log_default.log(1, `Switched theme to ${label}`, themeStyle)();
104827
+ this.updateHTML();
104828
+ }
104829
+ }
104830
+ /** Read browser preference */
104831
+ _getInitialThemeMode() {
104832
+ const { initialThemeMode } = this.props;
104833
+ return initialThemeMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : initialThemeMode;
104834
+ }
104949
104835
  };
104950
- var ViewSelectorWidget = _ViewSelectorWidget;
104951
- ViewSelectorWidget.defaultProps = {
104836
+ var ThemeWidget = _ThemeWidget;
104837
+ ThemeWidget.defaultProps = {
104952
104838
  ...Widget.defaultProps,
104953
- id: "view-selector",
104839
+ id: "theme",
104954
104840
  placement: "top-left",
104955
- label: "Split View",
104956
- initialViewMode: "single",
104957
- onViewModeChange: (viewMode) => {
104958
- console.log(viewMode);
104841
+ viewId: null,
104842
+ lightModeLabel: "Light Mode",
104843
+ lightModeTheme: LightGlassTheme,
104844
+ darkModeLabel: "Dark Mode",
104845
+ darkModeTheme: DarkGlassTheme,
104846
+ initialThemeMode: "auto"
104847
+ };
104848
+
104849
+ // ../widgets/src/loading-widget.tsx
104850
+ var _LoadingWidget = class extends Widget {
104851
+ constructor(props = {}) {
104852
+ super(props, _LoadingWidget.defaultProps);
104853
+ this.className = "deck-widget-loading";
104854
+ this.placement = "top-left";
104855
+ this.loading = true;
104856
+ this.setProps(this.props);
104857
+ }
104858
+ setProps(props) {
104859
+ this.placement = props.placement ?? this.placement;
104860
+ this.viewId = props.viewId ?? this.viewId;
104861
+ super.setProps(props);
104862
+ }
104863
+ onRenderHTML(rootElement) {
104864
+ B3(
104865
+ // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
104866
+ this.loading && /* @__PURE__ */ u4(
104867
+ IconButton,
104868
+ {
104869
+ className: "deck-widget-spinner",
104870
+ label: this.props.label,
104871
+ onClick: this.handleClick.bind(this)
104872
+ }
104873
+ ),
104874
+ rootElement
104875
+ );
104876
+ }
104877
+ onRedraw({ layers }) {
104878
+ const loading = layers.some((layer) => !layer.isLoaded);
104879
+ if (loading !== this.loading) {
104880
+ this.loading = loading;
104881
+ this.updateHTML();
104882
+ }
104883
+ }
104884
+ // TODO(ibgreen) - this should not be a button, see above.
104885
+ handleClick() {
104959
104886
  }
104960
104887
  };
104961
- var ICON_STYLE = { width: "24px", height: "24px" };
104962
- var ICONS = {
104963
- single: () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: /* @__PURE__ */ u4("rect", { x: "4", y: "4", width: "16", height: "16", stroke: "black", fill: "none", strokeWidth: "2" }) }),
104964
- "split-horizontal": () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
104965
- /* @__PURE__ */ u4("rect", { x: "4", y: "4", width: "16", height: "7", stroke: "black", fill: "none", strokeWidth: "2" }),
104966
- /* @__PURE__ */ u4("rect", { x: "4", y: "13", width: "16", height: "7", stroke: "black", fill: "none", strokeWidth: "2" })
104967
- ] }),
104968
- "split-vertical": () => /* @__PURE__ */ u4("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
104969
- /* @__PURE__ */ u4("rect", { x: "4", y: "4", width: "7", height: "16", stroke: "black", fill: "none", strokeWidth: "2" }),
104970
- /* @__PURE__ */ u4("rect", { x: "13", y: "4", width: "7", height: "16", stroke: "black", fill: "none", strokeWidth: "2" })
104971
- ] })
104888
+ var LoadingWidget = _LoadingWidget;
104889
+ LoadingWidget.defaultProps = {
104890
+ ...Widget.defaultProps,
104891
+ id: "loading",
104892
+ placement: "top-left",
104893
+ viewId: null,
104894
+ label: "Loading layer data"
104895
+ };
104896
+
104897
+ // ../widgets/src/fps-widget.tsx
104898
+ var _FpsWidget = class extends Widget {
104899
+ constructor(props = {}) {
104900
+ super(props, _FpsWidget.defaultProps);
104901
+ this.className = "deck-widget-fps";
104902
+ this.placement = "top-left";
104903
+ this._lastFps = -1;
104904
+ this.setProps(this.props);
104905
+ }
104906
+ setProps(props) {
104907
+ this.placement = props.placement ?? this.placement;
104908
+ this.viewId = props.viewId ?? this.viewId;
104909
+ super.setProps(props);
104910
+ }
104911
+ onAdd({}) {
104912
+ this._lastFps = this._getFps();
104913
+ requestAnimationFrame(() => this._animate());
104914
+ }
104915
+ onRenderHTML(rootElement) {
104916
+ const fps = this._getFps();
104917
+ B3(
104918
+ /* @__PURE__ */ u4(IconButton, { children: /* @__PURE__ */ u4("div", { className: "text", children: [
104919
+ "FPS",
104920
+ /* @__PURE__ */ u4("br", {}),
104921
+ fps
104922
+ ] }) }),
104923
+ rootElement
104924
+ );
104925
+ }
104926
+ _animate() {
104927
+ const fps = this._getFps();
104928
+ if (this._lastFps !== fps) {
104929
+ this._lastFps = fps;
104930
+ this.updateHTML();
104931
+ }
104932
+ requestAnimationFrame(() => this._animate());
104933
+ }
104934
+ _getFps() {
104935
+ return Math.round(this.deck?.metrics.fps ?? 0);
104936
+ }
104937
+ };
104938
+ var FpsWidget = _FpsWidget;
104939
+ FpsWidget.defaultProps = {
104940
+ ...Widget.defaultProps,
104941
+ id: "fps",
104942
+ placement: "top-left",
104943
+ viewId: null
104944
+ };
104945
+
104946
+ // ../widgets/src/stats-widget.tsx
104947
+ init_dist4();
104948
+ var RIGHT_ARROW = "\u25B6";
104949
+ var DOWN_ARROW = "\u2B07";
104950
+ var DEFAULT_COUNT_FORMATTER = (stat) => `${stat.name}: ${stat.count}`;
104951
+ function formatTime2(time) {
104952
+ return time < 1e3 ? `${time.toFixed(2)}ms` : `${(time / 1e3).toFixed(2)}s`;
104953
+ }
104954
+ function formatMemory(bytes) {
104955
+ const mb = bytes / 1e6;
104956
+ return `${mb.toFixed(1)} MB`;
104957
+ }
104958
+ var DEFAULT_FORMATTERS = {
104959
+ count: DEFAULT_COUNT_FORMATTER,
104960
+ averageTime: (stat) => `${stat.name}: ${formatTime2(stat.getAverageTime())}`,
104961
+ totalTime: (stat) => `${stat.name}: ${formatTime2(stat.time)}`,
104962
+ fps: (stat) => `${stat.name}: ${Math.round(stat.getHz())}fps`,
104963
+ memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
104964
+ };
104965
+ var _StatsWidget = class extends Widget {
104966
+ constructor(props = {}) {
104967
+ super(props, _StatsWidget.defaultProps);
104968
+ this.className = "deck-widget-stats";
104969
+ this.placement = "top-left";
104970
+ this._counter = 0;
104971
+ this.collapsed = true;
104972
+ this._toggleCollapsed = () => {
104973
+ this.collapsed = !this.collapsed;
104974
+ this.updateHTML();
104975
+ };
104976
+ this._formatters = { ...DEFAULT_FORMATTERS };
104977
+ this._resetOnUpdate = { ...this.props.resetOnUpdate };
104978
+ this._stats = this.props.stats;
104979
+ this.setProps(props);
104980
+ }
104981
+ setProps(props) {
104982
+ this.placement = props.placement ?? this.placement;
104983
+ this.viewId = props.viewId ?? this.viewId;
104984
+ this._stats = this._getStats();
104985
+ if (props.formatters) {
104986
+ for (const name13 in props.formatters) {
104987
+ const f6 = props.formatters[name13];
104988
+ this._formatters[name13] = typeof f6 === "string" ? DEFAULT_FORMATTERS[f6] || DEFAULT_COUNT_FORMATTER : f6;
104989
+ }
104990
+ }
104991
+ if (props.resetOnUpdate) {
104992
+ this._resetOnUpdate = { ...props.resetOnUpdate };
104993
+ }
104994
+ super.setProps(props);
104995
+ }
104996
+ onAdd() {
104997
+ this._stats = this._getStats();
104998
+ this.updateHTML();
104999
+ }
105000
+ onRenderHTML(rootElement) {
105001
+ const stats2 = this._stats;
105002
+ const collapsed = this.collapsed;
105003
+ const title = this.props.title || stats2?.id || "Stats";
105004
+ const items = [];
105005
+ if (!collapsed && stats2) {
105006
+ stats2.forEach((stat) => {
105007
+ const lines = this._getLines(stat);
105008
+ if (this._resetOnUpdate && this._resetOnUpdate[stat.name]) {
105009
+ stat.reset();
105010
+ }
105011
+ lines.forEach((line, i7) => {
105012
+ items.push(
105013
+ /* @__PURE__ */ u4("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i7}`)
105014
+ );
105015
+ });
105016
+ });
105017
+ }
105018
+ B3(
105019
+ /* @__PURE__ */ u4("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [
105020
+ /* @__PURE__ */ u4(
105021
+ "div",
105022
+ {
105023
+ className: "deck-widget-stats-header",
105024
+ style: { cursor: "pointer", pointerEvents: "auto" },
105025
+ onClick: this._toggleCollapsed,
105026
+ children: [
105027
+ collapsed ? RIGHT_ARROW : DOWN_ARROW,
105028
+ " ",
105029
+ title
105030
+ ]
105031
+ }
105032
+ ),
105033
+ !collapsed && /* @__PURE__ */ u4("div", { className: "deck-widget-stats-content", children: items })
105034
+ ] }),
105035
+ rootElement
105036
+ );
105037
+ }
105038
+ onRedraw() {
105039
+ const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
105040
+ if (this._counter++ % framesPerUpdate === 0) {
105041
+ this._stats = this._getStats();
105042
+ this.updateHTML();
105043
+ }
105044
+ }
105045
+ _getStats() {
105046
+ switch (this.props.type) {
105047
+ case "deck":
105048
+ return this.deck?.stats;
105049
+ case "luma":
105050
+ return Array.from(luma.stats.stats.values())[0];
105051
+ case "device":
105052
+ const device = this.deck?.device;
105053
+ const stats2 = device?.statsManager.stats.values();
105054
+ return stats2 ? Array.from(stats2)[0] : void 0;
105055
+ case "custom":
105056
+ return this.props.stats;
105057
+ default:
105058
+ throw new Error(`Unknown stats type: ${this.props.type}`);
105059
+ }
105060
+ }
105061
+ _getLines(stat) {
105062
+ const formatter = this._formatters[stat.name] || this._formatters[stat.type || ""] || DEFAULT_COUNT_FORMATTER;
105063
+ return formatter(stat).split("\n");
105064
+ }
105065
+ };
105066
+ var StatsWidget = _StatsWidget;
105067
+ StatsWidget.defaultProps = {
105068
+ ...Widget.defaultProps,
105069
+ type: "deck",
105070
+ placement: "top-left",
105071
+ viewId: null,
105072
+ stats: void 0,
105073
+ title: "Stats",
105074
+ framesPerUpdate: 1,
105075
+ formatters: {},
105076
+ resetOnUpdate: {},
105077
+ id: "stats"
104972
105078
  };
104973
- var MENU_ITEMS = [
104974
- { value: "single", icon: ICONS.single, label: "Single View" },
104975
- { value: "split-horizontal", icon: ICONS["split-horizontal"], label: "Split Horizontal" },
104976
- { value: "split-vertical", icon: ICONS["split-vertical"], label: "Split Vertical" }
104977
- ];
104978
105079
 
104979
105080
  // bundle.ts
104980
105081
  var h32 = __toESM(require_h3_js(), 1);