@tldraw/editor 5.3.0-next.4123e97459a9 → 5.3.0-next.5cb71bb7e8fe

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist-cjs/index.d.ts +115 -3
  2. package/dist-cjs/index.js +13 -3
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +24 -19
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/EditorPortal.js +44 -0
  7. package/dist-cjs/lib/components/EditorPortal.js.map +7 -0
  8. package/dist-cjs/lib/components/LiveCollaborators.js +102 -0
  9. package/dist-cjs/lib/components/LiveCollaborators.js.map +7 -0
  10. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -10
  11. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +3 -3
  12. package/dist-cjs/lib/components/default-components/DefaultCursor.js +59 -0
  13. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +7 -0
  14. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +4 -4
  15. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  16. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  17. package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
  18. package/dist-cjs/lib/hooks/useCursor.js +1 -0
  19. package/dist-cjs/lib/hooks/useCursor.js.map +2 -2
  20. package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
  21. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  22. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +49 -18
  23. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +3 -3
  24. package/dist-cjs/lib/hooks/useTransform.js +45 -0
  25. package/dist-cjs/lib/hooks/useTransform.js.map +7 -0
  26. package/dist-cjs/lib/license/LicenseManager.js +51 -2
  27. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  28. package/dist-cjs/lib/license/useLicenseManagerState.js +7 -0
  29. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  30. package/dist-cjs/lib/utils/collaborators.js +27 -0
  31. package/dist-cjs/lib/utils/collaborators.js.map +7 -0
  32. package/dist-cjs/lib/utils/getHtmlLayerTransform.js +33 -0
  33. package/dist-cjs/lib/utils/getHtmlLayerTransform.js.map +7 -0
  34. package/dist-cjs/version.js +3 -3
  35. package/dist-cjs/version.js.map +1 -1
  36. package/dist-esm/index.d.mts +115 -3
  37. package/dist-esm/index.mjs +19 -4
  38. package/dist-esm/index.mjs.map +2 -2
  39. package/dist-esm/lib/TldrawEditor.mjs +23 -18
  40. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  41. package/dist-esm/lib/components/EditorPortal.mjs +24 -0
  42. package/dist-esm/lib/components/EditorPortal.mjs.map +7 -0
  43. package/dist-esm/lib/components/LiveCollaborators.mjs +82 -0
  44. package/dist-esm/lib/components/LiveCollaborators.mjs.map +7 -0
  45. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -11
  46. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  47. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +29 -0
  48. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +7 -0
  49. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +5 -5
  50. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  51. package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
  52. package/dist-esm/lib/hooks/useCursor.mjs +1 -0
  53. package/dist-esm/lib/hooks/useCursor.mjs.map +2 -2
  54. package/dist-esm/lib/hooks/useEditorComponents.mjs +2 -0
  55. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  56. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +50 -19
  57. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +3 -3
  58. package/dist-esm/lib/hooks/useTransform.mjs +25 -0
  59. package/dist-esm/lib/hooks/useTransform.mjs.map +7 -0
  60. package/dist-esm/lib/license/LicenseManager.mjs +52 -3
  61. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  62. package/dist-esm/lib/license/useLicenseManagerState.mjs +7 -0
  63. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  64. package/dist-esm/lib/utils/collaborators.mjs +7 -0
  65. package/dist-esm/lib/utils/collaborators.mjs.map +7 -0
  66. package/dist-esm/lib/utils/getHtmlLayerTransform.mjs +13 -0
  67. package/dist-esm/lib/utils/getHtmlLayerTransform.mjs.map +7 -0
  68. package/dist-esm/version.mjs +3 -3
  69. package/dist-esm/version.mjs.map +1 -1
  70. package/editor.css +43 -4
  71. package/package.json +7 -7
  72. package/src/index.ts +14 -2
  73. package/src/lib/TldrawEditor.tsx +21 -13
  74. package/src/lib/components/EditorPortal.tsx +77 -0
  75. package/src/lib/components/LiveCollaborators.tsx +133 -0
  76. package/src/lib/components/default-components/DefaultCanvas.tsx +5 -17
  77. package/src/lib/components/default-components/DefaultCursor.tsx +65 -0
  78. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +46 -1
  79. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +10 -5
  80. package/src/lib/editor/types/external-content.ts +5 -0
  81. package/src/lib/hooks/EditorComponentsContext.tsx +2 -0
  82. package/src/lib/hooks/useCursor.ts +1 -0
  83. package/src/lib/hooks/useEditorComponents.tsx +2 -0
  84. package/src/lib/hooks/usePassThroughWheelEvents.ts +83 -24
  85. package/src/lib/hooks/useTransform.ts +38 -0
  86. package/src/lib/license/LicenseManager.test.ts +146 -0
  87. package/src/lib/license/LicenseManager.ts +98 -2
  88. package/src/lib/license/useLicenseManagerState.ts +17 -1
  89. package/src/lib/test/EditorPortal.test.tsx +48 -0
  90. package/src/lib/test/usePassThroughWheelEvents.test.tsx +77 -0
  91. package/src/lib/utils/collaborators.test.ts +34 -0
  92. package/src/lib/utils/collaborators.ts +25 -0
  93. package/src/lib/utils/getHtmlLayerTransform.ts +22 -0
  94. package/src/version.ts +3 -3
  95. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +0 -51
  96. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +0 -7
  97. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +0 -31
  98. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +0 -7
  99. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +0 -32
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/components/default-components/DefaultCanvas.tsx"],
4
- "sourcesContent": ["import { react } from '@tldraw/state'\nimport { useQuickReactor, useValue } from '@tldraw/state-react'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { modulate, objectMapValues } from '@tldraw/utils'\nimport classNames from 'classnames'\nimport { Fragment, JSX, useEffect, useRef, useState } from 'react'\nimport { tlenv } from '../../globals/environment'\nimport { useEditorComponents } from '../../hooks/EditorComponentsContext'\nimport { useCanvasEvents } from '../../hooks/useCanvasEvents'\nimport { useCoarsePointer } from '../../hooks/useCoarsePointer'\nimport { useContainer } from '../../hooks/useContainer'\nimport { useDocumentEvents } from '../../hooks/useDocumentEvents'\nimport { useEditor } from '../../hooks/useEditor'\nimport { useFixSafariDoubleTapZoomPencilEvents } from '../../hooks/useFixSafariDoubleTapZoomPencilEvents'\nimport { useGestureEvents } from '../../hooks/useGestureEvents'\nimport { useScreenBounds } from '../../hooks/useScreenBounds'\nimport { ShapeCullingProvider, useShapeCulling } from '../../hooks/useShapeCulling'\nimport { Box } from '../../primitives/Box'\nimport { toDomPrecision } from '../../primitives/utils'\nimport { debugFlags } from '../../utils/debug-flags'\nimport { setStyleProperty } from '../../utils/dom'\nimport { MenuClickCapture } from '../MenuClickCapture'\nimport { Shape } from '../Shape'\nimport { CanvasOverlays } from './CanvasOverlays'\n\n/** @public */\nexport interface TLCanvasComponentProps {\n\tclassName?: string\n}\n\n/** @public @react */\nexport function DefaultCanvas({ className }: TLCanvasComponentProps) {\n\tconst editor = useEditor()\n\n\tconst { Background, SvgDefs } = useEditorComponents()\n\n\tconst rCanvas = useRef<HTMLDivElement>(null)\n\tconst rHtmlLayer = useRef<HTMLDivElement>(null)\n\tconst container = useContainer()\n\n\tuseScreenBounds(rCanvas)\n\tuseDocumentEvents()\n\tuseCoarsePointer()\n\n\tuseGestureEvents(rCanvas)\n\tuseFixSafariDoubleTapZoomPencilEvents(rCanvas)\n\n\tuseQuickReactor(\n\t\t'update canvas state data attributes',\n\t\t() => {\n\t\t\tconst canvas = rCanvas.current\n\t\t\tif (!canvas) return\n\n\t\t\tcanvas.setAttribute(\n\t\t\t\t'data-iseditinganything',\n\t\t\t\teditor.getEditingShapeId() === null ? 'false' : 'true'\n\t\t\t)\n\t\t\tcanvas.setAttribute(\n\t\t\t\t'data-isselectinganything',\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ? 'false' : 'true'\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst rMemoizedStuff = useRef({ lodDisableTextOutline: false, canUpdateTextOutline: true })\n\n\tuseQuickReactor(\n\t\t'set text outline',\n\t\tfunction setTextOutline() {\n\t\t\tif (rMemoizedStuff.current.canUpdateTextOutline) {\n\t\t\t\tif (tlenv.isSafari) {\n\t\t\t\t\t// We don't allow text outlines on safari for performance reasons\n\t\t\t\t\tcontainer.style.setProperty('--tl-text-outline', 'none')\n\t\t\t\t\trMemoizedStuff.current.canUpdateTextOutline = false // will prevent this check in the future\n\t\t\t\t} else {\n\t\t\t\t\tconst efficientZoom = editor.getEfficientZoomLevel()\n\t\t\t\t\t// If we're zoomed way out, and have this option enabled, then we hide text outline\n\t\t\t\t\tconst lodDisableTextOutline = efficientZoom < editor.options.textShadowLod\n\t\t\t\t\t// Skip the style update if the property is the same as it was before\n\t\t\t\t\tif (lodDisableTextOutline !== rMemoizedStuff.current.lodDisableTextOutline) {\n\t\t\t\t\t\tcontainer.style.setProperty(\n\t\t\t\t\t\t\t'--tl-text-outline',\n\t\t\t\t\t\t\tlodDisableTextOutline ? 'none' : `var(--tl-text-outline-reference)`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\trMemoizedStuff.current.lodDisableTextOutline = lodDisableTextOutline\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, container]\n\t)\n\n\tuseQuickReactor(\n\t\t'position layers',\n\t\tfunction positionLayersWhenCameraMoves() {\n\t\t\tconst { x, y, z } = editor.getCamera()\n\n\t\t\t// Because the html container has a width/height of 1px, we\n\t\t\t// need to create a small offset when zoomed to ensure that\n\t\t\t// the html container and svg container are lined up exactly.\n\t\t\tconst offset =\n\t\t\t\tz >= 1 ? modulate(z, [1, 8], [0.125, 0.5], true) : modulate(z, [0.1, 1], [-2, 0.125], true)\n\n\t\t\tsetStyleProperty(\n\t\t\t\trHtmlLayer.current,\n\t\t\t\t'transform',\n\t\t\t\t`scale(${toDomPrecision(z)}) translate(${toDomPrecision(\n\t\t\t\t\tx + offset\n\t\t\t\t)}px,${toDomPrecision(y + offset)}px)`\n\t\t\t)\n\t\t},\n\t\t[editor, container]\n\t)\n\n\tconst events = useCanvasEvents()\n\n\tconst shapeSvgDefs = useValue(\n\t\t'shapeSvgDefs',\n\t\t() => {\n\t\t\tconst shapeSvgDefsByKey = new Map<string, JSX.Element>()\n\t\t\tfor (const util of objectMapValues(editor.shapeUtils)) {\n\t\t\t\tif (!util) return\n\t\t\t\tconst defs = util.getCanvasSvgDefs()\n\t\t\t\tfor (const { key, component: Component } of defs) {\n\t\t\t\t\tif (shapeSvgDefsByKey.has(key)) continue\n\t\t\t\t\tshapeSvgDefsByKey.set(key, <Component key={key} />)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn [...shapeSvgDefsByKey.values()]\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst hideShapes = useValue('debug_shapes', () => debugFlags.hideShapes.get(), [debugFlags])\n\n\tconst isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor])\n\tconst { Grid } = useEditorComponents()\n\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tref={rCanvas}\n\t\t\t\tdraggable={false}\n\t\t\t\tclassName={classNames('tl-canvas', className)}\n\t\t\t\tdata-testid=\"canvas\"\n\t\t\t\t{...events}\n\t\t\t>\n\t\t\t\t<svg className=\"tl-svg-context\" aria-hidden=\"true\">\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t{shapeSvgDefs}\n\t\t\t\t\t\t{SvgDefs && <SvgDefs />}\n\t\t\t\t\t</defs>\n\t\t\t\t</svg>\n\t\t\t\t{Background && (\n\t\t\t\t\t<div className=\"tl-background__wrapper\">\n\t\t\t\t\t\t<Background />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{isGridMode && Grid && <GridWrapper />}\n\t\t\t\t<div ref={rHtmlLayer} className=\"tl-html-layer tl-shapes\" draggable={false}>\n\t\t\t\t\t<OnTheCanvasWrapper />\n\t\t\t\t\t{hideShapes ? null : <ShapesLayer canvasRef={rCanvas} />}\n\t\t\t\t</div>\n\t\t\t\t<CanvasOverlays />\n\t\t\t\t<MovingCameraHitTestBlocker />\n\t\t\t</div>\n\t\t\t<InFrontOfTheCanvasWrapper />\n\t\t\t<MenuClickCapture />\n\t\t</>\n\t)\n}\n\nfunction InFrontOfTheCanvasWrapper() {\n\tconst editor = useEditor()\n\tconst { InFrontOfTheCanvas } = useEditorComponents()\n\tif (!InFrontOfTheCanvas) return null\n\treturn (\n\t\t<div\n\t\t\tclassName=\"tl-canvas__in-front\"\n\t\t\tonPointerDown={editor.markEventAsHandled}\n\t\t\tonPointerUp={editor.markEventAsHandled}\n\t\t\tonTouchStart={editor.markEventAsHandled}\n\t\t\tonTouchEnd={editor.markEventAsHandled}\n\t\t>\n\t\t\t<InFrontOfTheCanvas />\n\t\t</div>\n\t)\n}\n\nfunction GridWrapper() {\n\tconst editor = useEditor()\n\tconst gridSize = useValue('gridSize', () => editor.getDocumentSettings().gridSize, [editor])\n\tconst { x, y, z } = useValue('camera', () => editor.getCamera(), [editor])\n\tconst { Grid } = useEditorComponents()\n\n\tif (!Grid) return null\n\n\treturn <Grid x={x} y={y} z={z} size={gridSize} />\n}\n\nfunction ShapesLayer({ canvasRef }: { canvasRef: { readonly current: HTMLDivElement | null } }) {\n\tconst editor = useEditor()\n\tconst debugSvg = useValue('debug svg', () => debugFlags.debugSvg.get(), [debugFlags])\n\tconst renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])\n\n\treturn (\n\t\t<ShapeCullingProvider>\n\t\t\t{renderingShapes.map((result) =>\n\t\t\t\tdebugSvg ? (\n\t\t\t\t\t<Fragment key={result.id + '_fragment'}>\n\t\t\t\t\t\t<Shape {...result} />\n\t\t\t\t\t\t<DebugSvgCopy id={result.id} mode=\"iframe\" />\n\t\t\t\t\t</Fragment>\n\t\t\t\t) : (\n\t\t\t\t\t<Shape key={result.id + '_shape'} {...result} />\n\t\t\t\t)\n\t\t\t)}\n\t\t\t<CullingController />\n\t\t\t{tlenv.isSafari && <ReflowIfNeeded canvasRef={canvasRef} />}\n\t\t</ShapeCullingProvider>\n\t)\n}\nfunction ReflowIfNeeded({ canvasRef }: { canvasRef: { readonly current: HTMLDivElement | null } }) {\n\tconst editor = useEditor()\n\tconst culledShapesRef = useRef<Set<TLShapeId>>(new Set())\n\tuseQuickReactor(\n\t\t'reflow for culled shapes',\n\t\t() => {\n\t\t\tconst culledShapes = editor.getCulledShapes()\n\t\t\tif (culledShapesRef.current === culledShapes) return\n\n\t\t\tculledShapesRef.current = culledShapes\n\t\t\tconst canvas = canvasRef.current\n\t\t\tif (!canvas) return\n\t\t\t// This causes a reflow\n\t\t\t// https://gist.github.com/paulirish/5d52fb081b3570c81e3a\n\t\t\tconst _height = canvas.offsetHeight\n\t\t},\n\t\t[editor, canvasRef]\n\t)\n\treturn null\n}\n\n/**\n * Centralized culling controller that updates shape container visibility.\n * This single reactor replaces per-shape subscriptions for O(1) instead of O(N) subscriptions.\n */\nfunction CullingController() {\n\tconst editor = useEditor()\n\tconst { updateCulling } = useShapeCulling()\n\n\tuseQuickReactor(\n\t\t'update shape culling',\n\t\t() => {\n\t\t\tconst culledShapes = editor.getCulledShapes()\n\t\t\tupdateCulling(culledShapes)\n\t\t},\n\t\t[editor, updateCulling]\n\t)\n\n\treturn null\n}\n\nfunction DebugSvgCopy({ id, mode }: { id: TLShapeId; mode: 'img' | 'iframe' }) {\n\tconst editor = useEditor()\n\n\tconst [image, setImage] = useState<{ src: string; bounds: Box } | null>(null)\n\n\tconst isInRoot = useValue(\n\t\t'is in root',\n\t\t() => {\n\t\t\tconst shape = editor.getShape(id)\n\t\t\treturn shape?.parentId === editor.getCurrentPageId()\n\t\t},\n\t\t[editor, id]\n\t)\n\n\tuseEffect(() => {\n\t\tif (!isInRoot) return\n\n\t\tlet latest = null\n\t\tconst unsubscribe = react('shape to svg', async () => {\n\t\t\tconst renderId = Math.random()\n\t\t\tlatest = renderId\n\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tconst isSingleFrame = !!shape && editor.isShapeFrameLike(shape)\n\t\t\tconst padding = isSingleFrame ? 0 : 10\n\t\t\tlet bounds = editor.getShapePageBounds(id)\n\t\t\tif (!bounds) return\n\t\t\tbounds = bounds.clone().expandBy(padding)\n\n\t\t\tconst result = await editor.getSvgString([id], { padding })\n\n\t\t\tif (latest !== renderId || !result) return\n\n\t\t\tconst svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(result.svg)}`\n\t\t\tsetImage({ src: svgDataUrl, bounds })\n\t\t})\n\n\t\treturn () => {\n\t\t\tlatest = null\n\t\t\tunsubscribe()\n\t\t}\n\t}, [editor, id, isInRoot])\n\n\tif (!isInRoot || !image) return null\n\n\tif (mode === 'iframe') {\n\t\treturn (\n\t\t\t<iframe\n\t\t\t\tsrc={image.src}\n\t\t\t\twidth={image.bounds.width}\n\t\t\t\theight={image.bounds.height}\n\t\t\t\treferrerPolicy=\"no-referrer\"\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tleft: 0,\n\t\t\t\t\tborder: 'none',\n\t\t\t\t\ttransform: `translate(${image.bounds.x}px, ${image.bounds.maxY + 12}px)`,\n\t\t\t\t\toutline: '1px solid black',\n\t\t\t\t\tmaxWidth: 'none',\n\t\t\t\t}}\n\t\t\t/>\n\t\t)\n\t}\n\treturn (\n\t\t<img\n\t\t\tsrc={image.src}\n\t\t\twidth={image.bounds.width}\n\t\t\theight={image.bounds.height}\n\t\t\treferrerPolicy=\"no-referrer\"\n\t\t\tstyle={{\n\t\t\t\tposition: 'absolute',\n\t\t\t\ttop: 0,\n\t\t\t\tleft: 0,\n\t\t\t\ttransform: `translate(${image.bounds.x}px, ${image.bounds.maxY + 12}px)`,\n\t\t\t\toutline: '1px solid black',\n\t\t\t\tmaxWidth: 'none',\n\t\t\t}}\n\t\t/>\n\t)\n}\n\nfunction OnTheCanvasWrapper() {\n\tconst { OnTheCanvas } = useEditorComponents()\n\tif (!OnTheCanvas) return null\n\treturn <OnTheCanvas />\n}\n\nfunction MovingCameraHitTestBlocker() {\n\tconst editor = useEditor()\n\tconst cameraState = useValue('camera state', () => editor.getCameraState(), [editor])\n\n\treturn (\n\t\t<div\n\t\t\tclassName={classNames('tl-hit-test-blocker', {\n\t\t\t\t'tl-hit-test-blocker__hidden': cameraState === 'idle',\n\t\t\t})}\n\t\t/>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { react } from '@tldraw/state'\nimport { useQuickReactor, useValue } from '@tldraw/state-react'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport classNames from 'classnames'\nimport { Fragment, JSX, useEffect, useRef, useState } from 'react'\nimport { tlenv } from '../../globals/environment'\nimport { useEditorComponents } from '../../hooks/EditorComponentsContext'\nimport { useCanvasEvents } from '../../hooks/useCanvasEvents'\nimport { useCoarsePointer } from '../../hooks/useCoarsePointer'\nimport { useContainer } from '../../hooks/useContainer'\nimport { useDocumentEvents } from '../../hooks/useDocumentEvents'\nimport { useEditor } from '../../hooks/useEditor'\nimport { useFixSafariDoubleTapZoomPencilEvents } from '../../hooks/useFixSafariDoubleTapZoomPencilEvents'\nimport { useGestureEvents } from '../../hooks/useGestureEvents'\nimport { useScreenBounds } from '../../hooks/useScreenBounds'\nimport { ShapeCullingProvider, useShapeCulling } from '../../hooks/useShapeCulling'\nimport { Box } from '../../primitives/Box'\nimport { debugFlags } from '../../utils/debug-flags'\nimport { setStyleProperty } from '../../utils/dom'\nimport { getHtmlLayerTransform } from '../../utils/getHtmlLayerTransform'\nimport { LiveCollaborators } from '../LiveCollaborators'\nimport { MenuClickCapture } from '../MenuClickCapture'\nimport { Shape } from '../Shape'\nimport { CanvasOverlays } from './CanvasOverlays'\n\n/** @public */\nexport interface TLCanvasComponentProps {\n\tclassName?: string\n}\n\n/** @public @react */\nexport function DefaultCanvas({ className }: TLCanvasComponentProps) {\n\tconst editor = useEditor()\n\n\tconst { Background, SvgDefs } = useEditorComponents()\n\n\tconst rCanvas = useRef<HTMLDivElement>(null)\n\tconst rHtmlLayer = useRef<HTMLDivElement>(null)\n\tconst container = useContainer()\n\n\tuseScreenBounds(rCanvas)\n\tuseDocumentEvents()\n\tuseCoarsePointer()\n\n\tuseGestureEvents(rCanvas)\n\tuseFixSafariDoubleTapZoomPencilEvents(rCanvas)\n\n\tuseQuickReactor(\n\t\t'update canvas state data attributes',\n\t\t() => {\n\t\t\tconst canvas = rCanvas.current\n\t\t\tif (!canvas) return\n\n\t\t\tcanvas.setAttribute(\n\t\t\t\t'data-iseditinganything',\n\t\t\t\teditor.getEditingShapeId() === null ? 'false' : 'true'\n\t\t\t)\n\t\t\tcanvas.setAttribute(\n\t\t\t\t'data-isselectinganything',\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ? 'false' : 'true'\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst rMemoizedStuff = useRef({ lodDisableTextOutline: false, canUpdateTextOutline: true })\n\n\tuseQuickReactor(\n\t\t'set text outline',\n\t\tfunction setTextOutline() {\n\t\t\tif (rMemoizedStuff.current.canUpdateTextOutline) {\n\t\t\t\tif (tlenv.isSafari) {\n\t\t\t\t\t// We don't allow text outlines on safari for performance reasons\n\t\t\t\t\tcontainer.style.setProperty('--tl-text-outline', 'none')\n\t\t\t\t\trMemoizedStuff.current.canUpdateTextOutline = false // will prevent this check in the future\n\t\t\t\t} else {\n\t\t\t\t\tconst efficientZoom = editor.getEfficientZoomLevel()\n\t\t\t\t\t// If we're zoomed way out, and have this option enabled, then we hide text outline\n\t\t\t\t\tconst lodDisableTextOutline = efficientZoom < editor.options.textShadowLod\n\t\t\t\t\t// Skip the style update if the property is the same as it was before\n\t\t\t\t\tif (lodDisableTextOutline !== rMemoizedStuff.current.lodDisableTextOutline) {\n\t\t\t\t\t\tcontainer.style.setProperty(\n\t\t\t\t\t\t\t'--tl-text-outline',\n\t\t\t\t\t\t\tlodDisableTextOutline ? 'none' : `var(--tl-text-outline-reference)`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\trMemoizedStuff.current.lodDisableTextOutline = lodDisableTextOutline\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, container]\n\t)\n\n\tuseQuickReactor(\n\t\t'position layers',\n\t\tfunction positionLayersWhenCameraMoves() {\n\t\t\tsetStyleProperty(rHtmlLayer.current, 'transform', getHtmlLayerTransform(editor))\n\t\t},\n\t\t[editor, container]\n\t)\n\n\tconst events = useCanvasEvents()\n\n\tconst shapeSvgDefs = useValue(\n\t\t'shapeSvgDefs',\n\t\t() => {\n\t\t\tconst shapeSvgDefsByKey = new Map<string, JSX.Element>()\n\t\t\tfor (const util of objectMapValues(editor.shapeUtils)) {\n\t\t\t\tif (!util) return\n\t\t\t\tconst defs = util.getCanvasSvgDefs()\n\t\t\t\tfor (const { key, component: Component } of defs) {\n\t\t\t\t\tif (shapeSvgDefsByKey.has(key)) continue\n\t\t\t\t\tshapeSvgDefsByKey.set(key, <Component key={key} />)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn [...shapeSvgDefsByKey.values()]\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst hideShapes = useValue('debug_shapes', () => debugFlags.hideShapes.get(), [debugFlags])\n\n\tconst isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor])\n\tconst { Grid } = useEditorComponents()\n\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tref={rCanvas}\n\t\t\t\tdraggable={false}\n\t\t\t\tclassName={classNames('tl-canvas', className)}\n\t\t\t\tdata-testid=\"canvas\"\n\t\t\t\t{...events}\n\t\t\t>\n\t\t\t\t<svg className=\"tl-svg-context\" aria-hidden=\"true\">\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t{shapeSvgDefs}\n\t\t\t\t\t\t{SvgDefs && <SvgDefs />}\n\t\t\t\t\t</defs>\n\t\t\t\t</svg>\n\t\t\t\t{Background && (\n\t\t\t\t\t<div className=\"tl-background__wrapper\">\n\t\t\t\t\t\t<Background />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{isGridMode && Grid && <GridWrapper />}\n\t\t\t\t<div ref={rHtmlLayer} className=\"tl-html-layer tl-shapes\" draggable={false}>\n\t\t\t\t\t<OnTheCanvasWrapper />\n\t\t\t\t\t{hideShapes ? null : <ShapesLayer canvasRef={rCanvas} />}\n\t\t\t\t</div>\n\t\t\t\t<CanvasOverlays />\n\t\t\t\t<MovingCameraHitTestBlocker />\n\t\t\t</div>\n\t\t\t<InFrontOfTheCanvasWrapper />\n\t\t\t<LiveCollaborators />\n\t\t\t<MenuClickCapture />\n\t\t</>\n\t)\n}\n\nfunction InFrontOfTheCanvasWrapper() {\n\tconst editor = useEditor()\n\tconst { InFrontOfTheCanvas } = useEditorComponents()\n\tif (!InFrontOfTheCanvas) return null\n\treturn (\n\t\t<div\n\t\t\tclassName=\"tl-canvas__in-front\"\n\t\t\tonPointerDown={editor.markEventAsHandled}\n\t\t\tonPointerUp={editor.markEventAsHandled}\n\t\t\tonTouchStart={editor.markEventAsHandled}\n\t\t\tonTouchEnd={editor.markEventAsHandled}\n\t\t>\n\t\t\t<InFrontOfTheCanvas />\n\t\t</div>\n\t)\n}\n\nfunction GridWrapper() {\n\tconst editor = useEditor()\n\tconst gridSize = useValue('gridSize', () => editor.getDocumentSettings().gridSize, [editor])\n\tconst { x, y, z } = useValue('camera', () => editor.getCamera(), [editor])\n\tconst { Grid } = useEditorComponents()\n\n\tif (!Grid) return null\n\n\treturn <Grid x={x} y={y} z={z} size={gridSize} />\n}\n\nfunction ShapesLayer({ canvasRef }: { canvasRef: { readonly current: HTMLDivElement | null } }) {\n\tconst editor = useEditor()\n\tconst debugSvg = useValue('debug svg', () => debugFlags.debugSvg.get(), [debugFlags])\n\tconst renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])\n\n\treturn (\n\t\t<ShapeCullingProvider>\n\t\t\t{renderingShapes.map((result) =>\n\t\t\t\tdebugSvg ? (\n\t\t\t\t\t<Fragment key={result.id + '_fragment'}>\n\t\t\t\t\t\t<Shape {...result} />\n\t\t\t\t\t\t<DebugSvgCopy id={result.id} mode=\"iframe\" />\n\t\t\t\t\t</Fragment>\n\t\t\t\t) : (\n\t\t\t\t\t<Shape key={result.id + '_shape'} {...result} />\n\t\t\t\t)\n\t\t\t)}\n\t\t\t<CullingController />\n\t\t\t{tlenv.isSafari && <ReflowIfNeeded canvasRef={canvasRef} />}\n\t\t</ShapeCullingProvider>\n\t)\n}\nfunction ReflowIfNeeded({ canvasRef }: { canvasRef: { readonly current: HTMLDivElement | null } }) {\n\tconst editor = useEditor()\n\tconst culledShapesRef = useRef<Set<TLShapeId>>(new Set())\n\tuseQuickReactor(\n\t\t'reflow for culled shapes',\n\t\t() => {\n\t\t\tconst culledShapes = editor.getCulledShapes()\n\t\t\tif (culledShapesRef.current === culledShapes) return\n\n\t\t\tculledShapesRef.current = culledShapes\n\t\t\tconst canvas = canvasRef.current\n\t\t\tif (!canvas) return\n\t\t\t// This causes a reflow\n\t\t\t// https://gist.github.com/paulirish/5d52fb081b3570c81e3a\n\t\t\tconst _height = canvas.offsetHeight\n\t\t},\n\t\t[editor, canvasRef]\n\t)\n\treturn null\n}\n\n/**\n * Centralized culling controller that updates shape container visibility.\n * This single reactor replaces per-shape subscriptions for O(1) instead of O(N) subscriptions.\n */\nfunction CullingController() {\n\tconst editor = useEditor()\n\tconst { updateCulling } = useShapeCulling()\n\n\tuseQuickReactor(\n\t\t'update shape culling',\n\t\t() => {\n\t\t\tconst culledShapes = editor.getCulledShapes()\n\t\t\tupdateCulling(culledShapes)\n\t\t},\n\t\t[editor, updateCulling]\n\t)\n\n\treturn null\n}\n\nfunction DebugSvgCopy({ id, mode }: { id: TLShapeId; mode: 'img' | 'iframe' }) {\n\tconst editor = useEditor()\n\n\tconst [image, setImage] = useState<{ src: string; bounds: Box } | null>(null)\n\n\tconst isInRoot = useValue(\n\t\t'is in root',\n\t\t() => {\n\t\t\tconst shape = editor.getShape(id)\n\t\t\treturn shape?.parentId === editor.getCurrentPageId()\n\t\t},\n\t\t[editor, id]\n\t)\n\n\tuseEffect(() => {\n\t\tif (!isInRoot) return\n\n\t\tlet latest = null\n\t\tconst unsubscribe = react('shape to svg', async () => {\n\t\t\tconst renderId = Math.random()\n\t\t\tlatest = renderId\n\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tconst isSingleFrame = !!shape && editor.isShapeFrameLike(shape)\n\t\t\tconst padding = isSingleFrame ? 0 : 10\n\t\t\tlet bounds = editor.getShapePageBounds(id)\n\t\t\tif (!bounds) return\n\t\t\tbounds = bounds.clone().expandBy(padding)\n\n\t\t\tconst result = await editor.getSvgString([id], { padding })\n\n\t\t\tif (latest !== renderId || !result) return\n\n\t\t\tconst svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(result.svg)}`\n\t\t\tsetImage({ src: svgDataUrl, bounds })\n\t\t})\n\n\t\treturn () => {\n\t\t\tlatest = null\n\t\t\tunsubscribe()\n\t\t}\n\t}, [editor, id, isInRoot])\n\n\tif (!isInRoot || !image) return null\n\n\tif (mode === 'iframe') {\n\t\treturn (\n\t\t\t<iframe\n\t\t\t\tsrc={image.src}\n\t\t\t\twidth={image.bounds.width}\n\t\t\t\theight={image.bounds.height}\n\t\t\t\treferrerPolicy=\"no-referrer\"\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: 'absolute',\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tleft: 0,\n\t\t\t\t\tborder: 'none',\n\t\t\t\t\ttransform: `translate(${image.bounds.x}px, ${image.bounds.maxY + 12}px)`,\n\t\t\t\t\toutline: '1px solid black',\n\t\t\t\t\tmaxWidth: 'none',\n\t\t\t\t}}\n\t\t\t/>\n\t\t)\n\t}\n\treturn (\n\t\t<img\n\t\t\tsrc={image.src}\n\t\t\twidth={image.bounds.width}\n\t\t\theight={image.bounds.height}\n\t\t\treferrerPolicy=\"no-referrer\"\n\t\t\tstyle={{\n\t\t\t\tposition: 'absolute',\n\t\t\t\ttop: 0,\n\t\t\t\tleft: 0,\n\t\t\t\ttransform: `translate(${image.bounds.x}px, ${image.bounds.maxY + 12}px)`,\n\t\t\t\toutline: '1px solid black',\n\t\t\t\tmaxWidth: 'none',\n\t\t\t}}\n\t\t/>\n\t)\n}\n\nfunction OnTheCanvasWrapper() {\n\tconst { OnTheCanvas } = useEditorComponents()\n\tif (!OnTheCanvas) return null\n\treturn <OnTheCanvas />\n}\n\nfunction MovingCameraHitTestBlocker() {\n\tconst editor = useEditor()\n\tconst cameraState = useValue('camera state', () => editor.getCameraState(), [editor])\n\n\treturn (\n\t\t<div\n\t\t\tclassName={classNames('tl-hit-test-blocker', {\n\t\t\t\t'tl-hit-test-blocker__hidden': cameraState === 'idle',\n\t\t\t})}\n\t\t/>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["Fragment"]
7
7
  }
@@ -0,0 +1,29 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import classNames from "classnames";
3
+ import { memo, useRef } from "react";
4
+ import { useSharedSafeId } from "../../hooks/useSafeId.mjs";
5
+ import { useTransform } from "../../hooks/useTransform.mjs";
6
+ const DefaultCursor = memo(function DefaultCursor2({
7
+ className,
8
+ zoom,
9
+ point,
10
+ color,
11
+ name,
12
+ chatMessage
13
+ }) {
14
+ const rCursor = useRef(null);
15
+ useTransform(rCursor, point?.x, point?.y, 1 / zoom);
16
+ const cursorId = useSharedSafeId("cursor");
17
+ if (!point) return null;
18
+ return /* @__PURE__ */ jsxs("div", { ref: rCursor, className: classNames("tl-collaborators__item", className), children: [
19
+ /* @__PURE__ */ jsx("svg", { className: "tl-cursor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("use", { href: `#${cursorId}`, color }) }),
20
+ chatMessage ? /* @__PURE__ */ jsxs(Fragment, { children: [
21
+ name && /* @__PURE__ */ jsx("div", { className: "tl-nametag-title", style: { color }, children: name }),
22
+ /* @__PURE__ */ jsx("div", { className: "tl-nametag-chat", style: { backgroundColor: color }, children: chatMessage })
23
+ ] }) : name && /* @__PURE__ */ jsx("div", { className: "tl-nametag", style: { backgroundColor: color }, children: name })
24
+ ] });
25
+ });
26
+ export {
27
+ DefaultCursor
28
+ };
29
+ //# sourceMappingURL=DefaultCursor.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/lib/components/default-components/DefaultCursor.tsx"],
4
+ "sourcesContent": ["import { VecModel } from '@tldraw/tlschema'\nimport classNames from 'classnames'\nimport { memo, useRef } from 'react'\nimport { useSharedSafeId } from '../../hooks/useSafeId'\nimport { useTransform } from '../../hooks/useTransform'\n\n/** @public */\nexport interface TLCursorProps {\n\tuserId: string\n\tclassName?: string\n\tpoint: VecModel | null\n\tzoom: number\n\tcolor?: string\n\tname: string | null\n\tchatMessage: string\n}\n\n/**\n * The default collaborator cursor: the arrow glyph plus a name tag or chat bubble. The arrow is a\n * `<use>` of a shared `<defs>` symbol that `LiveCollaborators` renders, so a `DefaultCursor`\n * used outside that layer draws no arrow.\n *\n * @public @react\n */\nexport const DefaultCursor = memo(function DefaultCursor({\n\tclassName,\n\tzoom,\n\tpoint,\n\tcolor,\n\tname,\n\tchatMessage,\n}: TLCursorProps) {\n\tconst rCursor = useRef<HTMLDivElement>(null)\n\tuseTransform(rCursor, point?.x, point?.y, 1 / zoom)\n\n\tconst cursorId = useSharedSafeId('cursor')\n\n\tif (!point) return null\n\n\treturn (\n\t\t<div ref={rCursor} className={classNames('tl-collaborators__item', className)}>\n\t\t\t<svg className=\"tl-cursor\" aria-hidden=\"true\">\n\t\t\t\t<use href={`#${cursorId}`} color={color} />\n\t\t\t</svg>\n\t\t\t{chatMessage ? (\n\t\t\t\t<>\n\t\t\t\t\t{name && (\n\t\t\t\t\t\t<div className=\"tl-nametag-title\" style={{ color }}>\n\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"tl-nametag-chat\" style={{ backgroundColor: color }}>\n\t\t\t\t\t\t{chatMessage}\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\tname && (\n\t\t\t\t\t<div className=\"tl-nametag\" style={{ backgroundColor: color }}>\n\t\t\t\t\t\t{name}\n\t\t\t\t\t</div>\n\t\t\t\t)\n\t\t\t)}\n\t\t</div>\n\t)\n})\n"],
5
+ "mappings": "AA0CI,SAGA,UAHA,KAGA,YAHA;AAzCJ,OAAO,gBAAgB;AACvB,SAAS,MAAM,cAAc;AAC7B,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAoBtB,MAAM,gBAAgB,KAAK,SAASA,eAAc;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAkB;AACjB,QAAM,UAAU,OAAuB,IAAI;AAC3C,eAAa,SAAS,OAAO,GAAG,OAAO,GAAG,IAAI,IAAI;AAElD,QAAM,WAAW,gBAAgB,QAAQ;AAEzC,MAAI,CAAC,MAAO,QAAO;AAEnB,SACC,qBAAC,SAAI,KAAK,SAAS,WAAW,WAAW,0BAA0B,SAAS,GAC3E;AAAA,wBAAC,SAAI,WAAU,aAAY,eAAY,QACtC,8BAAC,SAAI,MAAM,IAAI,QAAQ,IAAI,OAAc,GAC1C;AAAA,IACC,cACA,iCACE;AAAA,cACA,oBAAC,SAAI,WAAU,oBAAmB,OAAO,EAAE,MAAM,GAC/C,gBACF;AAAA,MAED,oBAAC,SAAI,WAAU,mBAAkB,OAAO,EAAE,iBAAiB,MAAM,GAC/D,uBACF;AAAA,OACD,IAEA,QACC,oBAAC,SAAI,WAAU,cAAa,OAAO,EAAE,iBAAiB,MAAM,GAC1D,gBACF;AAAA,KAGH;AAEF,CAAC;",
6
+ "names": ["DefaultCursor"]
7
+ }
@@ -9,7 +9,7 @@ var __decorateClass = (decorators, target, key, kind) => {
9
9
  return result;
10
10
  };
11
11
  import { EMPTY_ARRAY, atom, computed } from "@tldraw/state";
12
- import { maxBy } from "@tldraw/utils";
12
+ import { areArraysShallowEqual, maxBy } from "@tldraw/utils";
13
13
  class CollaboratorsManager {
14
14
  constructor(editor) {
15
15
  this.editor = editor;
@@ -82,16 +82,16 @@ __decorateClass([
82
82
  computed
83
83
  ], CollaboratorsManager.prototype, "_getCollaboratorsQuery", 1);
84
84
  __decorateClass([
85
- computed
85
+ computed({ isEqual: areArraysShallowEqual })
86
86
  ], CollaboratorsManager.prototype, "getCollaborators", 1);
87
87
  __decorateClass([
88
- computed
88
+ computed({ isEqual: areArraysShallowEqual })
89
89
  ], CollaboratorsManager.prototype, "getCollaboratorsOnCurrentPage", 1);
90
90
  __decorateClass([
91
- computed
91
+ computed({ isEqual: areArraysShallowEqual })
92
92
  ], CollaboratorsManager.prototype, "getVisibleCollaborators", 1);
93
93
  __decorateClass([
94
- computed
94
+ computed({ isEqual: areArraysShallowEqual })
95
95
  ], CollaboratorsManager.prototype, "getVisibleCollaboratorsOnCurrentPage", 1);
96
96
  export {
97
97
  CollaboratorsManager
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts"],
4
- "sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport type { TLInstancePresence } from '@tldraw/tlschema'\nimport { maxBy } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * Tracks remote peers and exposes the collaborator-related queries used by the\n * editor and its overlays. Encapsulates the visibility clock that periodically\n * re-evaluates which collaborators should be visible based on activity.\n *\n * Accessed via {@link Editor.collaborators}.\n *\n * @public\n */\nexport class CollaboratorsManager {\n\tconstructor(private readonly editor: Editor) {}\n\n\tprivate _visibilityClockStarted = false\n\n\tprivate _startVisibilityClock() {\n\t\tif (this._visibilityClockStarted) return\n\t\tthis._visibilityClockStarted = true\n\n\t\t// Editor disposes `editor.timers` on its own teardown, so the interval is\n\t\t// automatically cleared when the editor is disposed.\n\t\tthis.editor.timers.setInterval(() => {\n\t\t\tthis._visibilityClock.set(Date.now())\n\t\t}, this.editor.options.collaboratorCheckIntervalMs)\n\t}\n\n\t/**\n\t * Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}.\n\t * Ticked on a fixed interval so callers don't need to manage their own activity timers.\n\t */\n\tprivate readonly _visibilityClock = atom('collaboratorVisibilityClock', Date.now())\n\n\t@computed\n\tprivate _getCollaboratorsQuery() {\n\t\treturn this.editor.store.query.records('instance_presence', () => ({\n\t\t\tuserId: { neq: this.editor.user.getRecordId() },\n\t\t}))\n\t}\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed\n\tgetCollaborators(): TLInstancePresence[] {\n\t\tconst allPresenceRecords = this._getCollaboratorsQuery().get()\n\t\tif (!allPresenceRecords.length) return EMPTY_ARRAY\n\t\tconst userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()\n\t\treturn userIds.map((id) => {\n\t\t\tconst latestPresence = maxBy(\n\t\t\t\tallPresenceRecords.filter((c) => c.userId === id),\n\t\t\t\t(p) => p.lastActivityTimestamp ?? 0\n\t\t\t)\n\t\t\treturn latestPresence!\n\t\t})\n\t}\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators on the current page.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed\n\tgetCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity\n\t * state (active / idle / inactive) and visibility rules such as following and\n\t * highlighted users. Re-evaluates on the visibility clock, so callers don't need to\n\t * drive their own activity timer.\n\t */\n\t@computed\n\tgetVisibleCollaborators(): TLInstancePresence[] {\n\t\tconst { editor } = this\n\t\tconst { collaboratorInactiveTimeoutMs, collaboratorIdleTimeoutMs } = editor.options\n\n\t\tthis._startVisibilityClock()\n\t\tthis._visibilityClock.get()\n\t\tconst now = Date.now()\n\t\tconst collaborators = this.getCollaborators()\n\t\tif (!collaborators.length) return EMPTY_ARRAY\n\n\t\tconst { followingUserId, highlightedUserIds } = this.editor.getInstanceState()\n\t\tconst currentUserId = this.editor.user.getRecordId()\n\n\t\treturn collaborators.filter((presence) => {\n\t\t\tconst { lastActivityTimestamp, userId, chatMessage } = presence\n\n\t\t\t// Treat a missing or zero `lastActivityTimestamp` as \"active right now\"\n\t\t\t// (elapsed = 0) so newly-joined peers aren't immediately classified as\n\t\t\t// idle/inactive. The broadcast default for peers who haven't moved their\n\t\t\t// pointer yet is `0` (e.g. someone on a touch device who joins and just\n\t\t\t// watches), so a plain `?? now` would leave them hidden. See issue #9017.\n\t\t\tconst elapsed = lastActivityTimestamp ? Math.max(0, now - lastActivityTimestamp) : 0\n\n\t\t\tif (elapsed > collaboratorInactiveTimeoutMs) {\n\t\t\t\t// Inactive: If they're inactive, only show if we're following them or they're highlighted\n\t\t\t\treturn followingUserId === userId || highlightedUserIds.includes(userId)\n\t\t\t}\n\n\t\t\tif (elapsed > collaboratorIdleTimeoutMs) {\n\t\t\t\t// Idle: If they're idle and following us, hide them unless they have a chat message or are highlighted\n\t\t\t\tif (presence.followingUserId === currentUserId) {\n\t\t\t\t\treturn !!(chatMessage || highlightedUserIds.includes(userId))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Active\n\t\t\treturn true\n\t\t})\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI, filtered to those on the current page.\n\t */\n\t@computed\n\tgetVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getVisibleCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;AAAA,SAAS,aAAa,MAAM,gBAAgB;AAE5C,SAAS,aAAa;AAYf,MAAM,qBAAqB;AAAA,EACjC,YAA6B,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EAErB,0BAA0B;AAAA,EAE1B,wBAAwB;AAC/B,QAAI,KAAK,wBAAyB;AAClC,SAAK,0BAA0B;AAI/B,SAAK,OAAO,OAAO,YAAY,MAAM;AACpC,WAAK,iBAAiB,IAAI,KAAK,IAAI,CAAC;AAAA,IACrC,GAAG,KAAK,OAAO,QAAQ,2BAA2B;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMiB,mBAAmB,KAAK,+BAA+B,KAAK,IAAI,CAAC;AAAA,EAG1E,yBAAyB;AAChC,WAAO,KAAK,OAAO,MAAM,MAAM,QAAQ,qBAAqB,OAAO;AAAA,MAClE,QAAQ,EAAE,KAAK,KAAK,OAAO,KAAK,YAAY,EAAE;AAAA,IAC/C,EAAE;AAAA,EACH;AAAA,EAOA,mBAAyC;AACxC,UAAM,qBAAqB,KAAK,uBAAuB,EAAE,IAAI;AAC7D,QAAI,CAAC,mBAAmB,OAAQ,QAAO;AACvC,UAAM,UAAU,CAAC,GAAG,IAAI,IAAI,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK;AAC3E,WAAO,QAAQ,IAAI,CAAC,OAAO;AAC1B,YAAM,iBAAiB;AAAA,QACtB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE;AAAA,QAChD,CAAC,MAAM,EAAE,yBAAyB;AAAA,MACnC;AACA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,gCAAsD;AACrD,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,iBAAiB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EAC/E;AAAA,EAUA,0BAAgD;AAC/C,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,EAAE,+BAA+B,0BAA0B,IAAI,OAAO;AAE5E,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB,IAAI;AAC1B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,KAAK,iBAAiB;AAC5C,QAAI,CAAC,cAAc,OAAQ,QAAO;AAElC,UAAM,EAAE,iBAAiB,mBAAmB,IAAI,KAAK,OAAO,iBAAiB;AAC7E,UAAM,gBAAgB,KAAK,OAAO,KAAK,YAAY;AAEnD,WAAO,cAAc,OAAO,CAAC,aAAa;AACzC,YAAM,EAAE,uBAAuB,QAAQ,YAAY,IAAI;AAOvD,YAAM,UAAU,wBAAwB,KAAK,IAAI,GAAG,MAAM,qBAAqB,IAAI;AAEnF,UAAI,UAAU,+BAA+B;AAE5C,eAAO,oBAAoB,UAAU,mBAAmB,SAAS,MAAM;AAAA,MACxE;AAEA,UAAI,UAAU,2BAA2B;AAExC,YAAI,SAAS,oBAAoB,eAAe;AAC/C,iBAAO,CAAC,EAAE,eAAe,mBAAmB,SAAS,MAAM;AAAA,QAC5D;AAAA,MACD;AAGA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,uCAA6D;AAC5D,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,wBAAwB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EACtF;AACD;AA3FS;AAAA,EADP;AAAA,GAtBW,qBAuBJ;AAWR;AAAA,EADC;AAAA,GAjCW,qBAkCZ;AAkBA;AAAA,EADC;AAAA,GAnDW,qBAoDZ;AAaA;AAAA,EADC;AAAA,GAhEW,qBAiEZ;AA6CA;AAAA,EADC;AAAA,GA7GW,qBA8GZ;",
4
+ "sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport type { TLInstancePresence } from '@tldraw/tlschema'\nimport { areArraysShallowEqual, maxBy } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * Tracks remote peers and exposes the collaborator-related queries used by the\n * editor and its overlays. Encapsulates the visibility clock that periodically\n * re-evaluates which collaborators should be visible based on activity.\n *\n * Accessed via {@link Editor.collaborators}.\n *\n * @public\n */\nexport class CollaboratorsManager {\n\tconstructor(private readonly editor: Editor) {}\n\n\tprivate _visibilityClockStarted = false\n\n\tprivate _startVisibilityClock() {\n\t\tif (this._visibilityClockStarted) return\n\t\tthis._visibilityClockStarted = true\n\n\t\t// Editor disposes `editor.timers` on its own teardown, so the interval is\n\t\t// automatically cleared when the editor is disposed.\n\t\tthis.editor.timers.setInterval(() => {\n\t\t\tthis._visibilityClock.set(Date.now())\n\t\t}, this.editor.options.collaboratorCheckIntervalMs)\n\t}\n\n\t/**\n\t * Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}.\n\t * Ticked on a fixed interval so callers don't need to manage their own activity timers.\n\t */\n\tprivate readonly _visibilityClock = atom('collaboratorVisibilityClock', Date.now())\n\n\t@computed\n\tprivate _getCollaboratorsQuery() {\n\t\treturn this.editor.store.query.records('instance_presence', () => ({\n\t\t\tuserId: { neq: this.editor.user.getRecordId() },\n\t\t}))\n\t}\n\n\t// These queries all derive fresh arrays with map/filter, so they compare results with\n\t// shallow (element-identity) equality: a presence update that leaves a derived list's\n\t// elements unchanged \u2014 e.g. a peer moving on another page \u2014 keeps the previous array's\n\t// identity and doesn't invalidate downstream subscribers such as the cursor layer.\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetCollaborators(): TLInstancePresence[] {\n\t\tconst allPresenceRecords = this._getCollaboratorsQuery().get()\n\t\tif (!allPresenceRecords.length) return EMPTY_ARRAY\n\t\tconst userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()\n\t\treturn userIds.map((id) => {\n\t\t\tconst latestPresence = maxBy(\n\t\t\t\tallPresenceRecords.filter((c) => c.userId === id),\n\t\t\t\t(p) => p.lastActivityTimestamp ?? 0\n\t\t\t)\n\t\t\treturn latestPresence!\n\t\t})\n\t}\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators on the current page.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity\n\t * state (active / idle / inactive) and visibility rules such as following and\n\t * highlighted users. Re-evaluates on the visibility clock, so callers don't need to\n\t * drive their own activity timer.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetVisibleCollaborators(): TLInstancePresence[] {\n\t\tconst { editor } = this\n\t\tconst { collaboratorInactiveTimeoutMs, collaboratorIdleTimeoutMs } = editor.options\n\n\t\tthis._startVisibilityClock()\n\t\tthis._visibilityClock.get()\n\t\tconst now = Date.now()\n\t\tconst collaborators = this.getCollaborators()\n\t\tif (!collaborators.length) return EMPTY_ARRAY\n\n\t\tconst { followingUserId, highlightedUserIds } = this.editor.getInstanceState()\n\t\tconst currentUserId = this.editor.user.getRecordId()\n\n\t\treturn collaborators.filter((presence) => {\n\t\t\tconst { lastActivityTimestamp, userId, chatMessage } = presence\n\n\t\t\t// Treat a missing or zero `lastActivityTimestamp` as \"active right now\"\n\t\t\t// (elapsed = 0) so newly-joined peers aren't immediately classified as\n\t\t\t// idle/inactive. The broadcast default for peers who haven't moved their\n\t\t\t// pointer yet is `0` (e.g. someone on a touch device who joins and just\n\t\t\t// watches), so a plain `?? now` would leave them hidden. See issue #9017.\n\t\t\tconst elapsed = lastActivityTimestamp ? Math.max(0, now - lastActivityTimestamp) : 0\n\n\t\t\tif (elapsed > collaboratorInactiveTimeoutMs) {\n\t\t\t\t// Inactive: If they're inactive, only show if we're following them or they're highlighted\n\t\t\t\treturn followingUserId === userId || highlightedUserIds.includes(userId)\n\t\t\t}\n\n\t\t\tif (elapsed > collaboratorIdleTimeoutMs) {\n\t\t\t\t// Idle: If they're idle and following us, hide them unless they have a chat message or are highlighted\n\t\t\t\tif (presence.followingUserId === currentUserId) {\n\t\t\t\t\treturn !!(chatMessage || highlightedUserIds.includes(userId))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Active\n\t\t\treturn true\n\t\t})\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI, filtered to those on the current page.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getVisibleCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/EditorComponentsContext.tsx"],
4
- "sourcesContent": ["import { ComponentType, RefAttributes, createContext, useContext } from 'react'\nimport type { TLCanvasComponentProps } from '../components/default-components/DefaultCanvas'\nimport type { TLErrorFallbackComponent } from '../components/default-components/DefaultErrorFallback'\nimport type { TLGridProps } from '../components/default-components/DefaultGrid'\nimport type { TLShapeErrorFallbackComponent } from '../components/default-components/DefaultShapeErrorFallback'\nimport type { TLShapeWrapperProps } from '../components/default-components/DefaultShapeWrapper'\n\n/** @public */\nexport interface TLEditorComponents {\n\tBackground?: ComponentType | null\n\tCanvas?: ComponentType<TLCanvasComponentProps> | null\n\tGrid?: ComponentType<TLGridProps> | null\n\tInFrontOfTheCanvas?: ComponentType | null\n\tLoadingScreen?: ComponentType | null\n\tOnTheCanvas?: ComponentType | null\n\tShapeWrapper?: ComponentType<TLShapeWrapperProps & RefAttributes<HTMLDivElement>> | null\n\tSpinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null\n\tSvgDefs?: ComponentType | null\n\n\t// These will always have defaults\n\tErrorFallback?: TLErrorFallbackComponent\n\tShapeErrorFallback?: TLShapeErrorFallbackComponent\n}\n\nexport const EditorComponentsContext = createContext<null | Required<TLEditorComponents>>(null)\n\n/** @public */\nexport function useEditorComponents() {\n\tconst components = useContext(EditorComponentsContext)\n\tif (!components) {\n\t\tthrow new Error('useEditorComponents must be used inside of <EditorComponentsProvider />')\n\t}\n\treturn components\n}\n"],
5
- "mappings": "AAAA,SAAuC,eAAe,kBAAkB;AAwBjE,MAAM,0BAA0B,cAAmD,IAAI;AAGvF,SAAS,sBAAsB;AACrC,QAAM,aAAa,WAAW,uBAAuB;AACrD,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,MAAM,yEAAyE;AAAA,EAC1F;AACA,SAAO;AACR;",
4
+ "sourcesContent": ["import { ComponentType, RefAttributes, createContext, useContext } from 'react'\nimport type { TLCanvasComponentProps } from '../components/default-components/DefaultCanvas'\nimport type { TLCursorProps } from '../components/default-components/DefaultCursor'\nimport type { TLErrorFallbackComponent } from '../components/default-components/DefaultErrorFallback'\nimport type { TLGridProps } from '../components/default-components/DefaultGrid'\nimport type { TLShapeErrorFallbackComponent } from '../components/default-components/DefaultShapeErrorFallback'\nimport type { TLShapeWrapperProps } from '../components/default-components/DefaultShapeWrapper'\n\n/** @public */\nexport interface TLEditorComponents {\n\tBackground?: ComponentType | null\n\tCanvas?: ComponentType<TLCanvasComponentProps> | null\n\tCollaboratorCursor?: ComponentType<TLCursorProps> | null\n\tGrid?: ComponentType<TLGridProps> | null\n\tInFrontOfTheCanvas?: ComponentType | null\n\tLoadingScreen?: ComponentType | null\n\tOnTheCanvas?: ComponentType | null\n\tShapeWrapper?: ComponentType<TLShapeWrapperProps & RefAttributes<HTMLDivElement>> | null\n\tSpinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null\n\tSvgDefs?: ComponentType | null\n\n\t// These will always have defaults\n\tErrorFallback?: TLErrorFallbackComponent\n\tShapeErrorFallback?: TLShapeErrorFallbackComponent\n}\n\nexport const EditorComponentsContext = createContext<null | Required<TLEditorComponents>>(null)\n\n/** @public */\nexport function useEditorComponents() {\n\tconst components = useContext(EditorComponentsContext)\n\tif (!components) {\n\t\tthrow new Error('useEditorComponents must be used inside of <EditorComponentsProvider />')\n\t}\n\treturn components\n}\n"],
5
+ "mappings": "AAAA,SAAuC,eAAe,kBAAkB;AA0BjE,MAAM,0BAA0B,cAAmD,IAAI;AAGvF,SAAS,sBAAsB;AACrC,QAAM,aAAa,WAAW,uBAAuB;AACrD,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,MAAM,yEAAyE;AAAA,EAC1F;AACA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,7 @@ const STATIC_CURSORS = [
17
17
  "default",
18
18
  "pointer",
19
19
  "cross",
20
+ "comment",
20
21
  "move",
21
22
  "grab",
22
23
  "grabbing",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useCursor.ts"],
4
- "sourcesContent": ["import { useQuickReactor } from '@tldraw/state-react'\nimport { TLCursorType } from '@tldraw/tlschema'\nimport { PI, radiansToDegrees } from '../primitives/utils'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nconst CORNER_SVG = `<path d='m19.7432 17.0869-4.072 4.068 2.829 2.828-8.473-.013-.013-8.47 2.841 2.842 4.075-4.068 1.414-1.415-2.844-2.842h8.486v8.484l-2.83-2.827z' fill='%23fff'/><path d='m18.6826 16.7334-4.427 4.424 1.828 1.828-5.056-.016-.014-5.054 1.842 1.841 4.428-4.422 2.474-2.475-1.844-1.843h5.073v5.071l-1.83-1.828z' fill='%23000'/>`\nconst EDGE_SVG = `<path d='m9 17.9907v.005l5.997 5.996.001-3.999h1.999 2.02v4l5.98-6.001-5.98-5.999.001 4.019-2.021.002h-2l.001-4.022zm1.411.003 3.587-3.588-.001 2.587h3.5 2.521v-2.585l3.565 3.586-3.564 3.585-.001-2.585h-2.521l-3.499-.001-.001 2.586z' fill='%23fff'/><path d='m17.4971 18.9932h2.521v2.586l3.565-3.586-3.565-3.585v2.605h-2.521-3.5v-2.607l-3.586 3.587 3.586 3.586v-2.587z' fill='%23000'/>`\nconst ROTATE_CORNER_SVG = `<path d=\"M22.4789 9.45728L25.9935 12.9942L22.4789 16.5283V14.1032C18.126 14.1502 14.6071 17.6737 14.5675 22.0283H17.05L13.513 25.543L9.97889 22.0283H12.5674C12.6071 16.5691 17.0214 12.1503 22.4789 12.1031L22.4789 9.45728Z\" fill=\"black\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M21.4789 7.03223L27.4035 12.9945L21.4789 18.9521V15.1868C18.4798 15.6549 16.1113 18.0273 15.649 21.0284H19.475L13.5128 26.953L7.55519 21.0284H11.6189C12.1243 15.8155 16.2679 11.6677 21.4789 11.1559L21.4789 7.03223ZM22.4789 12.1031C17.0214 12.1503 12.6071 16.5691 12.5674 22.0284H9.97889L13.513 25.543L17.05 22.0284H14.5675C14.5705 21.6896 14.5947 21.3558 14.6386 21.0284C15.1157 17.4741 17.9266 14.6592 21.4789 14.1761C21.8063 14.1316 22.1401 14.1069 22.4789 14.1032V16.5284L25.9935 12.9942L22.4789 9.45729L22.4789 12.1031Z\" fill=\"white\"/>`\n\nfunction getCursorCss(\n\tsvg: string,\n\tr: number,\n\ttr: number,\n\tf: boolean,\n\tcolor: string,\n\thotspotX = 16,\n\thotspotY = 16\n) {\n\tconst a = (-tr - r) * (PI / 180)\n\tconst s = Math.sin(a)\n\tconst c = Math.cos(a)\n\tconst dx = 1 * c - 1 * s\n\tconst dy = 1 * s + 1 * c\n\n\treturn (\n\t\t`url(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: ${color};'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='${dx}' dy='${dy}' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(${\n\t\t\tr + tr\n\t\t} 16 16)${f ? ` scale(-1,-1) translate(0, -32)` : ''}' filter='url(%23shadow)'>` +\n\t\tsvg.replaceAll(`\"`, `'`) +\n\t\t`</g></svg>\") ${hotspotX} ${hotspotY}, pointer`\n\t)\n}\n\nconst STATIC_CURSORS = [\n\t'default',\n\t'pointer',\n\t'cross',\n\t'move',\n\t'grab',\n\t'grabbing',\n\t'text',\n\t'zoom-in',\n\t'zoom-out',\n]\n\ntype CursorFunction = (rotation: number, flip: boolean, color: string) => string\n\nconst CURSORS: Record<TLCursorType, CursorFunction> = {\n\tnone: () => 'none',\n\t'ew-resize': (r, f, c) => getCursorCss(EDGE_SVG, r, 0, f, c),\n\t'ns-resize': (r, f, c) => getCursorCss(EDGE_SVG, r, 90, f, c),\n\t'nesw-resize': (r, f, c) => getCursorCss(CORNER_SVG, r, 0, f, c),\n\t'nwse-resize': (r, f, c) => getCursorCss(CORNER_SVG, r, 90, f, c),\n\t'nwse-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 0, f, c),\n\t'nesw-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 90, f, c),\n\t'senw-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 180, f, c),\n\t'swne-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 270, f, c),\n}\n\n/** @public */\nexport function getCursor(cursor: TLCursorType, rotation = 0, color = 'black') {\n\treturn CURSORS[cursor](radiansToDegrees(rotation), false, color)\n}\n\nexport function useCursor() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tuseQuickReactor(\n\t\t'useCursor',\n\t\t() => {\n\t\t\tconst { type, rotation } = editor.getInstanceState().cursor\n\n\t\t\tif (STATIC_CURSORS.includes(type)) {\n\t\t\t\tcontainer.style.setProperty('--tl-cursor', `var(--tl-cursor-${type})`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst { cursor } = editor.getCurrentTheme().colors[editor.getColorMode()]\n\t\t\tcontainer.style.setProperty('--tl-cursor', getCursor(type, rotation, cursor))\n\t\t},\n\t\t[editor, container]\n\t)\n}\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;AAEhC,SAAS,IAAI,wBAAwB;AACrC,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAE1B,MAAM,aAAa;AACnB,MAAM,WAAW;AACjB,MAAM,oBAAoB;AAE1B,SAAS,aACR,KACA,GACA,IACA,GACA,OACA,WAAW,IACX,WAAW,IACV;AACD,QAAM,KAAK,CAAC,KAAK,MAAM,KAAK;AAC5B,QAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAM,KAAK,IAAI,IAAI,IAAI;AACvB,QAAM,KAAK,IAAI,IAAI,IAAI;AAEvB,SACC,4HAA4H,KAAK,mIAAmI,EAAE,SAAS,EAAE,6FAChR,IAAI,EACL,UAAU,IAAI,oCAAoC,EAAE,+BACpD,IAAI,WAAW,KAAK,GAAG,IACvB,gBAAgB,QAAQ,IAAI,QAAQ;AAEtC;AAEA,MAAM,iBAAiB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAIA,MAAM,UAAgD;AAAA,EACrD,MAAM,MAAM;AAAA,EACZ,aAAa,CAAC,GAAG,GAAG,MAAM,aAAa,UAAU,GAAG,GAAG,GAAG,CAAC;AAAA,EAC3D,aAAa,CAAC,GAAG,GAAG,MAAM,aAAa,UAAU,GAAG,IAAI,GAAG,CAAC;AAAA,EAC5D,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,YAAY,GAAG,GAAG,GAAG,CAAC;AAAA,EAC/D,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,YAAY,GAAG,IAAI,GAAG,CAAC;AAAA,EAChE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,GAAG,GAAG,CAAC;AAAA,EACtE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,IAAI,GAAG,CAAC;AAAA,EACvE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,KAAK,GAAG,CAAC;AAAA,EACxE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,KAAK,GAAG,CAAC;AACzE;AAGO,SAAS,UAAU,QAAsB,WAAW,GAAG,QAAQ,SAAS;AAC9E,SAAO,QAAQ,MAAM,EAAE,iBAAiB,QAAQ,GAAG,OAAO,KAAK;AAChE;AAEO,SAAS,YAAY;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,aAAa;AAE/B;AAAA,IACC;AAAA,IACA,MAAM;AACL,YAAM,EAAE,MAAM,SAAS,IAAI,OAAO,iBAAiB,EAAE;AAErD,UAAI,eAAe,SAAS,IAAI,GAAG;AAClC,kBAAU,MAAM,YAAY,eAAe,mBAAmB,IAAI,GAAG;AACrE;AAAA,MACD;AAEA,YAAM,EAAE,OAAO,IAAI,OAAO,gBAAgB,EAAE,OAAO,OAAO,aAAa,CAAC;AACxE,gBAAU,MAAM,YAAY,eAAe,UAAU,MAAM,UAAU,MAAM,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,QAAQ,SAAS;AAAA,EACnB;AACD;",
4
+ "sourcesContent": ["import { useQuickReactor } from '@tldraw/state-react'\nimport { TLCursorType } from '@tldraw/tlschema'\nimport { PI, radiansToDegrees } from '../primitives/utils'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nconst CORNER_SVG = `<path d='m19.7432 17.0869-4.072 4.068 2.829 2.828-8.473-.013-.013-8.47 2.841 2.842 4.075-4.068 1.414-1.415-2.844-2.842h8.486v8.484l-2.83-2.827z' fill='%23fff'/><path d='m18.6826 16.7334-4.427 4.424 1.828 1.828-5.056-.016-.014-5.054 1.842 1.841 4.428-4.422 2.474-2.475-1.844-1.843h5.073v5.071l-1.83-1.828z' fill='%23000'/>`\nconst EDGE_SVG = `<path d='m9 17.9907v.005l5.997 5.996.001-3.999h1.999 2.02v4l5.98-6.001-5.98-5.999.001 4.019-2.021.002h-2l.001-4.022zm1.411.003 3.587-3.588-.001 2.587h3.5 2.521v-2.585l3.565 3.586-3.564 3.585-.001-2.585h-2.521l-3.499-.001-.001 2.586z' fill='%23fff'/><path d='m17.4971 18.9932h2.521v2.586l3.565-3.586-3.565-3.585v2.605h-2.521-3.5v-2.607l-3.586 3.587 3.586 3.586v-2.587z' fill='%23000'/>`\nconst ROTATE_CORNER_SVG = `<path d=\"M22.4789 9.45728L25.9935 12.9942L22.4789 16.5283V14.1032C18.126 14.1502 14.6071 17.6737 14.5675 22.0283H17.05L13.513 25.543L9.97889 22.0283H12.5674C12.6071 16.5691 17.0214 12.1503 22.4789 12.1031L22.4789 9.45728Z\" fill=\"black\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M21.4789 7.03223L27.4035 12.9945L21.4789 18.9521V15.1868C18.4798 15.6549 16.1113 18.0273 15.649 21.0284H19.475L13.5128 26.953L7.55519 21.0284H11.6189C12.1243 15.8155 16.2679 11.6677 21.4789 11.1559L21.4789 7.03223ZM22.4789 12.1031C17.0214 12.1503 12.6071 16.5691 12.5674 22.0284H9.97889L13.513 25.543L17.05 22.0284H14.5675C14.5705 21.6896 14.5947 21.3558 14.6386 21.0284C15.1157 17.4741 17.9266 14.6592 21.4789 14.1761C21.8063 14.1316 22.1401 14.1069 22.4789 14.1032V16.5284L25.9935 12.9942L22.4789 9.45729L22.4789 12.1031Z\" fill=\"white\"/>`\n\nfunction getCursorCss(\n\tsvg: string,\n\tr: number,\n\ttr: number,\n\tf: boolean,\n\tcolor: string,\n\thotspotX = 16,\n\thotspotY = 16\n) {\n\tconst a = (-tr - r) * (PI / 180)\n\tconst s = Math.sin(a)\n\tconst c = Math.cos(a)\n\tconst dx = 1 * c - 1 * s\n\tconst dy = 1 * s + 1 * c\n\n\treturn (\n\t\t`url(\"data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: ${color};'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='${dx}' dy='${dy}' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(${\n\t\t\tr + tr\n\t\t} 16 16)${f ? ` scale(-1,-1) translate(0, -32)` : ''}' filter='url(%23shadow)'>` +\n\t\tsvg.replaceAll(`\"`, `'`) +\n\t\t`</g></svg>\") ${hotspotX} ${hotspotY}, pointer`\n\t)\n}\n\nconst STATIC_CURSORS = [\n\t'default',\n\t'pointer',\n\t'cross',\n\t'comment',\n\t'move',\n\t'grab',\n\t'grabbing',\n\t'text',\n\t'zoom-in',\n\t'zoom-out',\n]\n\ntype CursorFunction = (rotation: number, flip: boolean, color: string) => string\n\nconst CURSORS: Record<TLCursorType, CursorFunction> = {\n\tnone: () => 'none',\n\t'ew-resize': (r, f, c) => getCursorCss(EDGE_SVG, r, 0, f, c),\n\t'ns-resize': (r, f, c) => getCursorCss(EDGE_SVG, r, 90, f, c),\n\t'nesw-resize': (r, f, c) => getCursorCss(CORNER_SVG, r, 0, f, c),\n\t'nwse-resize': (r, f, c) => getCursorCss(CORNER_SVG, r, 90, f, c),\n\t'nwse-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 0, f, c),\n\t'nesw-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 90, f, c),\n\t'senw-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 180, f, c),\n\t'swne-rotate': (r, f, c) => getCursorCss(ROTATE_CORNER_SVG, r, 270, f, c),\n}\n\n/** @public */\nexport function getCursor(cursor: TLCursorType, rotation = 0, color = 'black') {\n\treturn CURSORS[cursor](radiansToDegrees(rotation), false, color)\n}\n\nexport function useCursor() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tuseQuickReactor(\n\t\t'useCursor',\n\t\t() => {\n\t\t\tconst { type, rotation } = editor.getInstanceState().cursor\n\n\t\t\tif (STATIC_CURSORS.includes(type)) {\n\t\t\t\tcontainer.style.setProperty('--tl-cursor', `var(--tl-cursor-${type})`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst { cursor } = editor.getCurrentTheme().colors[editor.getColorMode()]\n\t\t\tcontainer.style.setProperty('--tl-cursor', getCursor(type, rotation, cursor))\n\t\t},\n\t\t[editor, container]\n\t)\n}\n"],
5
+ "mappings": "AAAA,SAAS,uBAAuB;AAEhC,SAAS,IAAI,wBAAwB;AACrC,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAE1B,MAAM,aAAa;AACnB,MAAM,WAAW;AACjB,MAAM,oBAAoB;AAE1B,SAAS,aACR,KACA,GACA,IACA,GACA,OACA,WAAW,IACX,WAAW,IACV;AACD,QAAM,KAAK,CAAC,KAAK,MAAM,KAAK;AAC5B,QAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAM,KAAK,IAAI,IAAI,IAAI;AACvB,QAAM,KAAK,IAAI,IAAI,IAAI;AAEvB,SACC,4HAA4H,KAAK,mIAAmI,EAAE,SAAS,EAAE,6FAChR,IAAI,EACL,UAAU,IAAI,oCAAoC,EAAE,+BACpD,IAAI,WAAW,KAAK,GAAG,IACvB,gBAAgB,QAAQ,IAAI,QAAQ;AAEtC;AAEA,MAAM,iBAAiB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAIA,MAAM,UAAgD;AAAA,EACrD,MAAM,MAAM;AAAA,EACZ,aAAa,CAAC,GAAG,GAAG,MAAM,aAAa,UAAU,GAAG,GAAG,GAAG,CAAC;AAAA,EAC3D,aAAa,CAAC,GAAG,GAAG,MAAM,aAAa,UAAU,GAAG,IAAI,GAAG,CAAC;AAAA,EAC5D,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,YAAY,GAAG,GAAG,GAAG,CAAC;AAAA,EAC/D,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,YAAY,GAAG,IAAI,GAAG,CAAC;AAAA,EAChE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,GAAG,GAAG,CAAC;AAAA,EACtE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,IAAI,GAAG,CAAC;AAAA,EACvE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,KAAK,GAAG,CAAC;AAAA,EACxE,eAAe,CAAC,GAAG,GAAG,MAAM,aAAa,mBAAmB,GAAG,KAAK,GAAG,CAAC;AACzE;AAGO,SAAS,UAAU,QAAsB,WAAW,GAAG,QAAQ,SAAS;AAC9E,SAAO,QAAQ,MAAM,EAAE,iBAAiB,QAAQ,GAAG,OAAO,KAAK;AAChE;AAEO,SAAS,YAAY;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,aAAa;AAE/B;AAAA,IACC;AAAA,IACA,MAAM;AACL,YAAM,EAAE,MAAM,SAAS,IAAI,OAAO,iBAAiB,EAAE;AAErD,UAAI,eAAe,SAAS,IAAI,GAAG;AAClC,kBAAU,MAAM,YAAY,eAAe,mBAAmB,IAAI,GAAG;AACrE;AAAA,MACD;AAEA,YAAM,EAAE,OAAO,IAAI,OAAO,gBAAgB,EAAE,OAAO,OAAO,aAAa,CAAC;AACxE,gBAAU,MAAM,YAAY,eAAe,UAAU,MAAM,UAAU,MAAM,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,QAAQ,SAAS;AAAA,EACnB;AACD;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
3
  import { DefaultBackground } from "../components/default-components/DefaultBackground.mjs";
4
4
  import { DefaultCanvas } from "../components/default-components/DefaultCanvas.mjs";
5
+ import { DefaultCursor } from "../components/default-components/DefaultCursor.mjs";
5
6
  import { DefaultErrorFallback } from "../components/default-components/DefaultErrorFallback.mjs";
6
7
  import { DefaultGrid } from "../components/default-components/DefaultGrid.mjs";
7
8
  import { DefaultLoadingScreen } from "../components/default-components/DefaultLoadingScreen.mjs";
@@ -21,6 +22,7 @@ function EditorComponentsProvider({
21
22
  () => ({
22
23
  Background: DefaultBackground,
23
24
  Canvas: DefaultCanvas,
25
+ CollaboratorCursor: DefaultCursor,
24
26
  Grid: DefaultGrid,
25
27
  InFrontOfTheCanvas: null,
26
28
  LoadingScreen: DefaultLoadingScreen,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useEditorComponents.tsx"],
4
- "sourcesContent": ["import { ReactNode, useMemo } from 'react'\nimport { DefaultBackground } from '../components/default-components/DefaultBackground'\nimport { DefaultCanvas } from '../components/default-components/DefaultCanvas'\nimport { DefaultErrorFallback } from '../components/default-components/DefaultErrorFallback'\nimport { DefaultGrid } from '../components/default-components/DefaultGrid'\nimport { DefaultLoadingScreen } from '../components/default-components/DefaultLoadingScreen'\nimport { DefaultShapeErrorFallback } from '../components/default-components/DefaultShapeErrorFallback'\nimport { DefaultShapeWrapper } from '../components/default-components/DefaultShapeWrapper'\nimport { DefaultSpinner } from '../components/default-components/DefaultSpinner'\nimport { DefaultSvgDefs } from '../components/default-components/DefaultSvgDefs'\nimport { EditorComponentsContext } from './EditorComponentsContext'\nimport type { TLEditorComponents } from './EditorComponentsContext'\nimport { useShallowObjectIdentity } from './useIdentity'\n\nexport { useEditorComponents } from './EditorComponentsContext'\nexport type { TLEditorComponents } from './EditorComponentsContext'\n\ninterface ComponentsContextProviderProps {\n\toverrides?: TLEditorComponents\n\tchildren: ReactNode\n}\n\nexport function EditorComponentsProvider({\n\toverrides = {},\n\tchildren,\n}: ComponentsContextProviderProps) {\n\tconst _overrides = useShallowObjectIdentity(overrides)\n\tconst value = useMemo(\n\t\t(): Required<TLEditorComponents> => ({\n\t\t\tBackground: DefaultBackground,\n\t\t\tCanvas: DefaultCanvas,\n\t\t\tGrid: DefaultGrid,\n\t\t\tInFrontOfTheCanvas: null,\n\t\t\tLoadingScreen: DefaultLoadingScreen,\n\t\t\tOnTheCanvas: null,\n\t\t\tShapeWrapper: DefaultShapeWrapper,\n\t\t\tSpinner: DefaultSpinner,\n\t\t\tSvgDefs: DefaultSvgDefs,\n\n\t\t\tErrorFallback: DefaultErrorFallback,\n\t\t\tShapeErrorFallback: DefaultShapeErrorFallback,\n\n\t\t\t..._overrides,\n\t\t}),\n\t\t[_overrides]\n\t)\n\n\treturn (\n\t\t<EditorComponentsContext.Provider value={value}>{children}</EditorComponentsContext.Provider>\n\t)\n}\n"],
5
- "mappings": "AAgDE;AAhDF,SAAoB,eAAe;AACnC,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,+BAA+B;AAExC,SAAS,gCAAgC;AAEzC,SAAS,2BAA2B;AAQ7B,SAAS,yBAAyB;AAAA,EACxC,YAAY,CAAC;AAAA,EACb;AACD,GAAmC;AAClC,QAAM,aAAa,yBAAyB,SAAS;AACrD,QAAM,QAAQ;AAAA,IACb,OAAqC;AAAA,MACpC,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,oBAAoB;AAAA,MACpB,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,SAAS;AAAA,MACT,SAAS;AAAA,MAET,eAAe;AAAA,MACf,oBAAoB;AAAA,MAEpB,GAAG;AAAA,IACJ;AAAA,IACA,CAAC,UAAU;AAAA,EACZ;AAEA,SACC,oBAAC,wBAAwB,UAAxB,EAAiC,OAAe,UAAS;AAE5D;",
4
+ "sourcesContent": ["import { ReactNode, useMemo } from 'react'\nimport { DefaultBackground } from '../components/default-components/DefaultBackground'\nimport { DefaultCanvas } from '../components/default-components/DefaultCanvas'\nimport { DefaultCursor } from '../components/default-components/DefaultCursor'\nimport { DefaultErrorFallback } from '../components/default-components/DefaultErrorFallback'\nimport { DefaultGrid } from '../components/default-components/DefaultGrid'\nimport { DefaultLoadingScreen } from '../components/default-components/DefaultLoadingScreen'\nimport { DefaultShapeErrorFallback } from '../components/default-components/DefaultShapeErrorFallback'\nimport { DefaultShapeWrapper } from '../components/default-components/DefaultShapeWrapper'\nimport { DefaultSpinner } from '../components/default-components/DefaultSpinner'\nimport { DefaultSvgDefs } from '../components/default-components/DefaultSvgDefs'\nimport { EditorComponentsContext } from './EditorComponentsContext'\nimport type { TLEditorComponents } from './EditorComponentsContext'\nimport { useShallowObjectIdentity } from './useIdentity'\n\nexport { useEditorComponents } from './EditorComponentsContext'\nexport type { TLEditorComponents } from './EditorComponentsContext'\n\ninterface ComponentsContextProviderProps {\n\toverrides?: TLEditorComponents\n\tchildren: ReactNode\n}\n\nexport function EditorComponentsProvider({\n\toverrides = {},\n\tchildren,\n}: ComponentsContextProviderProps) {\n\tconst _overrides = useShallowObjectIdentity(overrides)\n\tconst value = useMemo(\n\t\t(): Required<TLEditorComponents> => ({\n\t\t\tBackground: DefaultBackground,\n\t\t\tCanvas: DefaultCanvas,\n\t\t\tCollaboratorCursor: DefaultCursor,\n\t\t\tGrid: DefaultGrid,\n\t\t\tInFrontOfTheCanvas: null,\n\t\t\tLoadingScreen: DefaultLoadingScreen,\n\t\t\tOnTheCanvas: null,\n\t\t\tShapeWrapper: DefaultShapeWrapper,\n\t\t\tSpinner: DefaultSpinner,\n\t\t\tSvgDefs: DefaultSvgDefs,\n\n\t\t\tErrorFallback: DefaultErrorFallback,\n\t\t\tShapeErrorFallback: DefaultShapeErrorFallback,\n\n\t\t\t..._overrides,\n\t\t}),\n\t\t[_overrides]\n\t)\n\n\treturn (\n\t\t<EditorComponentsContext.Provider value={value}>{children}</EditorComponentsContext.Provider>\n\t)\n}\n"],
5
+ "mappings": "AAkDE;AAlDF,SAAoB,eAAe;AACnC,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,+BAA+B;AAExC,SAAS,gCAAgC;AAEzC,SAAS,2BAA2B;AAQ7B,SAAS,yBAAyB;AAAA,EACxC,YAAY,CAAC;AAAA,EACb;AACD,GAAmC;AAClC,QAAM,aAAa,yBAAyB,SAAS;AACrD,QAAM,QAAQ;AAAA,IACb,OAAqC;AAAA,MACpC,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,oBAAoB;AAAA,MACpB,MAAM;AAAA,MACN,oBAAoB;AAAA,MACpB,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,SAAS;AAAA,MACT,SAAS;AAAA,MAET,eAAe;AAAA,MACf,oBAAoB;AAAA,MAEpB,GAAG;AAAA,IACJ;AAAA,IACA,CAAC,UAAU;AAAA,EACZ;AAEA,SACC,oBAAC,wBAAwB,UAAxB,EAAiC,OAAe,UAAS;AAE5D;",
6
6
  "names": []
7
7
  }
@@ -1,33 +1,64 @@
1
- import { useEffect } from "react";
1
+ import { useEffect, useRef } from "react";
2
2
  import { preventDefault } from "../utils/dom.mjs";
3
3
  import { useContainer } from "./useContainer.mjs";
4
4
  import { useMaybeEditor } from "./useEditor.mjs";
5
+ import { useEvent } from "./useEvent.mjs";
6
+ function isScrollableOverflow(overflow) {
7
+ return overflow === "auto" || overflow === "scroll" || overflow === "overlay";
8
+ }
9
+ function isScrollable(elm) {
10
+ const overflowsY = elm.scrollHeight > elm.clientHeight;
11
+ const overflowsX = elm.scrollWidth > elm.clientWidth;
12
+ if (!overflowsY && !overflowsX) return false;
13
+ const style = getComputedStyle(elm);
14
+ return overflowsY && isScrollableOverflow(style.overflowY) || overflowsX && isScrollableOverflow(style.overflowX);
15
+ }
16
+ function hasScrollableElement(target, root) {
17
+ if (!(target instanceof Node)) {
18
+ return isScrollable(root);
19
+ }
20
+ let elm = target instanceof Element ? target : target.parentElement;
21
+ while (elm) {
22
+ if (elm instanceof HTMLElement && isScrollable(elm)) {
23
+ return true;
24
+ }
25
+ if (elm === root) return false;
26
+ elm = elm.parentElement;
27
+ }
28
+ return false;
29
+ }
5
30
  function usePassThroughWheelEvents(ref) {
6
31
  if (!ref) throw Error("usePassThroughWheelEvents must be passed a ref");
7
32
  const container = useContainer();
8
33
  const editor = useMaybeEditor();
9
- useEffect(() => {
10
- function onWheel(e) {
11
- if (!editor?.getInstanceState().isFocused) return;
12
- if (e.isSpecialRedispatchedEvent) return;
13
- const elm2 = ref.current;
14
- if (elm2 && elm2.scrollHeight > elm2.clientHeight) {
15
- return;
16
- }
17
- preventDefault(e);
18
- const cvs = container.querySelector(".tl-canvas");
19
- if (!cvs) return;
20
- const newEvent = new WheelEvent("wheel", e);
21
- newEvent.isSpecialRedispatchedEvent = true;
22
- cvs.dispatchEvent(newEvent);
34
+ const onWheel = useEvent((e) => {
35
+ if (!editor?.getInstanceState().isFocused) return;
36
+ if (e.isSpecialRedispatchedEvent) return;
37
+ const elm = ref.current;
38
+ if (elm && hasScrollableElement(e.target, elm)) {
39
+ return;
23
40
  }
41
+ preventDefault(e);
42
+ const cvs = container.querySelector(".tl-canvas");
43
+ if (!cvs) return;
44
+ const newEvent = new WheelEvent("wheel", e);
45
+ newEvent.isSpecialRedispatchedEvent = true;
46
+ cvs.dispatchEvent(newEvent);
47
+ });
48
+ const attached = useRef(null);
49
+ useEffect(() => {
24
50
  const elm = ref.current;
25
- if (!elm) return;
26
- elm.addEventListener("wheel", onWheel, { passive: false });
51
+ if (elm === attached.current) return;
52
+ attached.current?.removeEventListener("wheel", onWheel);
53
+ attached.current = elm;
54
+ elm?.addEventListener("wheel", onWheel, { passive: false });
55
+ });
56
+ useEffect(() => {
27
57
  return () => {
28
- elm.removeEventListener("wheel", onWheel);
58
+ attached.current?.removeEventListener("wheel", onWheel);
59
+ attached.current = null;
29
60
  };
30
- }, [container, editor, ref]);
61
+ }, [onWheel]);
31
62
  }
32
63
  export {
33
64
  usePassThroughWheelEvents
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/usePassThroughWheelEvents.ts"],
4
- "sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\nimport { useMaybeEditor } from './useEditor'\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement | null>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\tconst container = useContainer()\n\tconst editor = useMaybeEditor()\n\n\tuseEffect(() => {\n\t\tfunction onWheel(e: WheelEvent) {\n\t\t\t// Only pass through wheel events if the editor is focused\n\t\t\tif (!editor?.getInstanceState().isFocused) return\n\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\t// if the element is scrollable, don't redispatch the event\n\t\t\tconst elm = ref.current\n\t\t\tif (elm && elm.scrollHeight > elm.clientHeight) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('wheel', onWheel, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('wheel', onWheel)\n\t\t}\n\t}, [container, editor, ref])\n}\n"],
5
- "mappings": "AAAA,SAAoB,iBAAiB;AACrC,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAGxB,SAAS,0BAA0B,KAAoC;AAC7E,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AACtE,QAAM,YAAY,aAAa;AAC/B,QAAM,SAAS,eAAe;AAE9B,YAAU,MAAM;AACf,aAAS,QAAQ,GAAe;AAE/B,UAAI,CAAC,QAAQ,iBAAiB,EAAE,UAAW;AAE3C,UAAK,EAAU,2BAA4B;AAG3C,YAAMA,OAAM,IAAI;AAChB,UAAIA,QAAOA,KAAI,eAAeA,KAAI,cAAc;AAC/C;AAAA,MACD;AAEA,qBAAe,CAAC;AAChB,YAAM,MAAM,UAAU,cAAc,YAAY;AAChD,UAAI,CAAC,IAAK;AACV,YAAM,WAAW,IAAI,WAAW,SAAS,CAAQ;AAChD,MAAC,SAAiB,6BAA6B;AAChD,UAAI,cAAc,QAAQ;AAAA,IAC3B;AAEA,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AACzD,WAAO,MAAM;AACZ,UAAI,oBAAoB,SAAS,OAAO;AAAA,IACzC;AAAA,EACD,GAAG,CAAC,WAAW,QAAQ,GAAG,CAAC;AAC5B;",
6
- "names": ["elm"]
4
+ "sourcesContent": ["import { RefObject, useEffect, useRef } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\nimport { useMaybeEditor } from './useEditor'\nimport { useEvent } from './useEvent'\n\nfunction isScrollableOverflow(overflow: string) {\n\treturn overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay'\n}\n\n// An element only consumes a wheel event when it's a real scroll container: its content overflows\n// AND its computed overflow is scrollable. Checking scrollHeight/scrollWidth alone is not enough \u2014\n// an `overflow: visible` element whose content merely overflows (e.g. a comment pin whose hover\n// `transform: scale()` inflates scrollHeight past clientHeight) never scrolls, so the wheel must\n// still pass through to the canvas.\nfunction isScrollable(elm: HTMLElement) {\n\t// Cheap first: if nothing overflows, it can't scroll \u2014 skip the getComputedStyle read. This runs\n\t// per ancestor on every wheel event, so avoid resolving styles for the common (non-overflowing) case.\n\tconst overflowsY = elm.scrollHeight > elm.clientHeight\n\tconst overflowsX = elm.scrollWidth > elm.clientWidth\n\tif (!overflowsY && !overflowsX) return false\n\n\tconst style = getComputedStyle(elm)\n\treturn (\n\t\t(overflowsY && isScrollableOverflow(style.overflowY)) ||\n\t\t(overflowsX && isScrollableOverflow(style.overflowX))\n\t)\n}\n\n// Walk from the wheeled element up to (and including) the pass-through root, looking for a scroll\n// container. If one is found, the wheel belongs to it \u2014 don't redispatch it to the canvas.\nfunction hasScrollableElement(target: EventTarget | null, root: HTMLElement) {\n\tif (!(target instanceof Node)) {\n\t\treturn isScrollable(root)\n\t}\n\n\tlet elm: Element | null = target instanceof Element ? target : target.parentElement\n\twhile (elm) {\n\t\tif (elm instanceof HTMLElement && isScrollable(elm)) {\n\t\t\treturn true\n\t\t}\n\t\tif (elm === root) return false\n\t\telm = elm.parentElement\n\t}\n\n\treturn false\n}\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement | null>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\tconst container = useContainer()\n\tconst editor = useMaybeEditor()\n\n\tconst onWheel = useEvent((e: WheelEvent) => {\n\t\t// Only pass through wheel events if the editor is focused\n\t\tif (!editor?.getInstanceState().isFocused) return\n\n\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t// If the wheel is over a scrollable element, let it scroll instead of redispatching.\n\t\tconst elm = ref.current\n\t\tif (elm && hasScrollableElement(e.target, elm)) {\n\t\t\treturn\n\t\t}\n\n\t\tpreventDefault(e)\n\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\tif (!cvs) return\n\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\tcvs.dispatchEvent(newEvent)\n\t})\n\n\t// The element the listener is currently on, which isn't necessarily `ref.current`: a `RefObject`\n\t// gives no notification when its element changes.\n\tconst attached = useRef<HTMLElement | null>(null)\n\n\t// Deliberately no dependency array \u2014 this re-checks the ref after every render of the component\n\t// that owns it, and re-attaches when the element has been swapped. A component that keeps the\n\t// hook mounted while unmounting and remounting the element it points at (a comment pin culled\n\t// while its anchor is off screen, say) would otherwise leave the listener on the discarded node\n\t// and silently lose pass-through once the element comes back.\n\tuseEffect(() => {\n\t\tconst elm = ref.current\n\t\tif (elm === attached.current) return\n\t\tattached.current?.removeEventListener('wheel', onWheel)\n\t\tattached.current = elm\n\t\telm?.addEventListener('wheel', onWheel, { passive: false })\n\t})\n\n\t// Teardown is its own effect so the re-attach check above can skip returning a cleanup that\n\t// would tear the listener down and rebuild it on every render.\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tattached.current?.removeEventListener('wheel', onWheel)\n\t\t\tattached.current = null\n\t\t}\n\t}, [onWheel])\n}\n"],
5
+ "mappings": "AAAA,SAAoB,WAAW,cAAc;AAC7C,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AAEzB,SAAS,qBAAqB,UAAkB;AAC/C,SAAO,aAAa,UAAU,aAAa,YAAY,aAAa;AACrE;AAOA,SAAS,aAAa,KAAkB;AAGvC,QAAM,aAAa,IAAI,eAAe,IAAI;AAC1C,QAAM,aAAa,IAAI,cAAc,IAAI;AACzC,MAAI,CAAC,cAAc,CAAC,WAAY,QAAO;AAEvC,QAAM,QAAQ,iBAAiB,GAAG;AAClC,SACE,cAAc,qBAAqB,MAAM,SAAS,KAClD,cAAc,qBAAqB,MAAM,SAAS;AAErD;AAIA,SAAS,qBAAqB,QAA4B,MAAmB;AAC5E,MAAI,EAAE,kBAAkB,OAAO;AAC9B,WAAO,aAAa,IAAI;AAAA,EACzB;AAEA,MAAI,MAAsB,kBAAkB,UAAU,SAAS,OAAO;AACtE,SAAO,KAAK;AACX,QAAI,eAAe,eAAe,aAAa,GAAG,GAAG;AACpD,aAAO;AAAA,IACR;AACA,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,IAAI;AAAA,EACX;AAEA,SAAO;AACR;AAGO,SAAS,0BAA0B,KAAoC;AAC7E,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AACtE,QAAM,YAAY,aAAa;AAC/B,QAAM,SAAS,eAAe;AAE9B,QAAM,UAAU,SAAS,CAAC,MAAkB;AAE3C,QAAI,CAAC,QAAQ,iBAAiB,EAAE,UAAW;AAE3C,QAAK,EAAU,2BAA4B;AAG3C,UAAM,MAAM,IAAI;AAChB,QAAI,OAAO,qBAAqB,EAAE,QAAQ,GAAG,GAAG;AAC/C;AAAA,IACD;AAEA,mBAAe,CAAC;AAChB,UAAM,MAAM,UAAU,cAAc,YAAY;AAChD,QAAI,CAAC,IAAK;AACV,UAAM,WAAW,IAAI,WAAW,SAAS,CAAQ;AAChD,IAAC,SAAiB,6BAA6B;AAChD,QAAI,cAAc,QAAQ;AAAA,EAC3B,CAAC;AAID,QAAM,WAAW,OAA2B,IAAI;AAOhD,YAAU,MAAM;AACf,UAAM,MAAM,IAAI;AAChB,QAAI,QAAQ,SAAS,QAAS;AAC9B,aAAS,SAAS,oBAAoB,SAAS,OAAO;AACtD,aAAS,UAAU;AACnB,SAAK,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AAAA,EAC3D,CAAC;AAID,YAAU,MAAM;AACf,WAAO,MAAM;AACZ,eAAS,SAAS,oBAAoB,SAAS,OAAO;AACtD,eAAS,UAAU;AAAA,IACpB;AAAA,EACD,GAAG,CAAC,OAAO,CAAC;AACb;",
6
+ "names": []
7
7
  }
@@ -0,0 +1,25 @@
1
+ import { useLayoutEffect } from "react";
2
+ function useTransform(ref, x, y, scale, rotate, additionalOffset) {
3
+ const additionalOffsetX = additionalOffset?.x;
4
+ const additionalOffsetY = additionalOffset?.y;
5
+ useLayoutEffect(() => {
6
+ const elm = ref.current;
7
+ if (!elm) return;
8
+ if (x === void 0) return;
9
+ let trans = `translate(${x}px, ${y}px)`;
10
+ if (scale !== void 0) {
11
+ trans += ` scale(${scale})`;
12
+ }
13
+ if (rotate !== void 0) {
14
+ trans += ` rotate(${rotate}rad)`;
15
+ }
16
+ if (additionalOffsetX !== void 0 && additionalOffsetY !== void 0) {
17
+ trans += ` translate(${additionalOffsetX}px, ${additionalOffsetY}px)`;
18
+ }
19
+ elm.style.transform = trans;
20
+ }, [additionalOffsetX, additionalOffsetY, ref, rotate, scale, x, y]);
21
+ }
22
+ export {
23
+ useTransform
24
+ };
25
+ //# sourceMappingURL=useTransform.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/hooks/useTransform.ts"],
4
+ "sourcesContent": ["import { useLayoutEffect } from 'react'\nimport { VecLike } from '../primitives/Vec'\n\n/**\n * Position an element by writing its `transform` directly, outside React's render output \u2014 the\n * cheap path for elements that move at pointer/presence frequency.\n * @public\n */\nexport function useTransform(\n\tref: React.RefObject<HTMLElement | SVGElement | null>,\n\tx?: number,\n\ty?: number,\n\tscale?: number,\n\trotate?: number,\n\tadditionalOffset?: VecLike\n) {\n\t// Read the offset into primitives so the effect can depend on them, not the object identity.\n\tconst additionalOffsetX = additionalOffset?.x\n\tconst additionalOffsetY = additionalOffset?.y\n\n\tuseLayoutEffect(() => {\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\t\tif (x === undefined) return\n\n\t\tlet trans = `translate(${x}px, ${y}px)`\n\t\tif (scale !== undefined) {\n\t\t\ttrans += ` scale(${scale})`\n\t\t}\n\t\tif (rotate !== undefined) {\n\t\t\ttrans += ` rotate(${rotate}rad)`\n\t\t}\n\t\tif (additionalOffsetX !== undefined && additionalOffsetY !== undefined) {\n\t\t\ttrans += ` translate(${additionalOffsetX}px, ${additionalOffsetY}px)`\n\t\t}\n\t\telm.style.transform = trans\n\t}, [additionalOffsetX, additionalOffsetY, ref, rotate, scale, x, y])\n}\n"],
5
+ "mappings": "AAAA,SAAS,uBAAuB;AAQzB,SAAS,aACf,KACA,GACA,GACA,OACA,QACA,kBACC;AAED,QAAM,oBAAoB,kBAAkB;AAC5C,QAAM,oBAAoB,kBAAkB;AAE5C,kBAAgB,MAAM;AACrB,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM,OAAW;AAErB,QAAI,QAAQ,aAAa,CAAC,OAAO,CAAC;AAClC,QAAI,UAAU,QAAW;AACxB,eAAS,UAAU,KAAK;AAAA,IACzB;AACA,QAAI,WAAW,QAAW;AACzB,eAAS,WAAW,MAAM;AAAA,IAC3B;AACA,QAAI,sBAAsB,UAAa,sBAAsB,QAAW;AACvE,eAAS,cAAc,iBAAiB,OAAO,iBAAiB;AAAA,IACjE;AACA,QAAI,MAAM,YAAY;AAAA,EACvB,GAAG,CAAC,mBAAmB,mBAAmB,KAAK,QAAQ,OAAO,GAAG,CAAC,CAAC;AACpE;",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- import { atom } from "@tldraw/state";
1
+ import { atom, transact } from "@tldraw/state";
2
2
  import { publishDates, version } from "../../version.mjs";
3
3
  import { getDefaultCdnBaseUrl } from "../utils/assets.mjs";
4
4
  import { importPublicKey, str2ab } from "../utils/licensing.mjs";
@@ -18,7 +18,12 @@ const FLAGS = {
18
18
  EVALUATION_LICENSE: 1 << 4,
19
19
  // Native means the license is for native apps which switches
20
20
  // on special-case logic.
21
- NATIVE_LICENSE: 1 << 5
21
+ NATIVE_LICENSE: 1 << 5,
22
+ // -- FEATURE FLAGS --
23
+ // Collaboration is the umbrella flag for collaboration features; it grants all sub-features.
24
+ FEAT_COLLABORATION: 1 << 6,
25
+ // Commenting is the first sub-feature of collaboration.
26
+ FEAT_COMMENTING: 1 << 7
22
27
  };
23
28
  const HIGHEST_FLAG = Math.max(...Object.values(FLAGS));
24
29
  const PROPERTIES = {
@@ -30,18 +35,32 @@ const PROPERTIES = {
30
35
  const NUMBER_OF_KNOWN_PROPERTIES = Object.keys(PROPERTIES).length;
31
36
  const LICENSE_EMAIL = "sales@tldraw.com";
32
37
  const WATERMARK_TRACK_SRC = `${getDefaultCdnBaseUrl()}/watermarks/watermark-track.svg`;
38
+ const NO_FEATURES = {
39
+ collaboration: false,
40
+ commenting: false
41
+ };
42
+ const ALL_FEATURES = {
43
+ collaboration: true,
44
+ commenting: true
45
+ };
33
46
  class LicenseManager {
34
47
  publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHJh0uUfxHtCGyerXmmatE368Hd9rI6LH9oPDQihnaCryRFWEVeOvf9U/SPbyxX74LFyJs5tYeAHq5Nc0Ax25LQ";
35
48
  isDevelopment;
36
49
  isTest;
37
50
  isCryptoAvailable;
38
51
  state = atom("license state", "pending");
52
+ featureFlags = atom("license feature flags", {
53
+ ...NO_FEATURES
54
+ });
39
55
  verbose = true;
40
56
  constructor(licenseKey, testPublicKey) {
41
57
  this.isTest = process.env.NODE_ENV === "test";
42
58
  this.isDevelopment = this.getIsDevelopment();
43
59
  this.publicKey = testPublicKey || this.publicKey;
44
60
  this.isCryptoAvailable = !!crypto.subtle;
61
+ if (this.isDevelopment) {
62
+ this.featureFlags.set({ ...ALL_FEATURES });
63
+ }
45
64
  this.getLicenseFromKey(licenseKey).then((result) => {
46
65
  const licenseState = getLicenseState(
47
66
  result,
@@ -49,12 +68,22 @@ class LicenseManager {
49
68
  this.isDevelopment
50
69
  );
51
70
  this.maybeTrack(result, licenseState);
52
- this.state.set(licenseState);
71
+ transact(() => {
72
+ this.state.set(licenseState);
73
+ this.featureFlags.set(getEnabledFeatures(result, licenseState, this.isDevelopment));
74
+ });
53
75
  }).catch((error) => {
54
76
  console.error("License validation failed:", error);
55
77
  this.state.set("unlicensed");
56
78
  });
57
79
  }
80
+ /**
81
+ * Returns whether a given licensable feature is enabled. Reactive: reading this inside a signal
82
+ * recomputes when license validation resolves.
83
+ */
84
+ isFeatureEnabled(feature) {
85
+ return this.featureFlags.get()[feature];
86
+ }
58
87
  getIsDevelopment() {
59
88
  return !["https:", "vscode-webview:"].includes(window.location.protocol) || this.isLoopbackHost(window.location.hostname) || process.env.NODE_ENV !== "production";
60
89
  }
@@ -169,6 +198,8 @@ class LicenseManager {
169
198
  const isEvaluationLicense = this.isFlagEnabled(licenseInfo.flags, FLAGS.EVALUATION_LICENSE);
170
199
  const isAnnualLicenseExpired = isAnnualLicense && this.isAnnualLicenseExpired(expiryDate);
171
200
  const isPerpetualLicenseExpired = isPerpetualLicense && this.isPerpetualLicenseExpired(expiryDate);
201
+ const isCollaborationEnabled = this.isFlagEnabled(licenseInfo.flags, FLAGS.FEAT_COLLABORATION);
202
+ const isCommentingEnabled = isCollaborationEnabled || this.isFlagEnabled(licenseInfo.flags, FLAGS.FEAT_COMMENTING);
172
203
  const daysSinceExpiry = isPerpetualLicense && !isPerpetualLicenseExpired ? 0 : this.getDaysSinceExpiry(expiryDate);
173
204
  const result = {
174
205
  license: licenseInfo,
@@ -185,6 +216,8 @@ class LicenseManager {
185
216
  isLicensedWithWatermark: this.isFlagEnabled(licenseInfo.flags, FLAGS.WITH_WATERMARK),
186
217
  isEvaluationLicense,
187
218
  isEvaluationLicenseExpired: isEvaluationLicense && this.isEvaluationLicenseExpired(expiryDate),
219
+ isCollaborationEnabled,
220
+ isCommentingEnabled,
188
221
  daysSinceExpiry
189
222
  };
190
223
  this.outputLicenseInfoIfNeeded(result);
@@ -310,6 +343,21 @@ class LicenseManager {
310
343
  }
311
344
  static className = "tl-watermark_SEE-LICENSE";
312
345
  }
346
+ function getEnabledFeatures(result, licenseState, isDevelopment) {
347
+ if (isDevelopment) {
348
+ return { ...ALL_FEATURES };
349
+ }
350
+ if (!result.isLicenseParseable || licenseState !== "licensed" && licenseState !== "licensed-with-watermark") {
351
+ return { ...NO_FEATURES };
352
+ }
353
+ if (result.isEvaluationLicense) {
354
+ return { ...ALL_FEATURES };
355
+ }
356
+ return {
357
+ collaboration: result.isCollaborationEnabled,
358
+ commenting: result.isCommentingEnabled
359
+ };
360
+ }
313
361
  function getLicenseState(result, outputMessages, isDevelopment) {
314
362
  if (!result.isLicenseParseable) {
315
363
  if (isDevelopment) {
@@ -373,6 +421,7 @@ export {
373
421
  FLAGS,
374
422
  LicenseManager,
375
423
  PROPERTIES,
424
+ getEnabledFeatures,
376
425
  getLicenseState
377
426
  };
378
427
  //# sourceMappingURL=LicenseManager.mjs.map