@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.
- package/dist-cjs/index.d.ts +668 -96
- package/dist-cjs/index.js +16 -3
- package/dist-cjs/index.js.map +3 -3
- package/dist-cjs/lib/TldrawEditor.js +55 -12
- package/dist-cjs/lib/TldrawEditor.js.map +3 -3
- package/dist-cjs/lib/components/MenuClickCapture.js +16 -1
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +3 -3
- package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +7 -0
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/config/defaultAssets.js +36 -0
- package/dist-cjs/lib/config/defaultAssets.js.map +7 -0
- package/dist-cjs/lib/editor/Editor.js +215 -5
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/assets/AssetUtil.js +66 -0
- package/dist-cjs/lib/editor/assets/AssetUtil.js.map +7 -0
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js +80 -0
- package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js.map +7 -0
- package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js +466 -0
- package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js +17 -0
- package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js.map +7 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +106 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +7 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js +586 -0
- package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js.map +7 -0
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +6 -4
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +11 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +6 -0
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +14 -17
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/SvgExportContext.js.map +2 -2
- package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
- package/dist-cjs/lib/exports/getSvgJsx.js +12 -7
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +18 -1
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/hooks/{useIsDarkMode.js → useColorMode.js} +14 -10
- package/dist-cjs/lib/hooks/useColorMode.js.map +7 -0
- package/dist-cjs/lib/hooks/useCursor.js +3 -7
- package/dist-cjs/lib/hooks/useCursor.js.map +2 -2
- package/dist-cjs/lib/hooks/useDarkMode.js +4 -4
- package/dist-cjs/lib/hooks/useDarkMode.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +2 -1
- package/dist-cjs/lib/utils/reparenting.js.map +2 -2
- package/dist-cjs/lib/utils/richText.js.map +2 -2
- package/dist-cjs/lib/utils/runtime.js +2 -1
- package/dist-cjs/lib/utils/runtime.js.map +2 -2
- package/dist-cjs/lib/utils/sync/hardReset.js +0 -8
- package/dist-cjs/lib/utils/sync/hardReset.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +668 -96
- package/dist-esm/index.mjs +17 -6
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +58 -12
- package/dist-esm/lib/TldrawEditor.mjs.map +3 -3
- package/dist-esm/lib/components/MenuClickCapture.mjs +16 -1
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +3 -3
- package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +10 -1
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/config/defaultAssets.mjs +16 -0
- package/dist-esm/lib/config/defaultAssets.mjs.map +7 -0
- package/dist-esm/lib/editor/Editor.mjs +215 -5
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/assets/AssetUtil.mjs +46 -0
- package/dist-esm/lib/editor/assets/AssetUtil.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs +60 -0
- package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs +438 -0
- package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs +1 -0
- package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +88 -0
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs +568 -0
- package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +6 -4
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +11 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +6 -0
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +14 -17
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/editor/types/SvgExportContext.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +12 -10
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/globals/environment.mjs +18 -1
- package/dist-esm/lib/globals/environment.mjs.map +2 -2
- package/dist-esm/lib/hooks/useColorMode.mjs +19 -0
- package/dist-esm/lib/hooks/useColorMode.mjs.map +7 -0
- package/dist-esm/lib/hooks/useCursor.mjs +3 -7
- package/dist-esm/lib/hooks/useCursor.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDarkMode.mjs +4 -4
- package/dist-esm/lib/hooks/useDarkMode.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +2 -1
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/lib/utils/richText.mjs.map +2 -2
- package/dist-esm/lib/utils/runtime.mjs +2 -1
- package/dist-esm/lib/utils/runtime.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/hardReset.mjs +0 -8
- package/dist-esm/lib/utils/sync/hardReset.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +0 -33
- package/package.json +7 -7
- package/src/index.ts +23 -6
- package/src/lib/TldrawEditor.tsx +90 -13
- package/src/lib/components/MenuClickCapture.tsx +20 -0
- package/src/lib/components/default-components/CanvasShapeIndicators.tsx +3 -3
- package/src/lib/config/createTLStore.ts +22 -1
- package/src/lib/config/defaultAssets.ts +19 -0
- package/src/lib/editor/Editor.ts +301 -27
- package/src/lib/editor/assets/AssetUtil.ts +85 -0
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +9 -2
- package/src/lib/editor/managers/FontManager/FontManager.ts +1 -67
- package/src/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.ts +82 -0
- package/src/lib/editor/managers/PerformanceManager/PerformanceManager.test.ts +522 -0
- package/src/lib/editor/managers/PerformanceManager/PerformanceManager.ts +583 -0
- package/src/lib/editor/managers/PerformanceManager/perf-types.ts +196 -0
- package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +116 -0
- package/src/lib/editor/managers/ThemeManager/defaultThemes.ts +605 -0
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +23 -29
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -3
- package/src/lib/editor/shapes/ShapeUtil.ts +28 -3
- package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
- package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +7 -0
- package/src/lib/editor/tools/StateNode.ts +16 -18
- package/src/lib/editor/types/SvgExportContext.tsx +5 -0
- package/src/lib/editor/types/external-content.ts +1 -0
- package/src/lib/exports/getSvgJsx.tsx +21 -15
- package/src/lib/globals/environment.ts +18 -0
- package/src/lib/hooks/{useIsDarkMode.ts → useColorMode.ts} +9 -5
- package/src/lib/hooks/useCursor.ts +3 -7
- package/src/lib/hooks/useDarkMode.ts +4 -4
- package/src/lib/utils/reparenting.ts +6 -2
- package/src/lib/utils/richText.ts +1 -1
- package/src/lib/utils/runtime.ts +3 -1
- package/src/lib/utils/sync/hardReset.ts +0 -8
- package/src/version.ts +3 -3
- package/dist-cjs/lib/hooks/useIsDarkMode.js.map +0 -7
- package/dist-esm/lib/hooks/useIsDarkMode.mjs +0 -15
- 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
|
+
}
|