@weasel-js/minimap 1.15.1 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -220,8 +220,8 @@ interface MinimapCanvasProps<TData, TLayer extends string, TPose> extends Surfac
220
220
  /** Paint order for the minimap, keyed as `layerVisibility`. */
221
221
  layerOrder?: SceneViewLayers['layerOrder'];
222
222
  /** Optional animator: the minimap repaints on its ticks and paints its
223
- * `colorOverrides`. Pass the main canvas's animator so both show the same
224
- * colors. */
223
+ * `colorOverrides` and `trimOverrides`. Pass the main canvas's animator so
224
+ * both show the same colors and trims. */
225
225
  animator?: Animator;
226
226
  /** Visual tuning of the indicator stroke. */
227
227
  indicatorStyle?: IndicatorStyle;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { PointerProviderIfRoot, AUTO_POSE_DESCRIPTOR, poseDescriptorForNode, effectivePose, documentPose, FALLBACK_ACCENT, normalizeView, useDeviceProfile, accentColorOf, SceneViewCanvas } from '@weasel-js/core';
1
+ import { PointerProviderIfRoot, AUTO_POSE_DESCRIPTOR, poseDescriptorForNode, effectivePose, documentPose, FALLBACK_ACCENT, normalizeView, usePublishPointer, useDeviceProfile, accentColorOf, SceneViewCanvas } from '@weasel-js/core';
2
2
  import { useState, useCallback, useMemo, useRef, useEffect, useSyncExternalStore } from 'react';
3
- import { DepRegistryProvider, ActionsProvider, ActiveToolContextProvider, useDepSource, useActionsRegistry, useGestureDispatcher, usePointerPosition, usePointerContext } from '@weasel-js/routing/react';
3
+ import { DepRegistryProvider, ActionsProvider, ActiveToolContextProvider, useDepSource, useActionsRegistry, useGestureDispatcher, usePointerPosition } from '@weasel-js/routing/react';
4
4
  import { useLatest, assignRef } from '@weasel-js/react';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
 
@@ -294,27 +294,6 @@ function useFitView(scene, width, height, fit, descriptor) {
294
294
  return normalizeView(computeFitView(scene, { width, height }, fit, descriptor));
295
295
  }, [scene, sceneVersion, overrideGeneration, width, height, fit, descriptor]);
296
296
  }
297
- function usePublishPointer(canvas, clientToWorld, viewId) {
298
- const pointerStore = usePointerContext();
299
- useEffect(() => {
300
- const el = canvas.current;
301
- if (!el || !pointerStore) return;
302
- const onMove = (e) => {
303
- const w = clientToWorld(e.clientX, e.clientY);
304
- pointerStore.set({ world: { x: w.x, y: w.y }, viewId });
305
- };
306
- const onLeave = (e) => {
307
- if (e.buttons !== 0) return;
308
- if (pointerStore.get()?.viewId === viewId) pointerStore.set(null);
309
- };
310
- el.addEventListener("pointermove", onMove);
311
- el.addEventListener("pointerleave", onLeave);
312
- return () => {
313
- el.removeEventListener("pointermove", onMove);
314
- el.removeEventListener("pointerleave", onLeave);
315
- };
316
- }, [canvas, pointerStore, clientToWorld, viewId]);
317
- }
318
297
  var MINIMAP_INPUT = {
319
298
  id: "minimap",
320
299
  eligibility: { always: true },
@@ -392,7 +371,7 @@ function MinimapCanvasInner(props) {
392
371
  keyboard: false,
393
372
  channels: CHANNELS
394
373
  });
395
- usePublishPointer(localCanvasRef, clientToWorld, id);
374
+ usePublishPointer(localCanvasRef, (_pointer, cx, cy) => ({ world: clientToWorld(cx, cy), viewId: id }));
396
375
  const pointer = usePointerPosition();
397
376
  const { dpr } = useDeviceProfile();
398
377
  const cursorLayer = useMemo(() => createLinkedCursorLayer({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/minimapMath.ts","../src/actions.ts","../src/layers.ts","../src/createMinimapContribution.ts","../src/useFitView.ts","../src/usePublishPointer.ts","../src/MinimapCanvas.tsx"],"names":["normalizeView","AUTO_POSE_DESCRIPTOR","useMemo","useEffect","accentColorOf"],"mappings":"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","file":"index.js","sourcesContent":["/** Pure math helpers for `<MinimapCanvas>`: no GL, DOM, or renderer. */\nimport { documentPose, effectivePose } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { Bounds, ViewportDims } from '@weasel-js/core';\nimport { poseDescriptorForNode, type PoseDescriptor } from '@weasel-js/core';\nimport type { PathDrawCommand } from '@weasel-js/core';\n\n/** Inset (CSS px) around the fit rect on each side. Matches the spec note. */\nconst DEFAULT_FIT_PADDING = 8;\n\n/** Fallback view returned when there is nothing to fit (empty scene, etc.). */\nexport const FALLBACK_FIT_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Fit policy for `<MinimapCanvas>` / `computeFitView`.\n *\n * - `\"scene\"` — union of leaf-node pose AABBs (via the pose descriptor), read\n * from the document: a pose override moves what is painted and not the frame.\n * - `\"live\"` — the same union over the poses being shown, overrides included,\n * so the frame follows a drag, a settle, or a simulation writing overrides.\n * Refit on every override publish, which is a sweep of every leaf and\n * rescales the whole minimap while one moves past the edge.\n * - `{ kind: \"world\", rect }` — caller-supplied world rect.\n * - `(scene, dims) => View` — caller derives the view directly.\n */\nexport type MinimapFit<TData, TLayer extends string, TPose> =\n | 'scene'\n | 'live'\n | { kind: 'world'; rect: Bounds }\n | ((scene: Scene<TData, TLayer, TPose>, dims: ViewportDims) => View);\n\n/** Style for the visible-window indicator rect drawn over the minimap. */\nexport interface IndicatorStyle {\n /** Stroke color (any CSS color). Defaults to `'#ffffff'`. */\n stroke?: string;\n /** Stroke width in CSS px. Defaults to `1`. */\n width?: number;\n /** Dash pattern in CSS px. Defaults to `[2, 3]`. */\n dash?: number[];\n}\n\nconst DEFAULT_INDICATOR_STROKE = '#ffffff';\nconst DEFAULT_INDICATOR_WIDTH = 1;\nconst DEFAULT_INDICATOR_DASH: readonly number[] = [2, 3];\n\n/** Internal: fit a world rect uniformly into dims with padding. */\nfunction fitRectInto(rect: Bounds, dims: ViewportDims, padding: number): View {\n const availW = Math.max(1, dims.width - padding * 2);\n const availH = Math.max(1, dims.height - padding * 2);\n const hasW = rect.width > 0;\n const hasH = rect.height > 0;\n if (!hasW && !hasH) {\n return {\n x: rect.x - dims.width / 2,\n y: rect.y - dims.height / 2,\n scale: { x: 1, y: 1 },\n };\n }\n const s = Math.min(\n hasW ? availW / rect.width : Infinity,\n hasH ? availH / rect.height : Infinity,\n );\n const worldCx = rect.x + Math.max(0, rect.width) / 2;\n const worldCy = rect.y + Math.max(0, rect.height) / 2;\n return {\n x: worldCx - dims.width / (2 * s),\n y: worldCy - dims.height / (2 * s),\n scale: { x: s, y: s },\n };\n}\n\n/**\n * Compute the AABB union of all leaf-node poses in the scene, via\n * `descriptor`. Container nodes are skipped — their effective bounds come\n * from their descendant leaves, and so is a leaf whose bounds are not finite,\n * which would otherwise make the whole union non-finite. Returns `null` when\n * the scene has no leaf with bounds.\n */\nfunction sceneLeafBounds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n poseOf: typeof documentPose,\n): Bounds | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n let any = false;\n for (const node of scene.nodes.values()) {\n if (node.kind !== 'mark') continue;\n const b = poseDescriptorForNode(descriptor, node).getBounds(poseOf(scene, node));\n if (!Number.isFinite(b.x + b.y + b.width + b.height)) continue;\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n const right = b.x + b.width;\n const bottom = b.y + b.height;\n if (right > maxX) maxX = right;\n if (bottom > maxY) maxY = bottom;\n any = true;\n }\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/** Options for {@link computeFitView}. */\nexport interface ComputeFitViewOptions {\n /** Inset (CSS px) around the fit rect on each side. Defaults to `8`. */\n padding?: number;\n}\n\n/**\n * Derive a `View` that fits the chosen world rect into a minimap of\n * `dims.width × dims.height` CSS px. Uniform scale on both axes; the rect\n * is centered with `padding` (default 8) on each side. The smaller-axis\n * fit ratio wins, so the whole rect is visible. An empty scene gives\n * {@link FALLBACK_FIT_VIEW}; a function `fit` is passed through unchanged.\n *\n * A rect with one zero axis (a line) is fitted along the other. One with no\n * positive axis falls back to identity scale centered on its top-left.\n */\nexport function computeFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n dims: ViewportDims,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n opts: ComputeFitViewOptions = {},\n): View {\n const padding = opts.padding ?? DEFAULT_FIT_PADDING;\n\n if (typeof fit === 'function') {\n return fit(scene, dims);\n }\n if (fit === 'scene' || fit === 'live') {\n const rect = sceneLeafBounds(scene, descriptor, fit === 'live' ? effectivePose : documentPose);\n if (rect === null) return FALLBACK_FIT_VIEW;\n return fitRectInto(rect, dims, padding);\n }\n // { kind: 'world', rect }\n return fitRectInto(fit.rect, dims, padding);\n}\n\n/**\n * Build the visible-window indicator `DrawCommand` for the minimap: the main\n * canvas's visible world rect as a dashed-stroke `path` rect with no fill, in\n * world coordinates. Pass the `minimapView` it will be drawn through, so the\n * stroke width and dash come out in CSS px at any fit scale; without it they\n * are in world units.\n */\nexport function computeIndicatorCommand(\n mainView: View,\n mainViewDims: ViewportDims,\n style: IndicatorStyle = {},\n minimapView?: View,\n): PathDrawCommand {\n // World-space rect, returned as-is: the caller wraps the command list in a\n // group transformed by the minimap's view, and screen coords here would get\n // that transform applied a second time.\n const x = mainView.x;\n const y = mainView.y;\n const width = mainViewDims.width / mainView.scale.x;\n const height = mainViewDims.height / mainView.scale.y;\n const px = minimapView ? 1 / Math.abs(minimapView.scale.x) : 1;\n return {\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n stroke: {\n paint: { kind: 'solid', color: style.stroke ?? DEFAULT_INDICATOR_STROKE },\n width: (style.width ?? DEFAULT_INDICATOR_WIDTH) * px,\n dash: (style.dash ?? DEFAULT_INDICATOR_DASH).map((d) => d * px),\n },\n };\n}\n","/**\n * The minimap's input: press to recenter the main camera on a world point,\n * drag to keep it there. Both read `rootView` — never `view`, which inside the\n * minimap's own view answers the minimap's camera.\n */\nimport type { Action, InvocationCtx, OngoingHandle } from '@weasel-js/routing';\nimport type { ViewApi } from '@weasel-js/core';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\n/** Action id for `minimapCenterAction`. */\nexport const MINIMAP_CENTER = 'minimap.center';\n/** Action id for `minimapPanAction`. */\nexport const MINIMAP_PAN = 'minimap.pan';\n\n/** Move `root` so world point (`x`, `y`) sits at the center of its host,\n * keeping its scale. A no-op while the host is unmeasured. */\nexport function centerRootOn(root: ViewApi, x: number, y: number): void {\n const size = root.hostSize?.();\n if (!size) return;\n const v = root.get();\n root.set({\n x: x - size.width / (2 * v.scale.x),\n y: y - size.height / (2 * v.scale.y),\n scale: v.scale,\n });\n}\n\nfunction rootOf(deps: InvocationCtx['deps']): ViewApi | undefined {\n return deps.rootView;\n}\n\n/** Recenter the main camera on the pressed world point. */\nexport function minimapCenterAction(): Action {\n return {\n id: MINIMAP_CENTER,\n label: 'Minimap — center the view here',\n requires: ['rootView'],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const world = (params as ImmediateParams | undefined)?.world;\n const root = rootOf(deps);\n if (!root || !world) return;\n centerRootOn(root, world.x, world.y);\n },\n },\n };\n}\n\n/** Keep the main camera centered on the pointer's world point while dragging.\n * Pair it with `minimap.center` on `pointerDown`, which handles the press.\n * A recenter lands the moment it is computed, so there is nothing to commit\n * and a cancel ends where a release does. */\nexport function minimapPanAction(): Action {\n return {\n id: MINIMAP_PAN,\n label: 'Minimap — pan the view',\n requires: ['rootView'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx): OngoingHandle {\n const root = rootOf(ctx.deps);\n if (!root) return {};\n // The press already centered (`minimap.center`); the move that crossed\n // the drag threshold is pumped to `onMove` like every later one.\n return {\n onMove: (m) => {\n const p = m.drag?.current ?? m.world;\n centerRootOn(root, p.x, p.y);\n },\n onEnd: () => {},\n };\n },\n },\n };\n}\n","/**\n * The minimap's chrome: the main camera's visible rect, drawn in the minimap,\n * and the linked crosshair, drawn in every view the pointer is not over.\n */\nimport type { RenderLayer } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core';\nimport type { ViewApi } from '@weasel-js/core';\nimport type { PointerWorldPos } from '@weasel-js/routing/react';\nimport { computeIndicatorCommand, type IndicatorStyle } from './minimapMath';\n\n/** What a layer's `data` carries for this feature: the view it draws for. */\nfunction viewIdOf(data: unknown): string | null {\n return (data as { viewId?: string | null } | null)?.viewId ?? null;\n}\n\n/**\n * The main camera's visible world rect, painted only in view `viewId`. Its\n * stroke and dash are screen pixels whatever the minimap's scale.\n */\nexport function createIndicatorLayer(opts: {\n viewId: string;\n root: () => ViewApi | undefined;\n style?: IndicatorStyle;\n}): RenderLayer<unknown> {\n const { viewId, root, style = {} } = opts;\n return {\n id: `${viewId}.indicator`,\n label: 'Minimap indicator',\n draw: (data, view: View): DrawCommand[] => {\n if (viewIdOf(data) !== viewId) return [];\n const r = root();\n const size = r?.hostSize?.();\n if (!r || !size) return [];\n return [computeIndicatorCommand(r.get(), size, style, view)];\n },\n };\n}\n\n/** Screen-pixel geometry of the crosshair: arm length, the gap at its center,\n * line thickness, and the dark halo that keeps it legible over any content. */\nconst ARM = 9;\nconst GAP = 3;\nconst THICK = 2;\nconst HALO = 1;\n/** The halo's color: dark enough to separate the accent from any content. */\nexport const CROSSHAIR_HALO = 'rgba(0, 0, 0, 0.6)';\n\n/** An axis-aligned rect, `{ x, y, w, h }`. */\nexport interface CrosshairRect { x: number; y: number; w: number; h: number }\n\n/**\n * The linked crosshair at (`x`, `y`) as rects in the caller's units, where one\n * screen pixel is `unitsPerPx` of them: four halo rects to paint first, then\n * four bars. A painter with no weasel renderer — a 2D canvas — draws the same\n * crosshair from these.\n */\nexport function crosshairRects(\n x: number, y: number, unitsPerPx: number,\n): { halo: CrosshairRect[]; bars: CrosshairRect[] } {\n const k = unitsPerPx;\n const arm = ARM * k, gap = GAP * k, t = THICK * k, h = HALO * k;\n const bars: CrosshairRect[] = [\n { x: x - gap - arm, y: y - t / 2, w: arm, h: t },\n { x: x + gap, y: y - t / 2, w: arm, h: t },\n { x: x - t / 2, y: y - gap - arm, w: t, h: arm },\n { x: x - t / 2, y: y + gap, w: t, h: arm },\n ];\n const halo = bars.map((b) => ({ x: b.x - h, y: b.y - h, w: b.w + 2 * h, h: b.h + 2 * h }));\n return { halo, bars };\n}\n\n/**\n * A crosshair at the pointer's world point, in each of `views` the pointer is\n * not over — the other side of a linked cursor. A fixed screen size in every\n * view. `views` omitted paints in every view.\n */\nexport function createLinkedCursorLayer(opts: {\n id: string;\n pointer: () => PointerWorldPos;\n color: () => string;\n views?: readonly (string | null)[];\n}): RenderLayer<unknown> {\n const { id, pointer, color, views } = opts;\n return {\n id,\n label: 'Linked cursor',\n draw: (data, view: View): DrawCommand[] => {\n const p = pointer();\n const here = viewIdOf(data);\n if (!p || p.viewId === here || (views && !views.includes(here))) return [];\n const { halo, bars } = crosshairRects(p.world.x, p.world.y, 1 / view.scale.x);\n const rect = (r: CrosshairRect, c: string): DrawCommand => ({\n kind: 'path',\n path: { kind: 'rect', x: r.x, y: r.y, width: r.w, height: r.h },\n fill: { paint: { kind: 'solid', color: c } },\n });\n const accent = color();\n return [\n ...halo.map((r) => rect(r, CROSSHAIR_HALO)),\n ...bars.map((r) => rect(r, accent)),\n ];\n },\n };\n}\n","/**\n * A minimap over a `<SceneCanvas>`, written as one `SurfaceContribution`: a\n * view through a fit camera, the pan that moves the main camera from inside\n * it, the visible-rect indicator, and a linked crosshair.\n */\nimport type { Dims, RenderLayer } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { PoseDescriptor } from '@weasel-js/core';\nimport { AUTO_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport type { PointerWorldPos } from '@weasel-js/routing/react';\nimport type { CanvasExtensionApi } from '@weasel-js/core';\nimport type { ViewRect } from '@weasel-js/core';\nimport type { ContributionDepReader, SurfaceContribution } from '@weasel-js/core';\nimport {\n computeFitView, FALLBACK_FIT_VIEW, type IndicatorStyle, type MinimapFit,\n} from './minimapMath';\nimport {\n MINIMAP_CENTER, MINIMAP_PAN, minimapCenterAction, minimapPanAction,\n} from './actions';\nimport { createIndicatorLayer, createLinkedCursorLayer } from './layers';\nimport { accentColorOf, FALLBACK_ACCENT } from '@weasel-js/core';\n\n\n/**\n * The linked crosshair's live half: follows the pointer store, and asks the\n * surface to repaint whenever the crosshair shows, moves, or has just hidden.\n * `ownViews` are the view ids this surface paints; the crosshair shows in any\n * of them the pointer is not over.\n */\nfunction linkedCursor(id: string, ownViews: readonly (string | null)[]) {\n let pointer: PointerWorldPos = null;\n let color = FALLBACK_ACCENT;\n const visible = (p: PointerWorldPos): boolean =>\n p !== null && ownViews.some((v) => v !== p.viewId);\n const layer = createLinkedCursorLayer({ id, pointer: () => pointer, color: () => color, views: ownViews });\n const attach = (api: CanvasExtensionApi, deps: ContributionDepReader): (() => void) => {\n const store = deps.get('pointer');\n if (!store) return () => {};\n color = accentColorOf(api.element);\n pointer = store.get();\n const off = store.subscribe(() => {\n const prev = pointer;\n pointer = store.get();\n if (visible(prev) || visible(pointer)) api.requestRedraw();\n });\n return () => {\n off();\n pointer = null;\n api.requestRedraw();\n };\n };\n return { layer, attach };\n}\n\n/** Options for {@link createLinkedCursorContribution}. */\nexport interface LinkedCursorOptions {\n /** Entry id. Default `'linked-cursor'`. */\n id?: string;\n /** Views on this surface the crosshair may paint in. Default `[null]`, the\n * surface's own camera — the main canvas beside a detached minimap. */\n views?: readonly (string | null)[];\n}\n\n/**\n * The linked crosshair alone: this surface paints the pointer's position\n * whenever the pointer is over some other view — another surface under the\n * same `<PointerContextProvider>`, such as a detached `<MinimapCanvas>`.\n *\n * A surface carrying `createMinimapContribution` already has one, in both its\n * views; this is for a surface without.\n */\nexport function createLinkedCursorContribution(opts: LinkedCursorOptions = {}): SurfaceContribution {\n const id = opts.id ?? 'linked-cursor';\n const cursor = linkedCursor(id, opts.views ?? [null]);\n return { id, eligibility: { always: true }, overlay: cursor.layer, attach: cursor.attach };\n}\n\n/** Options for {@link createMinimapContribution}. */\nexport interface MinimapContributionOptions<TData = unknown, TLayer extends string = string, TPose = unknown> {\n /** View and entry id. Default `'minimap'`. */\n id?: string;\n /** Where the minimap paints, in CSS pixels from the canvas top-left. A\n * function is re-read every frame, so it can follow the canvas's size. */\n rect: ViewRect | ((outer: View, dims: Dims) => ViewRect);\n /** What the minimap frames. Default `'scene'`, the union of leaf poses. */\n fit?: MinimapFit<TData, TLayer, TPose>;\n /** The surface layers the minimap paints. Default: all of them. */\n layers?: (surface: readonly RenderLayer<unknown>[]) => readonly RenderLayer<unknown>[];\n /** Ground painted before the minimap's layers, so the main canvas does not\n * show through. */\n background?: string;\n /** The visible-rect indicator's stroke. */\n indicatorStyle?: IndicatorStyle;\n}\n\nconst DEFAULT_BACKGROUND = '#18181c';\n\n/**\n * A minimap inside a `<SceneCanvas>`, installed with one entry:\n *\n * ```tsx\n * const minimap = useMemo(() => createMinimapContribution({ rect: { x: 8, y: 8, w: 160, h: 110 } }), []);\n * <SceneCanvas ambient={[minimap]} … />\n * ```\n *\n * Press in it to center the main camera on that point, drag to keep it there.\n * It shows the main camera's visible rect, and a crosshair where the pointer is\n * over the other view. Build it once — each instance keeps the live state its\n * layers read.\n */\nexport function createMinimapContribution<TData = unknown, TLayer extends string = string, TPose = unknown>(\n opts: MinimapContributionOptions<TData, TLayer, TPose>,\n): SurfaceContribution {\n const id = opts.id ?? 'minimap';\n const fit = (opts.fit ?? 'scene') as MinimapFit<unknown, string, unknown>;\n let deps: ContributionDepReader | null = null;\n\n // The fit follows the scene, so it is recomputed only when the scene's\n // version or the minimap's size changes — it is read on every frame and\n // every event.\n let cached: { scene: unknown; version: number; shown: number; w: number; h: number; view: View } | null = null;\n const fitCamera = (outer: View, dims: Dims): View => {\n const scene = deps?.get('scene') as Scene<unknown, string, unknown> | undefined;\n if (!scene) return FALLBACK_FIT_VIEW;\n const r = typeof opts.rect === 'function' ? opts.rect(outer, dims) : opts.rect;\n const version = scene.getVersion();\n // Only a live fit reads overrides, so only it refits when they publish.\n const shown = fit === 'live' ? scene.overrides.getGeneration() : 0;\n if (cached && cached.scene === scene && cached.version === version && cached.shown === shown\n && cached.w === r.w && cached.h === r.h) {\n return cached.view;\n }\n const descriptor = (deps?.get('poseDescriptor') ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<unknown>;\n const view = computeFitView(scene, { width: r.w, height: r.h }, fit, descriptor);\n cached = { scene, version, shown, w: r.w, h: r.h, view };\n return view;\n };\n\n const cursor = linkedCursor(`${id}.cursor`, [null, id]);\n const indicator = createIndicatorLayer({\n viewId: id,\n root: () => deps?.get('rootView'),\n ...(opts.indicatorStyle ? { style: opts.indicatorStyle } : {}),\n });\n\n return {\n id,\n eligibility: { always: true },\n views: [{\n id,\n bounds: opts.rect,\n view: fitCamera,\n interactive: true,\n background: opts.background ?? DEFAULT_BACKGROUND,\n ...(opts.layers ? { layers: opts.layers } : {}),\n }],\n actions: [minimapCenterAction(), minimapPanAction()],\n bindings: [\n { spec: { kind: 'pointerDown' }, actionId: MINIMAP_CENTER, opts: { id: `${id}.pointerDown`, views: [id] } },\n { spec: { kind: 'drag' }, actionId: MINIMAP_PAN, opts: { id: `${id}.drag`, views: [id] } },\n ],\n overlay: [indicator, cursor.layer],\n attach: (api, reader) => {\n deps = reader;\n const detachCursor = cursor.attach(api, reader);\n // The fit camera had no scene until now.\n api.requestRedraw();\n return () => {\n detachCursor();\n deps = null;\n cached = null;\n };\n },\n };\n}\n","import { useMemo, useSyncExternalStore } from 'react';\nimport { normalizeView, type PoseDescriptor, type Scene, type View } from '@weasel-js/core';\nimport { computeFitView, type MinimapFit } from './minimapMath';\n\nconst NEVER = (): (() => void) => () => {};\n\n/**\n * The minimap's fit view, recomputed when what it frames changes: the scene's\n * version always, and the overrides only under a `\"live\"` fit, the one policy\n * that reads them.\n */\nexport function useFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n width: number,\n height: number,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n): View {\n const sceneVersion = useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);\n const overrideGeneration = useSyncExternalStore(\n fit === 'live' ? scene.overrides.subscribe : NEVER,\n scene.overrides.getGeneration,\n scene.overrides.getGeneration,\n );\n return useMemo<View>(() => {\n // A `fit` function's result is the caller's; pointer math divides by its scale.\n return normalizeView(computeFitView(scene, { width, height }, fit, descriptor));\n // The two counters are what say the scene's contents moved.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scene, sceneVersion, overrideGeneration, width, height, fit, descriptor]);\n}\n","import { useEffect, type RefObject } from 'react';\nimport { usePointerContext } from '@weasel-js/routing/react';\n\n/**\n * Publishes the pointer's world point over `canvas` to the shared pointer\n * store under `viewId`, and clears it when the pointer leaves with no button\n * held — a drag that leaves keeps its claim.\n */\nexport function usePublishPointer(\n canvas: RefObject<HTMLCanvasElement | null>,\n clientToWorld: (cx: number, cy: number) => { x: number; y: number },\n viewId: string,\n): void {\n const pointerStore = usePointerContext();\n useEffect(() => {\n const el = canvas.current;\n if (!el || !pointerStore) return;\n const onMove = (e: PointerEvent): void => {\n const w = clientToWorld(e.clientX, e.clientY);\n pointerStore.set({ world: { x: w.x, y: w.y }, viewId });\n };\n const onLeave = (e: PointerEvent): void => {\n if (e.buttons !== 0) return;\n if (pointerStore.get()?.viewId === viewId) pointerStore.set(null);\n };\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerleave', onLeave);\n return () => {\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerleave', onLeave);\n };\n }, [canvas, pointerStore, clientToWorld, viewId]);\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { Ref } from 'react';\nimport {\n ActionsProvider,\n ActiveToolContextProvider,\n DepRegistryProvider,\n useActionsRegistry,\n useDepSource,\n useGestureDispatcher,\n} from '@weasel-js/routing/react';\nimport { assignRef, useLatest } from '@weasel-js/react';\nimport type { Tool } from '@weasel-js/routing';\nimport { PointerProviderIfRoot, useDeviceProfile } from '@weasel-js/core';\nimport { usePointerPosition } from '@weasel-js/routing/react';\nimport {\n MINIMAP_CENTER, MINIMAP_PAN, minimapCenterAction, minimapPanAction,\n} from './actions';\nimport { createLinkedCursorLayer } from './layers';\nimport type { CanvasSize, SurfaceSizeProps, ViewApi } from '@weasel-js/core';\nimport { SceneViewCanvas } from '@weasel-js/core';\nimport { computeIndicatorCommand } from './minimapMath';\nimport { useFitView } from './useFitView';\nimport { usePublishPointer } from './usePublishPointer';\nimport type { IndicatorStyle, MinimapFit } from './minimapMath';\nimport type { SceneViewDrawOne, SceneViewLayers } from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core';\nimport { normalizeView, type View } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { PoseDescriptor } from '@weasel-js/core';\nimport type { Animator } from '@weasel-js/animator';\nimport { AUTO_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport { accentColorOf } from '@weasel-js/core';\n\n/** Props for `<MinimapCanvas>`. */\nexport interface MinimapCanvasProps<TData, TLayer extends string, TPose> extends SurfaceSizeProps {\n /** Same scene the main canvas is rendering. */\n scene: Scene<TData, TLayer, TPose>;\n /** Main canvas's view — used to position the visible-window indicator and\n * to preserve `scale` when the minimap recenters. */\n mainView: View;\n /** Main canvas's CSS-pixel dims. Used to compute the visible-window rect\n * and the recenter offset. */\n mainViewDims: { width: number; height: number };\n /** Setter called on click (single recenter) and during drag (continuous\n * recenter). Receives a `View` with the same `scale` as `mainView` and\n * `(x, y)` shifted so the pointer's world point lands at the main\n * canvas's center. */\n onMainViewChange: (next: View) => void;\n /** Consumer's draw callback. Applied through the derived fit view; same\n * signature as `<SceneCanvas>`'s scene-slot `drawOne` so the consumer\n * can reuse it (typically simplified — just colored AABBs). */\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Fit policy. Defaults to `\"scene\"` (AABB union of leaf poses). See\n * `MinimapFit` for the full shape.\n *\n * `\"scene\"` frames **document** poses, not `scene.overrides` — a node\n * moved by an override paints where the override puts it, outside the\n * frame if it goes there, rather than making the whole minimap rescale on\n * every frame of a drag or a settle. `\"live\"` frames the overrides too. */\n fit?: MinimapFit<TData, TLayer, TPose>;\n /** How to read poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** Optional per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Pass the same function the main canvas uses so a\n * scoping-dim treatment shows up in the minimap too. */\n alphaFor?: (id: string) => number;\n /** Hide scene layers in the minimap, keyed `scene:<layerId>` as on\n * `<SceneCanvas>` — pass the main canvas's map so both show the same set. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Paint order for the minimap, keyed as `layerVisibility`. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Optional animator: the minimap repaints on its ticks and paints its\n * `colorOverrides`. Pass the main canvas's animator so both show the same\n * colors. */\n animator?: Animator;\n /** Visual tuning of the indicator stroke. */\n indicatorStyle?: IndicatorStyle;\n /** CSS class for the canvas. Its size is `width` × `height` unless a rule\n * on this class sizes it. Given neither, the minimap fills its parent and\n * fits the scene to the box it measures, again on every resize. */\n className?: string;\n /** Forwarded ref to the underlying `<canvas>`. */\n canvasRef?: Ref<HTMLCanvasElement>;\n /** The view id this minimap publishes its pointer under. Default\n * `'minimap'`. */\n id?: string;\n}\n\n/** The minimap's own bindings. Unscoped: this dispatcher routes no views, so\n * every input on it is the minimap's. */\nconst MINIMAP_INPUT: Tool = {\n id: 'minimap',\n eligibility: { always: true },\n bindings: [\n { spec: { kind: 'pointerDown' }, actionId: MINIMAP_CENTER, opts: { id: 'minimap.pointerDown' } },\n { spec: { kind: 'drag' }, actionId: MINIMAP_PAN, opts: { id: 'minimap.drag' } },\n ],\n};\nconst TOOLS_BY_ID: ReadonlyMap<string, Tool> = new Map([[MINIMAP_INPUT.id, MINIMAP_INPUT]]);\nconst CHANNELS = { wheel: false, pinch: false, contextMenu: false, ingest: false } as const;\n\nfunction MinimapCanvasInner<TData, TLayer extends string, TPose>(\n props: MinimapCanvasProps<TData, TLayer, TPose>,\n) {\n const {\n scene,\n mainView: mainViewProp,\n mainViewDims,\n onMainViewChange,\n drawOne,\n alphaFor,\n layerVisibility,\n layerOrder,\n animator,\n fit = 'scene',\n poseDescriptor,\n indicatorStyle,\n className,\n canvasRef,\n id = 'minimap',\n onResize,\n } = props;\n const [measured, setMeasured] = useState<CanvasSize | null>(null);\n const width = props.width ?? measured?.width ?? 0;\n const height = props.height ?? measured?.height ?? 0;\n const handleResize = useCallback((size: CanvasSize) => {\n setMeasured(size);\n onResize?.(size);\n }, [onResize]);\n const mainView = normalizeView(mainViewProp);\n\n const descriptor = (poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const fitView = useFitView(scene, width, height, fit, descriptor);\n\n const indicatorCmd = useMemo<DrawCommand>(() => {\n return computeIndicatorCommand(mainView, mainViewDims, indicatorStyle, fitView);\n }, [mainView, mainViewDims, indicatorStyle, fitView]);\n\n const localCanvasRef = useRef<HTMLCanvasElement | null>(null);\n const mainViewRef = useLatest(mainView);\n const mainViewDimsRef = useLatest(mainViewDims);\n const onMainViewChangeRef = useLatest(onMainViewChange);\n const fitViewRef = useLatest(fitView);\n\n // The main canvas's camera, which is what the minimap's actions move.\n const rootView = useMemo<ViewApi>(() => ({\n get: () => mainViewRef.current,\n set: (v) => onMainViewChangeRef.current(v),\n hostSize: () => mainViewDimsRef.current,\n }), [mainViewRef, onMainViewChangeRef, mainViewDimsRef]);\n useDepSource('rootView', () => rootView);\n\n const actions = useActionsRegistry();\n useEffect(() => {\n if (!actions) return;\n const off = [actions.register(minimapCenterAction()), actions.register(minimapPanAction())];\n return () => { for (const u of off) u(); };\n }, [actions]);\n\n /** Client point → world, through the fit camera. */\n const clientToWorld = useCallback((cx: number, cy: number) => {\n const rect = localCanvasRef.current?.getBoundingClientRect();\n const fv = fitViewRef.current;\n return {\n x: fv.x + (cx - (rect?.left ?? 0)) / fv.scale.x,\n y: fv.y + (cy - (rect?.top ?? 0)) / fv.scale.y,\n };\n }, [fitViewRef]);\n\n useGestureDispatcher({\n canvasRef: localCanvasRef,\n actions: actions!,\n entriesById: TOOLS_BY_ID,\n clientToWorld,\n keyboard: false,\n channels: CHANNELS,\n });\n\n usePublishPointer(localCanvasRef, clientToWorld, id);\n\n const pointer = usePointerPosition();\n const { dpr } = useDeviceProfile();\n const cursorLayer = useMemo(() => createLinkedCursorLayer({\n id: `${id}.cursor`,\n pointer: () => pointer,\n color: () => accentColorOf(localCanvasRef.current),\n }), [id, pointer]);\n\n const extraCommands = useMemo<DrawCommand[]>(() => [\n indicatorCmd,\n ...cursorLayer.draw({ viewId: id }, fitView, { width, height, dpr }),\n ], [indicatorCmd, cursorLayer, id, fitView, width, height, dpr]);\n\n // `<SceneViewCanvas>` doesn't expose its element except via `canvasRef`, so\n // we route both our internal ref and the consumer's ref through one setter.\n const setCanvasRef = useCallback((el: HTMLCanvasElement | null) => {\n localCanvasRef.current = el;\n assignRef(canvasRef, el);\n }, [canvasRef]);\n\n return (\n <SceneViewCanvas\n scene={scene}\n view={fitView}\n width={props.width}\n height={props.height}\n onResize={handleResize}\n drawOne={drawOne}\n extraCommands={extraCommands}\n alphaFor={alphaFor}\n layerVisibility={layerVisibility}\n layerOrder={layerOrder}\n animator={animator}\n className={className}\n canvasRef={setCanvasRef}\n />\n );\n}\n\n/**\n * Opinionated minimap on a canvas of its own. Renders the same `scene` the\n * main canvas is showing through a derived \"fit\" view, and overlays a dashed\n * indicator of where the main canvas is looking. Press anywhere in it to\n * recenter the main view on that world point; drag to pan continuously.\n *\n * Its dispatcher, actions and deps are its own, so it never competes with\n * the main canvas for input. Put one `<PointerContextProvider>` around it and\n * the main canvas for linked cursors both ways.\n *\n * `forwardRef` is not used because the component is generic over\n * `<TData, TLayer, TPose>` and React's `forwardRef` erases generics. The\n * `canvasRef` prop is the plain-prop equivalent.\n */\nexport function MinimapCanvas<TData, TLayer extends string, TPose>(\n props: MinimapCanvasProps<TData, TLayer, TPose>,\n) {\n return (\n <DepRegistryProvider>\n <ActionsProvider>\n <ActiveToolContextProvider>\n <PointerProviderIfRoot>\n <MinimapCanvasInner {...props} />\n </PointerProviderIfRoot>\n </ActiveToolContextProvider>\n </ActionsProvider>\n </DepRegistryProvider>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/minimapMath.ts","../src/actions.ts","../src/layers.ts","../src/createMinimapContribution.ts","../src/useFitView.ts","../src/MinimapCanvas.tsx"],"names":["normalizeView","AUTO_POSE_DESCRIPTOR","useMemo","accentColorOf"],"mappings":"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","file":"index.js","sourcesContent":["/** Pure math helpers for `<MinimapCanvas>`: no GL, DOM, or renderer. */\nimport { documentPose, effectivePose } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { Bounds, ViewportDims } from '@weasel-js/core';\nimport { poseDescriptorForNode, type PoseDescriptor } from '@weasel-js/core';\nimport type { PathDrawCommand } from '@weasel-js/core';\n\n/** Inset (CSS px) around the fit rect on each side. Matches the spec note. */\nconst DEFAULT_FIT_PADDING = 8;\n\n/** Fallback view returned when there is nothing to fit (empty scene, etc.). */\nexport const FALLBACK_FIT_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Fit policy for `<MinimapCanvas>` / `computeFitView`.\n *\n * - `\"scene\"` — union of leaf-node pose AABBs (via the pose descriptor), read\n * from the document: a pose override moves what is painted and not the frame.\n * - `\"live\"` — the same union over the poses being shown, overrides included,\n * so the frame follows a drag, a settle, or a simulation writing overrides.\n * Refit on every override publish, which is a sweep of every leaf and\n * rescales the whole minimap while one moves past the edge.\n * - `{ kind: \"world\", rect }` — caller-supplied world rect.\n * - `(scene, dims) => View` — caller derives the view directly.\n */\nexport type MinimapFit<TData, TLayer extends string, TPose> =\n | 'scene'\n | 'live'\n | { kind: 'world'; rect: Bounds }\n | ((scene: Scene<TData, TLayer, TPose>, dims: ViewportDims) => View);\n\n/** Style for the visible-window indicator rect drawn over the minimap. */\nexport interface IndicatorStyle {\n /** Stroke color (any CSS color). Defaults to `'#ffffff'`. */\n stroke?: string;\n /** Stroke width in CSS px. Defaults to `1`. */\n width?: number;\n /** Dash pattern in CSS px. Defaults to `[2, 3]`. */\n dash?: number[];\n}\n\nconst DEFAULT_INDICATOR_STROKE = '#ffffff';\nconst DEFAULT_INDICATOR_WIDTH = 1;\nconst DEFAULT_INDICATOR_DASH: readonly number[] = [2, 3];\n\n/** Internal: fit a world rect uniformly into dims with padding. */\nfunction fitRectInto(rect: Bounds, dims: ViewportDims, padding: number): View {\n const availW = Math.max(1, dims.width - padding * 2);\n const availH = Math.max(1, dims.height - padding * 2);\n const hasW = rect.width > 0;\n const hasH = rect.height > 0;\n if (!hasW && !hasH) {\n return {\n x: rect.x - dims.width / 2,\n y: rect.y - dims.height / 2,\n scale: { x: 1, y: 1 },\n };\n }\n const s = Math.min(\n hasW ? availW / rect.width : Infinity,\n hasH ? availH / rect.height : Infinity,\n );\n const worldCx = rect.x + Math.max(0, rect.width) / 2;\n const worldCy = rect.y + Math.max(0, rect.height) / 2;\n return {\n x: worldCx - dims.width / (2 * s),\n y: worldCy - dims.height / (2 * s),\n scale: { x: s, y: s },\n };\n}\n\n/**\n * Compute the AABB union of all leaf-node poses in the scene, via\n * `descriptor`. Container nodes are skipped — their effective bounds come\n * from their descendant leaves, and so is a leaf whose bounds are not finite,\n * which would otherwise make the whole union non-finite. Returns `null` when\n * the scene has no leaf with bounds.\n */\nfunction sceneLeafBounds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n poseOf: typeof documentPose,\n): Bounds | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n let any = false;\n for (const node of scene.nodes.values()) {\n if (node.kind !== 'mark') continue;\n const b = poseDescriptorForNode(descriptor, node).getBounds(poseOf(scene, node));\n if (!Number.isFinite(b.x + b.y + b.width + b.height)) continue;\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n const right = b.x + b.width;\n const bottom = b.y + b.height;\n if (right > maxX) maxX = right;\n if (bottom > maxY) maxY = bottom;\n any = true;\n }\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/** Options for {@link computeFitView}. */\nexport interface ComputeFitViewOptions {\n /** Inset (CSS px) around the fit rect on each side. Defaults to `8`. */\n padding?: number;\n}\n\n/**\n * Derive a `View` that fits the chosen world rect into a minimap of\n * `dims.width × dims.height` CSS px. Uniform scale on both axes; the rect\n * is centered with `padding` (default 8) on each side. The smaller-axis\n * fit ratio wins, so the whole rect is visible. An empty scene gives\n * {@link FALLBACK_FIT_VIEW}; a function `fit` is passed through unchanged.\n *\n * A rect with one zero axis (a line) is fitted along the other. One with no\n * positive axis falls back to identity scale centered on its top-left.\n */\nexport function computeFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n dims: ViewportDims,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n opts: ComputeFitViewOptions = {},\n): View {\n const padding = opts.padding ?? DEFAULT_FIT_PADDING;\n\n if (typeof fit === 'function') {\n return fit(scene, dims);\n }\n if (fit === 'scene' || fit === 'live') {\n const rect = sceneLeafBounds(scene, descriptor, fit === 'live' ? effectivePose : documentPose);\n if (rect === null) return FALLBACK_FIT_VIEW;\n return fitRectInto(rect, dims, padding);\n }\n // { kind: 'world', rect }\n return fitRectInto(fit.rect, dims, padding);\n}\n\n/**\n * Build the visible-window indicator `DrawCommand` for the minimap: the main\n * canvas's visible world rect as a dashed-stroke `path` rect with no fill, in\n * world coordinates. Pass the `minimapView` it will be drawn through, so the\n * stroke width and dash come out in CSS px at any fit scale; without it they\n * are in world units.\n */\nexport function computeIndicatorCommand(\n mainView: View,\n mainViewDims: ViewportDims,\n style: IndicatorStyle = {},\n minimapView?: View,\n): PathDrawCommand {\n // World-space rect, returned as-is: the caller wraps the command list in a\n // group transformed by the minimap's view, and screen coords here would get\n // that transform applied a second time.\n const x = mainView.x;\n const y = mainView.y;\n const width = mainViewDims.width / mainView.scale.x;\n const height = mainViewDims.height / mainView.scale.y;\n const px = minimapView ? 1 / Math.abs(minimapView.scale.x) : 1;\n return {\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n stroke: {\n paint: { kind: 'solid', color: style.stroke ?? DEFAULT_INDICATOR_STROKE },\n width: (style.width ?? DEFAULT_INDICATOR_WIDTH) * px,\n dash: (style.dash ?? DEFAULT_INDICATOR_DASH).map((d) => d * px),\n },\n };\n}\n","/**\n * The minimap's input: press to recenter the main camera on a world point,\n * drag to keep it there. Both read `rootView` — never `view`, which inside the\n * minimap's own view answers the minimap's camera.\n */\nimport type { Action, InvocationCtx, OngoingHandle } from '@weasel-js/routing';\nimport type { ViewApi } from '@weasel-js/core';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\n/** Action id for `minimapCenterAction`. */\nexport const MINIMAP_CENTER = 'minimap.center';\n/** Action id for `minimapPanAction`. */\nexport const MINIMAP_PAN = 'minimap.pan';\n\n/** Move `root` so world point (`x`, `y`) sits at the center of its host,\n * keeping its scale. A no-op while the host is unmeasured. */\nexport function centerRootOn(root: ViewApi, x: number, y: number): void {\n const size = root.hostSize?.();\n if (!size) return;\n const v = root.get();\n root.set({\n x: x - size.width / (2 * v.scale.x),\n y: y - size.height / (2 * v.scale.y),\n scale: v.scale,\n });\n}\n\nfunction rootOf(deps: InvocationCtx['deps']): ViewApi | undefined {\n return deps.rootView;\n}\n\n/** Recenter the main camera on the pressed world point. */\nexport function minimapCenterAction(): Action {\n return {\n id: MINIMAP_CENTER,\n label: 'Minimap — center the view here',\n requires: ['rootView'],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const world = (params as ImmediateParams | undefined)?.world;\n const root = rootOf(deps);\n if (!root || !world) return;\n centerRootOn(root, world.x, world.y);\n },\n },\n };\n}\n\n/** Keep the main camera centered on the pointer's world point while dragging.\n * Pair it with `minimap.center` on `pointerDown`, which handles the press.\n * A recenter lands the moment it is computed, so there is nothing to commit\n * and a cancel ends where a release does. */\nexport function minimapPanAction(): Action {\n return {\n id: MINIMAP_PAN,\n label: 'Minimap — pan the view',\n requires: ['rootView'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx): OngoingHandle {\n const root = rootOf(ctx.deps);\n if (!root) return {};\n // The press already centered (`minimap.center`); the move that crossed\n // the drag threshold is pumped to `onMove` like every later one.\n return {\n onMove: (m) => {\n const p = m.drag?.current ?? m.world;\n centerRootOn(root, p.x, p.y);\n },\n onEnd: () => {},\n };\n },\n },\n };\n}\n","/**\n * The minimap's chrome: the main camera's visible rect, drawn in the minimap,\n * and the linked crosshair, drawn in every view the pointer is not over.\n */\nimport type { RenderLayer } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core';\nimport type { ViewApi } from '@weasel-js/core';\nimport type { PointerWorldPos } from '@weasel-js/routing/react';\nimport { computeIndicatorCommand, type IndicatorStyle } from './minimapMath';\n\n/** What a layer's `data` carries for this feature: the view it draws for. */\nfunction viewIdOf(data: unknown): string | null {\n return (data as { viewId?: string | null } | null)?.viewId ?? null;\n}\n\n/**\n * The main camera's visible world rect, painted only in view `viewId`. Its\n * stroke and dash are screen pixels whatever the minimap's scale.\n */\nexport function createIndicatorLayer(opts: {\n viewId: string;\n root: () => ViewApi | undefined;\n style?: IndicatorStyle;\n}): RenderLayer<unknown> {\n const { viewId, root, style = {} } = opts;\n return {\n id: `${viewId}.indicator`,\n label: 'Minimap indicator',\n draw: (data, view: View): DrawCommand[] => {\n if (viewIdOf(data) !== viewId) return [];\n const r = root();\n const size = r?.hostSize?.();\n if (!r || !size) return [];\n return [computeIndicatorCommand(r.get(), size, style, view)];\n },\n };\n}\n\n/** Screen-pixel geometry of the crosshair: arm length, the gap at its center,\n * line thickness, and the dark halo that keeps it legible over any content. */\nconst ARM = 9;\nconst GAP = 3;\nconst THICK = 2;\nconst HALO = 1;\n/** The halo's color: dark enough to separate the accent from any content. */\nexport const CROSSHAIR_HALO = 'rgba(0, 0, 0, 0.6)';\n\n/** An axis-aligned rect, `{ x, y, w, h }`. */\nexport interface CrosshairRect { x: number; y: number; w: number; h: number }\n\n/**\n * The linked crosshair at (`x`, `y`) as rects in the caller's units, where one\n * screen pixel is `unitsPerPx` of them: four halo rects to paint first, then\n * four bars. A painter with no weasel renderer — a 2D canvas — draws the same\n * crosshair from these.\n */\nexport function crosshairRects(\n x: number, y: number, unitsPerPx: number,\n): { halo: CrosshairRect[]; bars: CrosshairRect[] } {\n const k = unitsPerPx;\n const arm = ARM * k, gap = GAP * k, t = THICK * k, h = HALO * k;\n const bars: CrosshairRect[] = [\n { x: x - gap - arm, y: y - t / 2, w: arm, h: t },\n { x: x + gap, y: y - t / 2, w: arm, h: t },\n { x: x - t / 2, y: y - gap - arm, w: t, h: arm },\n { x: x - t / 2, y: y + gap, w: t, h: arm },\n ];\n const halo = bars.map((b) => ({ x: b.x - h, y: b.y - h, w: b.w + 2 * h, h: b.h + 2 * h }));\n return { halo, bars };\n}\n\n/**\n * A crosshair at the pointer's world point, in each of `views` the pointer is\n * not over — the other side of a linked cursor. A fixed screen size in every\n * view. `views` omitted paints in every view.\n */\nexport function createLinkedCursorLayer(opts: {\n id: string;\n pointer: () => PointerWorldPos;\n color: () => string;\n views?: readonly (string | null)[];\n}): RenderLayer<unknown> {\n const { id, pointer, color, views } = opts;\n return {\n id,\n label: 'Linked cursor',\n draw: (data, view: View): DrawCommand[] => {\n const p = pointer();\n const here = viewIdOf(data);\n if (!p || p.viewId === here || (views && !views.includes(here))) return [];\n const { halo, bars } = crosshairRects(p.world.x, p.world.y, 1 / view.scale.x);\n const rect = (r: CrosshairRect, c: string): DrawCommand => ({\n kind: 'path',\n path: { kind: 'rect', x: r.x, y: r.y, width: r.w, height: r.h },\n fill: { paint: { kind: 'solid', color: c } },\n });\n const accent = color();\n return [\n ...halo.map((r) => rect(r, CROSSHAIR_HALO)),\n ...bars.map((r) => rect(r, accent)),\n ];\n },\n };\n}\n","/**\n * A minimap over a `<SceneCanvas>`, written as one `SurfaceContribution`: a\n * view through a fit camera, the pan that moves the main camera from inside\n * it, the visible-rect indicator, and a linked crosshair.\n */\nimport type { Dims, RenderLayer } from '@weasel-js/core';\nimport type { View } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { PoseDescriptor } from '@weasel-js/core';\nimport { AUTO_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport type { PointerWorldPos } from '@weasel-js/routing/react';\nimport type { CanvasExtensionApi } from '@weasel-js/core';\nimport type { ViewRect } from '@weasel-js/core';\nimport type { ContributionDepReader, SurfaceContribution } from '@weasel-js/core';\nimport {\n computeFitView, FALLBACK_FIT_VIEW, type IndicatorStyle, type MinimapFit,\n} from './minimapMath';\nimport {\n MINIMAP_CENTER, MINIMAP_PAN, minimapCenterAction, minimapPanAction,\n} from './actions';\nimport { createIndicatorLayer, createLinkedCursorLayer } from './layers';\nimport { accentColorOf, FALLBACK_ACCENT } from '@weasel-js/core';\n\n\n/**\n * The linked crosshair's live half: follows the pointer store, and asks the\n * surface to repaint whenever the crosshair shows, moves, or has just hidden.\n * `ownViews` are the view ids this surface paints; the crosshair shows in any\n * of them the pointer is not over.\n */\nfunction linkedCursor(id: string, ownViews: readonly (string | null)[]) {\n let pointer: PointerWorldPos = null;\n let color = FALLBACK_ACCENT;\n const visible = (p: PointerWorldPos): boolean =>\n p !== null && ownViews.some((v) => v !== p.viewId);\n const layer = createLinkedCursorLayer({ id, pointer: () => pointer, color: () => color, views: ownViews });\n const attach = (api: CanvasExtensionApi, deps: ContributionDepReader): (() => void) => {\n const store = deps.get('pointer');\n if (!store) return () => {};\n color = accentColorOf(api.element);\n pointer = store.get();\n const off = store.subscribe(() => {\n const prev = pointer;\n pointer = store.get();\n if (visible(prev) || visible(pointer)) api.requestRedraw();\n });\n return () => {\n off();\n pointer = null;\n api.requestRedraw();\n };\n };\n return { layer, attach };\n}\n\n/** Options for {@link createLinkedCursorContribution}. */\nexport interface LinkedCursorOptions {\n /** Entry id. Default `'linked-cursor'`. */\n id?: string;\n /** Views on this surface the crosshair may paint in. Default `[null]`, the\n * surface's own camera — the main canvas beside a detached minimap. */\n views?: readonly (string | null)[];\n}\n\n/**\n * The linked crosshair alone: this surface paints the pointer's position\n * whenever the pointer is over some other view — another surface under the\n * same `<PointerContextProvider>`, such as a detached `<MinimapCanvas>`.\n *\n * A surface carrying `createMinimapContribution` already has one, in both its\n * views; this is for a surface without.\n */\nexport function createLinkedCursorContribution(opts: LinkedCursorOptions = {}): SurfaceContribution {\n const id = opts.id ?? 'linked-cursor';\n const cursor = linkedCursor(id, opts.views ?? [null]);\n return { id, eligibility: { always: true }, overlay: cursor.layer, attach: cursor.attach };\n}\n\n/** Options for {@link createMinimapContribution}. */\nexport interface MinimapContributionOptions<TData = unknown, TLayer extends string = string, TPose = unknown> {\n /** View and entry id. Default `'minimap'`. */\n id?: string;\n /** Where the minimap paints, in CSS pixels from the canvas top-left. A\n * function is re-read every frame, so it can follow the canvas's size. */\n rect: ViewRect | ((outer: View, dims: Dims) => ViewRect);\n /** What the minimap frames. Default `'scene'`, the union of leaf poses. */\n fit?: MinimapFit<TData, TLayer, TPose>;\n /** The surface layers the minimap paints. Default: all of them. */\n layers?: (surface: readonly RenderLayer<unknown>[]) => readonly RenderLayer<unknown>[];\n /** Ground painted before the minimap's layers, so the main canvas does not\n * show through. */\n background?: string;\n /** The visible-rect indicator's stroke. */\n indicatorStyle?: IndicatorStyle;\n}\n\nconst DEFAULT_BACKGROUND = '#18181c';\n\n/**\n * A minimap inside a `<SceneCanvas>`, installed with one entry:\n *\n * ```tsx\n * const minimap = useMemo(() => createMinimapContribution({ rect: { x: 8, y: 8, w: 160, h: 110 } }), []);\n * <SceneCanvas ambient={[minimap]} … />\n * ```\n *\n * Press in it to center the main camera on that point, drag to keep it there.\n * It shows the main camera's visible rect, and a crosshair where the pointer is\n * over the other view. Build it once — each instance keeps the live state its\n * layers read.\n */\nexport function createMinimapContribution<TData = unknown, TLayer extends string = string, TPose = unknown>(\n opts: MinimapContributionOptions<TData, TLayer, TPose>,\n): SurfaceContribution {\n const id = opts.id ?? 'minimap';\n const fit = (opts.fit ?? 'scene') as MinimapFit<unknown, string, unknown>;\n let deps: ContributionDepReader | null = null;\n\n // The fit follows the scene, so it is recomputed only when the scene's\n // version or the minimap's size changes — it is read on every frame and\n // every event.\n let cached: { scene: unknown; version: number; shown: number; w: number; h: number; view: View } | null = null;\n const fitCamera = (outer: View, dims: Dims): View => {\n const scene = deps?.get('scene') as Scene<unknown, string, unknown> | undefined;\n if (!scene) return FALLBACK_FIT_VIEW;\n const r = typeof opts.rect === 'function' ? opts.rect(outer, dims) : opts.rect;\n const version = scene.getVersion();\n // Only a live fit reads overrides, so only it refits when they publish.\n const shown = fit === 'live' ? scene.overrides.getGeneration() : 0;\n if (cached && cached.scene === scene && cached.version === version && cached.shown === shown\n && cached.w === r.w && cached.h === r.h) {\n return cached.view;\n }\n const descriptor = (deps?.get('poseDescriptor') ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<unknown>;\n const view = computeFitView(scene, { width: r.w, height: r.h }, fit, descriptor);\n cached = { scene, version, shown, w: r.w, h: r.h, view };\n return view;\n };\n\n const cursor = linkedCursor(`${id}.cursor`, [null, id]);\n const indicator = createIndicatorLayer({\n viewId: id,\n root: () => deps?.get('rootView'),\n ...(opts.indicatorStyle ? { style: opts.indicatorStyle } : {}),\n });\n\n return {\n id,\n eligibility: { always: true },\n views: [{\n id,\n bounds: opts.rect,\n view: fitCamera,\n interactive: true,\n background: opts.background ?? DEFAULT_BACKGROUND,\n ...(opts.layers ? { layers: opts.layers } : {}),\n }],\n actions: [minimapCenterAction(), minimapPanAction()],\n bindings: [\n { spec: { kind: 'pointerDown' }, actionId: MINIMAP_CENTER, opts: { id: `${id}.pointerDown`, views: [id] } },\n { spec: { kind: 'drag' }, actionId: MINIMAP_PAN, opts: { id: `${id}.drag`, views: [id] } },\n ],\n overlay: [indicator, cursor.layer],\n attach: (api, reader) => {\n deps = reader;\n const detachCursor = cursor.attach(api, reader);\n // The fit camera had no scene until now.\n api.requestRedraw();\n return () => {\n detachCursor();\n deps = null;\n cached = null;\n };\n },\n };\n}\n","import { useMemo, useSyncExternalStore } from 'react';\nimport { normalizeView, type PoseDescriptor, type Scene, type View } from '@weasel-js/core';\nimport { computeFitView, type MinimapFit } from './minimapMath';\n\nconst NEVER = (): (() => void) => () => {};\n\n/**\n * The minimap's fit view, recomputed when what it frames changes: the scene's\n * version always, and the overrides only under a `\"live\"` fit, the one policy\n * that reads them.\n */\nexport function useFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n width: number,\n height: number,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n): View {\n const sceneVersion = useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);\n const overrideGeneration = useSyncExternalStore(\n fit === 'live' ? scene.overrides.subscribe : NEVER,\n scene.overrides.getGeneration,\n scene.overrides.getGeneration,\n );\n return useMemo<View>(() => {\n // A `fit` function's result is the caller's; pointer math divides by its scale.\n return normalizeView(computeFitView(scene, { width, height }, fit, descriptor));\n // The two counters are what say the scene's contents moved.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scene, sceneVersion, overrideGeneration, width, height, fit, descriptor]);\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { Ref } from 'react';\nimport {\n ActionsProvider,\n ActiveToolContextProvider,\n DepRegistryProvider,\n useActionsRegistry,\n useDepSource,\n useGestureDispatcher,\n} from '@weasel-js/routing/react';\nimport { assignRef, useLatest } from '@weasel-js/react';\nimport type { Tool } from '@weasel-js/routing';\nimport { PointerProviderIfRoot, useDeviceProfile, usePublishPointer } from '@weasel-js/core';\nimport { usePointerPosition } from '@weasel-js/routing/react';\nimport {\n MINIMAP_CENTER, MINIMAP_PAN, minimapCenterAction, minimapPanAction,\n} from './actions';\nimport { createLinkedCursorLayer } from './layers';\nimport type { CanvasSize, SurfaceSizeProps, ViewApi } from '@weasel-js/core';\nimport { SceneViewCanvas } from '@weasel-js/core';\nimport { computeIndicatorCommand } from './minimapMath';\nimport { useFitView } from './useFitView';\nimport type { IndicatorStyle, MinimapFit } from './minimapMath';\nimport type { SceneViewDrawOne, SceneViewLayers } from '@weasel-js/core';\nimport type { DrawCommand } from '@weasel-js/core';\nimport { normalizeView, type View } from '@weasel-js/core';\nimport type { Scene } from '@weasel-js/core';\nimport type { PoseDescriptor } from '@weasel-js/core';\nimport type { Animator } from '@weasel-js/animator';\nimport { AUTO_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport { accentColorOf } from '@weasel-js/core';\n\n/** Props for `<MinimapCanvas>`. */\nexport interface MinimapCanvasProps<TData, TLayer extends string, TPose> extends SurfaceSizeProps {\n /** Same scene the main canvas is rendering. */\n scene: Scene<TData, TLayer, TPose>;\n /** Main canvas's view — used to position the visible-window indicator and\n * to preserve `scale` when the minimap recenters. */\n mainView: View;\n /** Main canvas's CSS-pixel dims. Used to compute the visible-window rect\n * and the recenter offset. */\n mainViewDims: { width: number; height: number };\n /** Setter called on click (single recenter) and during drag (continuous\n * recenter). Receives a `View` with the same `scale` as `mainView` and\n * `(x, y)` shifted so the pointer's world point lands at the main\n * canvas's center. */\n onMainViewChange: (next: View) => void;\n /** Consumer's draw callback. Applied through the derived fit view; same\n * signature as `<SceneCanvas>`'s scene-slot `drawOne` so the consumer\n * can reuse it (typically simplified — just colored AABBs). */\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Fit policy. Defaults to `\"scene\"` (AABB union of leaf poses). See\n * `MinimapFit` for the full shape.\n *\n * `\"scene\"` frames **document** poses, not `scene.overrides` — a node\n * moved by an override paints where the override puts it, outside the\n * frame if it goes there, rather than making the whole minimap rescale on\n * every frame of a drag or a settle. `\"live\"` frames the overrides too. */\n fit?: MinimapFit<TData, TLayer, TPose>;\n /** How to read poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** Optional per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Pass the same function the main canvas uses so a\n * scoping-dim treatment shows up in the minimap too. */\n alphaFor?: (id: string) => number;\n /** Hide scene layers in the minimap, keyed `scene:<layerId>` as on\n * `<SceneCanvas>` — pass the main canvas's map so both show the same set. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Paint order for the minimap, keyed as `layerVisibility`. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Optional animator: the minimap repaints on its ticks and paints its\n * `colorOverrides` and `trimOverrides`. Pass the main canvas's animator so\n * both show the same colors and trims. */\n animator?: Animator;\n /** Visual tuning of the indicator stroke. */\n indicatorStyle?: IndicatorStyle;\n /** CSS class for the canvas. Its size is `width` × `height` unless a rule\n * on this class sizes it. Given neither, the minimap fills its parent and\n * fits the scene to the box it measures, again on every resize. */\n className?: string;\n /** Forwarded ref to the underlying `<canvas>`. */\n canvasRef?: Ref<HTMLCanvasElement>;\n /** The view id this minimap publishes its pointer under. Default\n * `'minimap'`. */\n id?: string;\n}\n\n/** The minimap's own bindings. Unscoped: this dispatcher routes no views, so\n * every input on it is the minimap's. */\nconst MINIMAP_INPUT: Tool = {\n id: 'minimap',\n eligibility: { always: true },\n bindings: [\n { spec: { kind: 'pointerDown' }, actionId: MINIMAP_CENTER, opts: { id: 'minimap.pointerDown' } },\n { spec: { kind: 'drag' }, actionId: MINIMAP_PAN, opts: { id: 'minimap.drag' } },\n ],\n};\nconst TOOLS_BY_ID: ReadonlyMap<string, Tool> = new Map([[MINIMAP_INPUT.id, MINIMAP_INPUT]]);\nconst CHANNELS = { wheel: false, pinch: false, contextMenu: false, ingest: false } as const;\n\nfunction MinimapCanvasInner<TData, TLayer extends string, TPose>(\n props: MinimapCanvasProps<TData, TLayer, TPose>,\n) {\n const {\n scene,\n mainView: mainViewProp,\n mainViewDims,\n onMainViewChange,\n drawOne,\n alphaFor,\n layerVisibility,\n layerOrder,\n animator,\n fit = 'scene',\n poseDescriptor,\n indicatorStyle,\n className,\n canvasRef,\n id = 'minimap',\n onResize,\n } = props;\n const [measured, setMeasured] = useState<CanvasSize | null>(null);\n const width = props.width ?? measured?.width ?? 0;\n const height = props.height ?? measured?.height ?? 0;\n const handleResize = useCallback((size: CanvasSize) => {\n setMeasured(size);\n onResize?.(size);\n }, [onResize]);\n const mainView = normalizeView(mainViewProp);\n\n const descriptor = (poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const fitView = useFitView(scene, width, height, fit, descriptor);\n\n const indicatorCmd = useMemo<DrawCommand>(() => {\n return computeIndicatorCommand(mainView, mainViewDims, indicatorStyle, fitView);\n }, [mainView, mainViewDims, indicatorStyle, fitView]);\n\n const localCanvasRef = useRef<HTMLCanvasElement | null>(null);\n const mainViewRef = useLatest(mainView);\n const mainViewDimsRef = useLatest(mainViewDims);\n const onMainViewChangeRef = useLatest(onMainViewChange);\n const fitViewRef = useLatest(fitView);\n\n // The main canvas's camera, which is what the minimap's actions move.\n const rootView = useMemo<ViewApi>(() => ({\n get: () => mainViewRef.current,\n set: (v) => onMainViewChangeRef.current(v),\n hostSize: () => mainViewDimsRef.current,\n }), [mainViewRef, onMainViewChangeRef, mainViewDimsRef]);\n useDepSource('rootView', () => rootView);\n\n const actions = useActionsRegistry();\n useEffect(() => {\n if (!actions) return;\n const off = [actions.register(minimapCenterAction()), actions.register(minimapPanAction())];\n return () => { for (const u of off) u(); };\n }, [actions]);\n\n /** Client point → world, through the fit camera. */\n const clientToWorld = useCallback((cx: number, cy: number) => {\n const rect = localCanvasRef.current?.getBoundingClientRect();\n const fv = fitViewRef.current;\n return {\n x: fv.x + (cx - (rect?.left ?? 0)) / fv.scale.x,\n y: fv.y + (cy - (rect?.top ?? 0)) / fv.scale.y,\n };\n }, [fitViewRef]);\n\n useGestureDispatcher({\n canvasRef: localCanvasRef,\n actions: actions!,\n entriesById: TOOLS_BY_ID,\n clientToWorld,\n keyboard: false,\n channels: CHANNELS,\n });\n\n usePublishPointer(localCanvasRef, (_pointer, cx, cy) => ({ world: clientToWorld(cx, cy), viewId: id }));\n\n const pointer = usePointerPosition();\n const { dpr } = useDeviceProfile();\n const cursorLayer = useMemo(() => createLinkedCursorLayer({\n id: `${id}.cursor`,\n pointer: () => pointer,\n color: () => accentColorOf(localCanvasRef.current),\n }), [id, pointer]);\n\n const extraCommands = useMemo<DrawCommand[]>(() => [\n indicatorCmd,\n ...cursorLayer.draw({ viewId: id }, fitView, { width, height, dpr }),\n ], [indicatorCmd, cursorLayer, id, fitView, width, height, dpr]);\n\n // `<SceneViewCanvas>` doesn't expose its element except via `canvasRef`, so\n // we route both our internal ref and the consumer's ref through one setter.\n const setCanvasRef = useCallback((el: HTMLCanvasElement | null) => {\n localCanvasRef.current = el;\n assignRef(canvasRef, el);\n }, [canvasRef]);\n\n return (\n <SceneViewCanvas\n scene={scene}\n view={fitView}\n width={props.width}\n height={props.height}\n onResize={handleResize}\n drawOne={drawOne}\n extraCommands={extraCommands}\n alphaFor={alphaFor}\n layerVisibility={layerVisibility}\n layerOrder={layerOrder}\n animator={animator}\n className={className}\n canvasRef={setCanvasRef}\n />\n );\n}\n\n/**\n * Opinionated minimap on a canvas of its own. Renders the same `scene` the\n * main canvas is showing through a derived \"fit\" view, and overlays a dashed\n * indicator of where the main canvas is looking. Press anywhere in it to\n * recenter the main view on that world point; drag to pan continuously.\n *\n * Its dispatcher, actions and deps are its own, so it never competes with\n * the main canvas for input. Put one `<PointerContextProvider>` around it and\n * the main canvas for linked cursors both ways.\n *\n * `forwardRef` is not used because the component is generic over\n * `<TData, TLayer, TPose>` and React's `forwardRef` erases generics. The\n * `canvasRef` prop is the plain-prop equivalent.\n */\nexport function MinimapCanvas<TData, TLayer extends string, TPose>(\n props: MinimapCanvasProps<TData, TLayer, TPose>,\n) {\n return (\n <DepRegistryProvider>\n <ActionsProvider>\n <ActiveToolContextProvider>\n <PointerProviderIfRoot>\n <MinimapCanvasInner {...props} />\n </PointerProviderIfRoot>\n </ActiveToolContextProvider>\n </ActionsProvider>\n </DepRegistryProvider>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/minimap",
3
- "version": "1.15.1",
3
+ "version": "1.16.0",
4
4
  "description": "A minimap for a weasel canvas: a read-only overview with a viewport indicator you drag to pan, fit math, and a linked crosshair between surfaces.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,12 +37,12 @@
37
37
  "build": "tsup"
38
38
  },
39
39
  "dependencies": {
40
- "@weasel-js/react": "1.15.1",
41
- "@weasel-js/routing": "1.15.1"
40
+ "@weasel-js/react": "1.16.0",
41
+ "@weasel-js/routing": "1.16.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "@weasel-js/animator": "1.15.1",
45
- "@weasel-js/core": "1.15.1",
44
+ "@weasel-js/animator": "1.16.0",
45
+ "@weasel-js/core": "1.16.0",
46
46
  "react": ">=18"
47
47
  },
48
48
  "publishConfig": {