@tldraw/editor 3.9.0 → 3.10.0-canary.15f6aaa3d2d3

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 (131) hide show
  1. package/dist-cjs/index.d.ts +232 -3
  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 +63 -7
  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/useViewportHeight.js +56 -0
  43. package/dist-cjs/lib/hooks/useViewportHeight.js.map +7 -0
  44. package/dist-cjs/lib/options.js +2 -1
  45. package/dist-cjs/lib/options.js.map +2 -2
  46. package/dist-cjs/lib/utils/dom.js +1 -1
  47. package/dist-cjs/lib/utils/dom.js.map +2 -2
  48. package/dist-cjs/lib/utils/richText.js +46 -0
  49. package/dist-cjs/lib/utils/richText.js.map +7 -0
  50. package/dist-cjs/version.js +3 -3
  51. package/dist-cjs/version.js.map +1 -1
  52. package/dist-esm/index.d.mts +232 -3
  53. package/dist-esm/index.mjs +13 -1
  54. package/dist-esm/index.mjs.map +2 -2
  55. package/dist-esm/lib/TldrawEditor.mjs +33 -7
  56. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  57. package/dist-esm/lib/components/LiveCollaborators.mjs +5 -0
  58. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  59. package/dist-esm/lib/components/Shape.mjs +8 -1
  60. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  61. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  62. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  63. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  64. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  65. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  66. package/dist-esm/lib/editor/Editor.mjs +71 -8
  67. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  68. package/dist-esm/lib/editor/managers/FontManager.mjs +152 -0
  69. package/dist-esm/lib/editor/managers/FontManager.mjs.map +7 -0
  70. package/dist-esm/lib/editor/managers/TextManager.mjs +23 -17
  71. package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
  72. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +11 -0
  73. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  74. package/dist-esm/lib/exports/FontEmbedder.mjs +7 -2
  75. package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
  76. package/dist-esm/lib/exports/StyleEmbedder.mjs +1 -1
  77. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  78. package/dist-esm/lib/exports/exportToSvg.mjs +3 -2
  79. package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
  80. package/dist-esm/lib/exports/getSvgJsx.mjs +19 -2
  81. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  82. package/dist-esm/lib/exports/parseCss.mjs +1 -0
  83. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  84. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -2
  85. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  86. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +1 -1
  87. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  88. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +28 -0
  89. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +7 -0
  90. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  91. package/dist-esm/lib/hooks/useViewportHeight.mjs +36 -0
  92. package/dist-esm/lib/hooks/useViewportHeight.mjs.map +7 -0
  93. package/dist-esm/lib/options.mjs +2 -1
  94. package/dist-esm/lib/options.mjs.map +2 -2
  95. package/dist-esm/lib/utils/dom.mjs +1 -1
  96. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  97. package/dist-esm/lib/utils/richText.mjs +26 -0
  98. package/dist-esm/lib/utils/richText.mjs.map +7 -0
  99. package/dist-esm/version.mjs +3 -3
  100. package/dist-esm/version.mjs.map +1 -1
  101. package/editor.css +127 -13
  102. package/package.json +10 -7
  103. package/src/index.ts +15 -0
  104. package/src/lib/TldrawEditor.tsx +52 -4
  105. package/src/lib/components/LiveCollaborators.tsx +5 -0
  106. package/src/lib/components/Shape.tsx +9 -1
  107. package/src/lib/components/default-components/DefaultBrush.tsx +1 -0
  108. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -0
  109. package/src/lib/components/default-components/DefaultCursor.tsx +1 -0
  110. package/src/lib/components/default-components/DefaultScribble.tsx +1 -0
  111. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -0
  112. package/src/lib/editor/Editor.ts +91 -6
  113. package/src/lib/editor/managers/FontManager.ts +251 -0
  114. package/src/lib/editor/managers/TextManager.ts +42 -17
  115. package/src/lib/editor/shapes/ShapeUtil.ts +13 -0
  116. package/src/lib/editor/types/emit-types.ts +1 -0
  117. package/src/lib/editor/types/external-content.ts +1 -0
  118. package/src/lib/exports/FontEmbedder.ts +13 -1
  119. package/src/lib/exports/StyleEmbedder.ts +1 -1
  120. package/src/lib/exports/exportToSvg.tsx +4 -3
  121. package/src/lib/exports/getSvgJsx.tsx +22 -2
  122. package/src/lib/exports/parseCss.ts +1 -0
  123. package/src/lib/hooks/useCanvasEvents.ts +2 -1
  124. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -0
  125. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +29 -0
  126. package/src/lib/hooks/usePassThroughWheelEvents.ts +0 -1
  127. package/src/lib/hooks/useViewportHeight.ts +37 -0
  128. package/src/lib/options.ts +7 -0
  129. package/src/lib/utils/dom.ts +1 -1
  130. package/src/lib/utils/richText.ts +72 -0
  131. package/src/version.ts +3 -3
@@ -6,7 +6,7 @@ import {
6
6
  TLShapeId,
7
7
  getDefaultColorTheme,
8
8
  } from '@tldraw/tlschema'
9
- import { hasOwnProperty, promiseWithResolve } from '@tldraw/utils'
9
+ import { hasOwnProperty, promiseWithResolve, uniqueId } from '@tldraw/utils'
10
10
  import {
11
11
  ComponentType,
12
12
  Fragment,
@@ -21,6 +21,7 @@ import { flushSync } from 'react-dom'
21
21
  import { ErrorBoundary } from '../components/ErrorBoundary'
22
22
  import { InnerShape, InnerShapeBackground } from '../components/Shape'
23
23
  import { Editor, TLRenderingShape } from '../editor/Editor'
24
+ import { TLFontFace } from '../editor/managers/FontManager'
24
25
  import { ShapeUtil } from '../editor/shapes/ShapeUtil'
25
26
  import {
26
27
  SvgExportContext,
@@ -340,6 +341,25 @@ function SvgExport({
340
341
  })()
341
342
  }, [bbox, editor, exportContext, masksId, renderingShapes, singleFrameShapeId, stateAtom])
342
343
 
344
+ useEffect(() => {
345
+ const fontsInUse = new Set<TLFontFace>()
346
+ for (const { id } of renderingShapes) {
347
+ for (const font of editor.fonts.getShapeFontFaces(id)) {
348
+ fontsInUse.add(font)
349
+ }
350
+ }
351
+
352
+ for (const font of fontsInUse) {
353
+ addExportDef({
354
+ key: uniqueId(),
355
+ getElement: async () => {
356
+ const declaration = await editor.fonts.toEmbeddedCssDeclaration(font)
357
+ return <style>{declaration}</style>
358
+ },
359
+ })
360
+ }
361
+ }, [editor, renderingShapes, addExportDef])
362
+
343
363
  useEffect(() => {
344
364
  if (shapeElements === null) return
345
365
  onMount()
@@ -408,7 +428,7 @@ function ForeignObjectShape({
408
428
  y={bbox.minY}
409
429
  width={bbox.w}
410
430
  height={bbox.h}
411
- className="tl-shape-foreign-object"
431
+ className="tl-shape-foreign-object tl-export-embed-styles"
412
432
  >
413
433
  <div
414
434
  className={className}
@@ -134,6 +134,7 @@ const inheritedProperties = new Set([
134
134
  'list-style-type',
135
135
  'list-style',
136
136
  'orphans',
137
+ 'overflow-wrap',
137
138
  'quotes',
138
139
  'tab-size',
139
140
  'text-align',
@@ -100,8 +100,9 @@ export function useCanvasEvents() {
100
100
  if (
101
101
  e.target.tagName !== 'A' &&
102
102
  e.target.tagName !== 'TEXTAREA' &&
103
+ e.target.isContentEditable &&
103
104
  // When in EditingShape state, we are actually clicking on a 'DIV'
104
- // not A/TEXTAREA element yet. So, to preserve cursor position
105
+ // not A/TEXTAREA/contenteditable element yet. So, to preserve cursor position
105
106
  // for edit mode on mobile we need to not preventDefault.
106
107
  // TODO: Find out if we still need this preventDefault in general though.
107
108
  !(editor.getEditingShape() && e.target.className.includes('tl-text-content'))
@@ -25,6 +25,7 @@ export function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLE
25
25
  // Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input
26
26
  if (
27
27
  IGNORED_TAGS.includes((target as Element).tagName?.toLocaleLowerCase()) ||
28
+ (target as HTMLElement).isContentEditable ||
28
29
  editor.isIn('select.editing_shape')
29
30
  ) {
30
31
  return
@@ -0,0 +1,29 @@
1
+ import { RefObject, useEffect } from 'react'
2
+ import { preventDefault } from '../utils/dom'
3
+ import { useContainer } from './useContainer'
4
+
5
+ /** @public */
6
+ export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement>) {
7
+ if (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')
8
+ const container = useContainer()
9
+
10
+ useEffect(() => {
11
+ function onMouseOver(e: MouseEvent) {
12
+ if ((e as any).isSpecialRedispatchedEvent) return
13
+ preventDefault(e)
14
+ const cvs = container.querySelector('.tl-canvas')
15
+ if (!cvs) return
16
+ const newEvent = new PointerEvent(e.type, e as any)
17
+ ;(newEvent as any).isSpecialRedispatchedEvent = true
18
+ cvs.dispatchEvent(newEvent)
19
+ }
20
+
21
+ const elm = ref.current
22
+ if (!elm) return
23
+
24
+ elm.addEventListener('mouseover', onMouseOver, { passive: false })
25
+ return () => {
26
+ elm.removeEventListener('mouseover', onMouseOver)
27
+ }
28
+ }, [container, ref])
29
+ }
@@ -5,7 +5,6 @@ import { useContainer } from './useContainer'
5
5
  /** @public */
6
6
  export function usePassThroughWheelEvents(ref: RefObject<HTMLElement>) {
7
7
  if (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')
8
-
9
8
  const container = useContainer()
10
9
 
11
10
  useEffect(() => {
@@ -0,0 +1,37 @@
1
+ import { useLayoutEffect, useState } from 'react'
2
+
3
+ /*!
4
+ * BSD License: https://github.com/outline/rich-markdown-editor/blob/main/LICENSE
5
+ * Copyright (c) 2020 General Outline, Inc (https://www.getoutline.com/) and individual contributors.
6
+ *
7
+ * Returns the height of the viewport.
8
+ * This is mainly to account for virtual keyboards on mobile devices.
9
+ *
10
+ * N.B. On iOS, you have to take into account the offsetTop as well so that you get an accurate position
11
+ * while using the virtual keyboard.
12
+ */
13
+ /** @public */
14
+ export function useViewportHeight(): number {
15
+ const visualViewport = window.visualViewport
16
+ const [height, setHeight] = useState<number>(() =>
17
+ visualViewport ? visualViewport.height + visualViewport.offsetTop : window.innerHeight
18
+ )
19
+
20
+ useLayoutEffect(() => {
21
+ const handleResize = () => {
22
+ const visualViewport = window.visualViewport
23
+ setHeight(() =>
24
+ visualViewport ? visualViewport.height + visualViewport.offsetTop : window.innerHeight
25
+ )
26
+ }
27
+
28
+ window.visualViewport?.addEventListener('resize', handleResize)
29
+ window.visualViewport?.addEventListener('scroll', handleResize)
30
+
31
+ return () => {
32
+ window.visualViewport?.removeEventListener('resize', handleResize)
33
+ window.visualViewport?.removeEventListener('scroll', handleResize)
34
+ }
35
+ }, [])
36
+ return height
37
+ }
@@ -69,6 +69,12 @@ export interface TldrawOptions {
69
69
  * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.
70
70
  */
71
71
  readonly enableToolbarKeyboardShortcuts: boolean
72
+ /**
73
+ * The maximum number of fonts that will be loaded while blocking the main rendering of the
74
+ * canvas. If there are more than this number of fonts needed, we'll just show the canvas right
75
+ * away and let the fonts load in in the background.
76
+ */
77
+ readonly maxFontsToLoadBeforeRender: number
72
78
  }
73
79
 
74
80
  /** @public */
@@ -114,4 +120,5 @@ export const defaultTldrawOptions = {
114
120
  createTextOnCanvasDoubleClick: true,
115
121
  exportProvider: Fragment,
116
122
  enableToolbarKeyboardShortcuts: true,
123
+ maxFontsToLoadBeforeRender: Infinity,
117
124
  } as const satisfies TldrawOptions
@@ -98,7 +98,7 @@ export function activeElementShouldCaptureKeys() {
98
98
  const { activeElement } = document
99
99
  return !!(
100
100
  activeElement &&
101
- (activeElement.getAttribute('contenteditable') ||
101
+ ((activeElement as HTMLElement).isContentEditable ||
102
102
  INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)
103
103
  )
104
104
  }
@@ -0,0 +1,72 @@
1
+ import { getSchema, JSONContent, Editor as TTEditor } from '@tiptap/core'
2
+ import { Node } from '@tiptap/pm/model'
3
+ import { EditorProviderProps } from '@tiptap/react'
4
+ import { TLRichText } from '@tldraw/tlschema'
5
+ import { assert } from '@tldraw/utils'
6
+ import { Editor } from '../editor/Editor'
7
+ import { TLFontFace } from '../editor/managers/FontManager'
8
+
9
+ /**
10
+ * This is the TipTap editor! Docs are {@link https://tiptap.dev/docs}.
11
+ *
12
+ * @public
13
+ */
14
+ export type TiptapEditor = TTEditor
15
+
16
+ /**
17
+ * A TipTap node. See {@link https://tiptap.dev/docs}.
18
+ * @public
19
+ */
20
+ export type TiptapNode = Node
21
+
22
+ /** @public */
23
+ export interface TLTextOptions {
24
+ tipTapConfig?: EditorProviderProps
25
+ addFontsFromNode?: RichTextFontVisitor
26
+ }
27
+
28
+ /** @public */
29
+ export interface RichTextFontVisitorState {
30
+ readonly family: string
31
+ readonly weight: string
32
+ readonly style: string
33
+ }
34
+
35
+ /** @public */
36
+ export type RichTextFontVisitor = (
37
+ node: TiptapNode,
38
+ state: RichTextFontVisitorState,
39
+ addFont: (font: TLFontFace) => void
40
+ ) => RichTextFontVisitorState
41
+
42
+ /** @public */
43
+ export function getFontsFromRichText(
44
+ editor: Editor,
45
+ richText: TLRichText,
46
+ initialState: RichTextFontVisitorState
47
+ ) {
48
+ const { tipTapConfig, addFontsFromNode } = editor.getTextOptions()
49
+ assert(tipTapConfig, 'textOptions.tipTapConfig must be set to use rich text')
50
+ assert(addFontsFromNode, 'textOptions.addFontsFromNode must be set to use rich text')
51
+
52
+ const schema = getSchema(tipTapConfig.extensions ?? [])
53
+ const rootNode = Node.fromJSON(schema, richText as JSONContent)
54
+
55
+ const fonts = new Set<TLFontFace>()
56
+
57
+ function addFont(font: TLFontFace) {
58
+ fonts.add(font)
59
+ }
60
+
61
+ function visit(node: TiptapNode, state: RichTextFontVisitorState) {
62
+ state = addFontsFromNode!(node, state, addFont)
63
+
64
+ for (const child of node.children) {
65
+ visit(child, state)
66
+ }
67
+ }
68
+
69
+ visit(rootNode, initialState)
70
+
71
+ return Array.from(fonts)
72
+ }
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.9.0'
4
+ export const version = '3.10.0-canary.15f6aaa3d2d3'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-03-03T11:42:22.746Z',
8
- patch: '2025-03-03T11:42:22.746Z',
7
+ minor: '2025-03-04T09:47:44.047Z',
8
+ patch: '2025-03-04T09:47:44.047Z',
9
9
  }