@ankhorage/zora 27.1.0 → 27.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.d.ts.map +1 -1
  4. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js +4 -2
  5. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js.map +1 -1
  6. package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.d.ts.map +1 -1
  7. package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.js +2 -1
  8. package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.js.map +1 -1
  9. package/dist/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.d.ts +4 -0
  10. package/dist/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.d.ts.map +1 -0
  11. package/dist/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.js +9 -0
  12. package/dist/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.js.map +1 -0
  13. package/package.json +4 -4
  14. package/src/features/accordion/adapters/inbound/AccordionItem.test.tsx +3 -3
  15. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts +4 -2
  16. package/src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts +2 -1
  17. package/src/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.test.ts +32 -0
  18. package/src/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.ts +10 -0
  19. package/web-dist/components/game/Game.js +1 -1
  20. package/web-dist/components/game-entity/GameEntity.js +1 -1
  21. package/web-dist/components/game-field/GameField.js +1 -1
  22. package/web-dist/components/game-input-zone/GameInputZone.js +1 -1
  23. package/web-dist/components/game-measurement-probe/GameMeasurementProbe.js +1 -1
  24. package/web-dist/components/game-overlay/GameOverlay.js +1 -1
  25. package/web-dist/components/graph-view/GraphView.js +13 -3
  26. package/web-dist/manifest.json +6 -6
  27. /package/web-dist/chunks/{Accordion-ve9f47b4.js → Accordion-enn0479f.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 27.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7b77abf: Fit GraphView canvases narrower than the requested padding without clipping rendered nodes.
8
+
9
+ ## 27.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 4eab6f3: Update dependencies: `@ankhorage/contracts`, `@ankhorage/runtime`, `@ankhorage/surface`.
14
+
3
15
  ## 27.1.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/zora
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v27.1.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v27.1.2](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
7
7
 
8
8
  Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"createGraphController.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAGhF,kGAAkG;AAClG,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,IAAI,EACR,aAAa,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAClC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI;;qBAkDjC,kBAAkB;IASnC,6EAA6E;;IAI7E,8FAA8F;;IAI9F,6FAA6F;;gBAIjF,OAAO;EAuBtB"}
1
+ {"version":3,"file":"createGraphController.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAIhF,kGAAkG;AAClG,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,IAAI,EACR,aAAa,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAClC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI;;qBAkDjC,kBAAkB;IASnC,6EAA6E;;IAI7E,8FAA8F;;IAI9F,6FAA6F;;gBAIjF,OAAO;EAuBtB"}
@@ -1,4 +1,5 @@
1
1
  import { fitGraphViewport } from './fitGraphViewport';
2
+ import { getGraphFitPadding } from './getGraphFitPadding';
2
3
  /*** Own viewport units and limits alongside the public controller, without exposing Cytoscape. */
3
4
  export function createGraphController(cy, fitPaddingRef, optimizeFit) {
4
5
  const state = {
@@ -189,8 +190,9 @@ function getNodeFitZoom(cy, padding) {
189
190
  if (cy.nodes().empty())
190
191
  return 1;
191
192
  const bounds = cy.nodes().boundingBox();
192
- const width = cy.width() - 2 * padding;
193
- const height = cy.height() - 2 * padding;
193
+ const fitPadding = getGraphFitPadding(cy, padding);
194
+ const width = cy.width() - 2 * fitPadding;
195
+ const height = cy.height() - 2 * fitPadding;
194
196
  if (width <= 0 || height <= 0 || bounds.w <= 0 || bounds.h <= 0)
195
197
  return 1;
196
198
  return Math.min(width / bounds.w, height / bounds.h);
@@ -1 +1 @@
1
- {"version":3,"file":"createGraphController.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type {\n GraphViewController,\n GraphViewFitOptions,\n GraphViewMeasurement,\n} from '../../../../../types/graph-view';\nimport type { GraphRuntimeUpdate } from '../../../../../types/graphViewRuntime';\nimport { fitGraphViewport } from './fitGraphViewport';\n\n/*** Own viewport units and limits alongside the public controller, without exposing Cytoscape. */\nexport function createGraphController(\n cy: Core,\n fitPaddingRef: { current: number },\n optimizeFit?: (options: GraphViewFitOptions) => void,\n) {\n const state = {\n scale: 1,\n relative: false,\n min: 0.05,\n max: 2,\n readableSize: 0,\n fitSize: Infinity,\n readableZoom: 0,\n fitZoom: Infinity,\n resizeSnapshot: null as GraphResizeSnapshot | null,\n };\n const controller: GraphViewController = {\n fit(options) {\n if (options?.optimizeSpacing && !options.nodeIds?.length && optimizeFit) {\n optimizeFit(options);\n return;\n }\n fitGraphViewport(\n cy,\n {\n nodeIds: options?.nodeIds,\n padding: options?.padding ?? fitPaddingRef.current,\n },\n state.fitZoom,\n );\n },\n measureNodes(nodeIds) {\n return measureRenderedNodes(cy, nodeIds);\n },\n getViewport() {\n return { pan: cy.pan(), zoom: cy.zoom() / state.scale };\n },\n getZoomRange() {\n return { min: cy.minZoom() / state.scale, max: cy.maxZoom() / state.scale };\n },\n setPan(pan) {\n cy.pan(pan);\n },\n setZoom(zoom) {\n setCenteredZoom(cy, zoom * state.scale);\n },\n zoomBy(factor) {\n setCenteredZoom(cy, cy.zoom() * factor);\n },\n };\n\n return {\n controller,\n configure(input: GraphRuntimeUpdate) {\n state.relative = input.zoomMode === 'fit-relative';\n state.min = input.minZoom ?? 0.05;\n state.max = input.maxZoom ?? 2;\n state.readableSize = positiveSize(input.minReadableLabelSize, 0);\n state.fitSize = positiveSize(input.maxFitLabelSize, Infinity);\n if (!state.relative) state.scale = 1;\n applyZoomLimits(cy, state);\n },\n /*** Return the native automatic-fit ceiling used by spacing optimization. */\n getFitZoomLimit() {\n return state.relative ? state.fitZoom : Math.min(state.fitZoom, state.max);\n },\n /*** Return the native label-readability threshold used before allowing background overlap. */\n getReadableZoomTarget() {\n return state.readableZoom;\n },\n /*** Capture the model point beneath the current viewport center before Cytoscape resizes. */\n beginResize() {\n state.resizeSnapshot = captureResizeSnapshot(cy);\n },\n settle(fit: boolean) {\n const logicalZoom = cy.zoom() / state.scale;\n const fonts = cy\n .nodes()\n .filter((node) => !node.isParent() && Boolean(node.style('label')))\n .map((node) => Number.parseFloat(String(node.style('font-size'))))\n .filter((size) => Number.isFinite(size) && size > 0);\n state.readableZoom = fonts.length\n ? state.readableSize / fonts.reduce((minimum, size) => Math.min(minimum, size), Infinity)\n : 0;\n state.fitZoom = fonts.length\n ? state.fitSize / fonts.reduce((maximum, size) => Math.max(maximum, size), 0)\n : Infinity;\n state.scale = state.relative\n ? Math.min(getNodeFitZoom(cy, fitPaddingRef.current), state.fitZoom)\n : 1;\n applyZoomLimits(cy, state);\n if (fit) {\n state.resizeSnapshot = null;\n controller.fit();\n } else if (settlePreservedViewport(cy, state, logicalZoom)) controller.fit();\n },\n };\n}\n\ninterface GraphResizeSnapshot {\n readonly center: { readonly x: number; readonly y: number };\n readonly nodeBounds: GraphNodeBoundsStatus;\n}\n\ntype GraphNodeBoundsStatus = 'clipped' | 'empty' | 'fit' | 'unusable';\n\n/*** Capture the model-space center and whether the prior viewport was an intact graph fit. */\nfunction captureResizeSnapshot(cy: Core): GraphResizeSnapshot | null {\n const viewport = { width: cy.width(), height: cy.height() };\n const zoom = cy.zoom();\n if (!isUsableViewport(viewport) || !Number.isFinite(zoom) || zoom <= 0) return null;\n const pan = cy.pan();\n const renderedCenter = { x: viewport.width / 2, y: viewport.height / 2 };\n return {\n center: {\n x: (renderedCenter.x - pan.x) / zoom,\n y: (renderedCenter.y - pan.y) / zoom,\n },\n nodeBounds: getNodeBoundsStatus(cy, viewport),\n };\n}\n\n/*** Preserve a manual viewport through resize, fitting only when an intact prior fit becomes invalid. */\nfunction settlePreservedViewport(\n cy: Core,\n state: {\n readonly relative: boolean;\n resizeSnapshot: GraphResizeSnapshot | null;\n readonly scale: number;\n },\n logicalZoom: number,\n): boolean {\n const snapshot = state.resizeSnapshot;\n state.resizeSnapshot = null;\n const zoom = state.relative ? logicalZoom * state.scale : cy.zoom();\n if (snapshot === null) {\n if (state.relative) setCenteredZoom(cy, zoom);\n return false;\n }\n const viewport = { width: cy.width(), height: cy.height() };\n if (!isUsableViewport(viewport)) return false;\n cy.viewport({\n zoom,\n pan: {\n x: viewport.width / 2 - snapshot.center.x * zoom,\n y: viewport.height / 2 - snapshot.center.y * zoom,\n },\n });\n const nodeBounds = getNodeBoundsStatus(cy, viewport);\n return nodeBounds === 'unusable' || (snapshot.nodeBounds === 'fit' && nodeBounds === 'clipped');\n}\n\n/*** Accept only finite non-empty node bounds that are wholly visible in a usable viewport. */\nfunction getNodeBoundsStatus(\n cy: Core,\n viewport: { readonly width: number; readonly height: number },\n): GraphNodeBoundsStatus {\n if (cy.nodes().empty()) return 'empty';\n if (!isUsableViewport(viewport)) return 'unusable';\n const bounds = cy.nodes().renderedBoundingBox();\n if (![bounds.x1, bounds.x2, bounds.y1, bounds.y2].every(Number.isFinite)) return 'unusable';\n const fitsHorizontally = bounds.x1 >= 0 && bounds.x2 <= viewport.width;\n const fitsVertically = bounds.y1 >= 0 && bounds.y2 <= viewport.height;\n return fitsHorizontally && fitsVertically ? 'fit' : 'clipped';\n}\n\n/*** Reject zero, negative, and non-finite renderer dimensions before viewport arithmetic. */\nfunction isUsableViewport(viewport: { readonly width: number; readonly height: number }): boolean {\n return (\n Number.isFinite(viewport.width) &&\n Number.isFinite(viewport.height) &&\n viewport.width > 0 &&\n viewport.height > 0\n );\n}\n\n/*** Measure rendered node hit geometry without exposing paint-only outlines or Cytoscape. */\nfunction measureRenderedNodes(cy: Core, nodeIds: readonly string[]): GraphViewMeasurement | null {\n const viewport = { width: cy.width(), height: cy.height() };\n if (viewport.width <= 0 || viewport.height <= 0 || nodeIds.length === 0) return null;\n\n const nodes = [...new Set(nodeIds)].flatMap((id) => {\n const node = cy.getElementById(id);\n if (!node.isNode()) return [];\n\n return [\n {\n id,\n position: node.renderedPosition(),\n size: { width: node.renderedOuterWidth(), height: node.renderedOuterHeight() },\n },\n ];\n });\n return nodes.length === 0 ? null : { nodes, viewport };\n}\n\n/*** Keep pointer, wheel, and programmatic zoom on the same limits in the engine's native units. */\nfunction applyZoomLimits(\n cy: Core,\n state: {\n readonly min: number;\n readonly max: number;\n readonly scale: number;\n readonly readableZoom: number;\n },\n) {\n const min = state.min * state.scale;\n cy.minZoom(Math.min(cy.minZoom(), min));\n cy.maxZoom(Math.max(min, state.max * state.scale, state.readableZoom));\n cy.minZoom(min);\n}\n\n/*** Ignore invalid optional typography targets at the public configuration boundary. */\nfunction positiveSize(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/***\n * Recompute the fit reference only after layout or resize, never on every pan/zoom event.\n * Cache the geometry-derived scale in the viewport owner; labels are included, edges excluded.\n */\nfunction getNodeFitZoom(cy: Core, padding: number): number {\n if (cy.nodes().empty()) return 1;\n const bounds = cy.nodes().boundingBox();\n const width = cy.width() - 2 * padding;\n const height = cy.height() - 2 * padding;\n if (width <= 0 || height <= 0 || bounds.w <= 0 || bounds.h <= 0) return 1;\n return Math.min(width / bounds.w, height / bounds.h);\n}\n\n/*** Zoom around the visible viewport center instead of its top-left corner. */\nfunction setCenteredZoom(cy: Core, zoom: number) {\n cy.zoom({ level: zoom, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } });\n}\n"]}
1
+ {"version":3,"file":"createGraphController.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type {\n GraphViewController,\n GraphViewFitOptions,\n GraphViewMeasurement,\n} from '../../../../../types/graph-view';\nimport type { GraphRuntimeUpdate } from '../../../../../types/graphViewRuntime';\nimport { fitGraphViewport } from './fitGraphViewport';\nimport { getGraphFitPadding } from './getGraphFitPadding';\n\n/*** Own viewport units and limits alongside the public controller, without exposing Cytoscape. */\nexport function createGraphController(\n cy: Core,\n fitPaddingRef: { current: number },\n optimizeFit?: (options: GraphViewFitOptions) => void,\n) {\n const state = {\n scale: 1,\n relative: false,\n min: 0.05,\n max: 2,\n readableSize: 0,\n fitSize: Infinity,\n readableZoom: 0,\n fitZoom: Infinity,\n resizeSnapshot: null as GraphResizeSnapshot | null,\n };\n const controller: GraphViewController = {\n fit(options) {\n if (options?.optimizeSpacing && !options.nodeIds?.length && optimizeFit) {\n optimizeFit(options);\n return;\n }\n fitGraphViewport(\n cy,\n {\n nodeIds: options?.nodeIds,\n padding: options?.padding ?? fitPaddingRef.current,\n },\n state.fitZoom,\n );\n },\n measureNodes(nodeIds) {\n return measureRenderedNodes(cy, nodeIds);\n },\n getViewport() {\n return { pan: cy.pan(), zoom: cy.zoom() / state.scale };\n },\n getZoomRange() {\n return { min: cy.minZoom() / state.scale, max: cy.maxZoom() / state.scale };\n },\n setPan(pan) {\n cy.pan(pan);\n },\n setZoom(zoom) {\n setCenteredZoom(cy, zoom * state.scale);\n },\n zoomBy(factor) {\n setCenteredZoom(cy, cy.zoom() * factor);\n },\n };\n\n return {\n controller,\n configure(input: GraphRuntimeUpdate) {\n state.relative = input.zoomMode === 'fit-relative';\n state.min = input.minZoom ?? 0.05;\n state.max = input.maxZoom ?? 2;\n state.readableSize = positiveSize(input.minReadableLabelSize, 0);\n state.fitSize = positiveSize(input.maxFitLabelSize, Infinity);\n if (!state.relative) state.scale = 1;\n applyZoomLimits(cy, state);\n },\n /*** Return the native automatic-fit ceiling used by spacing optimization. */\n getFitZoomLimit() {\n return state.relative ? state.fitZoom : Math.min(state.fitZoom, state.max);\n },\n /*** Return the native label-readability threshold used before allowing background overlap. */\n getReadableZoomTarget() {\n return state.readableZoom;\n },\n /*** Capture the model point beneath the current viewport center before Cytoscape resizes. */\n beginResize() {\n state.resizeSnapshot = captureResizeSnapshot(cy);\n },\n settle(fit: boolean) {\n const logicalZoom = cy.zoom() / state.scale;\n const fonts = cy\n .nodes()\n .filter((node) => !node.isParent() && Boolean(node.style('label')))\n .map((node) => Number.parseFloat(String(node.style('font-size'))))\n .filter((size) => Number.isFinite(size) && size > 0);\n state.readableZoom = fonts.length\n ? state.readableSize / fonts.reduce((minimum, size) => Math.min(minimum, size), Infinity)\n : 0;\n state.fitZoom = fonts.length\n ? state.fitSize / fonts.reduce((maximum, size) => Math.max(maximum, size), 0)\n : Infinity;\n state.scale = state.relative\n ? Math.min(getNodeFitZoom(cy, fitPaddingRef.current), state.fitZoom)\n : 1;\n applyZoomLimits(cy, state);\n if (fit) {\n state.resizeSnapshot = null;\n controller.fit();\n } else if (settlePreservedViewport(cy, state, logicalZoom)) controller.fit();\n },\n };\n}\n\ninterface GraphResizeSnapshot {\n readonly center: { readonly x: number; readonly y: number };\n readonly nodeBounds: GraphNodeBoundsStatus;\n}\n\ntype GraphNodeBoundsStatus = 'clipped' | 'empty' | 'fit' | 'unusable';\n\n/*** Capture the model-space center and whether the prior viewport was an intact graph fit. */\nfunction captureResizeSnapshot(cy: Core): GraphResizeSnapshot | null {\n const viewport = { width: cy.width(), height: cy.height() };\n const zoom = cy.zoom();\n if (!isUsableViewport(viewport) || !Number.isFinite(zoom) || zoom <= 0) return null;\n const pan = cy.pan();\n const renderedCenter = { x: viewport.width / 2, y: viewport.height / 2 };\n return {\n center: {\n x: (renderedCenter.x - pan.x) / zoom,\n y: (renderedCenter.y - pan.y) / zoom,\n },\n nodeBounds: getNodeBoundsStatus(cy, viewport),\n };\n}\n\n/*** Preserve a manual viewport through resize, fitting only when an intact prior fit becomes invalid. */\nfunction settlePreservedViewport(\n cy: Core,\n state: {\n readonly relative: boolean;\n resizeSnapshot: GraphResizeSnapshot | null;\n readonly scale: number;\n },\n logicalZoom: number,\n): boolean {\n const snapshot = state.resizeSnapshot;\n state.resizeSnapshot = null;\n const zoom = state.relative ? logicalZoom * state.scale : cy.zoom();\n if (snapshot === null) {\n if (state.relative) setCenteredZoom(cy, zoom);\n return false;\n }\n const viewport = { width: cy.width(), height: cy.height() };\n if (!isUsableViewport(viewport)) return false;\n cy.viewport({\n zoom,\n pan: {\n x: viewport.width / 2 - snapshot.center.x * zoom,\n y: viewport.height / 2 - snapshot.center.y * zoom,\n },\n });\n const nodeBounds = getNodeBoundsStatus(cy, viewport);\n return nodeBounds === 'unusable' || (snapshot.nodeBounds === 'fit' && nodeBounds === 'clipped');\n}\n\n/*** Accept only finite non-empty node bounds that are wholly visible in a usable viewport. */\nfunction getNodeBoundsStatus(\n cy: Core,\n viewport: { readonly width: number; readonly height: number },\n): GraphNodeBoundsStatus {\n if (cy.nodes().empty()) return 'empty';\n if (!isUsableViewport(viewport)) return 'unusable';\n const bounds = cy.nodes().renderedBoundingBox();\n if (![bounds.x1, bounds.x2, bounds.y1, bounds.y2].every(Number.isFinite)) return 'unusable';\n const fitsHorizontally = bounds.x1 >= 0 && bounds.x2 <= viewport.width;\n const fitsVertically = bounds.y1 >= 0 && bounds.y2 <= viewport.height;\n return fitsHorizontally && fitsVertically ? 'fit' : 'clipped';\n}\n\n/*** Reject zero, negative, and non-finite renderer dimensions before viewport arithmetic. */\nfunction isUsableViewport(viewport: { readonly width: number; readonly height: number }): boolean {\n return (\n Number.isFinite(viewport.width) &&\n Number.isFinite(viewport.height) &&\n viewport.width > 0 &&\n viewport.height > 0\n );\n}\n\n/*** Measure rendered node hit geometry without exposing paint-only outlines or Cytoscape. */\nfunction measureRenderedNodes(cy: Core, nodeIds: readonly string[]): GraphViewMeasurement | null {\n const viewport = { width: cy.width(), height: cy.height() };\n if (viewport.width <= 0 || viewport.height <= 0 || nodeIds.length === 0) return null;\n\n const nodes = [...new Set(nodeIds)].flatMap((id) => {\n const node = cy.getElementById(id);\n if (!node.isNode()) return [];\n\n return [\n {\n id,\n position: node.renderedPosition(),\n size: { width: node.renderedOuterWidth(), height: node.renderedOuterHeight() },\n },\n ];\n });\n return nodes.length === 0 ? null : { nodes, viewport };\n}\n\n/*** Keep pointer, wheel, and programmatic zoom on the same limits in the engine's native units. */\nfunction applyZoomLimits(\n cy: Core,\n state: {\n readonly min: number;\n readonly max: number;\n readonly scale: number;\n readonly readableZoom: number;\n },\n) {\n const min = state.min * state.scale;\n cy.minZoom(Math.min(cy.minZoom(), min));\n cy.maxZoom(Math.max(min, state.max * state.scale, state.readableZoom));\n cy.minZoom(min);\n}\n\n/*** Ignore invalid optional typography targets at the public configuration boundary. */\nfunction positiveSize(value: number | undefined, fallback: number): number {\n return value !== undefined && Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/***\n * Recompute the fit reference only after layout or resize, never on every pan/zoom event.\n * Cache the geometry-derived scale in the viewport owner; labels are included, edges excluded.\n */\nfunction getNodeFitZoom(cy: Core, padding: number): number {\n if (cy.nodes().empty()) return 1;\n const bounds = cy.nodes().boundingBox();\n const fitPadding = getGraphFitPadding(cy, padding);\n const width = cy.width() - 2 * fitPadding;\n const height = cy.height() - 2 * fitPadding;\n if (width <= 0 || height <= 0 || bounds.w <= 0 || bounds.h <= 0) return 1;\n return Math.min(width / bounds.w, height / bounds.h);\n}\n\n/*** Zoom around the visible viewport center instead of its top-left corner. */\nfunction setCenteredZoom(cy: Core, zoom: number) {\n cy.zoom({ level: zoom, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"fitGraphViewport.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,IAAI,EACR,OAAO,GAAE,mBAAwB,EACjC,OAAO,SAAW,GACjB,OAAO,CAWT"}
1
+ {"version":3,"file":"fitGraphViewport.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAG3E,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,IAAI,EACR,OAAO,GAAE,mBAAwB,EACjC,OAAO,SAAW,GACjB,OAAO,CAWT"}
@@ -1,3 +1,4 @@
1
+ import { getGraphFitPadding } from './getGraphFitPadding';
1
2
  /*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */
2
3
  export function fitGraphViewport(cy, options = {}, maxZoom = Infinity) {
3
4
  if (cy.destroyed())
@@ -5,7 +6,7 @@ export function fitGraphViewport(cy, options = {}, maxZoom = Infinity) {
5
6
  const nodes = getFitNodes(cy, options.nodeIds);
6
7
  if (nodes.empty())
7
8
  return false;
8
- cy.fit(nodes, options.padding ?? 50);
9
+ cy.fit(nodes, getGraphFitPadding(cy, options.padding ?? 50));
9
10
  if (cy.zoom() > maxZoom) {
10
11
  cy.zoom(maxZoom);
11
12
  cy.center(nodes);
@@ -1 +1 @@
1
- {"version":3,"file":"fitGraphViewport.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAIA,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAC9B,EAAQ,EACR,UAA+B,EAAE,EACjC,OAAO,GAAG,QAAQ;IAElB,IAAI,EAAE,CAAC,SAAS,EAAE;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAEhC,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;QACxB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2FAA2F;AAC3F,SAAS,WAAW,CAAC,EAAQ,EAAE,OAAsC;IACnE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;IACrE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type { GraphViewFitOptions } from '../../../../../types/graph-view';\n\n/*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */\nexport function fitGraphViewport(\n cy: Core,\n options: GraphViewFitOptions = {},\n maxZoom = Infinity,\n): boolean {\n if (cy.destroyed()) return false;\n const nodes = getFitNodes(cy, options.nodeIds);\n if (nodes.empty()) return false;\n\n cy.fit(nodes, options.padding ?? 50);\n if (cy.zoom() > maxZoom) {\n cy.zoom(maxZoom);\n cy.center(nodes);\n }\n return true;\n}\n\n/*** Resolve an optional node-id target without ever adding edges to the fit collection. */\nfunction getFitNodes(cy: Core, nodeIds: readonly string[] | undefined) {\n if (nodeIds === undefined || nodeIds.length === 0) return cy.nodes();\n const requestedIds = new Set(nodeIds);\n return cy.nodes().filter((node) => requestedIds.has(node.id()));\n}\n"]}
1
+ {"version":3,"file":"fitGraphViewport.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAC9B,EAAQ,EACR,UAA+B,EAAE,EACjC,OAAO,GAAG,QAAQ;IAElB,IAAI,EAAE,CAAC,SAAS,EAAE;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAEhC,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,kBAAkB,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7D,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;QACxB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2FAA2F;AAC3F,SAAS,WAAW,CAAC,EAAQ,EAAE,OAAsC;IACnE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;IACrE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type { GraphViewFitOptions } from '../../../../../types/graph-view';\nimport { getGraphFitPadding } from './getGraphFitPadding';\n\n/*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */\nexport function fitGraphViewport(\n cy: Core,\n options: GraphViewFitOptions = {},\n maxZoom = Infinity,\n): boolean {\n if (cy.destroyed()) return false;\n const nodes = getFitNodes(cy, options.nodeIds);\n if (nodes.empty()) return false;\n\n cy.fit(nodes, getGraphFitPadding(cy, options.padding ?? 50));\n if (cy.zoom() > maxZoom) {\n cy.zoom(maxZoom);\n cy.center(nodes);\n }\n return true;\n}\n\n/*** Resolve an optional node-id target without ever adding edges to the fit collection. */\nfunction getFitNodes(cy: Core, nodeIds: readonly string[] | undefined) {\n if (nodeIds === undefined || nodeIds.length === 0) return cy.nodes();\n const requestedIds = new Set(nodeIds);\n return cy.nodes().filter((node) => requestedIds.has(node.id()));\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Core } from 'cytoscape';
2
+ /*** Limits requested fit padding so every usable canvas retains graph space. */
3
+ export declare function getGraphFitPadding(cy: Core, padding: number): number;
4
+ //# sourceMappingURL=getGraphFitPadding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getGraphFitPadding.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMpE"}
@@ -0,0 +1,9 @@
1
+ /*** Limits requested fit padding so every usable canvas retains graph space. */
2
+ export function getGraphFitPadding(cy, padding) {
3
+ const viewportSize = Math.min(cy.width(), cy.height());
4
+ if (!Number.isFinite(viewportSize) || viewportSize <= 0)
5
+ return 0;
6
+ const requestedPadding = Number.isFinite(padding) ? Math.max(0, padding) : 0;
7
+ return Math.min(requestedPadding, viewportSize / 4);
8
+ }
9
+ //# sourceMappingURL=getGraphFitPadding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getGraphFitPadding.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,EAAQ,EAAE,OAAe;IAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAElE,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;AACtD,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\n/*** Limits requested fit padding so every usable canvas retains graph space. */\nexport function getGraphFitPadding(cy: Core, padding: number): number {\n const viewportSize = Math.min(cy.width(), cy.height());\n if (!Number.isFinite(viewportSize) || viewportSize <= 0) return 0;\n\n const requestedPadding = Number.isFinite(padding) ? Math.max(0, padding) : 0;\n return Math.min(requestedPadding, viewportSize / 4);\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "27.1.0",
4
+ "version": "27.1.2",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -43,12 +43,12 @@
43
43
  "dependencies": {
44
44
  "@ankhorage/capability": "^0.2.2",
45
45
  "@ankhorage/color-theory": "^0.3.2",
46
- "@ankhorage/contracts": "^26.8.0",
46
+ "@ankhorage/contracts": "^26.10.0",
47
47
  "@ankhorage/game": "^0.1.3",
48
48
  "@ankhorage/grid-view": "^0.5.0",
49
49
  "@ankhorage/reader": "^0.1.2",
50
- "@ankhorage/runtime": "^5.3.6",
51
- "@ankhorage/surface": "^9.5.0",
50
+ "@ankhorage/runtime": "^5.4.0",
51
+ "@ankhorage/surface": "^9.5.3",
52
52
  "@ankhorage/utility": "^1.10.0",
53
53
  "expo-document-picker": "~57.0.3",
54
54
  "expo-image-picker": "~57.0.20"
@@ -39,9 +39,9 @@ test('leaves native trigger accessibility owned by the published Surface primiti
39
39
  ]);
40
40
 
41
41
  expect(zoraSource).not.toContain('accessibilityState');
42
- expect(surfaceTrigger).toContain('accessibilityRole="button"');
43
- expect(surfaceTrigger).toContain(
44
- 'accessibilityState={{ disabled: resolvedDisabled, expanded: item.open }}',
42
+ expect(surfaceTrigger).toMatch(/\baccessibilityRole\s*:\s*["']button["']/u);
43
+ expect(surfaceTrigger).toMatch(
44
+ /\baccessibilityState\s*:\s*\{\s*disabled\s*:\s*resolvedDisabled\s*,\s*expanded\s*:\s*item\.open\s*\}/u,
45
45
  );
46
46
  });
47
47
 
@@ -7,6 +7,7 @@ import type {
7
7
  } from '../../../../../types/graph-view';
8
8
  import type { GraphRuntimeUpdate } from '../../../../../types/graphViewRuntime';
9
9
  import { fitGraphViewport } from './fitGraphViewport';
10
+ import { getGraphFitPadding } from './getGraphFitPadding';
10
11
 
11
12
  /*** Own viewport units and limits alongside the public controller, without exposing Cytoscape. */
12
13
  export function createGraphController(
@@ -233,8 +234,9 @@ function positiveSize(value: number | undefined, fallback: number): number {
233
234
  function getNodeFitZoom(cy: Core, padding: number): number {
234
235
  if (cy.nodes().empty()) return 1;
235
236
  const bounds = cy.nodes().boundingBox();
236
- const width = cy.width() - 2 * padding;
237
- const height = cy.height() - 2 * padding;
237
+ const fitPadding = getGraphFitPadding(cy, padding);
238
+ const width = cy.width() - 2 * fitPadding;
239
+ const height = cy.height() - 2 * fitPadding;
238
240
  if (width <= 0 || height <= 0 || bounds.w <= 0 || bounds.h <= 0) return 1;
239
241
  return Math.min(width / bounds.w, height / bounds.h);
240
242
  }
@@ -1,6 +1,7 @@
1
1
  import type { Core } from 'cytoscape';
2
2
 
3
3
  import type { GraphViewFitOptions } from '../../../../../types/graph-view';
4
+ import { getGraphFitPadding } from './getGraphFitPadding';
4
5
 
5
6
  /*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */
6
7
  export function fitGraphViewport(
@@ -12,7 +13,7 @@ export function fitGraphViewport(
12
13
  const nodes = getFitNodes(cy, options.nodeIds);
13
14
  if (nodes.empty()) return false;
14
15
 
15
- cy.fit(nodes, options.padding ?? 50);
16
+ cy.fit(nodes, getGraphFitPadding(cy, options.padding ?? 50));
16
17
  if (cy.zoom() > maxZoom) {
17
18
  cy.zoom(maxZoom);
18
19
  cy.center(nodes);
@@ -0,0 +1,32 @@
1
+ import { expect, test } from 'bun:test';
2
+ import cytoscape from 'cytoscape';
3
+
4
+ import { getGraphFitPadding } from './getGraphFitPadding';
5
+
6
+ test('keeps requested padding only while the canvas retains graph space', () => {
7
+ const cy = cytoscape({ headless: true });
8
+ cy.width = () => 800;
9
+ cy.height = () => 600;
10
+ try {
11
+ expect(getGraphFitPadding(cy, 50)).toBe(50);
12
+
13
+ cy.width = () => 38;
14
+ expect(getGraphFitPadding(cy, 50)).toBe(9.5);
15
+ } finally {
16
+ cy.destroy();
17
+ }
18
+ });
19
+
20
+ test('rejects unusable canvas dimensions and negative padding', () => {
21
+ const cy = cytoscape({ headless: true });
22
+ cy.width = () => 0;
23
+ cy.height = () => 520;
24
+ try {
25
+ expect(getGraphFitPadding(cy, 50)).toBe(0);
26
+
27
+ cy.width = () => 800;
28
+ expect(getGraphFitPadding(cy, -50)).toBe(0);
29
+ } finally {
30
+ cy.destroy();
31
+ }
32
+ });
@@ -0,0 +1,10 @@
1
+ import type { Core } from 'cytoscape';
2
+
3
+ /*** Limits requested fit padding so every usable canvas retains graph space. */
4
+ export function getGraphFitPadding(cy: Core, padding: number): number {
5
+ const viewportSize = Math.min(cy.width(), cy.height());
6
+ if (!Number.isFinite(viewportSize) || viewportSize <= 0) return 0;
7
+
8
+ const requestedPadding = Number.isFinite(padding) ? Math.max(0, padding) : 0;
9
+ return Math.min(requestedPadding, viewportSize / 4);
10
+ }
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -6,7 +6,7 @@ import {
6
6
  GameInputZone2,
7
7
  GameMeasurementProbe2,
8
8
  GameOverlay2
9
- } from "../../chunks/Accordion-ve9f47b4.js";
9
+ } from "../../chunks/Accordion-enn0479f.js";
10
10
  import"../../chunks/Accordion-6wrr910w.js";
11
11
  import"../../chunks/Accordion-vj1m2wck.js";
12
12
  import"../../chunks/Accordion-zj4wnm3e.js";
@@ -123099,6 +123099,15 @@ function findExpansionFactor(accepts) {
123099
123099
  return findMinimumAcceptedFactor(maximum / 2, maximum, accepts);
123100
123100
  }
123101
123101
 
123102
+ // src/features/graph-view/adapters/inbound/web-artifact/getGraphFitPadding.ts
123103
+ function getGraphFitPadding(cy, padding) {
123104
+ const viewportSize = Math.min(cy.width(), cy.height());
123105
+ if (!Number.isFinite(viewportSize) || viewportSize <= 0)
123106
+ return 0;
123107
+ const requestedPadding = Number.isFinite(padding) ? Math.max(0, padding) : 0;
123108
+ return Math.min(requestedPadding, viewportSize / 4);
123109
+ }
123110
+
123102
123111
  // src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts
123103
123112
  function fitGraphViewport(cy, options = {}, maxZoom = Infinity) {
123104
123113
  if (cy.destroyed())
@@ -123106,7 +123115,7 @@ function fitGraphViewport(cy, options = {}, maxZoom = Infinity) {
123106
123115
  const nodes = getFitNodes(cy, options.nodeIds);
123107
123116
  if (nodes.empty())
123108
123117
  return false;
123109
- cy.fit(nodes, options.padding ?? 50);
123118
+ cy.fit(nodes, getGraphFitPadding(cy, options.padding ?? 50));
123110
123119
  if (cy.zoom() > maxZoom) {
123111
123120
  cy.zoom(maxZoom);
123112
123121
  cy.center(nodes);
@@ -123282,8 +123291,9 @@ function getNodeFitZoom(cy, padding) {
123282
123291
  if (cy.nodes().empty())
123283
123292
  return 1;
123284
123293
  const bounds = cy.nodes().boundingBox();
123285
- const width = cy.width() - 2 * padding;
123286
- const height = cy.height() - 2 * padding;
123294
+ const fitPadding = getGraphFitPadding(cy, padding);
123295
+ const width = cy.width() - 2 * fitPadding;
123296
+ const height = cy.height() - 2 * fitPadding;
123287
123297
  if (width <= 0 || height <= 0 || bounds.w <= 0 || bounds.h <= 0)
123288
123298
  return 1;
123289
123299
  return Math.min(width / bounds.w, height / bounds.h);
@@ -1516,10 +1516,10 @@
1516
1516
  "chunks/Accordion-0x8jncra.js",
1517
1517
  "chunks/Accordion-58yzdsab.js",
1518
1518
  "chunks/Accordion-6wrr910w.js",
1519
+ "chunks/Accordion-enn0479f.js",
1519
1520
  "chunks/Accordion-fcczvwgg.js",
1520
1521
  "chunks/Accordion-fre84yaq.js",
1521
1522
  "chunks/Accordion-nh0cs9hq.js",
1522
- "chunks/Accordion-ve9f47b4.js",
1523
1523
  "chunks/Accordion-vj1m2wck.js",
1524
1524
  "chunks/Accordion-xgpzh4wq.js",
1525
1525
  "chunks/Accordion-yq16wwh5.js",
@@ -1539,10 +1539,10 @@
1539
1539
  "chunks/Accordion-0x8jncra.js",
1540
1540
  "chunks/Accordion-58yzdsab.js",
1541
1541
  "chunks/Accordion-6wrr910w.js",
1542
+ "chunks/Accordion-enn0479f.js",
1542
1543
  "chunks/Accordion-fcczvwgg.js",
1543
1544
  "chunks/Accordion-fre84yaq.js",
1544
1545
  "chunks/Accordion-nh0cs9hq.js",
1545
- "chunks/Accordion-ve9f47b4.js",
1546
1546
  "chunks/Accordion-vj1m2wck.js",
1547
1547
  "chunks/Accordion-xgpzh4wq.js",
1548
1548
  "chunks/Accordion-yq16wwh5.js",
@@ -1562,10 +1562,10 @@
1562
1562
  "chunks/Accordion-0x8jncra.js",
1563
1563
  "chunks/Accordion-58yzdsab.js",
1564
1564
  "chunks/Accordion-6wrr910w.js",
1565
+ "chunks/Accordion-enn0479f.js",
1565
1566
  "chunks/Accordion-fcczvwgg.js",
1566
1567
  "chunks/Accordion-fre84yaq.js",
1567
1568
  "chunks/Accordion-nh0cs9hq.js",
1568
- "chunks/Accordion-ve9f47b4.js",
1569
1569
  "chunks/Accordion-vj1m2wck.js",
1570
1570
  "chunks/Accordion-xgpzh4wq.js",
1571
1571
  "chunks/Accordion-yq16wwh5.js",
@@ -1585,10 +1585,10 @@
1585
1585
  "chunks/Accordion-0x8jncra.js",
1586
1586
  "chunks/Accordion-58yzdsab.js",
1587
1587
  "chunks/Accordion-6wrr910w.js",
1588
+ "chunks/Accordion-enn0479f.js",
1588
1589
  "chunks/Accordion-fcczvwgg.js",
1589
1590
  "chunks/Accordion-fre84yaq.js",
1590
1591
  "chunks/Accordion-nh0cs9hq.js",
1591
- "chunks/Accordion-ve9f47b4.js",
1592
1592
  "chunks/Accordion-vj1m2wck.js",
1593
1593
  "chunks/Accordion-xgpzh4wq.js",
1594
1594
  "chunks/Accordion-yq16wwh5.js",
@@ -1608,10 +1608,10 @@
1608
1608
  "chunks/Accordion-0x8jncra.js",
1609
1609
  "chunks/Accordion-58yzdsab.js",
1610
1610
  "chunks/Accordion-6wrr910w.js",
1611
+ "chunks/Accordion-enn0479f.js",
1611
1612
  "chunks/Accordion-fcczvwgg.js",
1612
1613
  "chunks/Accordion-fre84yaq.js",
1613
1614
  "chunks/Accordion-nh0cs9hq.js",
1614
- "chunks/Accordion-ve9f47b4.js",
1615
1615
  "chunks/Accordion-vj1m2wck.js",
1616
1616
  "chunks/Accordion-xgpzh4wq.js",
1617
1617
  "chunks/Accordion-yq16wwh5.js",
@@ -1631,10 +1631,10 @@
1631
1631
  "chunks/Accordion-0x8jncra.js",
1632
1632
  "chunks/Accordion-58yzdsab.js",
1633
1633
  "chunks/Accordion-6wrr910w.js",
1634
+ "chunks/Accordion-enn0479f.js",
1634
1635
  "chunks/Accordion-fcczvwgg.js",
1635
1636
  "chunks/Accordion-fre84yaq.js",
1636
1637
  "chunks/Accordion-nh0cs9hq.js",
1637
- "chunks/Accordion-ve9f47b4.js",
1638
1638
  "chunks/Accordion-vj1m2wck.js",
1639
1639
  "chunks/Accordion-xgpzh4wq.js",
1640
1640
  "chunks/Accordion-yq16wwh5.js",