@tldraw/editor 4.6.0-next.20de11b7e238 → 4.6.0-next.241e87d4700a

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 (156) hide show
  1. package/dist-cjs/index.d.ts +668 -96
  2. package/dist-cjs/index.js +16 -3
  3. package/dist-cjs/index.js.map +3 -3
  4. package/dist-cjs/lib/TldrawEditor.js +55 -12
  5. package/dist-cjs/lib/TldrawEditor.js.map +3 -3
  6. package/dist-cjs/lib/components/MenuClickCapture.js +16 -1
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +3 -3
  9. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +2 -2
  10. package/dist-cjs/lib/config/createTLStore.js +7 -0
  11. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  12. package/dist-cjs/lib/config/defaultAssets.js +36 -0
  13. package/dist-cjs/lib/config/defaultAssets.js.map +7 -0
  14. package/dist-cjs/lib/editor/Editor.js +215 -5
  15. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  16. package/dist-cjs/lib/editor/assets/AssetUtil.js +66 -0
  17. package/dist-cjs/lib/editor/assets/AssetUtil.js.map +7 -0
  18. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js +80 -0
  20. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js.map +7 -0
  21. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js +466 -0
  22. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js.map +7 -0
  23. package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js +17 -0
  24. package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js.map +7 -0
  25. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +106 -0
  26. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +7 -0
  27. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js +586 -0
  28. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js.map +7 -0
  29. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +6 -4
  30. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  31. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +11 -2
  32. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  33. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  34. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +6 -0
  36. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  37. package/dist-cjs/lib/editor/tools/StateNode.js +14 -17
  38. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  39. package/dist-cjs/lib/editor/types/SvgExportContext.js.map +2 -2
  40. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  41. package/dist-cjs/lib/exports/getSvgJsx.js +12 -7
  42. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  43. package/dist-cjs/lib/globals/environment.js +18 -1
  44. package/dist-cjs/lib/globals/environment.js.map +2 -2
  45. package/dist-cjs/lib/hooks/{useIsDarkMode.js → useColorMode.js} +14 -10
  46. package/dist-cjs/lib/hooks/useColorMode.js.map +7 -0
  47. package/dist-cjs/lib/hooks/useCursor.js +3 -7
  48. package/dist-cjs/lib/hooks/useCursor.js.map +2 -2
  49. package/dist-cjs/lib/hooks/useDarkMode.js +4 -4
  50. package/dist-cjs/lib/hooks/useDarkMode.js.map +2 -2
  51. package/dist-cjs/lib/utils/reparenting.js +2 -1
  52. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  53. package/dist-cjs/lib/utils/richText.js.map +2 -2
  54. package/dist-cjs/lib/utils/runtime.js +2 -1
  55. package/dist-cjs/lib/utils/runtime.js.map +2 -2
  56. package/dist-cjs/lib/utils/sync/hardReset.js +0 -8
  57. package/dist-cjs/lib/utils/sync/hardReset.js.map +2 -2
  58. package/dist-cjs/version.js +3 -3
  59. package/dist-cjs/version.js.map +1 -1
  60. package/dist-esm/index.d.mts +668 -96
  61. package/dist-esm/index.mjs +17 -6
  62. package/dist-esm/index.mjs.map +2 -2
  63. package/dist-esm/lib/TldrawEditor.mjs +58 -12
  64. package/dist-esm/lib/TldrawEditor.mjs.map +3 -3
  65. package/dist-esm/lib/components/MenuClickCapture.mjs +16 -1
  66. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  67. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +3 -3
  68. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +2 -2
  69. package/dist-esm/lib/config/createTLStore.mjs +10 -1
  70. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  71. package/dist-esm/lib/config/defaultAssets.mjs +16 -0
  72. package/dist-esm/lib/config/defaultAssets.mjs.map +7 -0
  73. package/dist-esm/lib/editor/Editor.mjs +215 -5
  74. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  75. package/dist-esm/lib/editor/assets/AssetUtil.mjs +46 -0
  76. package/dist-esm/lib/editor/assets/AssetUtil.mjs.map +7 -0
  77. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  78. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs +60 -0
  79. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs.map +7 -0
  80. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs +438 -0
  81. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs.map +7 -0
  82. package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs +1 -0
  83. package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs.map +7 -0
  84. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +88 -0
  85. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +7 -0
  86. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs +568 -0
  87. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs.map +7 -0
  88. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +6 -4
  89. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  90. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +11 -2
  91. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  92. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  93. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  94. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +6 -0
  95. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  96. package/dist-esm/lib/editor/tools/StateNode.mjs +14 -17
  97. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  98. package/dist-esm/lib/editor/types/SvgExportContext.mjs.map +2 -2
  99. package/dist-esm/lib/exports/getSvgJsx.mjs +12 -10
  100. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  101. package/dist-esm/lib/globals/environment.mjs +18 -1
  102. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  103. package/dist-esm/lib/hooks/useColorMode.mjs +19 -0
  104. package/dist-esm/lib/hooks/useColorMode.mjs.map +7 -0
  105. package/dist-esm/lib/hooks/useCursor.mjs +3 -7
  106. package/dist-esm/lib/hooks/useCursor.mjs.map +2 -2
  107. package/dist-esm/lib/hooks/useDarkMode.mjs +4 -4
  108. package/dist-esm/lib/hooks/useDarkMode.mjs.map +2 -2
  109. package/dist-esm/lib/utils/reparenting.mjs +2 -1
  110. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  111. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  112. package/dist-esm/lib/utils/runtime.mjs +2 -1
  113. package/dist-esm/lib/utils/runtime.mjs.map +2 -2
  114. package/dist-esm/lib/utils/sync/hardReset.mjs +0 -8
  115. package/dist-esm/lib/utils/sync/hardReset.mjs.map +2 -2
  116. package/dist-esm/version.mjs +3 -3
  117. package/dist-esm/version.mjs.map +1 -1
  118. package/editor.css +0 -33
  119. package/package.json +7 -7
  120. package/src/index.ts +23 -6
  121. package/src/lib/TldrawEditor.tsx +90 -13
  122. package/src/lib/components/MenuClickCapture.tsx +20 -0
  123. package/src/lib/components/default-components/CanvasShapeIndicators.tsx +3 -3
  124. package/src/lib/config/createTLStore.ts +22 -1
  125. package/src/lib/config/defaultAssets.ts +19 -0
  126. package/src/lib/editor/Editor.ts +301 -27
  127. package/src/lib/editor/assets/AssetUtil.ts +85 -0
  128. package/src/lib/editor/managers/FontManager/FontManager.test.ts +9 -2
  129. package/src/lib/editor/managers/FontManager/FontManager.ts +1 -67
  130. package/src/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.ts +82 -0
  131. package/src/lib/editor/managers/PerformanceManager/PerformanceManager.test.ts +522 -0
  132. package/src/lib/editor/managers/PerformanceManager/PerformanceManager.ts +583 -0
  133. package/src/lib/editor/managers/PerformanceManager/perf-types.ts +196 -0
  134. package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +116 -0
  135. package/src/lib/editor/managers/ThemeManager/defaultThemes.ts +605 -0
  136. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +23 -29
  137. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -3
  138. package/src/lib/editor/shapes/ShapeUtil.ts +28 -3
  139. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  140. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +7 -0
  141. package/src/lib/editor/tools/StateNode.ts +16 -18
  142. package/src/lib/editor/types/SvgExportContext.tsx +5 -0
  143. package/src/lib/editor/types/external-content.ts +1 -0
  144. package/src/lib/exports/getSvgJsx.tsx +21 -15
  145. package/src/lib/globals/environment.ts +18 -0
  146. package/src/lib/hooks/{useIsDarkMode.ts → useColorMode.ts} +9 -5
  147. package/src/lib/hooks/useCursor.ts +3 -7
  148. package/src/lib/hooks/useDarkMode.ts +4 -4
  149. package/src/lib/utils/reparenting.ts +6 -2
  150. package/src/lib/utils/richText.ts +1 -1
  151. package/src/lib/utils/runtime.ts +3 -1
  152. package/src/lib/utils/sync/hardReset.ts +0 -8
  153. package/src/version.ts +3 -3
  154. package/dist-cjs/lib/hooks/useIsDarkMode.js.map +0 -7
  155. package/dist-esm/lib/hooks/useIsDarkMode.mjs +0 -15
  156. package/dist-esm/lib/hooks/useIsDarkMode.mjs.map +0 -7
package/editor.css CHANGED
@@ -166,7 +166,6 @@
166
166
  /* UI */
167
167
  --tl-color-low: hsl(204, 16%, 94%);
168
168
  --tl-color-low-border: hsl(204, 16%, 92%);
169
- --tl-color-culled: hsl(204, 14%, 93%);
170
169
  --tl-color-muted-none: hsl(0, 0%, 0%, 0%);
171
170
  --tl-color-muted-0: hsl(0, 0%, 0%, 2%);
172
171
  --tl-color-muted-1: hsl(0, 0%, 0%, 10%);
@@ -224,7 +223,6 @@
224
223
  /* UI */
225
224
  --tl-color-low: hsl(260, 4.5%, 10.5%);
226
225
  --tl-color-low-border: hsl(207, 10%, 10%);
227
- --tl-color-culled: hsl(210, 11%, 19%);
228
226
  --tl-color-muted-none: hsl(0, 0%, 100%, 0%);
229
227
  --tl-color-muted-0: hsl(0, 0%, 100%, 2%);
230
228
  --tl-color-muted-1: hsl(0, 0%, 100%, 10%);
@@ -625,37 +623,6 @@ input,
625
623
  overflow-wrap: break-word;
626
624
  }
627
625
 
628
- .tl-text-wrapper[data-font='draw'] {
629
- font-family: var(--tl-font-draw);
630
- }
631
-
632
- .tl-text-wrapper[data-font='sans'] {
633
- font-family: var(--tl-font-sans);
634
- }
635
-
636
- .tl-text-wrapper[data-font='serif'] {
637
- font-family: var(--tl-font-serif);
638
- }
639
-
640
- .tl-text-wrapper[data-font='mono'] {
641
- font-family: var(--tl-font-mono);
642
- }
643
-
644
- .tl-text-wrapper[data-align='start'],
645
- .tl-text-wrapper[data-align='start-legacy'] {
646
- text-align: left;
647
- }
648
-
649
- .tl-text-wrapper[data-align='middle'],
650
- .tl-text-wrapper[data-align='middle-legacy'] {
651
- text-align: center;
652
- }
653
-
654
- .tl-text-wrapper[data-align='end'],
655
- .tl-text-wrapper[data-align='end-legacy'] {
656
- text-align: right;
657
- }
658
-
659
626
  .tl-plain-text-wrapper[data-isediting='true'] .tl-text-content {
660
627
  opacity: 0;
661
628
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "tldraw infinite canvas SDK (editor).",
4
- "version": "4.6.0-next.20de11b7e238",
4
+ "version": "4.6.0-next.241e87d4700a",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -49,12 +49,12 @@
49
49
  "@tiptap/core": "^3.12.1",
50
50
  "@tiptap/pm": "^3.12.1",
51
51
  "@tiptap/react": "^3.12.1",
52
- "@tldraw/state": "4.6.0-next.20de11b7e238",
53
- "@tldraw/state-react": "4.6.0-next.20de11b7e238",
54
- "@tldraw/store": "4.6.0-next.20de11b7e238",
55
- "@tldraw/tlschema": "4.6.0-next.20de11b7e238",
56
- "@tldraw/utils": "4.6.0-next.20de11b7e238",
57
- "@tldraw/validate": "4.6.0-next.20de11b7e238",
52
+ "@tldraw/state": "4.6.0-next.241e87d4700a",
53
+ "@tldraw/state-react": "4.6.0-next.241e87d4700a",
54
+ "@tldraw/store": "4.6.0-next.241e87d4700a",
55
+ "@tldraw/tlschema": "4.6.0-next.241e87d4700a",
56
+ "@tldraw/utils": "4.6.0-next.241e87d4700a",
57
+ "@tldraw/validate": "4.6.0-next.241e87d4700a",
58
58
  "classnames": "^2.5.1",
59
59
  "eventemitter3": "^4.0.7",
60
60
  "idb": "^7.1.1",
package/src/index.ts CHANGED
@@ -95,6 +95,7 @@ export {
95
95
  type TLStoreOptions,
96
96
  type TLStoreSchemaOptions,
97
97
  } from './lib/config/createTLStore'
98
+ export { type TLAnyAssetUtilConstructor } from './lib/config/defaultAssets'
98
99
  export { type TLAnyBindingUtilConstructor } from './lib/config/defaultBindings'
99
100
  export { coreShapes, type TLAnyShapeUtilConstructor } from './lib/config/defaultShapes'
100
101
  export {
@@ -121,6 +122,7 @@ export {
121
122
  type TLUserPreferences,
122
123
  } from './lib/config/TLUserPreferences'
123
124
  export { DEFAULT_ANIMATION_OPTIONS, DEFAULT_CAMERA_OPTIONS, SIDES } from './lib/constants'
125
+ export { AssetUtil, type TLAssetUtilConstructor } from './lib/editor/assets/AssetUtil'
124
126
  export {
125
127
  BindingUtil,
126
128
  type BindingOnChangeOptions,
@@ -140,11 +142,7 @@ export {
140
142
  } from './lib/editor/Editor'
141
143
  export { ClickManager, type TLClickState } from './lib/editor/managers/ClickManager/ClickManager'
142
144
  export { EdgeScrollManager } from './lib/editor/managers/EdgeScrollManager/EdgeScrollManager'
143
- export {
144
- FontManager,
145
- type TLFontFace,
146
- type TLFontFaceSource,
147
- } from './lib/editor/managers/FontManager/FontManager'
145
+ export { FontManager } from './lib/editor/managers/FontManager/FontManager'
148
146
  export { HistoryManager } from './lib/editor/managers/HistoryManager/HistoryManager'
149
147
  export { InputsManager } from './lib/editor/managers/InputsManager/InputsManager'
150
148
  export {
@@ -173,7 +171,24 @@ export {
173
171
  type TLMeasureTextOpts,
174
172
  type TLMeasureTextSpanOpts,
175
173
  } from './lib/editor/managers/TextManager/TextManager'
174
+ export { DEFAULT_THEME } from './lib/editor/managers/ThemeManager/defaultThemes'
175
+ export { ThemeManager, resolveThemes } from './lib/editor/managers/ThemeManager/ThemeManager'
176
176
  export { TickManager } from './lib/editor/managers/TickManager/TickManager'
177
+ export { PerformanceApiAdapter } from './lib/editor/managers/PerformanceManager/PerformanceApiAdapter'
178
+ export { PerformanceManager } from './lib/editor/managers/PerformanceManager/PerformanceManager'
179
+ export {
180
+ type TLCameraEndPerfEvent,
181
+ type TLCameraStartPerfEvent,
182
+ type TLFramePerfEvent,
183
+ type TLInteractionEndPerfEvent,
184
+ type TLInteractionStartPerfEvent,
185
+ type TLPerfEventMap,
186
+ type TLPerfFrameTimeStats,
187
+ type TLPerfLongAnimationFrame,
188
+ type TLPerfLongAnimationFrameScript,
189
+ type TLShapeOperationPerfEvent,
190
+ type TLUndoRedoPerfEvent,
191
+ } from './lib/editor/managers/PerformanceManager/perf-types'
177
192
  export { UserPreferencesManager } from './lib/editor/managers/UserPreferencesManager/UserPreferencesManager'
178
193
  export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
179
194
  export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
@@ -300,6 +315,7 @@ export {
300
315
  useContainerIfExists,
301
316
  type ContainerProviderProps,
302
317
  } from './lib/hooks/useContainer'
318
+ export { useColorMode } from './lib/hooks/useColorMode'
303
319
  export { getCursor } from './lib/hooks/useCursor'
304
320
  export {
305
321
  EditorContext,
@@ -314,7 +330,6 @@ export { useEvent, useReactiveEvent } from './lib/hooks/useEvent'
314
330
  export { useGlobalMenuIsOpen } from './lib/hooks/useGlobalMenuIsOpen'
315
331
  export { useShallowArrayIdentity, useShallowObjectIdentity } from './lib/hooks/useIdentity'
316
332
  export { useIsCropping } from './lib/hooks/useIsCropping'
317
- export { useIsDarkMode } from './lib/hooks/useIsDarkMode'
318
333
  export { useIsEditing } from './lib/hooks/useIsEditing'
319
334
  export { useLocalStore } from './lib/hooks/useLocalStore'
320
335
  export { usePassThroughMouseOverEvents } from './lib/hooks/usePassThroughMouseOverEvents'
@@ -512,3 +527,5 @@ registerTldrawLibraryVersion(
512
527
  (globalThis as any).TLDRAW_LIBRARY_VERSION,
513
528
  (globalThis as any).TLDRAW_LIBRARY_MODULES
514
529
  )
530
+
531
+ export { getColorValue } from './lib/editor/managers/ThemeManager/defaultThemes'
@@ -1,5 +1,14 @@
1
1
  import { MigrationSequence, Store } from '@tldraw/store'
2
- import { TLShape, TLStore, TLStoreSnapshot } from '@tldraw/tlschema'
2
+ import {
3
+ TLShape,
4
+ TLStore,
5
+ TLStoreSnapshot,
6
+ TLTheme,
7
+ TLThemeId,
8
+ TLThemes,
9
+ registerColorsFromThemes,
10
+ registerFontsFromThemes,
11
+ } from '@tldraw/tlschema'
3
12
  import { annotateError, Required } from '@tldraw/utils'
4
13
  import classNames from 'classnames'
5
14
  import React, {
@@ -18,10 +27,12 @@ import { DefaultErrorFallback } from './components/default-components/DefaultErr
18
27
  import { OptionalErrorBoundary } from './components/ErrorBoundary'
19
28
  import { createTLCurrentUser, TLCurrentUser } from './config/createTLCurrentUser'
20
29
  import { TLStoreBaseOptions } from './config/createTLStore'
30
+ import { TLAnyAssetUtilConstructor } from './config/defaultAssets'
21
31
  import { TLAnyBindingUtilConstructor } from './config/defaultBindings'
22
32
  import { TLAnyShapeUtilConstructor } from './config/defaultShapes'
23
33
  import { TLEditorSnapshot } from './config/TLEditorSnapshot'
24
34
  import { Editor } from './editor/Editor'
35
+ import { resolveThemes } from './editor/managers/ThemeManager/ThemeManager'
25
36
  import { TLStateNodeConstructor } from './editor/tools/StateNode'
26
37
  import { TLCameraOptions } from './editor/types/misc-types'
27
38
  import { useEditorComponents } from './hooks/EditorComponentsContext'
@@ -121,6 +132,11 @@ export interface TldrawEditorBaseProps {
121
132
  */
122
133
  bindingUtils?: readonly TLAnyBindingUtilConstructor[]
123
134
 
135
+ /**
136
+ * An array of asset utils to use in the editor.
137
+ */
138
+ assetUtils?: readonly TLAnyAssetUtilConstructor[]
139
+
124
140
  /**
125
141
  * An array of tools to add to the editor's state chart.
126
142
  */
@@ -157,9 +173,23 @@ export interface TldrawEditorBaseProps {
157
173
  user?: TLCurrentUser
158
174
 
159
175
  /**
160
- * Whether to infer dark mode from the user's OS. Defaults to false.
176
+ * The editor's color scheme. Defaults to `'light'`.
177
+ *
178
+ * - `'light'` - Always use light mode.
179
+ * - `'dark'` - Always use dark mode.
180
+ * - `'system'` - Follow the OS color scheme preference.
161
181
  */
162
- inferDarkMode?: boolean
182
+ colorScheme?: 'light' | 'dark' | 'system'
183
+
184
+ /**
185
+ * Named themes for the editor.
186
+ */
187
+ themes?: Partial<TLThemes>
188
+
189
+ /**
190
+ * The id of the initially active theme. Defaults to `'default'`.
191
+ */
192
+ initialTheme?: TLThemeId
163
193
 
164
194
  /**
165
195
  * Camera options for the editor.
@@ -242,6 +272,7 @@ declare global {
242
272
 
243
273
  const EMPTY_SHAPE_UTILS_ARRAY = [] as const
244
274
  const EMPTY_BINDING_UTILS_ARRAY = [] as const
275
+ const EMPTY_ASSET_UTILS_ARRAY = [] as const
245
276
  const EMPTY_TOOLS_ARRAY = [] as const
246
277
  /** @internal */
247
278
  export const TL_CONTAINER_CLASS = 'tl-container'
@@ -259,6 +290,13 @@ export const TldrawEditor = memo(function TldrawEditor({
259
290
  deepLinks: _deepLinks,
260
291
  ...rest
261
292
  }: TldrawEditorProps) {
293
+ // Register custom colors and fonts before effects run. For external stores,
294
+ // users should also pass themes to createTLStore so they are
295
+ // registered before data is loaded into the store.
296
+ const resolvedThemes = resolveThemes(rest.themes)
297
+ registerColorsFromThemes(resolvedThemes)
298
+ registerFontsFromThemes(resolvedThemes)
299
+
262
300
  const [container, setContainer] = useState<HTMLElement | null>(null)
263
301
  const user = useMemo(() => _user ?? createTLCurrentUser(), [_user])
264
302
 
@@ -285,6 +323,7 @@ export const TldrawEditor = memo(function TldrawEditor({
285
323
  ...rest,
286
324
  shapeUtils: rest.shapeUtils ?? EMPTY_SHAPE_UTILS_ARRAY,
287
325
  bindingUtils: rest.bindingUtils ?? EMPTY_BINDING_UTILS_ARRAY,
326
+ assetUtils: rest.assetUtils ?? EMPTY_ASSET_UTILS_ARRAY,
288
327
  tools: rest.tools ?? EMPTY_TOOLS_ARRAY,
289
328
  components,
290
329
  options: useShallowObjectIdentity(mergedOptions),
@@ -332,7 +371,7 @@ export const TldrawEditor = memo(function TldrawEditor({
332
371
  function TldrawEditorWithOwnStore(
333
372
  props: Required<
334
373
  TldrawEditorProps & { store: undefined; user: TLCurrentUser },
335
- 'shapeUtils' | 'bindingUtils' | 'tools'
374
+ 'shapeUtils' | 'bindingUtils' | 'assetUtils' | 'tools'
336
375
  >
337
376
  ) {
338
377
  const {
@@ -341,17 +380,20 @@ function TldrawEditorWithOwnStore(
341
380
  initialData,
342
381
  shapeUtils,
343
382
  bindingUtils,
383
+ assetUtils,
344
384
  persistenceKey,
345
385
  sessionId,
346
386
  user,
347
387
  assets,
348
388
  users,
349
389
  migrations,
390
+ themes,
350
391
  } = props
351
392
 
352
393
  const syncedStore = useLocalStore({
353
394
  shapeUtils,
354
395
  bindingUtils,
396
+ assetUtils,
355
397
  initialData,
356
398
  persistenceKey,
357
399
  sessionId,
@@ -360,6 +402,7 @@ function TldrawEditorWithOwnStore(
360
402
  assets,
361
403
  users,
362
404
  migrations,
405
+ themes,
363
406
  })
364
407
 
365
408
  return <TldrawEditorWithLoadingStore {...props} store={syncedStore} user={user} />
@@ -371,7 +414,7 @@ const TldrawEditorWithLoadingStore = memo(function TldrawEditorBeforeLoading({
371
414
  ...rest
372
415
  }: Required<
373
416
  TldrawEditorProps & { store: TLStoreWithStatus; user: TLCurrentUser },
374
- 'shapeUtils' | 'bindingUtils' | 'tools'
417
+ 'shapeUtils' | 'bindingUtils' | 'assetUtils' | 'tools'
375
418
  >) {
376
419
  const container = useContainer()
377
420
 
@@ -417,22 +460,25 @@ function TldrawEditorWithReadyStore({
417
460
  tools,
418
461
  shapeUtils,
419
462
  bindingUtils,
463
+ assetUtils,
420
464
  user,
421
465
  initialState,
422
466
  autoFocus = true,
423
- inferDarkMode,
424
467
  // eslint-disable-next-line @typescript-eslint/no-deprecated
425
468
  cameraOptions,
426
469
  options,
427
470
  licenseKey,
428
471
  getShapeVisibility,
472
+ colorScheme,
429
473
  assetUrls,
474
+ themes,
475
+ initialTheme,
430
476
  }: Required<
431
477
  TldrawEditorProps & {
432
478
  store: TLStore
433
479
  user: TLCurrentUser
434
480
  },
435
- 'shapeUtils' | 'bindingUtils' | 'tools'
481
+ 'shapeUtils' | 'bindingUtils' | 'assetUtils' | 'tools'
436
482
  >) {
437
483
  const { ErrorFallback } = useEditorComponents()
438
484
  const container = useContainer()
@@ -448,44 +494,58 @@ function TldrawEditorWithReadyStore({
448
494
  const editorOptionsRef = useRef({
449
495
  // for these, it's because they're only used when the editor first mounts:
450
496
  autoFocus: autoFocus && !noAutoFocus(),
451
- inferDarkMode,
452
497
  initialState,
498
+ colorScheme,
453
499
 
454
500
  // for these, it's because we keep them up to date in a separate effect:
455
501
  cameraOptions,
456
502
  deepLinks,
503
+ themes,
504
+ initialTheme,
457
505
  })
458
506
 
459
507
  useLayoutEffect(() => {
460
508
  editorOptionsRef.current = {
461
509
  autoFocus: autoFocus && !noAutoFocus(),
462
- inferDarkMode,
463
510
  initialState,
511
+ colorScheme,
464
512
  cameraOptions,
465
513
  deepLinks,
514
+ themes,
515
+ initialTheme,
466
516
  }
467
- }, [autoFocus, inferDarkMode, initialState, cameraOptions, deepLinks])
517
+ }, [autoFocus, initialState, colorScheme, cameraOptions, deepLinks, themes, initialTheme])
468
518
 
469
519
  useLayoutEffect(
470
520
  () => {
471
- const { autoFocus, inferDarkMode, initialState, cameraOptions, deepLinks } =
472
- editorOptionsRef.current
521
+ const {
522
+ autoFocus,
523
+ initialState,
524
+ colorScheme: initColorScheme,
525
+ cameraOptions,
526
+ deepLinks,
527
+ themes,
528
+ initialTheme,
529
+ } = editorOptionsRef.current
473
530
  const editor = new Editor({
474
531
  store,
475
532
  shapeUtils,
476
533
  bindingUtils,
534
+ assetUtils,
477
535
  tools,
478
536
  getContainer: () => container,
479
537
  user,
480
538
  initialState,
481
539
  // we should check for some kind of query parameter that turns off autofocus
482
540
  autoFocus,
483
- inferDarkMode,
484
541
  cameraOptions,
485
542
  options,
486
543
  licenseKey,
487
544
  getShapeVisibility,
545
+ colorScheme: initColorScheme,
488
546
  fontAssetUrls: assetUrls?.fonts,
547
+ themes: themes,
548
+ initialTheme: initialTheme,
489
549
  })
490
550
 
491
551
  editor.updateViewportScreenBounds(canvasRef.current ?? container)
@@ -510,7 +570,9 @@ function TldrawEditorWithReadyStore({
510
570
  },
511
571
  // if any of these change, we need to recreate the editor.
512
572
  [
573
+ assetUtils,
513
574
  bindingUtils,
575
+ colorScheme,
514
576
  container,
515
577
  options,
516
578
  shapeUtils,
@@ -539,6 +601,21 @@ function TldrawEditorWithReadyStore({
539
601
  }
540
602
  }, [editor, cameraOptions, options?.camera])
541
603
 
604
+ // keep the editor up to date with the latest theme definitions
605
+ useLayoutEffect(() => {
606
+ if (editor && themes) {
607
+ for (const def of Object.values(themes) as TLTheme[]) {
608
+ editor.updateTheme(def)
609
+ }
610
+ }
611
+ }, [editor, themes])
612
+
613
+ useLayoutEffect(() => {
614
+ if (editor && initialTheme) {
615
+ editor.setCurrentTheme(initialTheme)
616
+ }
617
+ }, [editor, initialTheme])
618
+
542
619
  const crashingError = useSyncExternalStore(
543
620
  useCallback(
544
621
  (onStoreChange) => {
@@ -42,6 +42,22 @@ export function MenuClickCapture() {
42
42
  }
43
43
  rDidAPointerDownAndDragWhileMenuWasOpen.current = false
44
44
  }
45
+ if (e.button === 2) {
46
+ // Swallow the contextmenu event that follows this right-click pointerdown.
47
+ // clearOpenMenus() below triggers a synchronous render that unmounts this
48
+ // component, so our React onContextMenu handler won't be around to catch it.
49
+ // Without this, the contextmenu event reaches the Radix Trigger and briefly
50
+ // opens a new context menu (which then immediately dismisses — causing a flash).
51
+ const ownerDocument = editor.getContainerDocument()
52
+ ownerDocument.addEventListener(
53
+ 'contextmenu',
54
+ (event) => {
55
+ event.preventDefault()
56
+ event.stopImmediatePropagation()
57
+ },
58
+ { capture: true, once: true }
59
+ )
60
+ }
45
61
  editor.menus.clearOpenMenus()
46
62
  },
47
63
  [editor]
@@ -117,6 +133,10 @@ export function MenuClickCapture() {
117
133
  onPointerDown={handlePointerDown}
118
134
  onPointerMove={handlePointerMove}
119
135
  onPointerUp={handlePointerUp}
136
+ onContextMenu={(e) => {
137
+ e.preventDefault()
138
+ e.stopPropagation()
139
+ }}
120
140
  />
121
141
  )
122
142
  )
@@ -6,8 +6,8 @@ import { memo, useEffect, useRef } from 'react'
6
6
  import { Editor } from '../../editor/Editor'
7
7
  import { TLIndicatorPath } from '../../editor/shapes/ShapeUtil'
8
8
  import { getComputedStyle } from '../../exports/domUtils'
9
+ import { useColorMode } from '../../hooks/useColorMode'
9
10
  import { useEditor } from '../../hooks/useEditor'
10
- import { useIsDarkMode } from '../../hooks/useIsDarkMode'
11
11
  import { useActivePeerIds$ } from '../../hooks/usePeerIds'
12
12
 
13
13
  interface CollaboratorIndicatorData {
@@ -133,14 +133,14 @@ export const CanvasShapeIndicators = memo(function CanvasShapeIndicators() {
133
133
 
134
134
  // Cache the selected color to avoid getComputedStyle on every render
135
135
  const rSelectedColor = useRef<string | null>(null)
136
- const isDarkMode = useIsDarkMode()
136
+ const colorMode = useColorMode()
137
137
 
138
138
  useEffect(() => {
139
139
  const timer = editor.timers.setTimeout(() => {
140
140
  rSelectedColor.current = null
141
141
  }, 0)
142
142
  return () => clearTimeout(timer)
143
- }, [isDarkMode, editor])
143
+ }, [colorMode, editor])
144
144
 
145
145
  // Get active peer IDs (already handles time-based state transitions)
146
146
  const activePeerIds$ = useActivePeerIds$()
@@ -8,15 +8,20 @@ import {
8
8
  TLStore,
9
9
  TLStoreProps,
10
10
  TLStoreSnapshot,
11
+ TLThemes,
11
12
  TLUser,
12
13
  TLUserStore,
13
14
  UserRecordType,
14
15
  createCachedUserResolve,
15
16
  createTLSchema,
16
17
  createUserId,
18
+ registerColorsFromThemes,
19
+ registerFontsFromThemes,
17
20
  } from '@tldraw/tlschema'
18
21
  import { FileHelpers, assert } from '@tldraw/utils'
19
22
  import { Editor } from '../editor/Editor'
23
+ import { resolveThemes } from '../editor/managers/ThemeManager/ThemeManager'
24
+ import { TLAnyAssetUtilConstructor, checkAssets } from './defaultAssets'
20
25
  import { TLAnyBindingUtilConstructor, checkBindings } from './defaultBindings'
21
26
  import { TLAnyShapeUtilConstructor, checkShapesAndAddCore } from './defaultShapes'
22
27
  import { TLEditorSnapshot, loadSnapshot } from './TLEditorSnapshot'
@@ -36,6 +41,13 @@ export interface TLStoreBaseOptions {
36
41
  /** How should this store upload & resolve assets? */
37
42
  assets?: TLAssetStore
38
43
 
44
+ /**
45
+ * Named theme definitions. When provided, custom color names are automatically
46
+ * registered before the store is constructed so persisted data with those
47
+ * colors passes validation on load.
48
+ */
49
+ themes?: Partial<TLThemes>
50
+
39
51
  /** How should this store resolve users for attribution? */
40
52
  users?: TLUserStore
41
53
 
@@ -50,8 +62,9 @@ export type TLStoreSchemaOptions =
50
62
  }
51
63
  | {
52
64
  shapeUtils?: readonly TLAnyShapeUtilConstructor[]
53
- migrations?: readonly MigrationSequence[]
54
65
  bindingUtils?: readonly TLAnyBindingUtilConstructor[]
66
+ assetUtils?: readonly TLAnyAssetUtilConstructor[]
67
+ migrations?: readonly MigrationSequence[]
55
68
  records?: Record<string, CustomRecordInfo>
56
69
  }
57
70
 
@@ -114,6 +127,10 @@ export function createTLSchemaFromUtils(
114
127
  'bindingUtils' in opts && opts.bindingUtils
115
128
  ? utilsToMap(checkBindings(opts.bindingUtils))
116
129
  : undefined,
130
+ assets:
131
+ 'assetUtils' in opts && opts.assetUtils
132
+ ? utilsToMap(checkAssets(opts.assetUtils))
133
+ : undefined,
117
134
  records: 'records' in opts ? opts.records : undefined,
118
135
  migrations: 'migrations' in opts ? opts.migrations : undefined,
119
136
  })
@@ -134,8 +151,12 @@ export function createTLStore({
134
151
  users = defaultUserStore,
135
152
  onMount,
136
153
  collaboration,
154
+ themes,
137
155
  ...rest
138
156
  }: TLStoreOptions = {}): TLStore {
157
+ const resolvedThemes = resolveThemes(themes)
158
+ registerColorsFromThemes(resolvedThemes)
159
+ registerFontsFromThemes(resolvedThemes)
139
160
  const schema = createTLSchemaFromUtils(rest)
140
161
 
141
162
  const store = new Store({
@@ -0,0 +1,19 @@
1
+ import { TLAssetUtilConstructor } from '../editor/assets/AssetUtil'
2
+
3
+ /** @public */
4
+ export type TLAnyAssetUtilConstructor = TLAssetUtilConstructor<any>
5
+
6
+ export function checkAssets(customAssets: readonly TLAnyAssetUtilConstructor[]) {
7
+ const assets = [] as TLAnyAssetUtilConstructor[]
8
+
9
+ const addedCustomAssetTypes = new Set<string>()
10
+ for (const customAsset of customAssets) {
11
+ if (addedCustomAssetTypes.has(customAsset.type)) {
12
+ throw new Error(`Asset type "${customAsset.type}" is defined more than once`)
13
+ }
14
+ assets.push(customAsset)
15
+ addedCustomAssetTypes.add(customAsset.type)
16
+ }
17
+
18
+ return assets
19
+ }