@canvas-harness/core 0.1.0 → 0.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.
package/dist/index.d.cts CHANGED
@@ -817,7 +817,28 @@ type SnapshotEnv = {
817
817
  type NodeTypeDefOptions = {
818
818
  /** Unique type id, e.g. 'chart-card'. */
819
819
  type: string;
820
+ /**
821
+ * Canvas paint for the node body. Caller has already applied the
822
+ * camera + node transform, so paint at `(0, 0, node.w, node.h)`.
823
+ *
824
+ * Context state contract:
825
+ * - The renderer wraps this call in `ctx.save()` / `ctx.restore()`
826
+ * so any state you change (fillStyle, strokeStyle, lineWidth,
827
+ * setLineDash, globalAlpha, font, …) is automatically rolled
828
+ * back before the next node draws — set whatever you need
829
+ * without worrying about cleanup.
830
+ * - Conversely, **do NOT assume default state on entry.** Always
831
+ * set the styles you depend on; the previous node's values
832
+ * may still be in effect.
833
+ * - The transform is NOT save/restore-protected at this level
834
+ * (it's managed one frame up by the renderer). Don't leave
835
+ * `translate` / `rotate` / `scale` calls un-paired.
836
+ */
820
837
  renderCanvas?: (ctx: CanvasRenderingContext2D, node: Node, env: RenderEnv) => void;
838
+ /**
839
+ * Low-zoom / motion fallback paint — see ARCHITECTURE.md §5.3 LOD.
840
+ * Same context-state contract as `renderCanvas`.
841
+ */
821
842
  drawPlaceholder?: (ctx: CanvasRenderingContext2D, node: Node, env: RenderEnv) => void;
822
843
  /**
823
844
  * The React view component reference. Stored as `unknown` here because the
@@ -922,7 +943,14 @@ declare const defineNode: (opts: NodeTypeDefOptions) => NodeTypeDef;
922
943
  * nesting, no escapes — single-pass regex tokenization keeps layout
923
944
  * fast at scale. See ARCHITECTURE.md §8.
924
945
  */
925
- type InlineType = 'text' | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'link';
946
+ type InlineType = 'text' | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'link'
947
+ /**
948
+ * LaTeX math expression, content is the source between `$...$` (no
949
+ * leading/trailing whitespace, no line breaks). Rendered via MathJax
950
+ * SVG output and rasterized to an inline bitmap at paint time. See
951
+ * `text/math/`.
952
+ */
953
+ | 'math';
926
954
  type Token = {
927
955
  type: InlineType;
928
956
  content: string;
@@ -963,6 +991,12 @@ type LayoutOptions = {
963
991
  fontFamily: FontFamily;
964
992
  fontSize: FontSize;
965
993
  textStyle: TextStyle;
994
+ /**
995
+ * Used to key the math bitmap cache. Both layout (for measured dims)
996
+ * and paint (for the actual bitmap) need to query the same cache
997
+ * entry. Defaults to `#000000` for estimate-only callers.
998
+ */
999
+ textColor?: string;
966
1000
  };
967
1001
  /**
968
1002
  * Turns tokens into drawable lines. Output consumed by the canvas paint pass.
@@ -1037,6 +1071,97 @@ declare const subscribeFontEpoch: (listener: (epoch: number) => void) => (() =>
1037
1071
  */
1038
1072
  declare const getFontEpoch: () => number;
1039
1073
 
1074
+ /**
1075
+ * Lazy loader for MathJax — same pattern as `render/rough/loader.ts`.
1076
+ *
1077
+ * MathJax is ~600KB and only useful for scenes with LaTeX math. We
1078
+ * defer loading until the first `$...$` token requests a compile,
1079
+ * then convert LaTeX → SVG strings off the main rAF path.
1080
+ *
1081
+ * Loaded from jsDelivr CDN rather than bundled because the v4
1082
+ * tex-svg.js bundle uses `importScripts('sre/speech-worker.js')` for
1083
+ * accessibility; the relative URL resolves wrong when served from a
1084
+ * dev bundler's node_modules. The CDN serves sibling files at
1085
+ * predictable paths so the bundle finds its workers. Self-host by
1086
+ * pointing `VENDOR_URL` at your own copy of the v4 component files.
1087
+ *
1088
+ * Notes:
1089
+ * - MathJax's `tex-svg.js` bundle attaches itself to `window.MathJax`
1090
+ * when loaded. This is the documented v4 browser API.
1091
+ * - If a consumer also uses MathJax, they should configure the
1092
+ * global *before* canvas-harness's first math node loads.
1093
+ */
1094
+ type MathJaxLike = {
1095
+ tex2svgPromise(source: string, opts?: {
1096
+ display?: boolean;
1097
+ em?: number;
1098
+ ex?: number;
1099
+ containerWidth?: number;
1100
+ }): Promise<MathJaxSvgElement>;
1101
+ startup: {
1102
+ /**
1103
+ * The "lite adaptor" used by the SVG output in browser context.
1104
+ * `serializeXML` returns standalone-valid SVG markup with
1105
+ * namespaces intact (which `outerHTML` does not always do).
1106
+ */
1107
+ adaptor: {
1108
+ serializeXML?(el: MathJaxSvgElement): string;
1109
+ outerHTML(el: MathJaxSvgElement): string;
1110
+ };
1111
+ };
1112
+ };
1113
+ /**
1114
+ * MathJax returns a "lite element" virtual-DOM node, not a real SVG
1115
+ * element. We only need to serialize it via the adaptor.
1116
+ */
1117
+ type MathJaxSvgElement = unknown;
1118
+ declare global {
1119
+ var MathJax: unknown;
1120
+ }
1121
+ /**
1122
+ * Returns the configured MathJax instance if loaded, else `null` and
1123
+ * triggers the lazy import on first call. Subscribers via `onMathJaxReady`
1124
+ * are notified when the import resolves.
1125
+ */
1126
+ declare const getMathJax: () => MathJaxLike | null;
1127
+ /**
1128
+ * Registers a callback that fires once when MathJax becomes available.
1129
+ * No-op if already loaded — caller should check `getMathJax() !== null`
1130
+ * first. Used to trigger a re-paint of math-bearing text nodes.
1131
+ */
1132
+ declare const onMathJaxReady: (cb: () => void) => void;
1133
+
1134
+ /** Result of a successful math compile + raster. */
1135
+ type MathBitmap = {
1136
+ bitmap: ImageBitmap;
1137
+ /** Width in logical CSS pixels at the requested font size. */
1138
+ width: number;
1139
+ /** Height in logical CSS pixels at the requested font size. */
1140
+ height: number;
1141
+ /**
1142
+ * Baseline offset in CSS pixels (positive = bitmap top sits ABOVE
1143
+ * the text baseline; the bottom dips below by `height - baseline`).
1144
+ * Parsed from MathJax's `vertical-align: -Nex` style attribute.
1145
+ */
1146
+ baselineOffset: number;
1147
+ };
1148
+ declare const getMathEpoch: () => number;
1149
+ declare const subscribeMathEpoch: (cb: () => void) => (() => void);
1150
+ /**
1151
+ * Look up a math bitmap or kick off compilation. Returns the cached
1152
+ * bitmap if ready, `null` if still loading / queued (caller should
1153
+ * paint a placeholder this frame). Triggers a math-epoch bump when
1154
+ * the formula resolves.
1155
+ *
1156
+ * `sizePx` is the on-screen height in logical px (typically the line
1157
+ * height of the surrounding text × DPR).
1158
+ */
1159
+ declare const getMathBitmap: (source: string, color: string, sizePx: number) => MathBitmap | null;
1160
+ /** Test / debug aid. */
1161
+ declare const clearMathCache: () => void;
1162
+ /** Test / debug aid. */
1163
+ declare const getMathCacheSize: () => number;
1164
+
1040
1165
  /**
1041
1166
  * Buckets zoom to avoid cache churn from sub-1%-zoom changes.
1042
1167
  */
@@ -2224,6 +2349,15 @@ declare const worldViewport: (surface: CanvasSurface, camera: CameraState) => Wo
2224
2349
  * Wraps a draw callback in the local-frame transform for one node:
2225
2350
  * translates to the node's center, rotates by node.angle, then translates
2226
2351
  * back to the node's top-left so the drawer can build paths in (0..w, 0..h).
2352
+ *
2353
+ * Fast path: when `node.angle === 0` (the common case) we skip the
2354
+ * canvas2d save/restore pair entirely and manually un-translate after
2355
+ * the callback. `save()`/`restore()` allocate + swap a full graphics
2356
+ * state record; at 10k+ nodes/frame that's ~1ms of the paint budget.
2357
+ * The callback is responsible for not leaking transform state — all
2358
+ * built-in drawers (drawShape, drawCompositeRough, paintFrameNode,
2359
+ * paintImageNode, paintIconNode) honor this contract by either
2360
+ * leaving the transform untouched or restoring their own inner pushes.
2227
2361
  */
2228
2362
  declare const drawWithNodeTransform: (ctx: CanvasRenderingContext2D, node: Node, fn: () => void) => void;
2229
2363
 
@@ -3264,4 +3398,4 @@ declare const installedExtensions: (store: CanvasStore) => string[];
3264
3398
  */
3265
3399
  declare const VERSION = "0.0.0";
3266
3400
 
3267
- export { type AnthropicToolDef, type Arrowhead, BEZIER_SEGMENTS, type BatchId, type BitmapCacheEntry, type BitmapCacheRequest, type BuiltInNodeType, CODE_BG_COLOR, CODE_BLOCK_MARGIN_Y, CODE_BLOCK_PADDING_X, CONTENT_HEIGHT_BUFFER, CONTENT_PADDING, type CameraState, type CanvasBackground, type CanvasBackgroundPattern, type CanvasStore, type CanvasSurface, type ClientId, type ClipResult, type ConflictRecord, type ContextEdge, type ContextNode, DEFAULT_BACKGROUND, DEFAULT_CAMERA, DEFAULT_HIGHLIGHT_COLOR, DEFAULT_HIGHLIGHT_COLOR_DARK, DEFAULT_MINIMAP_MAX_NODES, DEFAULT_STYLE, DEFAULT_TEXT_COLOR, type DeserializeOptions, type DragOriginal, type DrawTextOptions, EDGE_HANDLE_SLOP_PX, EDGE_HIT_SLOP_PX, type Edge, type EdgeEnd, type EdgeGeometry, EdgeGeometryCache, type EdgeHit, type EdgeId, type EdgeStyle, type EditorAdapter, type EditorAdapterFactory, type EditorAdapterMountOptions, type EstimateOptions, type ExportOptions, type Extension, type ExtensionApi, FONT_FAMILY_MAP, FONT_SIZE_MAP, type FontFamily, type FontSize, type FrameLoop, type FrameStats, type GetContextOptions, type Group, type GroupId, type Hit, type IconNodeData, type IdGenerator, type ImageNodeData, type InlineType, type InteractionMode, type InteractionState, LINE_HEIGHT_MAP, LINK_COLOR, type LayoutLine, type LayoutOptions, MAX_IMAGE_BYTES, MAX_SVG_BYTES, MAX_ZOOM, MIN_ZOOM, type Migrator, type MinimapContentOptions, type Node, type NodeHit, type NodeId, type NodeType, type NodeTypeDef, type NodeTypeDefOptions, type Op, type OpBatch, type OpOrigin, PALM_REJECTION_GRACE_MS, type PalmRejectionState, type PathStyle, type PointerInfo, type PresenceEvent, type PresencePatch, type PresenceSlice, type PresenceState, type PrimitiveType, RESIZE_HANDLES, RESIZE_HANDLE_SIZE_PX, ROTATE_HANDLE_OFFSET_PX, ROTATE_HANDLE_RADIUS_PX, type RenderEnv, type Renderer, type RendererOptions, type ResizeHandle, SCHEMA_VERSION, type Scene, type SceneContextJson, type SchemaVersion, type SerializedClipboard, type SerializedScene, type Side, type SnapshotEnv, type SpatialId, type SpatialQuery, type SpatialResult, type StoreEventHandler, type StoreEventName, type StoreEvents, type StoreOptions, type StrokeStyle, type Style, type StyledRun, type SvgExportOptions, type SyncAdapter, type SyncAdapterCapabilities, type TextAlign, type TextStyle, type ThemeResolver, type Token, type Transform, UniformGrid, type Unsubscribe, VERSION, type Vec2, type WorldRect, applyCameraTransform, applySvgColor, arrowheadLength, asBatchId, asClientId, asEdgeId, asGroupId, asNodeId, attachSync, autoRouteControls, blobToDataUri, clampEffectiveScale, clampZoom, clearMeasureCache, clearSurface, clearTextBitmapCache, clipSamples, computeAutoFitHeight, computeEdgeGeometry, copy, createCanvasStore, createDefaultTextareaEditor, createFrameLoop, createPalmRejectionState, createRenderer, cubicBezier, cubicBezierTangent, cut, defineExtension, defineNode, deserializeClipboard, detectConflicts, downscaleImageBlob, drawArrowhead, drawEdge, drawMinimapViewport, drawShape, drawTextToCanvas, drawWithNodeTransform, edgeAABBFromSamples, edgeLabelBoundsWorld, emptyPresenceState, estimateMarkdownContentHeight, exportSelection, exportSelectionSvg, exportViewport, extractSvgDimensions, fromSerialized, fullVisibleClipResult, getCanvasFont, getContentHeight, getContext, getDpr, getFontEpoch, getMarkdownLineHeightPx, getOrRenderTextBitmap, getPointAndTangentAtArcLength, getTextBitmapCacheSize, handleEnter, handleWorldPositions, hitTestAny, hitTestEdge, hitTestHandles, hitTestPoint, hitTestRotateHandle, idleInteractionState, inflateRect, insertLink, installExtension, installedExtensions, inverseBatch, inverseOp, isAttached, isCanvasHarnessClipboard, isDrawablePrimitive, isMoving, isNodeRemoteEditing, layoutTokens, makeIdGenerator, marqueeNodes, measureText, midpointToCubicControls, minimapScreenToWorld, nodeAABB, nodeIntersectsRect, nodeLocalToWorld, notePenActive, notePenInactive, opSchemas, opSchemasAsAnthropicTools, paintBackground, panByScreen, paste, pointInNode, projectEndToWorld, projectToNodeBoundary, quantizeDpr, quantizeZoom, randomClientId, rectContainsPoint, rectFromPoints, rectsIntersect, registerMigrator, renderMinimapContent, resolveColor, resolveOpacity, resolveRenderScale, resolveStrokeWidth, rotateHandleWorldPosition, rotateVecByAngle, sampleBezier, sampleSelfLoop, samplesFor, sanitizeSvg, sceneBounds, screenToWorld, selfLoopGeometry, serializeSelection, setupSurface, shouldAutoFit, shouldRejectTouch, sideNormalLocal, sideOf, sizeSurface, storeToJSON, subscribeFontEpoch, tangentAtArcLength, toImageBlob, toSerialized, toggleBold, toggleCode, toggleItalic, toggleStrike, toggleUnderline, tokenize, unionRects, validateImageInput, validateSvgMarkup, viewportWorldRect, withAutoFitHeight, worldToNodeLocal, worldToScreen, worldViewport, worldViewportFromCamera, zoomAtScreenPoint };
3401
+ export { type AnthropicToolDef, type Arrowhead, BEZIER_SEGMENTS, type BatchId, type BitmapCacheEntry, type BitmapCacheRequest, type BuiltInNodeType, CODE_BG_COLOR, CODE_BLOCK_MARGIN_Y, CODE_BLOCK_PADDING_X, CONTENT_HEIGHT_BUFFER, CONTENT_PADDING, type CameraState, type CanvasBackground, type CanvasBackgroundPattern, type CanvasStore, type CanvasSurface, type ClientId, type ClipResult, type ConflictRecord, type ContextEdge, type ContextNode, DEFAULT_BACKGROUND, DEFAULT_CAMERA, DEFAULT_HIGHLIGHT_COLOR, DEFAULT_HIGHLIGHT_COLOR_DARK, DEFAULT_MINIMAP_MAX_NODES, DEFAULT_STYLE, DEFAULT_TEXT_COLOR, type DeserializeOptions, type DragOriginal, type DrawTextOptions, EDGE_HANDLE_SLOP_PX, EDGE_HIT_SLOP_PX, type Edge, type EdgeEnd, type EdgeGeometry, EdgeGeometryCache, type EdgeHit, type EdgeId, type EdgeStyle, type EditorAdapter, type EditorAdapterFactory, type EditorAdapterMountOptions, type EstimateOptions, type ExportOptions, type Extension, type ExtensionApi, FONT_FAMILY_MAP, FONT_SIZE_MAP, type FontFamily, type FontSize, type FrameLoop, type FrameStats, type GetContextOptions, type Group, type GroupId, type Hit, type IconNodeData, type IdGenerator, type ImageNodeData, type InlineType, type InteractionMode, type InteractionState, LINE_HEIGHT_MAP, LINK_COLOR, type LayoutLine, type LayoutOptions, MAX_IMAGE_BYTES, MAX_SVG_BYTES, MAX_ZOOM, MIN_ZOOM, type MathBitmap, type Migrator, type MinimapContentOptions, type Node, type NodeHit, type NodeId, type NodeType, type NodeTypeDef, type NodeTypeDefOptions, type Op, type OpBatch, type OpOrigin, PALM_REJECTION_GRACE_MS, type PalmRejectionState, type PathStyle, type PointerInfo, type PresenceEvent, type PresencePatch, type PresenceSlice, type PresenceState, type PrimitiveType, RESIZE_HANDLES, RESIZE_HANDLE_SIZE_PX, ROTATE_HANDLE_OFFSET_PX, ROTATE_HANDLE_RADIUS_PX, type RenderEnv, type Renderer, type RendererOptions, type ResizeHandle, SCHEMA_VERSION, type Scene, type SceneContextJson, type SchemaVersion, type SerializedClipboard, type SerializedScene, type Side, type SnapshotEnv, type SpatialId, type SpatialQuery, type SpatialResult, type StoreEventHandler, type StoreEventName, type StoreEvents, type StoreOptions, type StrokeStyle, type Style, type StyledRun, type SvgExportOptions, type SyncAdapter, type SyncAdapterCapabilities, type TextAlign, type TextStyle, type ThemeResolver, type Token, type Transform, UniformGrid, type Unsubscribe, VERSION, type Vec2, type WorldRect, applyCameraTransform, applySvgColor, arrowheadLength, asBatchId, asClientId, asEdgeId, asGroupId, asNodeId, attachSync, autoRouteControls, blobToDataUri, clampEffectiveScale, clampZoom, clearMathCache, clearMeasureCache, clearSurface, clearTextBitmapCache, clipSamples, computeAutoFitHeight, computeEdgeGeometry, copy, createCanvasStore, createDefaultTextareaEditor, createFrameLoop, createPalmRejectionState, createRenderer, cubicBezier, cubicBezierTangent, cut, defineExtension, defineNode, deserializeClipboard, detectConflicts, downscaleImageBlob, drawArrowhead, drawEdge, drawMinimapViewport, drawShape, drawTextToCanvas, drawWithNodeTransform, edgeAABBFromSamples, edgeLabelBoundsWorld, emptyPresenceState, estimateMarkdownContentHeight, exportSelection, exportSelectionSvg, exportViewport, extractSvgDimensions, fromSerialized, fullVisibleClipResult, getCanvasFont, getContentHeight, getContext, getDpr, getFontEpoch, getMarkdownLineHeightPx, getMathBitmap, getMathCacheSize, getMathEpoch, getMathJax, getOrRenderTextBitmap, getPointAndTangentAtArcLength, getTextBitmapCacheSize, handleEnter, handleWorldPositions, hitTestAny, hitTestEdge, hitTestHandles, hitTestPoint, hitTestRotateHandle, idleInteractionState, inflateRect, insertLink, installExtension, installedExtensions, inverseBatch, inverseOp, isAttached, isCanvasHarnessClipboard, isDrawablePrimitive, isMoving, isNodeRemoteEditing, layoutTokens, makeIdGenerator, marqueeNodes, measureText, midpointToCubicControls, minimapScreenToWorld, nodeAABB, nodeIntersectsRect, nodeLocalToWorld, notePenActive, notePenInactive, onMathJaxReady, opSchemas, opSchemasAsAnthropicTools, paintBackground, panByScreen, paste, pointInNode, projectEndToWorld, projectToNodeBoundary, quantizeDpr, quantizeZoom, randomClientId, rectContainsPoint, rectFromPoints, rectsIntersect, registerMigrator, renderMinimapContent, resolveColor, resolveOpacity, resolveRenderScale, resolveStrokeWidth, rotateHandleWorldPosition, rotateVecByAngle, sampleBezier, sampleSelfLoop, samplesFor, sanitizeSvg, sceneBounds, screenToWorld, selfLoopGeometry, serializeSelection, setupSurface, shouldAutoFit, shouldRejectTouch, sideNormalLocal, sideOf, sizeSurface, storeToJSON, subscribeFontEpoch, subscribeMathEpoch, tangentAtArcLength, toImageBlob, toSerialized, toggleBold, toggleCode, toggleItalic, toggleStrike, toggleUnderline, tokenize, unionRects, validateImageInput, validateSvgMarkup, viewportWorldRect, withAutoFitHeight, worldToNodeLocal, worldToScreen, worldViewport, worldViewportFromCamera, zoomAtScreenPoint };
package/dist/index.d.ts CHANGED
@@ -817,7 +817,28 @@ type SnapshotEnv = {
817
817
  type NodeTypeDefOptions = {
818
818
  /** Unique type id, e.g. 'chart-card'. */
819
819
  type: string;
820
+ /**
821
+ * Canvas paint for the node body. Caller has already applied the
822
+ * camera + node transform, so paint at `(0, 0, node.w, node.h)`.
823
+ *
824
+ * Context state contract:
825
+ * - The renderer wraps this call in `ctx.save()` / `ctx.restore()`
826
+ * so any state you change (fillStyle, strokeStyle, lineWidth,
827
+ * setLineDash, globalAlpha, font, …) is automatically rolled
828
+ * back before the next node draws — set whatever you need
829
+ * without worrying about cleanup.
830
+ * - Conversely, **do NOT assume default state on entry.** Always
831
+ * set the styles you depend on; the previous node's values
832
+ * may still be in effect.
833
+ * - The transform is NOT save/restore-protected at this level
834
+ * (it's managed one frame up by the renderer). Don't leave
835
+ * `translate` / `rotate` / `scale` calls un-paired.
836
+ */
820
837
  renderCanvas?: (ctx: CanvasRenderingContext2D, node: Node, env: RenderEnv) => void;
838
+ /**
839
+ * Low-zoom / motion fallback paint — see ARCHITECTURE.md §5.3 LOD.
840
+ * Same context-state contract as `renderCanvas`.
841
+ */
821
842
  drawPlaceholder?: (ctx: CanvasRenderingContext2D, node: Node, env: RenderEnv) => void;
822
843
  /**
823
844
  * The React view component reference. Stored as `unknown` here because the
@@ -922,7 +943,14 @@ declare const defineNode: (opts: NodeTypeDefOptions) => NodeTypeDef;
922
943
  * nesting, no escapes — single-pass regex tokenization keeps layout
923
944
  * fast at scale. See ARCHITECTURE.md §8.
924
945
  */
925
- type InlineType = 'text' | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'link';
946
+ type InlineType = 'text' | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'link'
947
+ /**
948
+ * LaTeX math expression, content is the source between `$...$` (no
949
+ * leading/trailing whitespace, no line breaks). Rendered via MathJax
950
+ * SVG output and rasterized to an inline bitmap at paint time. See
951
+ * `text/math/`.
952
+ */
953
+ | 'math';
926
954
  type Token = {
927
955
  type: InlineType;
928
956
  content: string;
@@ -963,6 +991,12 @@ type LayoutOptions = {
963
991
  fontFamily: FontFamily;
964
992
  fontSize: FontSize;
965
993
  textStyle: TextStyle;
994
+ /**
995
+ * Used to key the math bitmap cache. Both layout (for measured dims)
996
+ * and paint (for the actual bitmap) need to query the same cache
997
+ * entry. Defaults to `#000000` for estimate-only callers.
998
+ */
999
+ textColor?: string;
966
1000
  };
967
1001
  /**
968
1002
  * Turns tokens into drawable lines. Output consumed by the canvas paint pass.
@@ -1037,6 +1071,97 @@ declare const subscribeFontEpoch: (listener: (epoch: number) => void) => (() =>
1037
1071
  */
1038
1072
  declare const getFontEpoch: () => number;
1039
1073
 
1074
+ /**
1075
+ * Lazy loader for MathJax — same pattern as `render/rough/loader.ts`.
1076
+ *
1077
+ * MathJax is ~600KB and only useful for scenes with LaTeX math. We
1078
+ * defer loading until the first `$...$` token requests a compile,
1079
+ * then convert LaTeX → SVG strings off the main rAF path.
1080
+ *
1081
+ * Loaded from jsDelivr CDN rather than bundled because the v4
1082
+ * tex-svg.js bundle uses `importScripts('sre/speech-worker.js')` for
1083
+ * accessibility; the relative URL resolves wrong when served from a
1084
+ * dev bundler's node_modules. The CDN serves sibling files at
1085
+ * predictable paths so the bundle finds its workers. Self-host by
1086
+ * pointing `VENDOR_URL` at your own copy of the v4 component files.
1087
+ *
1088
+ * Notes:
1089
+ * - MathJax's `tex-svg.js` bundle attaches itself to `window.MathJax`
1090
+ * when loaded. This is the documented v4 browser API.
1091
+ * - If a consumer also uses MathJax, they should configure the
1092
+ * global *before* canvas-harness's first math node loads.
1093
+ */
1094
+ type MathJaxLike = {
1095
+ tex2svgPromise(source: string, opts?: {
1096
+ display?: boolean;
1097
+ em?: number;
1098
+ ex?: number;
1099
+ containerWidth?: number;
1100
+ }): Promise<MathJaxSvgElement>;
1101
+ startup: {
1102
+ /**
1103
+ * The "lite adaptor" used by the SVG output in browser context.
1104
+ * `serializeXML` returns standalone-valid SVG markup with
1105
+ * namespaces intact (which `outerHTML` does not always do).
1106
+ */
1107
+ adaptor: {
1108
+ serializeXML?(el: MathJaxSvgElement): string;
1109
+ outerHTML(el: MathJaxSvgElement): string;
1110
+ };
1111
+ };
1112
+ };
1113
+ /**
1114
+ * MathJax returns a "lite element" virtual-DOM node, not a real SVG
1115
+ * element. We only need to serialize it via the adaptor.
1116
+ */
1117
+ type MathJaxSvgElement = unknown;
1118
+ declare global {
1119
+ var MathJax: unknown;
1120
+ }
1121
+ /**
1122
+ * Returns the configured MathJax instance if loaded, else `null` and
1123
+ * triggers the lazy import on first call. Subscribers via `onMathJaxReady`
1124
+ * are notified when the import resolves.
1125
+ */
1126
+ declare const getMathJax: () => MathJaxLike | null;
1127
+ /**
1128
+ * Registers a callback that fires once when MathJax becomes available.
1129
+ * No-op if already loaded — caller should check `getMathJax() !== null`
1130
+ * first. Used to trigger a re-paint of math-bearing text nodes.
1131
+ */
1132
+ declare const onMathJaxReady: (cb: () => void) => void;
1133
+
1134
+ /** Result of a successful math compile + raster. */
1135
+ type MathBitmap = {
1136
+ bitmap: ImageBitmap;
1137
+ /** Width in logical CSS pixels at the requested font size. */
1138
+ width: number;
1139
+ /** Height in logical CSS pixels at the requested font size. */
1140
+ height: number;
1141
+ /**
1142
+ * Baseline offset in CSS pixels (positive = bitmap top sits ABOVE
1143
+ * the text baseline; the bottom dips below by `height - baseline`).
1144
+ * Parsed from MathJax's `vertical-align: -Nex` style attribute.
1145
+ */
1146
+ baselineOffset: number;
1147
+ };
1148
+ declare const getMathEpoch: () => number;
1149
+ declare const subscribeMathEpoch: (cb: () => void) => (() => void);
1150
+ /**
1151
+ * Look up a math bitmap or kick off compilation. Returns the cached
1152
+ * bitmap if ready, `null` if still loading / queued (caller should
1153
+ * paint a placeholder this frame). Triggers a math-epoch bump when
1154
+ * the formula resolves.
1155
+ *
1156
+ * `sizePx` is the on-screen height in logical px (typically the line
1157
+ * height of the surrounding text × DPR).
1158
+ */
1159
+ declare const getMathBitmap: (source: string, color: string, sizePx: number) => MathBitmap | null;
1160
+ /** Test / debug aid. */
1161
+ declare const clearMathCache: () => void;
1162
+ /** Test / debug aid. */
1163
+ declare const getMathCacheSize: () => number;
1164
+
1040
1165
  /**
1041
1166
  * Buckets zoom to avoid cache churn from sub-1%-zoom changes.
1042
1167
  */
@@ -2224,6 +2349,15 @@ declare const worldViewport: (surface: CanvasSurface, camera: CameraState) => Wo
2224
2349
  * Wraps a draw callback in the local-frame transform for one node:
2225
2350
  * translates to the node's center, rotates by node.angle, then translates
2226
2351
  * back to the node's top-left so the drawer can build paths in (0..w, 0..h).
2352
+ *
2353
+ * Fast path: when `node.angle === 0` (the common case) we skip the
2354
+ * canvas2d save/restore pair entirely and manually un-translate after
2355
+ * the callback. `save()`/`restore()` allocate + swap a full graphics
2356
+ * state record; at 10k+ nodes/frame that's ~1ms of the paint budget.
2357
+ * The callback is responsible for not leaking transform state — all
2358
+ * built-in drawers (drawShape, drawCompositeRough, paintFrameNode,
2359
+ * paintImageNode, paintIconNode) honor this contract by either
2360
+ * leaving the transform untouched or restoring their own inner pushes.
2227
2361
  */
2228
2362
  declare const drawWithNodeTransform: (ctx: CanvasRenderingContext2D, node: Node, fn: () => void) => void;
2229
2363
 
@@ -3264,4 +3398,4 @@ declare const installedExtensions: (store: CanvasStore) => string[];
3264
3398
  */
3265
3399
  declare const VERSION = "0.0.0";
3266
3400
 
3267
- export { type AnthropicToolDef, type Arrowhead, BEZIER_SEGMENTS, type BatchId, type BitmapCacheEntry, type BitmapCacheRequest, type BuiltInNodeType, CODE_BG_COLOR, CODE_BLOCK_MARGIN_Y, CODE_BLOCK_PADDING_X, CONTENT_HEIGHT_BUFFER, CONTENT_PADDING, type CameraState, type CanvasBackground, type CanvasBackgroundPattern, type CanvasStore, type CanvasSurface, type ClientId, type ClipResult, type ConflictRecord, type ContextEdge, type ContextNode, DEFAULT_BACKGROUND, DEFAULT_CAMERA, DEFAULT_HIGHLIGHT_COLOR, DEFAULT_HIGHLIGHT_COLOR_DARK, DEFAULT_MINIMAP_MAX_NODES, DEFAULT_STYLE, DEFAULT_TEXT_COLOR, type DeserializeOptions, type DragOriginal, type DrawTextOptions, EDGE_HANDLE_SLOP_PX, EDGE_HIT_SLOP_PX, type Edge, type EdgeEnd, type EdgeGeometry, EdgeGeometryCache, type EdgeHit, type EdgeId, type EdgeStyle, type EditorAdapter, type EditorAdapterFactory, type EditorAdapterMountOptions, type EstimateOptions, type ExportOptions, type Extension, type ExtensionApi, FONT_FAMILY_MAP, FONT_SIZE_MAP, type FontFamily, type FontSize, type FrameLoop, type FrameStats, type GetContextOptions, type Group, type GroupId, type Hit, type IconNodeData, type IdGenerator, type ImageNodeData, type InlineType, type InteractionMode, type InteractionState, LINE_HEIGHT_MAP, LINK_COLOR, type LayoutLine, type LayoutOptions, MAX_IMAGE_BYTES, MAX_SVG_BYTES, MAX_ZOOM, MIN_ZOOM, type Migrator, type MinimapContentOptions, type Node, type NodeHit, type NodeId, type NodeType, type NodeTypeDef, type NodeTypeDefOptions, type Op, type OpBatch, type OpOrigin, PALM_REJECTION_GRACE_MS, type PalmRejectionState, type PathStyle, type PointerInfo, type PresenceEvent, type PresencePatch, type PresenceSlice, type PresenceState, type PrimitiveType, RESIZE_HANDLES, RESIZE_HANDLE_SIZE_PX, ROTATE_HANDLE_OFFSET_PX, ROTATE_HANDLE_RADIUS_PX, type RenderEnv, type Renderer, type RendererOptions, type ResizeHandle, SCHEMA_VERSION, type Scene, type SceneContextJson, type SchemaVersion, type SerializedClipboard, type SerializedScene, type Side, type SnapshotEnv, type SpatialId, type SpatialQuery, type SpatialResult, type StoreEventHandler, type StoreEventName, type StoreEvents, type StoreOptions, type StrokeStyle, type Style, type StyledRun, type SvgExportOptions, type SyncAdapter, type SyncAdapterCapabilities, type TextAlign, type TextStyle, type ThemeResolver, type Token, type Transform, UniformGrid, type Unsubscribe, VERSION, type Vec2, type WorldRect, applyCameraTransform, applySvgColor, arrowheadLength, asBatchId, asClientId, asEdgeId, asGroupId, asNodeId, attachSync, autoRouteControls, blobToDataUri, clampEffectiveScale, clampZoom, clearMeasureCache, clearSurface, clearTextBitmapCache, clipSamples, computeAutoFitHeight, computeEdgeGeometry, copy, createCanvasStore, createDefaultTextareaEditor, createFrameLoop, createPalmRejectionState, createRenderer, cubicBezier, cubicBezierTangent, cut, defineExtension, defineNode, deserializeClipboard, detectConflicts, downscaleImageBlob, drawArrowhead, drawEdge, drawMinimapViewport, drawShape, drawTextToCanvas, drawWithNodeTransform, edgeAABBFromSamples, edgeLabelBoundsWorld, emptyPresenceState, estimateMarkdownContentHeight, exportSelection, exportSelectionSvg, exportViewport, extractSvgDimensions, fromSerialized, fullVisibleClipResult, getCanvasFont, getContentHeight, getContext, getDpr, getFontEpoch, getMarkdownLineHeightPx, getOrRenderTextBitmap, getPointAndTangentAtArcLength, getTextBitmapCacheSize, handleEnter, handleWorldPositions, hitTestAny, hitTestEdge, hitTestHandles, hitTestPoint, hitTestRotateHandle, idleInteractionState, inflateRect, insertLink, installExtension, installedExtensions, inverseBatch, inverseOp, isAttached, isCanvasHarnessClipboard, isDrawablePrimitive, isMoving, isNodeRemoteEditing, layoutTokens, makeIdGenerator, marqueeNodes, measureText, midpointToCubicControls, minimapScreenToWorld, nodeAABB, nodeIntersectsRect, nodeLocalToWorld, notePenActive, notePenInactive, opSchemas, opSchemasAsAnthropicTools, paintBackground, panByScreen, paste, pointInNode, projectEndToWorld, projectToNodeBoundary, quantizeDpr, quantizeZoom, randomClientId, rectContainsPoint, rectFromPoints, rectsIntersect, registerMigrator, renderMinimapContent, resolveColor, resolveOpacity, resolveRenderScale, resolveStrokeWidth, rotateHandleWorldPosition, rotateVecByAngle, sampleBezier, sampleSelfLoop, samplesFor, sanitizeSvg, sceneBounds, screenToWorld, selfLoopGeometry, serializeSelection, setupSurface, shouldAutoFit, shouldRejectTouch, sideNormalLocal, sideOf, sizeSurface, storeToJSON, subscribeFontEpoch, tangentAtArcLength, toImageBlob, toSerialized, toggleBold, toggleCode, toggleItalic, toggleStrike, toggleUnderline, tokenize, unionRects, validateImageInput, validateSvgMarkup, viewportWorldRect, withAutoFitHeight, worldToNodeLocal, worldToScreen, worldViewport, worldViewportFromCamera, zoomAtScreenPoint };
3401
+ export { type AnthropicToolDef, type Arrowhead, BEZIER_SEGMENTS, type BatchId, type BitmapCacheEntry, type BitmapCacheRequest, type BuiltInNodeType, CODE_BG_COLOR, CODE_BLOCK_MARGIN_Y, CODE_BLOCK_PADDING_X, CONTENT_HEIGHT_BUFFER, CONTENT_PADDING, type CameraState, type CanvasBackground, type CanvasBackgroundPattern, type CanvasStore, type CanvasSurface, type ClientId, type ClipResult, type ConflictRecord, type ContextEdge, type ContextNode, DEFAULT_BACKGROUND, DEFAULT_CAMERA, DEFAULT_HIGHLIGHT_COLOR, DEFAULT_HIGHLIGHT_COLOR_DARK, DEFAULT_MINIMAP_MAX_NODES, DEFAULT_STYLE, DEFAULT_TEXT_COLOR, type DeserializeOptions, type DragOriginal, type DrawTextOptions, EDGE_HANDLE_SLOP_PX, EDGE_HIT_SLOP_PX, type Edge, type EdgeEnd, type EdgeGeometry, EdgeGeometryCache, type EdgeHit, type EdgeId, type EdgeStyle, type EditorAdapter, type EditorAdapterFactory, type EditorAdapterMountOptions, type EstimateOptions, type ExportOptions, type Extension, type ExtensionApi, FONT_FAMILY_MAP, FONT_SIZE_MAP, type FontFamily, type FontSize, type FrameLoop, type FrameStats, type GetContextOptions, type Group, type GroupId, type Hit, type IconNodeData, type IdGenerator, type ImageNodeData, type InlineType, type InteractionMode, type InteractionState, LINE_HEIGHT_MAP, LINK_COLOR, type LayoutLine, type LayoutOptions, MAX_IMAGE_BYTES, MAX_SVG_BYTES, MAX_ZOOM, MIN_ZOOM, type MathBitmap, type Migrator, type MinimapContentOptions, type Node, type NodeHit, type NodeId, type NodeType, type NodeTypeDef, type NodeTypeDefOptions, type Op, type OpBatch, type OpOrigin, PALM_REJECTION_GRACE_MS, type PalmRejectionState, type PathStyle, type PointerInfo, type PresenceEvent, type PresencePatch, type PresenceSlice, type PresenceState, type PrimitiveType, RESIZE_HANDLES, RESIZE_HANDLE_SIZE_PX, ROTATE_HANDLE_OFFSET_PX, ROTATE_HANDLE_RADIUS_PX, type RenderEnv, type Renderer, type RendererOptions, type ResizeHandle, SCHEMA_VERSION, type Scene, type SceneContextJson, type SchemaVersion, type SerializedClipboard, type SerializedScene, type Side, type SnapshotEnv, type SpatialId, type SpatialQuery, type SpatialResult, type StoreEventHandler, type StoreEventName, type StoreEvents, type StoreOptions, type StrokeStyle, type Style, type StyledRun, type SvgExportOptions, type SyncAdapter, type SyncAdapterCapabilities, type TextAlign, type TextStyle, type ThemeResolver, type Token, type Transform, UniformGrid, type Unsubscribe, VERSION, type Vec2, type WorldRect, applyCameraTransform, applySvgColor, arrowheadLength, asBatchId, asClientId, asEdgeId, asGroupId, asNodeId, attachSync, autoRouteControls, blobToDataUri, clampEffectiveScale, clampZoom, clearMathCache, clearMeasureCache, clearSurface, clearTextBitmapCache, clipSamples, computeAutoFitHeight, computeEdgeGeometry, copy, createCanvasStore, createDefaultTextareaEditor, createFrameLoop, createPalmRejectionState, createRenderer, cubicBezier, cubicBezierTangent, cut, defineExtension, defineNode, deserializeClipboard, detectConflicts, downscaleImageBlob, drawArrowhead, drawEdge, drawMinimapViewport, drawShape, drawTextToCanvas, drawWithNodeTransform, edgeAABBFromSamples, edgeLabelBoundsWorld, emptyPresenceState, estimateMarkdownContentHeight, exportSelection, exportSelectionSvg, exportViewport, extractSvgDimensions, fromSerialized, fullVisibleClipResult, getCanvasFont, getContentHeight, getContext, getDpr, getFontEpoch, getMarkdownLineHeightPx, getMathBitmap, getMathCacheSize, getMathEpoch, getMathJax, getOrRenderTextBitmap, getPointAndTangentAtArcLength, getTextBitmapCacheSize, handleEnter, handleWorldPositions, hitTestAny, hitTestEdge, hitTestHandles, hitTestPoint, hitTestRotateHandle, idleInteractionState, inflateRect, insertLink, installExtension, installedExtensions, inverseBatch, inverseOp, isAttached, isCanvasHarnessClipboard, isDrawablePrimitive, isMoving, isNodeRemoteEditing, layoutTokens, makeIdGenerator, marqueeNodes, measureText, midpointToCubicControls, minimapScreenToWorld, nodeAABB, nodeIntersectsRect, nodeLocalToWorld, notePenActive, notePenInactive, onMathJaxReady, opSchemas, opSchemasAsAnthropicTools, paintBackground, panByScreen, paste, pointInNode, projectEndToWorld, projectToNodeBoundary, quantizeDpr, quantizeZoom, randomClientId, rectContainsPoint, rectFromPoints, rectsIntersect, registerMigrator, renderMinimapContent, resolveColor, resolveOpacity, resolveRenderScale, resolveStrokeWidth, rotateHandleWorldPosition, rotateVecByAngle, sampleBezier, sampleSelfLoop, samplesFor, sanitizeSvg, sceneBounds, screenToWorld, selfLoopGeometry, serializeSelection, setupSurface, shouldAutoFit, shouldRejectTouch, sideNormalLocal, sideOf, sizeSurface, storeToJSON, subscribeFontEpoch, subscribeMathEpoch, tangentAtArcLength, toImageBlob, toSerialized, toggleBold, toggleCode, toggleItalic, toggleStrike, toggleUnderline, tokenize, unionRects, validateImageInput, validateSvgMarkup, viewportWorldRect, withAutoFitHeight, worldToNodeLocal, worldToScreen, worldViewport, worldViewportFromCamera, zoomAtScreenPoint };