@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
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Common frame time statistics shared by interaction and camera end events.
3
+ * @public
4
+ */
5
+ export interface TLPerfFrameTimeStats {
6
+ /** Total duration of the session in ms. */
7
+ duration: number
8
+ /** Average frames per second during the session. */
9
+ fps: number
10
+ /** Total number of frames recorded. */
11
+ frameCount: number
12
+ /** Mean frame duration in ms. */
13
+ avgFrameTime: number
14
+ /** Median (p50) frame duration in ms. */
15
+ medianFrameTime: number
16
+ /** 95th percentile frame duration in ms. */
17
+ p95FrameTime: number
18
+ /** 99th percentile frame duration in ms. */
19
+ p99FrameTime: number
20
+ /** Shortest frame duration in ms. */
21
+ minFrameTime: number
22
+ /** Longest frame duration in ms. */
23
+ maxFrameTime: number
24
+ /** Raw frame durations for local analysis. Exclude when sending to analytics. */
25
+ frameTimes: number[]
26
+ /**
27
+ * Long animation frames observed during this period (Chromium 123+).
28
+ * Only present when the browser supports the Long Animation Frames API and
29
+ * at least one long frame was observed.
30
+ * Exclude when sending to analytics — entries are large and contain script URLs.
31
+ */
32
+ longAnimationFrames?: TLPerfLongAnimationFrame[]
33
+ }
34
+
35
+ /**
36
+ * Emitted when an interaction state (e.g. translating, resizing) is entered.
37
+ * @public
38
+ */
39
+ export interface TLInteractionStartPerfEvent {
40
+ /** The state node id (e.g. `'translating'`). */
41
+ name: string
42
+ /** Full tool path (e.g. `'select.translating'`). */
43
+ path: string
44
+ /** `performance.now()` when the interaction started. */
45
+ timestamp: number
46
+ }
47
+
48
+ /**
49
+ * Emitted when an interaction state is exited, with aggregated frame time stats.
50
+ * @public
51
+ */
52
+ export interface TLInteractionEndPerfEvent extends TLPerfFrameTimeStats {
53
+ /** The state node id (e.g. `'translating'`). */
54
+ name: string
55
+ /** Full tool path (e.g. `'select.translating'`). */
56
+ path: string
57
+ /** Total shapes on the current page. */
58
+ shapeCount: number
59
+ /** Breakdown of selected shape types at interaction start (e.g. `{ geo: 2, draw: 1 }`). */
60
+ selectedShapeTypes: Record<string, number>
61
+ /** Camera zoom level (`camera.z`) at interaction end. */
62
+ zoomLevel: number
63
+ /** `performance.now()` when the interaction ended. */
64
+ timestamp: number
65
+ }
66
+
67
+ /**
68
+ * Emitted when a camera operation (pan or zoom) begins.
69
+ * @public
70
+ */
71
+ export interface TLCameraStartPerfEvent {
72
+ /** Whether this is a pan or zoom operation. */
73
+ type: 'panning' | 'zooming'
74
+ /** `performance.now()` when the camera session started. */
75
+ timestamp: number
76
+ }
77
+
78
+ /**
79
+ * Emitted when a camera operation ends (after a 50ms debounce), with aggregated frame time stats.
80
+ * @public
81
+ */
82
+ export interface TLCameraEndPerfEvent extends TLPerfFrameTimeStats {
83
+ /** Whether this was a pan or zoom operation. */
84
+ type: 'panning' | 'zooming'
85
+ /** Total shapes on the current page. */
86
+ shapeCount: number
87
+ /** Number of shapes visible (not culled) in the viewport. */
88
+ visibleShapeCount: number
89
+ /** Number of shapes culled (off-screen) from rendering. */
90
+ culledShapeCount: number
91
+ /** Viewport width in screen pixels. */
92
+ viewportWidth: number
93
+ /** Viewport height in screen pixels. */
94
+ viewportHeight: number
95
+ /** Camera zoom level (`camera.z`) at session end. */
96
+ zoomLevel: number
97
+ /** `performance.now()` when the camera session ended. */
98
+ timestamp: number
99
+ }
100
+
101
+ /**
102
+ * Emitted when shapes are created, updated, or deleted.
103
+ * @public
104
+ */
105
+ export interface TLShapeOperationPerfEvent {
106
+ /** The operation type. */
107
+ operation: 'create' | 'update' | 'delete'
108
+ /** Number of shapes affected. */
109
+ count: number
110
+ /** Breakdown by shape type (e.g. `{ geo: 2, draw: 1 }`). */
111
+ shapeTypes: Record<string, number>
112
+ /** `performance.now()` when the operation occurred. */
113
+ timestamp: number
114
+ }
115
+
116
+ /**
117
+ * Emitted every animation frame when at least one `'frame'` listener is registered.
118
+ * @public
119
+ */
120
+ export interface TLFramePerfEvent {
121
+ /** Time since the last frame in ms. */
122
+ elapsed: number
123
+ /** Total shapes on the current page. */
124
+ shapeCount: number
125
+ /** Number of shapes culled (off-screen) from rendering. */
126
+ culledShapeCount: number
127
+ /** Number of shapes visible (not culled) in the viewport. */
128
+ visibleShapeCount: number
129
+ }
130
+
131
+ /**
132
+ * Emitted after an undo or redo operation.
133
+ * @public
134
+ */
135
+ export interface TLUndoRedoPerfEvent {
136
+ /** Whether this was an undo or redo. */
137
+ type: 'undo' | 'redo'
138
+ /** Number of undo steps remaining. */
139
+ undoDepth: number
140
+ /** Number of redo steps remaining. */
141
+ redoDepth: number
142
+ }
143
+
144
+ /**
145
+ * A long animation frame observed by the browser during an interaction.
146
+ * Available only in browsers that support the Long Animation Frames API (Chromium 123+).
147
+ * @public
148
+ */
149
+ export interface TLPerfLongAnimationFrame {
150
+ /** Frame start time (relative to timeOrigin). */
151
+ startTime: number
152
+ /** Total frame duration in ms. */
153
+ duration: number
154
+ /** Time the main thread was blocked in ms. */
155
+ blockingDuration: number
156
+ /** Scripts that contributed to the long frame. */
157
+ scripts: TLPerfLongAnimationFrameScript[]
158
+ }
159
+
160
+ /** A script attribution entry from a long animation frame. @public */
161
+ export interface TLPerfLongAnimationFrameScript {
162
+ /** The script source URL (may be empty for inline scripts). */
163
+ sourceURL: string
164
+ /** The function name or invoker description. */
165
+ invoker: string
166
+ /** Time spent in this script in ms. */
167
+ duration: number
168
+ }
169
+
170
+ /**
171
+ * Map of all performance event names to their payload types.
172
+ * Used with {@link PerformanceManager.on} and {@link PerformanceManager.once}.
173
+ * @public
174
+ */
175
+ export interface TLPerfEventMap {
176
+ /** An interaction state was entered. */
177
+ 'interaction-start': [TLInteractionStartPerfEvent]
178
+ /** An interaction state was exited, with aggregated frame time stats. */
179
+ 'interaction-end': [TLInteractionEndPerfEvent]
180
+ /** A camera operation (pan/zoom) began. */
181
+ 'camera-start': [TLCameraStartPerfEvent]
182
+ /** A camera operation ended (after debounce), with aggregated frame time stats. */
183
+ 'camera-end': [TLCameraEndPerfEvent]
184
+ /** Shapes were created. */
185
+ 'shapes-created': [TLShapeOperationPerfEvent]
186
+ /** Shapes were updated. */
187
+ 'shapes-updated': [TLShapeOperationPerfEvent]
188
+ /** Shapes were deleted. */
189
+ 'shapes-deleted': [TLShapeOperationPerfEvent]
190
+ /** An animation frame was rendered. Only fires when listeners are registered. */
191
+ frame: [TLFramePerfEvent]
192
+ /** An undo operation was performed. */
193
+ undo: [TLUndoRedoPerfEvent]
194
+ /** A redo operation was performed. */
195
+ redo: [TLUndoRedoPerfEvent]
196
+ }
@@ -0,0 +1,116 @@
1
+ import { Atom, atom, computed } from '@tldraw/state'
2
+ import { TLTheme, TLThemeId, TLThemes } from '@tldraw/tlschema'
3
+ import { structuredClone } from '@tldraw/utils'
4
+ import type { Editor } from '../../Editor'
5
+ import { DEFAULT_THEME } from './defaultThemes'
6
+
7
+ /**
8
+ * Resolve a partial set of user-provided themes into a complete `TLThemes`
9
+ * record by merging with `DEFAULT_THEME`. The result is suitable for passing to
10
+ * `registerColorsFromThemes`, `registerFontsFromThemes`, and the
11
+ * `ThemeManager` constructor.
12
+ *
13
+ * @public
14
+ */
15
+ export function resolveThemes(themes?: Partial<TLThemes>): TLThemes {
16
+ return { default: DEFAULT_THEME, ...themes } as TLThemes
17
+ }
18
+
19
+ /**
20
+ * Manages the editor's color themes.
21
+ *
22
+ * Stores named theme definitions (each containing light and dark color palettes
23
+ * alongside shared properties like font size). The current theme is resolved by
24
+ * combining the current theme name with the user's color mode preference.
25
+ *
26
+ * **Terminology:**
27
+ * - **Theme** (`TLTheme`): A named set of colors and typographic values for both light and dark modes.
28
+ * - **Color mode** (`'light' | 'dark'`): The resolved appearance mode, derived from the user's
29
+ * `colorScheme` preference (`'light' | 'dark' | 'system'`). Access via `getColorMode()`.
30
+ *
31
+ * @public
32
+ */
33
+ export class ThemeManager {
34
+ private readonly _themes: Atom<TLThemes>
35
+ private readonly _currentThemeId: Atom<TLThemeId>
36
+
37
+ constructor(
38
+ private readonly editor: Editor,
39
+ options: {
40
+ themes: TLThemes
41
+ initial: TLThemeId
42
+ }
43
+ ) {
44
+ this._themes = atom('ThemeManager._definitions', options.themes)
45
+ this._currentThemeId = atom('ThemeManager._currentThemeName', options.initial)
46
+ }
47
+
48
+ /** Get the current color mode based on the user's dark mode preference. */
49
+ @computed getColorMode(): 'light' | 'dark' {
50
+ return this.editor.user.getIsDarkMode() ? 'dark' : 'light'
51
+ }
52
+
53
+ /** Get all registered theme definitions. */
54
+ getThemes(): TLThemes {
55
+ return this._themes.get()
56
+ }
57
+
58
+ /** Get a single theme definition by id. */
59
+ getTheme(id: TLThemeId): TLTheme | undefined {
60
+ return this._themes.get()[id]
61
+ }
62
+
63
+ /** Get the id of the current theme. */
64
+ getCurrentThemeId(): TLThemeId {
65
+ return this._currentThemeId.get()
66
+ }
67
+
68
+ getCurrentTheme(): TLTheme {
69
+ return this._themes.get()[this.getCurrentThemeId()]!
70
+ }
71
+
72
+ /** Set the current theme by id. The theme must have been previously registered. */
73
+ setCurrentTheme(id: TLThemeId): void {
74
+ if (process.env.NODE_ENV !== 'production') {
75
+ if (!(id in this._themes.get())) {
76
+ console.warn(
77
+ `Theme '${id}' not found. Available themes: ${Object.keys(this._themes.get()).join(', ')}`
78
+ )
79
+ }
80
+ }
81
+
82
+ this._currentThemeId.set(id)
83
+ }
84
+
85
+ /** Replace all theme definitions, or update them via a callback that receives a deep copy. */
86
+ updateThemes(themes: TLThemes | ((themes: TLThemes) => TLThemes)): void {
87
+ this._themes.update((prev) => {
88
+ const next = typeof themes === 'function' ? themes(structuredClone(prev)) : themes
89
+ if (process.env.NODE_ENV !== 'production') {
90
+ if (!('default' in next)) {
91
+ console.warn("The 'default' theme cannot be removed.")
92
+ return prev
93
+ }
94
+ }
95
+ // If the current theme was removed, fall back to 'default'
96
+ if (!(this._currentThemeId.get() in next)) {
97
+ this._currentThemeId.set('default')
98
+ }
99
+ return next
100
+ })
101
+ }
102
+
103
+ /** Register or update a named theme definition. */
104
+ updateTheme(theme: TLTheme): void {
105
+ this._themes.update((prev) => ({
106
+ ...prev,
107
+ [theme.id]: theme,
108
+ }))
109
+ }
110
+
111
+ /** Clean up any resources held by the manager. */
112
+ dispose() {
113
+ // currently no subscriptions to tear down, but here for consistency
114
+ // with the manager pattern and for future use
115
+ }
116
+ }