@tldraw/editor 3.7.0-canary.b26822432001 → 3.7.0-canary.c01564bb95bb

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 (43) hide show
  1. package/dist-cjs/index.d.ts +3 -1
  2. package/dist-cjs/index.js +2 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +1 -1
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/LiveCollaborators.js +2 -1
  7. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +8 -2
  9. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/TextManager.js +9 -15
  11. package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
  12. package/dist-cjs/lib/exports/FontEmbedder.js +2 -1
  13. package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
  14. package/dist-cjs/lib/hooks/useEvent.js +18 -1
  15. package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
  16. package/dist-cjs/version.js +3 -3
  17. package/dist-cjs/version.js.map +1 -1
  18. package/dist-esm/index.d.mts +3 -1
  19. package/dist-esm/index.mjs +3 -2
  20. package/dist-esm/index.mjs.map +2 -2
  21. package/dist-esm/lib/TldrawEditor.mjs +1 -1
  22. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  23. package/dist-esm/lib/components/LiveCollaborators.mjs +2 -1
  24. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  25. package/dist-esm/lib/editor/Editor.mjs +9 -2
  26. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  27. package/dist-esm/lib/editor/managers/TextManager.mjs +9 -15
  28. package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
  29. package/dist-esm/lib/exports/FontEmbedder.mjs +2 -1
  30. package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
  31. package/dist-esm/lib/hooks/useEvent.mjs +18 -1
  32. package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
  33. package/dist-esm/version.mjs +3 -3
  34. package/dist-esm/version.mjs.map +1 -1
  35. package/package.json +7 -7
  36. package/src/index.ts +1 -1
  37. package/src/lib/TldrawEditor.tsx +1 -1
  38. package/src/lib/components/LiveCollaborators.tsx +3 -1
  39. package/src/lib/editor/Editor.ts +12 -5
  40. package/src/lib/editor/managers/TextManager.ts +9 -17
  41. package/src/lib/exports/FontEmbedder.ts +2 -1
  42. package/src/lib/hooks/useEvent.tsx +29 -0
  43. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/components/LiveCollaborators.tsx"],
4
- "sourcesContent": ["import { track } from '@tldraw/state-react'\nimport { TLInstancePresence } from '@tldraw/tlschema'\nimport { useEffect, useRef, useState } from 'react'\nimport { Editor } from '../editor/Editor'\nimport { useEditor } from '../hooks/useEditor'\nimport { useEditorComponents } from '../hooks/useEditorComponents'\nimport { usePeerIds } from '../hooks/usePeerIds'\nimport { usePresence } from '../hooks/usePresence'\n\nexport const LiveCollaborators = track(function Collaborators() {\n\tconst peerIds = usePeerIds()\n\treturn peerIds.map((id) => <CollaboratorGuard key={id} collaboratorId={id} />)\n})\n\nconst CollaboratorGuard = track(function CollaboratorGuard({\n\tcollaboratorId,\n}: {\n\tcollaboratorId: string\n}) {\n\tconst editor = useEditor()\n\tconst presence = usePresence(collaboratorId)\n\tconst collaboratorState = useCollaboratorState(editor, presence)\n\n\tif (!(presence && presence.currentPageId === editor.getCurrentPageId())) {\n\t\t// No need to render if we don't have a presence or if they're on a different page\n\t\treturn null\n\t}\n\n\tswitch (collaboratorState) {\n\t\tcase 'inactive': {\n\t\t\tconst { followingUserId, highlightedUserIds } = editor.getInstanceState()\n\t\t\t// If they're inactive and unless we're following them or they're highlighted, hide them\n\t\t\tif (!(followingUserId === presence.userId || highlightedUserIds.includes(presence.userId))) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tcase 'idle': {\n\t\t\tconst { highlightedUserIds } = editor.getInstanceState()\n\t\t\t// If they're idle and following us and unless they have a chat message or are highlighted, hide them\n\t\t\tif (\n\t\t\t\tpresence.followingUserId === editor.user.getId() &&\n\t\t\t\t!(presence.chatMessage || highlightedUserIds.includes(presence.userId))\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tcase 'active': {\n\t\t\t// If they're active, show them\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn <Collaborator latestPresence={presence} />\n})\n\nconst Collaborator = track(function Collaborator({\n\tlatestPresence,\n}: {\n\tlatestPresence: TLInstancePresence\n}) {\n\tconst editor = useEditor()\n\n\tconst {\n\t\tCollaboratorBrush,\n\t\tCollaboratorScribble,\n\t\tCollaboratorCursor,\n\t\tCollaboratorHint,\n\t\tCollaboratorShapeIndicator,\n\t} = useEditorComponents()\n\n\tconst zoomLevel = editor.getZoomLevel()\n\tconst viewportPageBounds = editor.getViewportPageBounds()\n\tconst { userId, chatMessage, brush, scribbles, selectedShapeIds, userName, cursor, color } =\n\t\tlatestPresence\n\n\t// Add a little padding to the top-left of the viewport\n\t// so that the cursor doesn't get cut off\n\tconst isCursorInViewport = !(\n\t\tcursor.x < viewportPageBounds.minX - 12 / zoomLevel ||\n\t\tcursor.y < viewportPageBounds.minY - 16 / zoomLevel ||\n\t\tcursor.x > viewportPageBounds.maxX - 12 / zoomLevel ||\n\t\tcursor.y > viewportPageBounds.maxY - 16 / zoomLevel\n\t)\n\n\treturn (\n\t\t<>\n\t\t\t{brush && CollaboratorBrush ? (\n\t\t\t\t<CollaboratorBrush\n\t\t\t\t\tclassName=\"tl-collaborator__brush\"\n\t\t\t\t\tkey={userId + '_brush'}\n\t\t\t\t\tbrush={brush}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\topacity={0.1}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{isCursorInViewport && CollaboratorCursor ? (\n\t\t\t\t<CollaboratorCursor\n\t\t\t\t\tclassName=\"tl-collaborator__cursor\"\n\t\t\t\t\tkey={userId + '_cursor'}\n\t\t\t\t\tpoint={cursor}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\tname={userName !== 'New User' ? userName : null}\n\t\t\t\t\tchatMessage={chatMessage ?? ''}\n\t\t\t\t/>\n\t\t\t) : CollaboratorHint ? (\n\t\t\t\t<CollaboratorHint\n\t\t\t\t\tclassName=\"tl-collaborator__cursor-hint\"\n\t\t\t\t\tkey={userId + '_cursor_hint'}\n\t\t\t\t\tpoint={cursor}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\tviewport={viewportPageBounds}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{CollaboratorScribble && scribbles.length ? (\n\t\t\t\t<>\n\t\t\t\t\t{scribbles.map((scribble) => (\n\t\t\t\t\t\t<CollaboratorScribble\n\t\t\t\t\t\t\tkey={userId + '_scribble_' + scribble.id}\n\t\t\t\t\t\t\tclassName=\"tl-collaborator__scribble\"\n\t\t\t\t\t\t\tscribble={scribble}\n\t\t\t\t\t\t\tcolor={color}\n\t\t\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\t\t\topacity={scribble.color === 'laser' ? 0.5 : 0.1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</>\n\t\t\t) : null}\n\t\t\t{CollaboratorShapeIndicator &&\n\t\t\t\tselectedShapeIds\n\t\t\t\t\t.filter((id) => !editor.isShapeHidden(id))\n\t\t\t\t\t.map((shapeId) => (\n\t\t\t\t\t\t<CollaboratorShapeIndicator\n\t\t\t\t\t\t\tclassName=\"tl-collaborator__shape-indicator\"\n\t\t\t\t\t\t\tkey={userId + '_' + shapeId}\n\t\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t\tcolor={color}\n\t\t\t\t\t\t\topacity={0.5}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t</>\n\t)\n})\n\nfunction getStateFromElapsedTime(editor: Editor, elapsed: number) {\n\treturn elapsed > editor.options.collaboratorInactiveTimeoutMs\n\t\t? 'inactive'\n\t\t: elapsed > editor.options.collaboratorIdleTimeoutMs\n\t\t\t? 'idle'\n\t\t\t: 'active'\n}\n\nfunction useCollaboratorState(editor: Editor, latestPresence: TLInstancePresence | null) {\n\tconst rLastActivityTimestamp = useRef(latestPresence?.lastActivityTimestamp ?? -1)\n\n\tconst [state, setState] = useState<'active' | 'idle' | 'inactive'>(() =>\n\t\tgetStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current)\n\t)\n\n\tuseEffect(() => {\n\t\tconst interval = editor.timers.setInterval(() => {\n\t\t\tsetState(getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current))\n\t\t}, editor.options.collaboratorCheckIntervalMs)\n\n\t\treturn () => clearInterval(interval)\n\t}, [editor])\n\n\tif (latestPresence) {\n\t\t// We can do this on every render, it's free and cheaper than an effect\n\t\t// remember, there can be lots and lots of cursors moving around all the time\n\t\trLastActivityTimestamp.current = latestPresence.lastActivityTimestamp\n\t}\n\n\treturn state\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { track } from '@tldraw/state-react'\nimport { TLInstancePresence } from '@tldraw/tlschema'\nimport { useEffect, useRef, useState } from 'react'\nimport { Editor } from '../editor/Editor'\nimport { useEditor } from '../hooks/useEditor'\nimport { useEditorComponents } from '../hooks/useEditorComponents'\nimport { usePeerIds } from '../hooks/usePeerIds'\nimport { usePresence } from '../hooks/usePresence'\n\nexport const LiveCollaborators = track(function Collaborators() {\n\tconst peerIds = usePeerIds()\n\treturn peerIds.map((id) => <CollaboratorGuard key={id} collaboratorId={id} />)\n})\n\nconst CollaboratorGuard = track(function CollaboratorGuard({\n\tcollaboratorId,\n}: {\n\tcollaboratorId: string\n}) {\n\tconst editor = useEditor()\n\tconst presence = usePresence(collaboratorId)\n\tconst collaboratorState = useCollaboratorState(editor, presence)\n\n\tif (!(presence && presence.currentPageId === editor.getCurrentPageId())) {\n\t\t// No need to render if we don't have a presence or if they're on a different page\n\t\treturn null\n\t}\n\n\tswitch (collaboratorState) {\n\t\tcase 'inactive': {\n\t\t\tconst { followingUserId, highlightedUserIds } = editor.getInstanceState()\n\t\t\t// If they're inactive and unless we're following them or they're highlighted, hide them\n\t\t\tif (!(followingUserId === presence.userId || highlightedUserIds.includes(presence.userId))) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tcase 'idle': {\n\t\t\tconst { highlightedUserIds } = editor.getInstanceState()\n\t\t\t// If they're idle and following us and unless they have a chat message or are highlighted, hide them\n\t\t\tif (\n\t\t\t\tpresence.followingUserId === editor.user.getId() &&\n\t\t\t\t!(presence.chatMessage || highlightedUserIds.includes(presence.userId))\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tcase 'active': {\n\t\t\t// If they're active, show them\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn <Collaborator latestPresence={presence} />\n})\n\nconst Collaborator = track(function Collaborator({\n\tlatestPresence,\n}: {\n\tlatestPresence: TLInstancePresence\n}) {\n\tconst editor = useEditor()\n\n\tconst {\n\t\tCollaboratorBrush,\n\t\tCollaboratorScribble,\n\t\tCollaboratorCursor,\n\t\tCollaboratorHint,\n\t\tCollaboratorShapeIndicator,\n\t} = useEditorComponents()\n\n\tconst zoomLevel = editor.getZoomLevel()\n\tconst viewportPageBounds = editor.getViewportPageBounds()\n\tconst { userId, chatMessage, brush, scribbles, selectedShapeIds, userName, cursor, color } =\n\t\tlatestPresence\n\n\tif (!cursor) return null\n\n\t// Add a little padding to the top-left of the viewport\n\t// so that the cursor doesn't get cut off\n\tconst isCursorInViewport = !(\n\t\tcursor.x < viewportPageBounds.minX - 12 / zoomLevel ||\n\t\tcursor.y < viewportPageBounds.minY - 16 / zoomLevel ||\n\t\tcursor.x > viewportPageBounds.maxX - 12 / zoomLevel ||\n\t\tcursor.y > viewportPageBounds.maxY - 16 / zoomLevel\n\t)\n\n\treturn (\n\t\t<>\n\t\t\t{brush && CollaboratorBrush ? (\n\t\t\t\t<CollaboratorBrush\n\t\t\t\t\tclassName=\"tl-collaborator__brush\"\n\t\t\t\t\tkey={userId + '_brush'}\n\t\t\t\t\tbrush={brush}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\topacity={0.1}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{isCursorInViewport && CollaboratorCursor ? (\n\t\t\t\t<CollaboratorCursor\n\t\t\t\t\tclassName=\"tl-collaborator__cursor\"\n\t\t\t\t\tkey={userId + '_cursor'}\n\t\t\t\t\tpoint={cursor}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\tname={userName !== 'New User' ? userName : null}\n\t\t\t\t\tchatMessage={chatMessage ?? ''}\n\t\t\t\t/>\n\t\t\t) : CollaboratorHint ? (\n\t\t\t\t<CollaboratorHint\n\t\t\t\t\tclassName=\"tl-collaborator__cursor-hint\"\n\t\t\t\t\tkey={userId + '_cursor_hint'}\n\t\t\t\t\tpoint={cursor}\n\t\t\t\t\tcolor={color}\n\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\tviewport={viewportPageBounds}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{CollaboratorScribble && scribbles.length ? (\n\t\t\t\t<>\n\t\t\t\t\t{scribbles.map((scribble) => (\n\t\t\t\t\t\t<CollaboratorScribble\n\t\t\t\t\t\t\tkey={userId + '_scribble_' + scribble.id}\n\t\t\t\t\t\t\tclassName=\"tl-collaborator__scribble\"\n\t\t\t\t\t\t\tscribble={scribble}\n\t\t\t\t\t\t\tcolor={color}\n\t\t\t\t\t\t\tzoom={zoomLevel}\n\t\t\t\t\t\t\topacity={scribble.color === 'laser' ? 0.5 : 0.1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</>\n\t\t\t) : null}\n\t\t\t{CollaboratorShapeIndicator &&\n\t\t\t\tselectedShapeIds\n\t\t\t\t\t.filter((id) => !editor.isShapeHidden(id))\n\t\t\t\t\t.map((shapeId) => (\n\t\t\t\t\t\t<CollaboratorShapeIndicator\n\t\t\t\t\t\t\tclassName=\"tl-collaborator__shape-indicator\"\n\t\t\t\t\t\t\tkey={userId + '_' + shapeId}\n\t\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t\tcolor={color}\n\t\t\t\t\t\t\topacity={0.5}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t</>\n\t)\n})\n\nfunction getStateFromElapsedTime(editor: Editor, elapsed: number) {\n\treturn elapsed > editor.options.collaboratorInactiveTimeoutMs\n\t\t? 'inactive'\n\t\t: elapsed > editor.options.collaboratorIdleTimeoutMs\n\t\t\t? 'idle'\n\t\t\t: 'active'\n}\n\nfunction useCollaboratorState(editor: Editor, latestPresence: TLInstancePresence | null) {\n\tconst rLastActivityTimestamp = useRef(latestPresence?.lastActivityTimestamp ?? -1)\n\n\tconst [state, setState] = useState<'active' | 'idle' | 'inactive'>(() =>\n\t\tgetStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current)\n\t)\n\n\tuseEffect(() => {\n\t\tconst interval = editor.timers.setInterval(() => {\n\t\t\tsetState(getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current))\n\t\t}, editor.options.collaboratorCheckIntervalMs)\n\n\t\treturn () => clearInterval(interval)\n\t}, [editor])\n\n\tif (latestPresence) {\n\t\t// We can do this on every render, it's free and cheaper than an effect\n\t\t// remember, there can be lots and lots of cursors moving around all the time\n\t\trLastActivityTimestamp.current = latestPresence.lastActivityTimestamp ?? Infinity\n\t}\n\n\treturn state\n}\n"],
5
+ "mappings": "AAW4B,SA6GxB,UA7GwB,KA8E1B,YA9E0B;AAX5B,SAAS,aAAa;AAEtB,SAAS,WAAW,QAAQ,gBAAgB;AAE5C,SAAS,iBAAiB;AAC1B,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAErB,MAAM,oBAAoB,MAAM,SAAS,gBAAgB;AAC/D,QAAM,UAAU,WAAW;AAC3B,SAAO,QAAQ,IAAI,CAAC,OAAO,oBAAC,qBAA2B,gBAAgB,MAApB,EAAwB,CAAE;AAC9E,CAAC;AAED,MAAM,oBAAoB,MAAM,SAASA,mBAAkB;AAAA,EAC1D;AACD,GAEG;AACF,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,YAAY,cAAc;AAC3C,QAAM,oBAAoB,qBAAqB,QAAQ,QAAQ;AAE/D,MAAI,EAAE,YAAY,SAAS,kBAAkB,OAAO,iBAAiB,IAAI;AAExE,WAAO;AAAA,EACR;AAEA,UAAQ,mBAAmB;AAAA,IAC1B,KAAK,YAAY;AAChB,YAAM,EAAE,iBAAiB,mBAAmB,IAAI,OAAO,iBAAiB;AAExE,UAAI,EAAE,oBAAoB,SAAS,UAAU,mBAAmB,SAAS,SAAS,MAAM,IAAI;AAC3F,eAAO;AAAA,MACR;AACA;AAAA,IACD;AAAA,IACA,KAAK,QAAQ;AACZ,YAAM,EAAE,mBAAmB,IAAI,OAAO,iBAAiB;AAEvD,UACC,SAAS,oBAAoB,OAAO,KAAK,MAAM,KAC/C,EAAE,SAAS,eAAe,mBAAmB,SAAS,SAAS,MAAM,IACpE;AACD,eAAO;AAAA,MACR;AACA;AAAA,IACD;AAAA,IACA,KAAK,UAAU;AAEd;AAAA,IACD;AAAA,EACD;AAEA,SAAO,oBAAC,gBAAa,gBAAgB,UAAU;AAChD,CAAC;AAED,MAAM,eAAe,MAAM,SAASC,cAAa;AAAA,EAChD;AACD,GAEG;AACF,QAAM,SAAS,UAAU;AAEzB,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI,oBAAoB;AAExB,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,qBAAqB,OAAO,sBAAsB;AACxD,QAAM,EAAE,QAAQ,aAAa,OAAO,WAAW,kBAAkB,UAAU,QAAQ,MAAM,IACxF;AAED,MAAI,CAAC,OAAQ,QAAO;AAIpB,QAAM,qBAAqB,EAC1B,OAAO,IAAI,mBAAmB,OAAO,KAAK,aAC1C,OAAO,IAAI,mBAAmB,OAAO,KAAK,aAC1C,OAAO,IAAI,mBAAmB,OAAO,KAAK,aAC1C,OAAO,IAAI,mBAAmB,OAAO,KAAK;AAG3C,SACC,iCACE;AAAA,aAAS,oBACT;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,QACA;AAAA,QACA,SAAS;AAAA;AAAA,MAHJ,SAAS;AAAA,IAIf,IACG;AAAA,IACH,sBAAsB,qBACtB;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QAEV,OAAO;AAAA,QACP;AAAA,QACA,MAAM;AAAA,QACN,MAAM,aAAa,aAAa,WAAW;AAAA,QAC3C,aAAa,eAAe;AAAA;AAAA,MALvB,SAAS;AAAA,IAMf,IACG,mBACH;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QAEV,OAAO;AAAA,QACP;AAAA,QACA,MAAM;AAAA,QACN,UAAU;AAAA;AAAA,MAJL,SAAS;AAAA,IAKf,IACG;AAAA,IACH,wBAAwB,UAAU,SAClC,gCACE,oBAAU,IAAI,CAAC,aACf;AAAA,MAAC;AAAA;AAAA,QAEA,WAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,SAAS,SAAS,UAAU,UAAU,MAAM;AAAA;AAAA,MALvC,SAAS,eAAe,SAAS;AAAA,IAMvC,CACA,GACF,IACG;AAAA,IACH,8BACA,iBACE,OAAO,CAAC,OAAO,CAAC,OAAO,cAAc,EAAE,CAAC,EACxC,IAAI,CAAC,YACL;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,QACA;AAAA,QACA,SAAS;AAAA;AAAA,MAHJ,SAAS,MAAM;AAAA,IAIrB,CACA;AAAA,KACJ;AAEF,CAAC;AAED,SAAS,wBAAwB,QAAgB,SAAiB;AACjE,SAAO,UAAU,OAAO,QAAQ,gCAC7B,aACA,UAAU,OAAO,QAAQ,4BACxB,SACA;AACL;AAEA,SAAS,qBAAqB,QAAgB,gBAA2C;AACxF,QAAM,yBAAyB,OAAO,gBAAgB,yBAAyB,EAAE;AAEjF,QAAM,CAAC,OAAO,QAAQ,IAAI;AAAA,IAAyC,MAClE,wBAAwB,QAAQ,KAAK,IAAI,IAAI,uBAAuB,OAAO;AAAA,EAC5E;AAEA,YAAU,MAAM;AACf,UAAM,WAAW,OAAO,OAAO,YAAY,MAAM;AAChD,eAAS,wBAAwB,QAAQ,KAAK,IAAI,IAAI,uBAAuB,OAAO,CAAC;AAAA,IACtF,GAAG,OAAO,QAAQ,2BAA2B;AAE7C,WAAO,MAAM,cAAc,QAAQ;AAAA,EACpC,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,gBAAgB;AAGnB,2BAAuB,UAAU,eAAe,yBAAyB;AAAA,EAC1E;AAEA,SAAO;AACR;",
6
6
  "names": ["CollaboratorGuard", "Collaborator"]
7
7
  }
@@ -86,6 +86,7 @@ import {
86
86
  hasOwnProperty,
87
87
  last,
88
88
  lerp,
89
+ maxBy,
89
90
  sortById,
90
91
  sortByIndex,
91
92
  structuredClone,
@@ -1776,6 +1777,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
1776
1777
  if (!followingUserId) return null;
1777
1778
  const leaderPresence = this.getCollaborators().find((c) => c.userId === followingUserId);
1778
1779
  if (!leaderPresence) return null;
1780
+ if (!leaderPresence.camera || !leaderPresence.screenBounds) return null;
1779
1781
  const { w: lw, h: lh } = leaderPresence.screenBounds;
1780
1782
  const { x: lx, y: ly, z: lz } = leaderPresence.camera;
1781
1783
  const theirViewport = new Box(-lx, -ly, lw / lz, lh / lz);
@@ -2470,6 +2472,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
2470
2472
  zoomToUser(userId, opts = { animation: { duration: 500 } }) {
2471
2473
  const presence = this.getCollaborators().find((c) => c.userId === userId);
2472
2474
  if (!presence) return this;
2475
+ const cursor = presence.cursor;
2476
+ if (!cursor) return this;
2473
2477
  this.run(() => {
2474
2478
  if (this.getInstanceState().followingUserId !== null) {
2475
2479
  this.stopFollowingUser();
@@ -2481,7 +2485,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
2481
2485
  if (opts && opts.animation && !isOnSamePage) {
2482
2486
  opts.animation = void 0;
2483
2487
  }
2484
- this.centerOnPoint(presence.cursor, opts);
2488
+ this.centerOnPoint(cursor, opts);
2485
2489
  const { highlightedUserIds } = this.getInstanceState();
2486
2490
  this.updateInstanceState({ highlightedUserIds: [...highlightedUserIds, userId] });
2487
2491
  this.timers.setTimeout(() => {
@@ -2638,7 +2642,10 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
2638
2642
  if (!allPresenceRecords.length) return EMPTY_ARRAY;
2639
2643
  const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort();
2640
2644
  return userIds.map((id) => {
2641
- const latestPresence = allPresenceRecords.filter((c) => c.userId === id).sort((a, b) => b.lastActivityTimestamp - a.lastActivityTimestamp)[0];
2645
+ const latestPresence = maxBy(
2646
+ allPresenceRecords.filter((c) => c.userId === id),
2647
+ (p) => p.lastActivityTimestamp ?? 0
2648
+ );
2642
2649
  return latestPresence;
2643
2650
  });
2644
2651
  }