@tldraw/editor 3.9.0 → 3.10.0-canary.3176429f9d1b

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 (137) hide show
  1. package/dist-cjs/index.d.ts +243 -5
  2. package/dist-cjs/index.js +9 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +32 -6
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/LiveCollaborators.js +5 -0
  7. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +7 -0
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  11. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  13. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  15. package/dist-cjs/lib/editor/Editor.js +68 -13
  16. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  17. package/dist-cjs/lib/editor/managers/FontManager.js +166 -0
  18. package/dist-cjs/lib/editor/managers/FontManager.js.map +7 -0
  19. package/dist-cjs/lib/editor/managers/TextManager.js +23 -17
  20. package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +11 -0
  22. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  23. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  24. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  25. package/dist-cjs/lib/exports/FontEmbedder.js +7 -2
  26. package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
  27. package/dist-cjs/lib/exports/StyleEmbedder.js +1 -1
  28. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  29. package/dist-cjs/lib/exports/exportToSvg.js +3 -2
  30. package/dist-cjs/lib/exports/exportToSvg.js.map +2 -2
  31. package/dist-cjs/lib/exports/getSvgJsx.js +18 -1
  32. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  33. package/dist-cjs/lib/exports/parseCss.js +1 -0
  34. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  35. package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -2
  36. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  37. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -1
  38. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  39. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +48 -0
  40. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +7 -0
  41. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  42. package/dist-cjs/lib/hooks/usePeerIds.js.map +1 -1
  43. package/dist-cjs/lib/hooks/usePresence.js.map +1 -1
  44. package/dist-cjs/lib/hooks/useViewportHeight.js +56 -0
  45. package/dist-cjs/lib/hooks/useViewportHeight.js.map +7 -0
  46. package/dist-cjs/lib/options.js +2 -1
  47. package/dist-cjs/lib/options.js.map +2 -2
  48. package/dist-cjs/lib/utils/dom.js +1 -1
  49. package/dist-cjs/lib/utils/dom.js.map +2 -2
  50. package/dist-cjs/lib/utils/richText.js +46 -0
  51. package/dist-cjs/lib/utils/richText.js.map +7 -0
  52. package/dist-cjs/version.js +3 -3
  53. package/dist-cjs/version.js.map +1 -1
  54. package/dist-esm/index.d.mts +243 -5
  55. package/dist-esm/index.mjs +13 -1
  56. package/dist-esm/index.mjs.map +2 -2
  57. package/dist-esm/lib/TldrawEditor.mjs +33 -7
  58. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  59. package/dist-esm/lib/components/LiveCollaborators.mjs +5 -0
  60. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  61. package/dist-esm/lib/components/Shape.mjs +8 -1
  62. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  63. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  64. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  65. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  66. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  67. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  68. package/dist-esm/lib/editor/Editor.mjs +71 -9
  69. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  70. package/dist-esm/lib/editor/managers/FontManager.mjs +152 -0
  71. package/dist-esm/lib/editor/managers/FontManager.mjs.map +7 -0
  72. package/dist-esm/lib/editor/managers/TextManager.mjs +23 -17
  73. package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
  74. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +11 -0
  75. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  76. package/dist-esm/lib/exports/FontEmbedder.mjs +7 -2
  77. package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
  78. package/dist-esm/lib/exports/StyleEmbedder.mjs +1 -1
  79. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  80. package/dist-esm/lib/exports/exportToSvg.mjs +3 -2
  81. package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
  82. package/dist-esm/lib/exports/getSvgJsx.mjs +19 -2
  83. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  84. package/dist-esm/lib/exports/parseCss.mjs +1 -0
  85. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  86. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -2
  87. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  88. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +1 -1
  89. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  90. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +28 -0
  91. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +7 -0
  92. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  93. package/dist-esm/lib/hooks/usePeerIds.mjs.map +1 -1
  94. package/dist-esm/lib/hooks/usePresence.mjs.map +1 -1
  95. package/dist-esm/lib/hooks/useViewportHeight.mjs +36 -0
  96. package/dist-esm/lib/hooks/useViewportHeight.mjs.map +7 -0
  97. package/dist-esm/lib/options.mjs +2 -1
  98. package/dist-esm/lib/options.mjs.map +2 -2
  99. package/dist-esm/lib/utils/dom.mjs +1 -1
  100. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  101. package/dist-esm/lib/utils/richText.mjs +26 -0
  102. package/dist-esm/lib/utils/richText.mjs.map +7 -0
  103. package/dist-esm/version.mjs +3 -3
  104. package/dist-esm/version.mjs.map +1 -1
  105. package/editor.css +127 -13
  106. package/package.json +10 -7
  107. package/src/index.ts +15 -0
  108. package/src/lib/TldrawEditor.tsx +52 -4
  109. package/src/lib/components/LiveCollaborators.tsx +5 -0
  110. package/src/lib/components/Shape.tsx +9 -1
  111. package/src/lib/components/default-components/DefaultBrush.tsx +1 -0
  112. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -0
  113. package/src/lib/components/default-components/DefaultCursor.tsx +1 -0
  114. package/src/lib/components/default-components/DefaultScribble.tsx +1 -0
  115. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -0
  116. package/src/lib/editor/Editor.ts +91 -7
  117. package/src/lib/editor/managers/FontManager.ts +251 -0
  118. package/src/lib/editor/managers/TextManager.ts +42 -17
  119. package/src/lib/editor/shapes/ShapeUtil.ts +13 -0
  120. package/src/lib/editor/types/emit-types.ts +1 -0
  121. package/src/lib/editor/types/external-content.ts +1 -0
  122. package/src/lib/exports/FontEmbedder.ts +13 -1
  123. package/src/lib/exports/StyleEmbedder.ts +1 -1
  124. package/src/lib/exports/exportToSvg.tsx +4 -3
  125. package/src/lib/exports/getSvgJsx.tsx +22 -2
  126. package/src/lib/exports/parseCss.ts +1 -0
  127. package/src/lib/hooks/useCanvasEvents.ts +2 -1
  128. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -0
  129. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +29 -0
  130. package/src/lib/hooks/usePassThroughWheelEvents.ts +0 -1
  131. package/src/lib/hooks/usePeerIds.ts +1 -1
  132. package/src/lib/hooks/usePresence.ts +2 -2
  133. package/src/lib/hooks/useViewportHeight.ts +37 -0
  134. package/src/lib/options.ts +7 -0
  135. package/src/lib/utils/dom.ts +1 -1
  136. package/src/lib/utils/richText.ts +72 -0
  137. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/options.ts"],
4
- "sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tenableToolbarKeyboardShortcuts: true,\n} as const satisfies TldrawOptions\n"],
5
- "mappings": "AAAA,SAAwB,gBAAgB;AA0EjC,MAAM,uBAAuB;AAAA,EACnC,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,WAAW;AAAA,IACV,EAAE,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/B,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,GAAG;AAAA,IAClC,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,EAAE;AAAA,IAC7B,EAAE,KAAK,KAAK,KAAK,KAAK,MAAM,EAAE;AAAA,EAC/B;AAAA,EACA,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,iCAAiC;AAAA,EACjC,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,gBAAgB;AAAA,EAChB,gCAAgC;AACjC;",
4
+ "sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n\t/**\n\t * The maximum number of fonts that will be loaded while blocking the main rendering of the\n\t * canvas. If there are more than this number of fonts needed, we'll just show the canvas right\n\t * away and let the fonts load in in the background.\n\t */\n\treadonly maxFontsToLoadBeforeRender: number\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tenableToolbarKeyboardShortcuts: true,\n\tmaxFontsToLoadBeforeRender: Infinity,\n} as const satisfies TldrawOptions\n"],
5
+ "mappings": "AAAA,SAAwB,gBAAgB;AAgFjC,MAAM,uBAAuB;AAAA,EACnC,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,WAAW;AAAA,IACV,EAAE,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/B,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,GAAG;AAAA,IAClC,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,EAAE;AAAA,IAC7B,EAAE,KAAK,KAAK,KAAK,KAAK,MAAM,EAAE;AAAA,EAC/B;AAAA,EACA,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,iCAAiC;AAAA,EACjC,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,gBAAgB;AAAA,EAChB,gCAAgC;AAAA,EAChC,4BAA4B;AAC7B;",
6
6
  "names": []
7
7
  }
@@ -43,7 +43,7 @@ const setStyleProperty = (elm, property, value) => {
43
43
  const INPUTS = ["input", "select", "button", "textarea"];
44
44
  function activeElementShouldCaptureKeys() {
45
45
  const { activeElement } = document;
46
- return !!(activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1));
46
+ return !!(activeElement && (activeElement.isContentEditable || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1));
47
47
  }
48
48
  export {
49
49
  activeElementShouldCaptureKeys,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/dom.ts"],
4
- "sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n\nconst INPUTS = ['input', 'select', 'button', 'textarea']\n\n/** @internal */\nexport function activeElementShouldCaptureKeys() {\n\tconst { activeElement } = document\n\treturn !!(\n\t\tactiveElement &&\n\t\t(activeElement.getAttribute('contenteditable') ||\n\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)\n\t)\n}\n"],
5
- "mappings": "AAgBA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;AAEA,MAAM,SAAS,CAAC,SAAS,UAAU,UAAU,UAAU;AAGhD,SAAS,iCAAiC;AAChD,QAAM,EAAE,cAAc,IAAI;AAC1B,SAAO,CAAC,EACP,kBACC,cAAc,aAAa,iBAAiB,KAC5C,OAAO,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI;AAEzD;",
4
+ "sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n\nconst INPUTS = ['input', 'select', 'button', 'textarea']\n\n/** @internal */\nexport function activeElementShouldCaptureKeys() {\n\tconst { activeElement } = document\n\treturn !!(\n\t\tactiveElement &&\n\t\t((activeElement as HTMLElement).isContentEditable ||\n\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)\n\t)\n}\n"],
5
+ "mappings": "AAgBA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;AAEA,MAAM,SAAS,CAAC,SAAS,UAAU,UAAU,UAAU;AAGhD,SAAS,iCAAiC;AAChD,QAAM,EAAE,cAAc,IAAI;AAC1B,SAAO,CAAC,EACP,kBACE,cAA8B,qBAC/B,OAAO,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI;AAEzD;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,26 @@
1
+ import { getSchema } from "@tiptap/core";
2
+ import { Node } from "@tiptap/pm/model";
3
+ import { assert } from "@tldraw/utils";
4
+ function getFontsFromRichText(editor, richText, initialState) {
5
+ const { tipTapConfig, addFontsFromNode } = editor.getTextOptions();
6
+ assert(tipTapConfig, "textOptions.tipTapConfig must be set to use rich text");
7
+ assert(addFontsFromNode, "textOptions.addFontsFromNode must be set to use rich text");
8
+ const schema = getSchema(tipTapConfig.extensions ?? []);
9
+ const rootNode = Node.fromJSON(schema, richText);
10
+ const fonts = /* @__PURE__ */ new Set();
11
+ function addFont(font) {
12
+ fonts.add(font);
13
+ }
14
+ function visit(node, state) {
15
+ state = addFontsFromNode(node, state, addFont);
16
+ for (const child of node.children) {
17
+ visit(child, state);
18
+ }
19
+ }
20
+ visit(rootNode, initialState);
21
+ return Array.from(fonts);
22
+ }
23
+ export {
24
+ getFontsFromRichText
25
+ };
26
+ //# sourceMappingURL=richText.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/utils/richText.ts"],
4
+ "sourcesContent": ["import { getSchema, JSONContent, Editor as TTEditor } from '@tiptap/core'\nimport { Node } from '@tiptap/pm/model'\nimport { EditorProviderProps } from '@tiptap/react'\nimport { TLRichText } from '@tldraw/tlschema'\nimport { assert } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { TLFontFace } from '../editor/managers/FontManager'\n\n/**\n * This is the TipTap editor! Docs are {@link https://tiptap.dev/docs}.\n *\n * @public\n */\nexport type TiptapEditor = TTEditor\n\n/**\n * A TipTap node. See {@link https://tiptap.dev/docs}.\n * @public\n */\nexport type TiptapNode = Node\n\n/** @public */\nexport interface TLTextOptions {\n\ttipTapConfig?: EditorProviderProps\n\taddFontsFromNode?: RichTextFontVisitor\n}\n\n/** @public */\nexport interface RichTextFontVisitorState {\n\treadonly family: string\n\treadonly weight: string\n\treadonly style: string\n}\n\n/** @public */\nexport type RichTextFontVisitor = (\n\tnode: TiptapNode,\n\tstate: RichTextFontVisitorState,\n\taddFont: (font: TLFontFace) => void\n) => RichTextFontVisitorState\n\n/** @public */\nexport function getFontsFromRichText(\n\teditor: Editor,\n\trichText: TLRichText,\n\tinitialState: RichTextFontVisitorState\n) {\n\tconst { tipTapConfig, addFontsFromNode } = editor.getTextOptions()\n\tassert(tipTapConfig, 'textOptions.tipTapConfig must be set to use rich text')\n\tassert(addFontsFromNode, 'textOptions.addFontsFromNode must be set to use rich text')\n\n\tconst schema = getSchema(tipTapConfig.extensions ?? [])\n\tconst rootNode = Node.fromJSON(schema, richText as JSONContent)\n\n\tconst fonts = new Set<TLFontFace>()\n\n\tfunction addFont(font: TLFontFace) {\n\t\tfonts.add(font)\n\t}\n\n\tfunction visit(node: TiptapNode, state: RichTextFontVisitorState) {\n\t\tstate = addFontsFromNode!(node, state, addFont)\n\n\t\tfor (const child of node.children) {\n\t\t\tvisit(child, state)\n\t\t}\n\t}\n\n\tvisit(rootNode, initialState)\n\n\treturn Array.from(fonts)\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAkD;AAC3D,SAAS,YAAY;AAGrB,SAAS,cAAc;AAsChB,SAAS,qBACf,QACA,UACA,cACC;AACD,QAAM,EAAE,cAAc,iBAAiB,IAAI,OAAO,eAAe;AACjE,SAAO,cAAc,uDAAuD;AAC5E,SAAO,kBAAkB,2DAA2D;AAEpF,QAAM,SAAS,UAAU,aAAa,cAAc,CAAC,CAAC;AACtD,QAAM,WAAW,KAAK,SAAS,QAAQ,QAAuB;AAE9D,QAAM,QAAQ,oBAAI,IAAgB;AAElC,WAAS,QAAQ,MAAkB;AAClC,UAAM,IAAI,IAAI;AAAA,EACf;AAEA,WAAS,MAAM,MAAkB,OAAiC;AACjE,YAAQ,iBAAkB,MAAM,OAAO,OAAO;AAE9C,eAAW,SAAS,KAAK,UAAU;AAClC,YAAM,OAAO,KAAK;AAAA,IACnB;AAAA,EACD;AAEA,QAAM,UAAU,YAAY;AAE5B,SAAO,MAAM,KAAK,KAAK;AACxB;",
6
+ "names": []
7
+ }
@@ -1,8 +1,8 @@
1
- const version = "3.9.0";
1
+ const version = "3.10.0-canary.3176429f9d1b";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-03-03T11:42:22.746Z",
5
- patch: "2025-03-03T11:42:22.746Z"
4
+ minor: "2025-03-04T14:05:00.041Z",
5
+ patch: "2025-03-04T14:05:00.041Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.9.0'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-03-03T11:42:22.746Z',\n\tpatch: '2025-03-03T11:42:22.746Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.10.0-canary.3176429f9d1b'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-03-04T14:05:00.041Z',\n\tpatch: '2025-03-04T14:05:00.041Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/editor.css CHANGED
@@ -156,6 +156,8 @@
156
156
  --color-text-1: hsl(0, 0%, 18%);
157
157
  --color-text-3: hsl(220, 2%, 65%);
158
158
  --color-text-shadow: hsl(0, 0%, 100%);
159
+ --color-text-highlight: hsl(52, 100%, 50%);
160
+ --color-text-highlight-p3: color(display-p3 0.972 0.8205 0.05);
159
161
  --color-primary: hsl(214, 84%, 56%);
160
162
  --color-success: hsl(123, 46%, 34%);
161
163
  --color-info: hsl(201, 98%, 41%);
@@ -202,6 +204,8 @@
202
204
  --color-text-1: hsl(0, 0%, 85%);
203
205
  --color-text-3: hsl(210, 6%, 45%);
204
206
  --color-text-shadow: hsl(210, 13%, 18%);
207
+ --color-text-highlight: hsl(52, 100%, 41%);
208
+ --color-text-highlight-p3: color(display-p3 0.8078 0.6225 0.0312);
205
209
  --color-primary: hsl(214, 84%, 56%);
206
210
  --color-success: hsl(123, 38%, 57%);
207
211
  --color-info: hsl(199, 92%, 56%);
@@ -248,7 +252,7 @@
248
252
  input,
249
253
  *[contenteditable],
250
254
  *[contenteditable] * {
251
- -webkit-user-select: text;
255
+ user-select: text;
252
256
  }
253
257
 
254
258
  /* -------------------------------------------------- */
@@ -722,17 +726,14 @@ input,
722
726
  opacity: 0.7;
723
727
  }
724
728
 
725
- /* -------------------------------------------------- */
726
- /* Spinner */
727
- /* -------------------------------------------------- */
728
-
729
+ /* --------------------- Spinner -------------------- */
729
730
  @keyframes spinner {
730
731
  to {
731
732
  transform: rotate(360deg);
732
733
  }
733
734
  }
734
735
 
735
- /* ------------------- Text Shape ------------------- */
736
+ /* ---------------------- Text ---------------------- */
736
737
 
737
738
  .tl-text-shape-label {
738
739
  position: relative;
@@ -782,10 +783,14 @@ input,
782
783
  text-align: right;
783
784
  }
784
785
 
785
- .tl-text-wrapper[data-isediting='true'] .tl-text-content {
786
+ .tl-plain-text-wrapper[data-isediting='true'] .tl-text-content {
786
787
  opacity: 0;
787
788
  }
788
789
 
790
+ .tl-rich-text-wrapper[data-isediting='true'] .tl-text-content {
791
+ display: none;
792
+ }
793
+
789
794
  .tl-text {
790
795
  /* remove overflow from textarea on windows */
791
796
  margin: 0px;
@@ -852,7 +857,6 @@ input,
852
857
  width: 100%;
853
858
  min-width: 1px;
854
859
  min-height: 1px;
855
- overflow: visible;
856
860
  outline: none;
857
861
  }
858
862
 
@@ -868,6 +872,7 @@ input,
868
872
  }
869
873
 
870
874
  .tl-text-content {
875
+ overflow: visible;
871
876
  pointer-events: none;
872
877
  }
873
878
 
@@ -875,10 +880,17 @@ input,
875
880
  resize: none;
876
881
  user-select: all;
877
882
  -webkit-user-select: text;
878
- overflow: hidden;
879
883
  cursor: var(--tl-cursor-text);
880
884
  }
881
885
 
886
+ .tl-text-input:not(.tl-rich-text) {
887
+ /*
888
+ * Note: this `overflow: hidden` is key for scrollbars to not show up
889
+ * plaintext/<textarea> editors.
890
+ */
891
+ overflow: hidden;
892
+ }
893
+
882
894
  .tl-text-wrapper[data-isediting='false'] .tl-text-input,
883
895
  .tl-arrow-label[data-isediting='false'] .tl-text-input {
884
896
  opacity: 0;
@@ -891,6 +903,109 @@ input,
891
903
  text-shadow: none;
892
904
  }
893
905
 
906
+ .tl-rich-text[data-iseditinganything='true'] {
907
+ cursor: var(--tl-cursor-text);
908
+ }
909
+
910
+ .tl-rich-text .ProseMirror {
911
+ word-wrap: break-word;
912
+ overflow-wrap: break-word;
913
+ white-space: pre-wrap;
914
+
915
+ /**
916
+ * Note: ProseMirror disables this in https://github.com/ProseMirror/prosemirror-view/commit/6b3b2205e2f3029cb8e8e86c55a190a22491df31
917
+ * However, that was from 8 years ago and the browser caret issue
918
+ * it mentions seems to be fixed. So, we're re-enabling it.
919
+ * We'll tell ProseMirror maybe to get rid of this on their end.
920
+ */
921
+ -webkit-font-variant-ligatures: inherit;
922
+ font-variant-ligatures: inherit;
923
+ font-feature-settings: inherit;
924
+
925
+ /**
926
+ * N.B. This following CSS Rule comes standard with the tiptap editor.
927
+ * Combined with the above rule that it supersedes, it allows for
928
+ * the auto-linking to work in text. Say, when typing example.com
929
+ * this helps it automatically turn that bit of text into a link.
930
+ *
931
+ * However, specifically, the break-spaces features seems to cause
932
+ * rendering differences when going in-and-out of edit mode. For example,
933
+ * the statically rendered text 'the rain in spain falls mainly on the plain'
934
+ * in a note shape will render differently when going in-and-out of edit mode.
935
+ *
936
+ * So, this is commented out to help make both the autolinking work (which now
937
+ * relies on the white-space: pre-wrap to work and to make the static/edit-mode
938
+ * rendering consistent.
939
+ * In the future, we might consider just making the static rendering just use
940
+ * white-space: break-spaces to make it consistent with the edit mode if need be.
941
+ *
942
+ * Also, the amount of ink I've spilt in my career writing comments explaining
943
+ * white-space in contenteditable is incredible.
944
+ *
945
+ /* white-space: break-spaces; */
946
+ }
947
+
948
+ .tl-rich-text p {
949
+ margin: 0;
950
+ }
951
+
952
+ .tl-rich-text ul,
953
+ .tl-rich-text ol {
954
+ text-align: left;
955
+ margin: 0;
956
+ }
957
+
958
+ .tl-rich-text h1,
959
+ .tl-rich-text h2,
960
+ .tl-rich-text h3,
961
+ .tl-rich-text h4,
962
+ .tl-rich-text h5,
963
+ .tl-rich-text h6 {
964
+ margin-top: 5px;
965
+ margin-bottom: 10px;
966
+ }
967
+
968
+ .tl-rich-text a {
969
+ color: var(--color-primary);
970
+ text-decoration: underline;
971
+ }
972
+
973
+ .tl-rich-text code {
974
+ font-family: var(--tl-font-mono);
975
+ }
976
+
977
+ .tl-rich-text mark {
978
+ background-color: #fddd00;
979
+ color: currentColor;
980
+ border-radius: 2px;
981
+ }
982
+
983
+ .tl-theme__light .tl-rich-text mark {
984
+ text-shadow: none;
985
+ }
986
+
987
+ .tl-theme__dark .tl-rich-text mark {
988
+ background-color: var(--color-text-highlight);
989
+ color: currentColor;
990
+ }
991
+
992
+ @supports (color: color(display-p3 1 1 1)) {
993
+ @media (color-gamut: p3) {
994
+ .tl-container:not(.tl-theme__force-sRGB) .tl-rich-text mark {
995
+ background-color: var(--color-text-highlight-p3);
996
+ }
997
+ }
998
+ }
999
+
1000
+ .tl-text-label__inner > .tl-text-input.tl-rich-text {
1001
+ display: none;
1002
+ position: static;
1003
+ }
1004
+
1005
+ .tl-text-wrapper[data-isediting='true'] .tl-rich-text {
1006
+ display: block;
1007
+ }
1008
+
894
1009
  /* ------------------- Snap Lines ------------------- */
895
1010
 
896
1011
  .tl-snap-indicator {
@@ -1175,10 +1290,6 @@ input,
1175
1290
  min-height: auto;
1176
1291
  }
1177
1292
 
1178
- .tl-text-label[data-isediting='true'] p {
1179
- opacity: 0;
1180
- }
1181
-
1182
1293
  .tl-arrow-hint {
1183
1294
  stroke: var(--color-text-1);
1184
1295
  fill: none;
@@ -1270,6 +1381,9 @@ input,
1270
1381
  opacity: 0;
1271
1382
  animation: fade-in 0.2s ease-in-out forwards;
1272
1383
  animation-delay: 0.2s;
1384
+ position: absolute;
1385
+ inset: 0px;
1386
+ z-index: var(--layer-canvas-blocker);
1273
1387
  }
1274
1388
 
1275
1389
  @keyframes fade-in {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.9.0",
4
+ "version": "3.10.0-canary.3176429f9d1b",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -45,12 +45,15 @@
45
45
  "lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
46
46
  },
47
47
  "dependencies": {
48
- "@tldraw/state": "3.9.0",
49
- "@tldraw/state-react": "3.9.0",
50
- "@tldraw/store": "3.9.0",
51
- "@tldraw/tlschema": "3.9.0",
52
- "@tldraw/utils": "3.9.0",
53
- "@tldraw/validate": "3.9.0",
48
+ "@tiptap/core": "^2.9.1",
49
+ "@tiptap/pm": "^2.9.1",
50
+ "@tiptap/react": "^2.9.1",
51
+ "@tldraw/state": "3.10.0-canary.3176429f9d1b",
52
+ "@tldraw/state-react": "3.10.0-canary.3176429f9d1b",
53
+ "@tldraw/store": "3.10.0-canary.3176429f9d1b",
54
+ "@tldraw/tlschema": "3.10.0-canary.3176429f9d1b",
55
+ "@tldraw/utils": "3.10.0-canary.3176429f9d1b",
56
+ "@tldraw/validate": "3.10.0-canary.3176429f9d1b",
54
57
  "@types/core-js": "^2.5.8",
55
58
  "@use-gesture/react": "^10.3.1",
56
59
  "classnames": "^2.5.1",
package/src/index.ts CHANGED
@@ -26,6 +26,14 @@ export {
26
26
  useValue,
27
27
  } from '@tldraw/state-react'
28
28
  export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
29
+ export {
30
+ getFontsFromRichText,
31
+ type RichTextFontVisitor,
32
+ type RichTextFontVisitorState,
33
+ type TLTextOptions,
34
+ type TiptapEditor,
35
+ type TiptapNode,
36
+ } from './lib/utils/richText'
29
37
  export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
30
38
  // eslint-disable-next-line local/no-export-star
31
39
  export * from '@tldraw/store'
@@ -163,6 +171,11 @@ export {
163
171
  } from './lib/editor/bindings/BindingUtil'
164
172
  export { ClickManager, type TLClickState } from './lib/editor/managers/ClickManager'
165
173
  export { EdgeScrollManager } from './lib/editor/managers/EdgeScrollManager'
174
+ export {
175
+ FontManager,
176
+ type TLFontFace,
177
+ type TLFontFaceSource,
178
+ } from './lib/editor/managers/FontManager'
166
179
  export { HistoryManager } from './lib/editor/managers/HistoryManager'
167
180
  export { ScribbleManager, type ScribbleItem } from './lib/editor/managers/ScribbleManager'
168
181
  export {
@@ -299,6 +312,7 @@ export { useIsCropping } from './lib/hooks/useIsCropping'
299
312
  export { useIsDarkMode } from './lib/hooks/useIsDarkMode'
300
313
  export { useIsEditing } from './lib/hooks/useIsEditing'
301
314
  export { useLocalStore } from './lib/hooks/useLocalStore'
315
+ export { usePassThroughMouseOverEvents } from './lib/hooks/usePassThroughMouseOverEvents'
302
316
  export { usePassThroughWheelEvents } from './lib/hooks/usePassThroughWheelEvents'
303
317
  export { usePeerIds } from './lib/hooks/usePeerIds'
304
318
  export { usePresence } from './lib/hooks/usePresence'
@@ -313,6 +327,7 @@ export {
313
327
  export { useSelectionEvents } from './lib/hooks/useSelectionEvents'
314
328
  export { useTLSchemaFromUtils, useTLStore } from './lib/hooks/useTLStore'
315
329
  export { useTransform } from './lib/hooks/useTransform'
330
+ export { useViewportHeight } from './lib/hooks/useViewportHeight'
316
331
  export {
317
332
  LicenseManager,
318
333
  type InvalidLicenseKeyResult,
@@ -14,7 +14,6 @@ import React, {
14
14
  } from 'react'
15
15
 
16
16
  import classNames from 'classnames'
17
- import { TLDeepLinkOptions } from '..'
18
17
  import { version } from '../version'
19
18
  import { OptionalErrorBoundary } from './components/ErrorBoundary'
20
19
  import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
@@ -44,7 +43,9 @@ import { useZoomCss } from './hooks/useZoomCss'
44
43
  import { LicenseProvider } from './license/LicenseProvider'
45
44
  import { Watermark } from './license/Watermark'
46
45
  import { TldrawOptions } from './options'
46
+ import { TLDeepLinkOptions } from './utils/deepLinks'
47
47
  import { stopEventPropagation } from './utils/dom'
48
+ import { TLTextOptions } from './utils/richText'
48
49
  import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
49
50
 
50
51
  /**
@@ -167,6 +168,11 @@ export interface TldrawEditorBaseProps {
167
168
  */
168
169
  cameraOptions?: Partial<TLCameraOptions>
169
170
 
171
+ /**
172
+ * Text options for the editor.
173
+ */
174
+ textOptions?: TLTextOptions
175
+
170
176
  /**
171
177
  * Options for the editor.
172
178
  */
@@ -190,6 +196,11 @@ export interface TldrawEditorBaseProps {
190
196
  * remain in the store and participate in all other operations.
191
197
  */
192
198
  isShapeHidden?(shape: TLShape, editor: Editor): boolean
199
+
200
+ /**
201
+ * The URLs for the fonts to use in the editor.
202
+ */
203
+ assetUrls?: { fonts?: { [key: string]: string | undefined } }
193
204
  }
194
205
 
195
206
  /**
@@ -372,10 +383,12 @@ function TldrawEditorWithReadyStore({
372
383
  autoFocus = true,
373
384
  inferDarkMode,
374
385
  cameraOptions,
386
+ textOptions,
375
387
  options,
376
388
  licenseKey,
377
389
  deepLinks: _deepLinks,
378
390
  isShapeHidden,
391
+ assetUrls,
379
392
  }: Required<
380
393
  TldrawEditorProps & {
381
394
  store: TLStore
@@ -430,9 +443,11 @@ function TldrawEditorWithReadyStore({
430
443
  autoFocus,
431
444
  inferDarkMode,
432
445
  cameraOptions,
446
+ textOptions,
433
447
  options,
434
448
  licenseKey,
435
449
  isShapeHidden,
450
+ fontAssetUrls: assetUrls?.fonts,
436
451
  })
437
452
 
438
453
  editor.updateViewportScreenBounds(canvasRef.current ?? container)
@@ -467,6 +482,8 @@ function TldrawEditorWithReadyStore({
467
482
  setEditor,
468
483
  licenseKey,
469
484
  isShapeHidden,
485
+ textOptions,
486
+ assetUrls,
470
487
  ]
471
488
  )
472
489
 
@@ -532,10 +549,41 @@ function TldrawEditorWithReadyStore({
532
549
  [editor, autoFocus]
533
550
  )
534
551
 
535
- const { Canvas } = useEditorComponents()
552
+ const [_fontLoadingState, setFontLoadingState] = useState<{
553
+ editor: Editor
554
+ isLoaded: boolean
555
+ } | null>(null)
556
+ let fontLoadingState = _fontLoadingState
557
+ if (editor !== fontLoadingState?.editor) {
558
+ fontLoadingState = null
559
+ }
560
+ useEffect(() => {
561
+ if (!editor) return
562
+ let isCancelled = false
563
+
564
+ setFontLoadingState({ editor, isLoaded: false })
565
+
566
+ editor.fonts
567
+ .loadRequiredFontsForCurrentPage(editor.options.maxFontsToLoadBeforeRender)
568
+ .finally(() => {
569
+ if (isCancelled) return
570
+ setFontLoadingState({ editor, isLoaded: true })
571
+ })
572
+
573
+ return () => {
574
+ isCancelled = true
575
+ }
576
+ }, [editor])
536
577
 
537
- if (!editor) {
538
- return <div className="tl-canvas" ref={canvasRef} />
578
+ const { Canvas, LoadingScreen } = useEditorComponents()
579
+
580
+ if (!editor || !fontLoadingState?.isLoaded) {
581
+ return (
582
+ <>
583
+ {LoadingScreen && <LoadingScreen />}
584
+ <div className="tl-canvas" ref={canvasRef} />
585
+ </>
586
+ )
539
587
  }
540
588
 
541
589
  return (
@@ -92,6 +92,7 @@ const Collaborator = track(function Collaborator({
92
92
  <CollaboratorBrush
93
93
  className="tl-collaborator__brush"
94
94
  key={userId + '_brush'}
95
+ userId={userId}
95
96
  brush={brush}
96
97
  color={color}
97
98
  opacity={0.1}
@@ -101,6 +102,7 @@ const Collaborator = track(function Collaborator({
101
102
  <CollaboratorCursor
102
103
  className="tl-collaborator__cursor"
103
104
  key={userId + '_cursor'}
105
+ userId={userId}
104
106
  point={cursor}
105
107
  color={color}
106
108
  zoom={zoomLevel}
@@ -111,6 +113,7 @@ const Collaborator = track(function Collaborator({
111
113
  <CollaboratorHint
112
114
  className="tl-collaborator__cursor-hint"
113
115
  key={userId + '_cursor_hint'}
116
+ userId={userId}
114
117
  point={cursor}
115
118
  color={color}
116
119
  zoom={zoomLevel}
@@ -123,6 +126,7 @@ const Collaborator = track(function Collaborator({
123
126
  <CollaboratorScribble
124
127
  key={userId + '_scribble_' + scribble.id}
125
128
  className="tl-collaborator__scribble"
129
+ userId={userId}
126
130
  scribble={scribble}
127
131
  color={color}
128
132
  zoom={zoomLevel}
@@ -138,6 +142,7 @@ const Collaborator = track(function Collaborator({
138
142
  <CollaboratorShapeIndicator
139
143
  className="tl-collaborator__shape-indicator"
140
144
  key={userId + '_' + shapeId}
145
+ userId={userId}
141
146
  shapeId={shapeId}
142
147
  color={color}
143
148
  opacity={0.5}
@@ -1,6 +1,7 @@
1
+ import { react } from '@tldraw/state'
1
2
  import { useQuickReactor, useStateTracking } from '@tldraw/state-react'
2
3
  import { TLShape, TLShapeId } from '@tldraw/tlschema'
3
- import { memo, useCallback, useRef } from 'react'
4
+ import { memo, useCallback, useEffect, useRef } from 'react'
4
5
  import { ShapeUtil } from '../editor/shapes/ShapeUtil'
5
6
  import { useEditor } from '../hooks/useEditor'
6
7
  import { useEditorComponents } from '../hooks/useEditorComponents'
@@ -41,6 +42,13 @@ export const Shape = memo(function Shape({
41
42
  const containerRef = useRef<HTMLDivElement>(null)
42
43
  const bgContainerRef = useRef<HTMLDivElement>(null)
43
44
 
45
+ useEffect(() => {
46
+ return react('load fonts', () => {
47
+ const fonts = editor.fonts.getShapeFontFaces(shape)
48
+ editor.fonts.requestFonts(fonts)
49
+ })
50
+ }, [editor, shape])
51
+
44
52
  const memoizedStuffRef = useRef({
45
53
  transform: '',
46
54
  clipPath: 'none',
@@ -5,6 +5,7 @@ import { toDomPrecision } from '../../primitives/utils'
5
5
 
6
6
  /** @public */
7
7
  export interface TLBrushProps {
8
+ userId?: string
8
9
  brush: BoxModel
9
10
  color?: string
10
11
  opacity?: number
@@ -9,6 +9,7 @@ import { clamp } from '../../primitives/utils'
9
9
 
10
10
  /** @public */
11
11
  export interface TLCollaboratorHintProps {
12
+ userId: string
12
13
  className?: string
13
14
  point: VecModel
14
15
  viewport: Box
@@ -6,6 +6,7 @@ import { useTransform } from '../../hooks/useTransform'
6
6
 
7
7
  /** @public */
8
8
  export interface TLCursorProps {
9
+ userId: string
9
10
  className?: string
10
11
  point: VecModel | null
11
12
  zoom: number
@@ -4,6 +4,7 @@ import { getSvgPathFromPoints } from '../../utils/getSvgPathFromPoints'
4
4
 
5
5
  /** @public */
6
6
  export interface TLScribbleProps {
7
+ userId?: string
7
8
  scribble: TLScribble
8
9
  zoom: number
9
10
  color?: string
@@ -38,6 +38,7 @@ const InnerIndicator = ({ editor, id }: { editor: Editor; id: TLShapeId }) => {
38
38
 
39
39
  /** @public */
40
40
  export interface TLShapeIndicatorProps {
41
+ userId?: string
41
42
  shapeId: TLShapeId
42
43
  color?: string | undefined
43
44
  opacity?: number