@tldraw/state-react 4.0.2 → 4.1.0-canary.0259516ffb8c

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.
@@ -11,18 +11,41 @@ import { Signal } from '@tldraw/state';
11
11
  * Any signals whose values are read while the component renders will be tracked.
12
12
  * If any of the tracked signals change later it will cause the component to re-render.
13
13
  *
14
- * This also wraps the component in a React.memo() call, so it will only re-render if the props change.
14
+ * This also wraps the component in a React.memo() call, so it will only re-render
15
+ * when props change OR when any tracked signals change. This provides optimal
16
+ * performance by preventing unnecessary re-renders while maintaining reactivity.
17
+ *
18
+ * The function handles special React component types like forwardRef and memo
19
+ * components automatically, preserving their behavior while adding reactivity.
20
+ *
21
+ * @param baseComponent - The React functional component to make reactive to signal changes
22
+ * @returns A memoized component that re-renders when props or tracked signals change
15
23
  *
16
24
  * @example
17
25
  * ```ts
26
+ * import { atom } from '@tldraw/state'
27
+ * import { track, useAtom } from '@tldraw/state-react'
28
+ *
18
29
  * const Counter = track(function Counter(props: CounterProps) {
19
30
  * const count = useAtom('count', 0)
20
31
  * const increment = useCallback(() => count.set(count.get() + 1), [count])
21
32
  * return <button onClick={increment}>{count.get()}</button>
22
33
  * })
34
+ *
35
+ * // Component automatically re-renders when count signal changes
36
+ * ```
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Works with forwardRef components
41
+ * const TrackedInput = track(React.forwardRef<HTMLInputElement, InputProps>(
42
+ * function TrackedInput(props, ref) {
43
+ * const theme = useValue(themeSignal)
44
+ * return <input ref={ref} style={{ color: theme.textColor }} {...props} />
45
+ * }
46
+ * ))
23
47
  * ```
24
48
  *
25
- * @param baseComponent - The base component to track.
26
49
  * @public
27
50
  */
28
51
  export declare function track<T extends FunctionComponent<any>>(baseComponent: T): React_2.NamedExoticComponent<React_2.ComponentProps<T>>;
@@ -49,33 +72,185 @@ export declare function track<T extends FunctionComponent<any>>(baseComponent: T
49
72
  */
50
73
  export declare function useAtom<Value, Diff = unknown>(name: string, valueOrInitialiser: (() => Value) | Value, options?: AtomOptions<Value, Diff>): Atom<Value, Diff>;
51
74
 
52
- /** @public */
75
+ /**
76
+ * Creates a new computed signal that automatically tracks its dependencies and recalculates when they change.
77
+ * This overload is for basic computed values without custom options.
78
+ *
79
+ * @param name - A descriptive name for the computed signal, used for debugging and identification
80
+ * @param compute - A function that computes the value, automatically tracking any signal dependencies
81
+ * @param deps - React dependency array that controls when the computed signal is recreated
82
+ * @returns A computed signal containing the calculated value
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const firstName = atom('firstName', 'John')
87
+ * const lastName = atom('lastName', 'Doe')
88
+ *
89
+ * function UserProfile() {
90
+ * const fullName = useComputed(
91
+ * 'fullName',
92
+ * () => `${firstName.get()} ${lastName.get()}`,
93
+ * [firstName, lastName]
94
+ * )
95
+ *
96
+ * return <div>Welcome, {fullName.get()}!</div>
97
+ * }
98
+ * ```
99
+ *
100
+ * @public
101
+ */
53
102
  export declare function useComputed<Value>(name: string, compute: () => Value, deps: any[]): Computed<Value>;
54
103
 
55
104
  /**
56
- * Creates a new computed signal and returns it. The computed signal will be created only once.
105
+ * Creates a new computed signal with custom options for advanced behavior like custom equality checking,
106
+ * diff computation, and history tracking. The computed signal will be created only once.
107
+ *
108
+ * @param name - A descriptive name for the computed signal, used for debugging and identification
109
+ * @param compute - A function that computes the value, automatically tracking any signal dependencies
110
+ * @param opts - Configuration options for the computed signal
111
+ * - isEqual - Custom equality function to determine if the computed value has changed
112
+ * - computeDiff - Function to compute diffs between old and new values for history tracking
113
+ * - historyLength - Maximum number of diffs to keep in history buffer for time-travel functionality
114
+ * @param deps - React dependency array that controls when the computed signal is recreated
115
+ * @returns A computed signal containing the calculated value with the specified options
57
116
  *
58
117
  * @example
59
118
  * ```ts
60
- * type GreeterProps = {
61
- * firstName: Signal<string>
62
- * lastName: Signal<string>
63
- * }
119
+ * function ShoppingCart() {
120
+ * const items = useAtom('items', [])
64
121
  *
65
- * const Greeter = track(function Greeter ({firstName, lastName}: GreeterProps) {
66
- * const fullName = useComputed('fullName', () => `${firstName.get()} ${lastName.get()}`)
67
- * return <div>Hello {fullName.get()}!</div>
68
- * })
122
+ * // Computed with custom equality to avoid recalculation for equivalent arrays
123
+ * const sortedItems = useComputed(
124
+ * 'sortedItems',
125
+ * () => items.get().sort((a, b) => a.name.localeCompare(b.name)),
126
+ * {
127
+ * isEqual: (a, b) => a.length === b.length && a.every((item, i) => item.id === b[i].id)
128
+ * },
129
+ * [items]
130
+ * )
131
+ *
132
+ * return <ItemList items={sortedItems.get()} />
133
+ * }
69
134
  * ```
70
135
  *
71
136
  * @public
72
137
  */
73
138
  export declare function useComputed<Value, Diff = unknown>(name: string, compute: () => Value, opts: ComputedOptions<Value, Diff>, deps: any[]): Computed<Value>;
74
139
 
75
- /** @public */
140
+ /**
141
+ * A React hook that runs side effects immediately in response to signal changes, without throttling.
142
+ * Unlike useReactor which batches updates to animation frames, useQuickReactor executes the effect
143
+ * function immediately when dependencies change, making it ideal for critical updates that cannot wait.
144
+ *
145
+ * The effect runs immediately when the component mounts and whenever tracked signals change.
146
+ * Updates are not throttled, so the effect executes synchronously on every change.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * function DataSynchronizer() {
151
+ * const criticalData = useAtom('criticalData', null)
152
+ *
153
+ * useQuickReactor('sync-data', () => {
154
+ * const data = criticalData.get()
155
+ * if (data) {
156
+ * // Send immediately - don't wait for next frame
157
+ * sendToServer(data)
158
+ * }
159
+ * }, [criticalData])
160
+ *
161
+ * return <div>Sync status updated</div>
162
+ * }
163
+ * ```
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * function CursorUpdater({ editor }) {
168
+ * useQuickReactor('update-cursor', () => {
169
+ * const cursor = editor.getInstanceState().cursor
170
+ * document.body.style.cursor = cursor.type
171
+ * }, [])
172
+ * }
173
+ * ```
174
+ *
175
+ * @param name - A descriptive name for the reactor, used for debugging and performance profiling
176
+ * @param reactFn - The effect function to execute when signals change. Should not return a value.
177
+ * @param deps - Optional dependency array that controls when the reactor is recreated. Works like useEffect deps.
178
+ * @public
179
+ */
76
180
  export declare function useQuickReactor(name: string, reactFn: () => void, deps?: any[]): void;
77
181
 
78
- /** @public */
182
+ /**
183
+ * A React hook that runs a side effect in response to changes in signals (reactive state).
184
+ *
185
+ * The effect function will automatically track any signals (atoms, computed values) that are
186
+ * accessed during its execution. When any of those signals change, the effect will be
187
+ * scheduled to run again on the next animation frame.
188
+ *
189
+ * This is useful for performing side effects (like updating the DOM, making API calls, or
190
+ * updating external state) in response to changes in tldraw's reactive state system, while
191
+ * keeping those effects efficiently batched and throttled.
192
+ *
193
+ * @example
194
+ * ```tsx
195
+ * import { useReactor, useEditor } from 'tldraw'
196
+ *
197
+ * function MyComponent() {
198
+ * const editor = useEditor()
199
+ *
200
+ * // Update document title when shapes change
201
+ * useReactor(
202
+ * 'update title',
203
+ * () => {
204
+ * const shapes = editor.getCurrentPageShapes()
205
+ * document.title = `Shapes: ${shapes.length}`
206
+ * },
207
+ * [editor]
208
+ * )
209
+ *
210
+ * return <div>...</div>
211
+ * }
212
+ * ```
213
+ *
214
+ * @example
215
+ * ```tsx
216
+ * import { useReactor, useEditor } from 'tldraw'
217
+ *
218
+ * function SelectionAnnouncer() {
219
+ * const editor = useEditor()
220
+ *
221
+ * // Announce selection changes for accessibility
222
+ * useReactor(
223
+ * 'announce selection',
224
+ * () => {
225
+ * const selectedIds = editor.getSelectedShapeIds()
226
+ * if (selectedIds.length > 0) {
227
+ * console.log(`Selected ${selectedIds.length} shape(s)`)
228
+ * }
229
+ * },
230
+ * [editor]
231
+ * )
232
+ *
233
+ * return null
234
+ * }
235
+ * ```
236
+ *
237
+ * @remarks
238
+ * The effect is throttled to run at most once per animation frame using `requestAnimationFrame`.
239
+ * This makes it suitable for effects that need to respond to state changes but don't need to
240
+ * run synchronously.
241
+ *
242
+ * If you need the effect to run immediately without throttling, use {@link useQuickReactor} instead.
243
+ *
244
+ * The effect function will be re-created when any of the `deps` change, similar to React's
245
+ * `useEffect`. The effect automatically tracks which signals it accesses, so you don't need
246
+ * to manually specify them as dependencies.
247
+ *
248
+ * @param name - A debug name for the effect, useful for debugging and performance profiling.
249
+ * @param reactFn - The effect function to run. Any signals accessed in this function will be tracked.
250
+ * @param deps - React dependencies array. The effect will be recreated when these change. Defaults to `[]`.
251
+ *
252
+ * @public
253
+ */
79
254
  export declare function useReactor(name: string, reactFn: () => void, deps?: any[] | undefined): void;
80
255
 
81
256
  /**
@@ -85,6 +260,11 @@ export declare function useReactor(name: string, reactFn: () => void, deps?: any
85
260
  *
86
261
  * See the `track` component wrapper, which uses this under the hood.
87
262
  *
263
+ * @param name - A debug name for the reactive tracking context
264
+ * @param render - The render function that accesses reactive values
265
+ * @param deps - Optional dependency array to control when the tracking context is recreated
266
+ * @returns The result of calling the render function
267
+ *
88
268
  * @example
89
269
  * ```ts
90
270
  * function MyComponent() {
@@ -100,40 +280,66 @@ export declare function useReactor(name: string, reactFn: () => void, deps?: any
100
280
  */
101
281
  export declare function useStateTracking<T>(name: string, render: () => T, deps?: unknown[]): T;
102
282
 
103
- /** @public */
104
- export declare function useValue<Value>(value: Signal<Value>): Value;
105
-
106
283
  /**
107
- * Extracts the value from a signal and subscribes to it.
284
+ * Extracts the current value from a signal and subscribes the component to changes.
285
+ *
286
+ * This is the most straightforward way to read signal values in React components.
287
+ * When the signal changes, the component will automatically re-render with the new value.
108
288
  *
109
- * Note that you do not need to use this hook if you are wrapping the component with {@link track}
289
+ * Note: You do not need to use this hook if you are wrapping the component with {@link track},
290
+ * as tracked components automatically subscribe to any signals accessed with `.get()`.
291
+ *
292
+ * @param value - The signal to read the value from
293
+ * @returns The current value of the signal
110
294
  *
111
295
  * @example
112
296
  * ```ts
113
- * const Counter: React.FC = () => {
114
- * const $count = useAtom('count', 0)
115
- * const increment = useCallback(() => $count.set($count.get() + 1), [count])
116
- * const currentCount = useValue($count)
117
- * return <button onClick={increment}>{currentCount}</button>
297
+ * import { atom } from '@tldraw/state'
298
+ * import { useValue } from '@tldraw/state-react'
299
+ *
300
+ * const count = atom('count', 0)
301
+ *
302
+ * function Counter() {
303
+ * const currentCount = useValue(count)
304
+ * return (
305
+ * <button onClick={() => count.set(currentCount + 1)}>
306
+ * Count: {currentCount}
307
+ * </button>
308
+ * )
118
309
  * }
119
310
  * ```
120
311
  *
121
- * You can also pass a function to compute the value and it will be memoized as in `useComputed`:
312
+ * @public
313
+ */
314
+ export declare function useValue<Value>(value: Signal<Value>): Value;
315
+
316
+ /**
317
+ * Creates a computed value with automatic dependency tracking and subscribes to changes.
318
+ *
319
+ * This overload allows you to compute a value from one or more signals with automatic
320
+ * memoization. The computed function will only re-execute when its dependencies change,
321
+ * and the component will only re-render when the computed result changes.
322
+ *
323
+ * @param name - A descriptive name for debugging purposes
324
+ * @param fn - Function that computes the value, should call `.get()` on any signals it depends on
325
+ * @param deps - Array of signals that the computed function depends on
326
+ * @returns The computed value
122
327
  *
123
328
  * @example
124
329
  * ```ts
125
- * type GreeterProps = {
126
- * firstName: Signal<string>
127
- * lastName: Signal<string>
128
- * }
330
+ * import { atom } from '@tldraw/state'
331
+ * import { useValue } from '@tldraw/state-react'
332
+ *
333
+ * const firstName = atom('firstName', 'John')
334
+ * const lastName = atom('lastName', 'Doe')
335
+ *
336
+ * function UserGreeting() {
337
+ * const fullName = useValue('fullName', () => {
338
+ * return `${firstName.get()} ${lastName.get()}`
339
+ * }, [firstName, lastName])
129
340
  *
130
- * const Greeter = track(function Greeter({ firstName, lastName }: GreeterProps) {
131
- * const fullName = useValue('fullName', () => `${firstName.get()} ${lastName.get()}`, [
132
- * firstName,
133
- * lastName,
134
- * ])
135
341
  * return <div>Hello {fullName}!</div>
136
- * })
342
+ * }
137
343
  * ```
138
344
  *
139
345
  * @public
package/dist-cjs/index.js CHANGED
@@ -37,7 +37,7 @@ var import_useStateTracking = require("./lib/useStateTracking");
37
37
  var import_useValue = require("./lib/useValue");
38
38
  (0, import_utils.registerTldrawLibraryVersion)(
39
39
  "@tldraw/state-react",
40
- "4.0.2",
40
+ "4.1.0-canary.0259516ffb8c",
41
41
  "cjs"
42
42
  );
43
43
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/track.ts"],
4
- "sourcesContent": ["import React, { forwardRef, FunctionComponent, memo } from 'react'\nimport { useStateTracking } from './useStateTracking'\n\nexport const ProxyHandlers = {\n\t/**\n\t * This is a function call trap for functional components. When this is called, we know it means\n\t * React did run 'Component()', that means we can use any hooks here to setup our effect and\n\t * store.\n\t *\n\t * With the native Proxy, all other calls such as access/setting to/of properties will be\n\t * forwarded to the target Component, so we don't need to copy the Component's own or inherited\n\t * properties.\n\t *\n\t * @see https://github.com/facebook/react/blob/2d80a0cd690bb5650b6c8a6c079a87b5dc42bd15/packages/react-reconciler/src/ReactFiberHooks.old.js#L460\n\t */\n\tapply(Component: FunctionComponent, thisArg: any, argumentsList: any) {\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\treturn useStateTracking(Component.displayName ?? Component.name ?? 'tracked(???)', () =>\n\t\t\tComponent.apply(thisArg, argumentsList)\n\t\t)\n\t},\n}\n\nexport const ReactMemoSymbol = Symbol.for('react.memo')\nexport const ReactForwardRefSymbol = Symbol.for('react.forward_ref')\n\n/**\n * Returns a tracked version of the given component.\n * Any signals whose values are read while the component renders will be tracked.\n * If any of the tracked signals change later it will cause the component to re-render.\n *\n * This also wraps the component in a React.memo() call, so it will only re-render if the props change.\n *\n * @example\n * ```ts\n * const Counter = track(function Counter(props: CounterProps) {\n * const count = useAtom('count', 0)\n * const increment = useCallback(() => count.set(count.get() + 1), [count])\n * return <button onClick={increment}>{count.get()}</button>\n * })\n * ```\n *\n * @param baseComponent - The base component to track.\n * @public\n */\nexport function track<T extends FunctionComponent<any>>(\n\tbaseComponent: T\n): React.NamedExoticComponent<React.ComponentProps<T>> {\n\tlet compare = null\n\tconst $$typeof = baseComponent['$$typeof' as keyof typeof baseComponent]\n\tif ($$typeof === ReactMemoSymbol) {\n\t\tbaseComponent = (baseComponent as any).type\n\t\tcompare = (baseComponent as any).compare\n\t}\n\tif ($$typeof === ReactForwardRefSymbol) {\n\t\treturn memo(forwardRef(new Proxy((baseComponent as any).render, ProxyHandlers) as any)) as any\n\t}\n\n\treturn memo(new Proxy(baseComponent, ProxyHandlers) as any, compare) as any\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2D;AAC3D,8BAAiC;AAE1B,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY5B,MAAM,WAA8B,SAAc,eAAoB;AAErE,eAAO;AAAA,MAAiB,UAAU,eAAe,UAAU,QAAQ;AAAA,MAAgB,MAClF,UAAU,MAAM,SAAS,aAAa;AAAA,IACvC;AAAA,EACD;AACD;AAEO,MAAM,kBAAkB,OAAO,IAAI,YAAY;AAC/C,MAAM,wBAAwB,OAAO,IAAI,mBAAmB;AAqB5D,SAAS,MACf,eACsD;AACtD,MAAI,UAAU;AACd,QAAM,WAAW,cAAc,UAAwC;AACvE,MAAI,aAAa,iBAAiB;AACjC,oBAAiB,cAAsB;AACvC,cAAW,cAAsB;AAAA,EAClC;AACA,MAAI,aAAa,uBAAuB;AACvC,eAAO,uBAAK,yBAAW,IAAI,MAAO,cAAsB,QAAQ,aAAa,CAAQ,CAAC;AAAA,EACvF;AAEA,aAAO,mBAAK,IAAI,MAAM,eAAe,aAAa,GAAU,OAAO;AACpE;",
4
+ "sourcesContent": ["import React, { forwardRef, FunctionComponent, memo } from 'react'\nimport { useStateTracking } from './useStateTracking'\n\n/**\n * Proxy handlers object used to intercept function calls to React components.\n * This enables automatic signal tracking by wrapping component execution\n * in reactive tracking context.\n *\n * The proxy intercepts the function call (apply trap) and wraps it with\n * useStateTracking to enable automatic dependency tracking for signals\n * accessed during render.\n *\n * @example\n * ```ts\n * // Used internally by track() function\n * const ProxiedComponent = new Proxy(MyComponent, ProxyHandlers)\n * ```\n *\n * @internal\n */\nexport const ProxyHandlers = {\n\t/**\n\t * This is a function call trap for functional components. When this is called, we know it means\n\t * React did run 'Component()', that means we can use any hooks here to setup our effect and\n\t * store.\n\t *\n\t * With the native Proxy, all other calls such as access/setting to/of properties will be\n\t * forwarded to the target Component, so we don't need to copy the Component's own or inherited\n\t * properties.\n\t *\n\t * @see https://github.com/facebook/react/blob/2d80a0cd690bb5650b6c8a6c079a87b5dc42bd15/packages/react-reconciler/src/ReactFiberHooks.old.js#L460\n\t */\n\tapply(Component: FunctionComponent, thisArg: any, argumentsList: any) {\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\treturn useStateTracking(Component.displayName ?? Component.name ?? 'tracked(???)', () =>\n\t\t\tComponent.apply(thisArg, argumentsList)\n\t\t)\n\t},\n}\n\n/**\n * React internal symbol for identifying memoized components.\n * Used to detect if a component is already wrapped with React.memo().\n *\n * @example\n * ```ts\n * const isMemoComponent = component['$$typeof'] === ReactMemoSymbol\n * ```\n *\n * @internal\n */\nexport const ReactMemoSymbol = Symbol.for('react.memo')\n\n/**\n * React internal symbol for identifying forward ref components.\n * Used to detect if a component is wrapped with React.forwardRef().\n *\n * @example\n * ```ts\n * const isForwardRefComponent = component['$$typeof'] === ReactForwardRefSymbol\n * ```\n *\n * @internal\n */\nexport const ReactForwardRefSymbol = Symbol.for('react.forward_ref')\n\n/**\n * Returns a tracked version of the given component.\n * Any signals whose values are read while the component renders will be tracked.\n * If any of the tracked signals change later it will cause the component to re-render.\n *\n * This also wraps the component in a React.memo() call, so it will only re-render\n * when props change OR when any tracked signals change. This provides optimal\n * performance by preventing unnecessary re-renders while maintaining reactivity.\n *\n * The function handles special React component types like forwardRef and memo\n * components automatically, preserving their behavior while adding reactivity.\n *\n * @param baseComponent - The React functional component to make reactive to signal changes\n * @returns A memoized component that re-renders when props or tracked signals change\n *\n * @example\n * ```ts\n * import { atom } from '@tldraw/state'\n * import { track, useAtom } from '@tldraw/state-react'\n *\n * const Counter = track(function Counter(props: CounterProps) {\n * const count = useAtom('count', 0)\n * const increment = useCallback(() => count.set(count.get() + 1), [count])\n * return <button onClick={increment}>{count.get()}</button>\n * })\n *\n * // Component automatically re-renders when count signal changes\n * ```\n *\n * @example\n * ```ts\n * // Works with forwardRef components\n * const TrackedInput = track(React.forwardRef<HTMLInputElement, InputProps>(\n * function TrackedInput(props, ref) {\n * const theme = useValue(themeSignal)\n * return <input ref={ref} style={{ color: theme.textColor }} {...props} />\n * }\n * ))\n * ```\n *\n * @public\n */\nexport function track<T extends FunctionComponent<any>>(\n\tbaseComponent: T\n): React.NamedExoticComponent<React.ComponentProps<T>> {\n\tlet compare = null\n\tconst $$typeof = baseComponent['$$typeof' as keyof typeof baseComponent]\n\tif ($$typeof === ReactMemoSymbol) {\n\t\tbaseComponent = (baseComponent as any).type\n\t\tcompare = (baseComponent as any).compare\n\t}\n\tif ($$typeof === ReactForwardRefSymbol) {\n\t\treturn memo(forwardRef(new Proxy((baseComponent as any).render, ProxyHandlers) as any)) as any\n\t}\n\n\treturn memo(new Proxy(baseComponent, ProxyHandlers) as any, compare) as any\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2D;AAC3D,8BAAiC;AAmB1B,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY5B,MAAM,WAA8B,SAAc,eAAoB;AAErE,eAAO;AAAA,MAAiB,UAAU,eAAe,UAAU,QAAQ;AAAA,MAAgB,MAClF,UAAU,MAAM,SAAS,aAAa;AAAA,IACvC;AAAA,EACD;AACD;AAaO,MAAM,kBAAkB,OAAO,IAAI,YAAY;AAa/C,MAAM,wBAAwB,OAAO,IAAI,mBAAmB;AA4C5D,SAAS,MACf,eACsD;AACtD,MAAI,UAAU;AACd,QAAM,WAAW,cAAc,UAAwC;AACvE,MAAI,aAAa,iBAAiB;AACjC,oBAAiB,cAAsB;AACvC,cAAW,cAAsB;AAAA,EAClC;AACA,MAAI,aAAa,uBAAuB;AACvC,eAAO,uBAAK,yBAAW,IAAI,MAAO,cAAsB,QAAQ,aAAa,CAAQ,CAAC;AAAA,EACvF;AAEA,aAAO,mBAAK,IAAI,MAAM,eAAe,aAAa,GAAU,OAAO;AACpE;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/useComputed.ts"],
4
- "sourcesContent": ["/* eslint-disable prefer-rest-params */\nimport { Computed, ComputedOptions, computed } from '@tldraw/state'\nimport { useMemo } from 'react'\n\n/** @public */\nexport function useComputed<Value>(name: string, compute: () => Value, deps: any[]): Computed<Value>\n\n/**\n * Creates a new computed signal and returns it. The computed signal will be created only once.\n *\n * @example\n * ```ts\n * type GreeterProps = {\n * firstName: Signal<string>\n * lastName: Signal<string>\n * }\n *\n * const Greeter = track(function Greeter ({firstName, lastName}: GreeterProps) {\n * const fullName = useComputed('fullName', () => `${firstName.get()} ${lastName.get()}`)\n * return <div>Hello {fullName.get()}!</div>\n * })\n * ```\n *\n * @public\n */\nexport function useComputed<Value, Diff = unknown>(\n\tname: string,\n\tcompute: () => Value,\n\topts: ComputedOptions<Value, Diff>,\n\tdeps: any[]\n): Computed<Value>\n/** @public */\nexport function useComputed() {\n\tconst name = arguments[0]\n\tconst compute = arguments[1]\n\tconst opts = arguments.length === 3 ? undefined : arguments[2]\n\tconst deps = arguments.length === 3 ? arguments[2] : arguments[3]\n\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\treturn useMemo(() => computed(`useComputed(${name})`, compute, opts), deps)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAoD;AACpD,mBAAwB;AA8BjB,SAAS,cAAc;AAC7B,QAAM,OAAO,UAAU,CAAC;AACxB,QAAM,UAAU,UAAU,CAAC;AAC3B,QAAM,OAAO,UAAU,WAAW,IAAI,SAAY,UAAU,CAAC;AAC7D,QAAM,OAAO,UAAU,WAAW,IAAI,UAAU,CAAC,IAAI,UAAU,CAAC;AAEhE,aAAO,sBAAQ,UAAM,uBAAS,eAAe,IAAI,KAAK,SAAS,IAAI,GAAG,IAAI;AAC3E;",
4
+ "sourcesContent": ["/* eslint-disable prefer-rest-params */\nimport { Computed, ComputedOptions, computed } from '@tldraw/state'\nimport { useMemo } from 'react'\n\n/**\n * Creates a new computed signal that automatically tracks its dependencies and recalculates when they change.\n * This overload is for basic computed values without custom options.\n *\n * @param name - A descriptive name for the computed signal, used for debugging and identification\n * @param compute - A function that computes the value, automatically tracking any signal dependencies\n * @param deps - React dependency array that controls when the computed signal is recreated\n * @returns A computed signal containing the calculated value\n *\n * @example\n * ```ts\n * const firstName = atom('firstName', 'John')\n * const lastName = atom('lastName', 'Doe')\n *\n * function UserProfile() {\n * const fullName = useComputed(\n * 'fullName',\n * () => `${firstName.get()} ${lastName.get()}`,\n * [firstName, lastName]\n * )\n *\n * return <div>Welcome, {fullName.get()}!</div>\n * }\n * ```\n *\n * @public\n */\nexport function useComputed<Value>(name: string, compute: () => Value, deps: any[]): Computed<Value>\n\n/**\n * Creates a new computed signal with custom options for advanced behavior like custom equality checking,\n * diff computation, and history tracking. The computed signal will be created only once.\n *\n * @param name - A descriptive name for the computed signal, used for debugging and identification\n * @param compute - A function that computes the value, automatically tracking any signal dependencies\n * @param opts - Configuration options for the computed signal\n * - isEqual - Custom equality function to determine if the computed value has changed\n * - computeDiff - Function to compute diffs between old and new values for history tracking\n * - historyLength - Maximum number of diffs to keep in history buffer for time-travel functionality\n * @param deps - React dependency array that controls when the computed signal is recreated\n * @returns A computed signal containing the calculated value with the specified options\n *\n * @example\n * ```ts\n * function ShoppingCart() {\n * const items = useAtom('items', [])\n *\n * // Computed with custom equality to avoid recalculation for equivalent arrays\n * const sortedItems = useComputed(\n * 'sortedItems',\n * () => items.get().sort((a, b) => a.name.localeCompare(b.name)),\n * {\n * isEqual: (a, b) => a.length === b.length && a.every((item, i) => item.id === b[i].id)\n * },\n * [items]\n * )\n *\n * return <ItemList items={sortedItems.get()} />\n * }\n * ```\n *\n * @public\n */\nexport function useComputed<Value, Diff = unknown>(\n\tname: string,\n\tcompute: () => Value,\n\topts: ComputedOptions<Value, Diff>,\n\tdeps: any[]\n): Computed<Value>\n/**\n * Implementation function that handles both overloaded signatures of useComputed.\n * Uses the arguments object to dynamically determine which signature was called.\n *\n * This function creates a memoized computed signal that automatically tracks dependencies\n * and only recreates when the dependency array changes, providing optimal performance\n * in React components.\n *\n * @public\n */\nexport function useComputed() {\n\tconst name = arguments[0]\n\tconst compute = arguments[1]\n\tconst opts = arguments.length === 3 ? undefined : arguments[2]\n\tconst deps = arguments.length === 3 ? arguments[2] : arguments[3]\n\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\treturn useMemo(() => computed(`useComputed(${name})`, compute, opts), deps)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAoD;AACpD,mBAAwB;AAiFjB,SAAS,cAAc;AAC7B,QAAM,OAAO,UAAU,CAAC;AACxB,QAAM,UAAU,UAAU,CAAC;AAC3B,QAAM,OAAO,UAAU,WAAW,IAAI,SAAY,UAAU,CAAC;AAC7D,QAAM,OAAO,UAAU,WAAW,IAAI,UAAU,CAAC,IAAI,UAAU,CAAC;AAEhE,aAAO,sBAAQ,UAAM,uBAAS,eAAe,IAAI,KAAK,SAAS,IAAI,GAAG,IAAI;AAC3E;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/useQuickReactor.ts"],
4
- "sourcesContent": ["import { EMPTY_ARRAY, EffectScheduler } from '@tldraw/state'\nimport { useEffect } from 'react'\n\n/** @public */\nexport function useQuickReactor(name: string, reactFn: () => void, deps: any[] = EMPTY_ARRAY) {\n\tuseEffect(() => {\n\t\tconst scheduler = new EffectScheduler(name, reactFn)\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6C;AAC7C,mBAA0B;AAGnB,SAAS,gBAAgB,MAAc,SAAqB,OAAc,0BAAa;AAC7F,8BAAU,MAAM;AACf,UAAM,YAAY,IAAI,6BAAgB,MAAM,OAAO;AACnD,cAAU,OAAO;AACjB,cAAU,QAAQ;AAClB,WAAO,MAAM;AACZ,gBAAU,OAAO;AAAA,IAClB;AAAA,EAED,GAAG,IAAI;AACR;",
4
+ "sourcesContent": ["import { EMPTY_ARRAY, EffectScheduler } from '@tldraw/state'\nimport { useEffect } from 'react'\n\n/**\n * A React hook that runs side effects immediately in response to signal changes, without throttling.\n * Unlike useReactor which batches updates to animation frames, useQuickReactor executes the effect\n * function immediately when dependencies change, making it ideal for critical updates that cannot wait.\n *\n * The effect runs immediately when the component mounts and whenever tracked signals change.\n * Updates are not throttled, so the effect executes synchronously on every change.\n *\n * @example\n * ```ts\n * function DataSynchronizer() {\n * const criticalData = useAtom('criticalData', null)\n *\n * useQuickReactor('sync-data', () => {\n * const data = criticalData.get()\n * if (data) {\n * // Send immediately - don't wait for next frame\n * sendToServer(data)\n * }\n * }, [criticalData])\n *\n * return <div>Sync status updated</div>\n * }\n * ```\n *\n * @example\n * ```ts\n * function CursorUpdater({ editor }) {\n * useQuickReactor('update-cursor', () => {\n * const cursor = editor.getInstanceState().cursor\n * document.body.style.cursor = cursor.type\n * }, [])\n * }\n * ```\n *\n * @param name - A descriptive name for the reactor, used for debugging and performance profiling\n * @param reactFn - The effect function to execute when signals change. Should not return a value.\n * @param deps - Optional dependency array that controls when the reactor is recreated. Works like useEffect deps.\n * @public\n */\nexport function useQuickReactor(name: string, reactFn: () => void, deps: any[] = EMPTY_ARRAY) {\n\tuseEffect(() => {\n\t\tconst scheduler = new EffectScheduler(name, reactFn)\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6C;AAC7C,mBAA0B;AA0CnB,SAAS,gBAAgB,MAAc,SAAqB,OAAc,0BAAa;AAC7F,8BAAU,MAAM;AACf,UAAM,YAAY,IAAI,6BAAgB,MAAM,OAAO;AACnD,cAAU,OAAO;AACjB,cAAU,QAAQ;AAClB,WAAO,MAAM;AACZ,gBAAU,OAAO;AAAA,IAClB;AAAA,EAED,GAAG,IAAI;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/useReactor.ts"],
4
- "sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport { throttleToNextFrame } from '@tldraw/utils'\nimport { useEffect } from 'react'\n\n/** @public */\nexport function useReactor(name: string, reactFn: () => void, deps: undefined | any[] = []) {\n\tuseEffect(() => {\n\t\tlet cancelFn: () => void | undefined\n\t\tconst scheduler = new EffectScheduler(name, reactFn, {\n\t\t\tscheduleEffect: (cb) => {\n\t\t\t\tcancelFn = throttleToNextFrame(cb)\n\t\t\t},\n\t\t})\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t\tcancelFn?.()\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgC;AAChC,mBAAoC;AACpC,mBAA0B;AAGnB,SAAS,WAAW,MAAc,SAAqB,OAA0B,CAAC,GAAG;AAC3F,8BAAU,MAAM;AACf,QAAI;AACJ,UAAM,YAAY,IAAI,6BAAgB,MAAM,SAAS;AAAA,MACpD,gBAAgB,CAAC,OAAO;AACvB,uBAAW,kCAAoB,EAAE;AAAA,MAClC;AAAA,IACD,CAAC;AACD,cAAU,OAAO;AACjB,cAAU,QAAQ;AAClB,WAAO,MAAM;AACZ,gBAAU,OAAO;AACjB,iBAAW;AAAA,IACZ;AAAA,EAED,GAAG,IAAI;AACR;",
4
+ "sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport { throttleToNextFrame } from '@tldraw/utils'\nimport { useEffect } from 'react'\n\n/**\n * A React hook that runs a side effect in response to changes in signals (reactive state).\n *\n * The effect function will automatically track any signals (atoms, computed values) that are\n * accessed during its execution. When any of those signals change, the effect will be\n * scheduled to run again on the next animation frame.\n *\n * This is useful for performing side effects (like updating the DOM, making API calls, or\n * updating external state) in response to changes in tldraw's reactive state system, while\n * keeping those effects efficiently batched and throttled.\n *\n * @example\n * ```tsx\n * import { useReactor, useEditor } from 'tldraw'\n *\n * function MyComponent() {\n * const editor = useEditor()\n *\n * // Update document title when shapes change\n * useReactor(\n * 'update title',\n * () => {\n * const shapes = editor.getCurrentPageShapes()\n * document.title = `Shapes: ${shapes.length}`\n * },\n * [editor]\n * )\n *\n * return <div>...</div>\n * }\n * ```\n *\n * @example\n * ```tsx\n * import { useReactor, useEditor } from 'tldraw'\n *\n * function SelectionAnnouncer() {\n * const editor = useEditor()\n *\n * // Announce selection changes for accessibility\n * useReactor(\n * 'announce selection',\n * () => {\n * const selectedIds = editor.getSelectedShapeIds()\n * if (selectedIds.length > 0) {\n * console.log(`Selected ${selectedIds.length} shape(s)`)\n * }\n * },\n * [editor]\n * )\n *\n * return null\n * }\n * ```\n *\n * @remarks\n * The effect is throttled to run at most once per animation frame using `requestAnimationFrame`.\n * This makes it suitable for effects that need to respond to state changes but don't need to\n * run synchronously.\n *\n * If you need the effect to run immediately without throttling, use {@link useQuickReactor} instead.\n *\n * The effect function will be re-created when any of the `deps` change, similar to React's\n * `useEffect`. The effect automatically tracks which signals it accesses, so you don't need\n * to manually specify them as dependencies.\n *\n * @param name - A debug name for the effect, useful for debugging and performance profiling.\n * @param reactFn - The effect function to run. Any signals accessed in this function will be tracked.\n * @param deps - React dependencies array. The effect will be recreated when these change. Defaults to `[]`.\n *\n * @public\n */\nexport function useReactor(name: string, reactFn: () => void, deps: undefined | any[] = []) {\n\tuseEffect(() => {\n\t\tlet cancelFn: () => void | undefined\n\t\tconst scheduler = new EffectScheduler(name, reactFn, {\n\t\t\tscheduleEffect: (cb) => {\n\t\t\t\tcancelFn = throttleToNextFrame(cb)\n\t\t\t},\n\t\t})\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t\tcancelFn?.()\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgC;AAChC,mBAAoC;AACpC,mBAA0B;AA0EnB,SAAS,WAAW,MAAc,SAAqB,OAA0B,CAAC,GAAG;AAC3F,8BAAU,MAAM;AACf,QAAI;AACJ,UAAM,YAAY,IAAI,6BAAgB,MAAM,SAAS;AAAA,MACpD,gBAAgB,CAAC,OAAO;AACvB,uBAAW,kCAAoB,EAAE;AAAA,MAClC;AAAA,IACD,CAAC;AACD,cAAU,OAAO;AACjB,cAAU,QAAQ;AAClB,WAAO,MAAM;AACZ,gBAAU,OAAO;AACjB,iBAAW;AAAA,IACZ;AAAA,EAED,GAAG,IAAI;AACR;",
6
6
  "names": []
7
7
  }
@@ -32,7 +32,7 @@ __export(useStateTracking_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(useStateTracking_exports);
34
34
  var import_state = require("@tldraw/state");
35
- var import_react = __toESM(require("react"));
35
+ var import_react = __toESM(require("react"), 1);
36
36
  function useStateTracking(name, render, deps = []) {
37
37
  const renderRef = import_react.default.useRef(render);
38
38
  renderRef.current = render;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/useStateTracking.ts"],
4
- "sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport React from 'react'\n\n/**\n * Wraps some synchronous react render logic in a reactive tracking context.\n *\n * This allows you to use reactive values transparently.\n *\n * See the `track` component wrapper, which uses this under the hood.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * return useStateTracking('MyComponent', () => {\n * const editor = useEditor()\n * return <div>Num shapes: {editor.getCurrentPageShapes().length}</div>\n * })\n * }\n * ```\n *\n *\n * @public\n */\nexport function useStateTracking<T>(name: string, render: () => T, deps: unknown[] = []): T {\n\t// This hook creates an effect scheduler that will trigger re-renders when its reactive dependencies change, but it\n\t// defers the actual execution of the effect to the consumer of this hook.\n\n\t// We need the exec fn to always be up-to-date when calling scheduler.execute() but it'd be wasteful to\n\t// instantiate a new EffectScheduler on every render, so we use an immediately-updated ref\n\t// to wrap it\n\tconst renderRef = React.useRef(render)\n\trenderRef.current = render\n\n\tconst [scheduler, subscribe, getSnapshot] = React.useMemo(() => {\n\t\tlet scheduleUpdate = null as null | (() => void)\n\t\t// useSyncExternalStore requires a subscribe function that returns an unsubscribe function\n\t\tconst subscribe = (cb: () => void) => {\n\t\t\tscheduleUpdate = cb\n\t\t\treturn () => {\n\t\t\t\tscheduleUpdate = null\n\t\t\t}\n\t\t}\n\n\t\tconst scheduler = new EffectScheduler(\n\t\t\t`useStateTracking(${name})`,\n\t\t\t// this is what `scheduler.execute()` will call\n\t\t\t() => renderRef.current?.(),\n\t\t\t// this is what will be invoked when @tldraw/state detects a change in an upstream reactive value\n\t\t\t{\n\t\t\t\tscheduleEffect() {\n\t\t\t\t\tscheduleUpdate?.()\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\n\t\t// we use an incrementing number based on when this\n\t\tconst getSnapshot = () => scheduler.scheduleCount\n\n\t\treturn [scheduler, subscribe, getSnapshot]\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [name, ...deps])\n\n\tReact.useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n\t// reactive dependencies are captured when `scheduler.execute()` is called\n\t// and then to make it reactive we wait for a `useEffect` to 'attach'\n\t// this allows us to avoid rendering outside of React's render phase\n\t// and avoid 'zombie' components that try to render with bad/deleted data before\n\t// react has a chance to umount them.\n\tReact.useEffect(() => {\n\t\tscheduler.attach()\n\t\t// do not execute, we only do that in render\n\t\tscheduler.maybeScheduleEffect()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t}\n\t}, [scheduler])\n\n\treturn scheduler.execute()\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgC;AAChC,mBAAkB;AAsBX,SAAS,iBAAoB,MAAc,QAAiB,OAAkB,CAAC,GAAM;AAO3F,QAAM,YAAY,aAAAA,QAAM,OAAO,MAAM;AACrC,YAAU,UAAU;AAEpB,QAAM,CAAC,WAAW,WAAW,WAAW,IAAI,aAAAA,QAAM,QAAQ,MAAM;AAC/D,QAAI,iBAAiB;AAErB,UAAMC,aAAY,CAAC,OAAmB;AACrC,uBAAiB;AACjB,aAAO,MAAM;AACZ,yBAAiB;AAAA,MAClB;AAAA,IACD;AAEA,UAAMC,aAAY,IAAI;AAAA,MACrB,oBAAoB,IAAI;AAAA;AAAA,MAExB,MAAM,UAAU,UAAU;AAAA;AAAA,MAE1B;AAAA,QACC,iBAAiB;AAChB,2BAAiB;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAGA,UAAMC,eAAc,MAAMD,WAAU;AAEpC,WAAO,CAACA,YAAWD,YAAWE,YAAW;AAAA,EAE1C,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;AAElB,eAAAH,QAAM,qBAAqB,WAAW,aAAa,WAAW;AAO9D,eAAAA,QAAM,UAAU,MAAM;AACrB,cAAU,OAAO;AAEjB,cAAU,oBAAoB;AAC9B,WAAO,MAAM;AACZ,gBAAU,OAAO;AAAA,IAClB;AAAA,EACD,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO,UAAU,QAAQ;AAC1B;",
4
+ "sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport React from 'react'\n\n/**\n * Wraps some synchronous react render logic in a reactive tracking context.\n *\n * This allows you to use reactive values transparently.\n *\n * See the `track` component wrapper, which uses this under the hood.\n *\n * @param name - A debug name for the reactive tracking context\n * @param render - The render function that accesses reactive values\n * @param deps - Optional dependency array to control when the tracking context is recreated\n * @returns The result of calling the render function\n *\n * @example\n * ```ts\n * function MyComponent() {\n * return useStateTracking('MyComponent', () => {\n * const editor = useEditor()\n * return <div>Num shapes: {editor.getCurrentPageShapes().length}</div>\n * })\n * }\n * ```\n *\n *\n * @public\n */\nexport function useStateTracking<T>(name: string, render: () => T, deps: unknown[] = []): T {\n\t// This hook creates an effect scheduler that will trigger re-renders when its reactive dependencies change, but it\n\t// defers the actual execution of the effect to the consumer of this hook.\n\n\t// We need the exec fn to always be up-to-date when calling scheduler.execute() but it'd be wasteful to\n\t// instantiate a new EffectScheduler on every render, so we use an immediately-updated ref\n\t// to wrap it\n\tconst renderRef = React.useRef(render)\n\trenderRef.current = render\n\n\tconst [scheduler, subscribe, getSnapshot] = React.useMemo(() => {\n\t\tlet scheduleUpdate = null as null | (() => void)\n\t\t// useSyncExternalStore requires a subscribe function that returns an unsubscribe function\n\t\tconst subscribe = (cb: () => void) => {\n\t\t\tscheduleUpdate = cb\n\t\t\treturn () => {\n\t\t\t\tscheduleUpdate = null\n\t\t\t}\n\t\t}\n\n\t\tconst scheduler = new EffectScheduler(\n\t\t\t`useStateTracking(${name})`,\n\t\t\t// this is what `scheduler.execute()` will call\n\t\t\t() => renderRef.current?.(),\n\t\t\t// this is what will be invoked when @tldraw/state detects a change in an upstream reactive value\n\t\t\t{\n\t\t\t\tscheduleEffect() {\n\t\t\t\t\tscheduleUpdate?.()\n\t\t\t\t},\n\t\t\t}\n\t\t)\n\n\t\t// we use an incrementing number based on when this\n\t\tconst getSnapshot = () => scheduler.scheduleCount\n\n\t\treturn [scheduler, subscribe, getSnapshot]\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [name, ...deps])\n\n\tReact.useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n\t// reactive dependencies are captured when `scheduler.execute()` is called\n\t// and then to make it reactive we wait for a `useEffect` to 'attach'\n\t// this allows us to avoid rendering outside of React's render phase\n\t// and avoid 'zombie' components that try to render with bad/deleted data before\n\t// react has a chance to umount them.\n\tReact.useEffect(() => {\n\t\tscheduler.attach()\n\t\t// do not execute, we only do that in render\n\t\tscheduler.maybeScheduleEffect()\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t}\n\t}, [scheduler])\n\n\treturn scheduler.execute()\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAgC;AAChC,mBAAkB;AA2BX,SAAS,iBAAoB,MAAc,QAAiB,OAAkB,CAAC,GAAM;AAO3F,QAAM,YAAY,aAAAA,QAAM,OAAO,MAAM;AACrC,YAAU,UAAU;AAEpB,QAAM,CAAC,WAAW,WAAW,WAAW,IAAI,aAAAA,QAAM,QAAQ,MAAM;AAC/D,QAAI,iBAAiB;AAErB,UAAMC,aAAY,CAAC,OAAmB;AACrC,uBAAiB;AACjB,aAAO,MAAM;AACZ,yBAAiB;AAAA,MAClB;AAAA,IACD;AAEA,UAAMC,aAAY,IAAI;AAAA,MACrB,oBAAoB,IAAI;AAAA;AAAA,MAExB,MAAM,UAAU,UAAU;AAAA;AAAA,MAE1B;AAAA,QACC,iBAAiB;AAChB,2BAAiB;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAGA,UAAMC,eAAc,MAAMD,WAAU;AAEpC,WAAO,CAACA,YAAWD,YAAWE,YAAW;AAAA,EAE1C,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;AAElB,eAAAH,QAAM,qBAAqB,WAAW,aAAa,WAAW;AAO9D,eAAAA,QAAM,UAAU,MAAM;AACrB,cAAU,OAAO;AAEjB,cAAU,oBAAoB;AAC9B,WAAO,MAAM;AACZ,gBAAU,OAAO;AAAA,IAClB;AAAA,EACD,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO,UAAU,QAAQ;AAC1B;",
6
6
  "names": ["React", "subscribe", "scheduler", "getSnapshot"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/useValue.ts"],
4
- "sourcesContent": ["/* eslint-disable prefer-rest-params */\nimport { Signal, computed, react } from '@tldraw/state'\nimport { useMemo, useSyncExternalStore } from 'react'\n\n/** @public */\nexport function useValue<Value>(value: Signal<Value>): Value\n\n/**\n * Extracts the value from a signal and subscribes to it.\n *\n * Note that you do not need to use this hook if you are wrapping the component with {@link track}\n *\n * @example\n * ```ts\n * const Counter: React.FC = () => {\n * const $count = useAtom('count', 0)\n * const increment = useCallback(() => $count.set($count.get() + 1), [count])\n * const currentCount = useValue($count)\n * return <button onClick={increment}>{currentCount}</button>\n * }\n * ```\n *\n * You can also pass a function to compute the value and it will be memoized as in `useComputed`:\n *\n * @example\n * ```ts\n * type GreeterProps = {\n * firstName: Signal<string>\n * lastName: Signal<string>\n * }\n *\n * const Greeter = track(function Greeter({ firstName, lastName }: GreeterProps) {\n * const fullName = useValue('fullName', () => `${firstName.get()} ${lastName.get()}`, [\n * firstName,\n * lastName,\n * ])\n * return <div>Hello {fullName}!</div>\n * })\n * ```\n *\n * @public\n */\nexport function useValue<Value>(name: string, fn: () => Value, deps: unknown[]): Value\n\n/** @public */\nexport function useValue() {\n\tconst args = arguments\n\t// deps will be either the computed or the deps array\n\tconst deps = args.length === 3 ? args[2] : [args[0]]\n\tconst name = args.length === 3 ? args[0] : `useValue(${args[0].name})`\n\n\tconst { $val, subscribe, getSnapshot } = useMemo(() => {\n\t\tconst $val =\n\t\t\targs.length === 1 ? (args[0] as Signal<any>) : (computed(name, args[1]) as Signal<any>)\n\n\t\treturn {\n\t\t\t$val,\n\t\t\tsubscribe: (notify: () => void) => {\n\t\t\t\treturn react(`useValue(${name})`, () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$val.get()\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Will be rethrown during render if the component doesn't unmount first.\n\t\t\t\t\t}\n\t\t\t\t\tnotify()\n\t\t\t\t})\n\t\t\t},\n\t\t\tgetSnapshot: () => $val.lastChangedEpoch,\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n\n\tuseSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\treturn $val.__unsafe__getWithoutCapture()\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAwC;AACxC,mBAA8C;AA2CvC,SAAS,WAAW;AAC1B,QAAM,OAAO;AAEb,QAAM,OAAO,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnD,QAAM,OAAO,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,IAAI;AAEnE,QAAM,EAAE,MAAM,WAAW,YAAY,QAAI,sBAAQ,MAAM;AACtD,UAAMA,QACL,KAAK,WAAW,IAAK,KAAK,CAAC,QAAqB,uBAAS,MAAM,KAAK,CAAC,CAAC;AAEvE,WAAO;AAAA,MACN,MAAAA;AAAA,MACA,WAAW,CAAC,WAAuB;AAClC,mBAAO,oBAAM,YAAY,IAAI,KAAK,MAAM;AACvC,cAAI;AACH,YAAAA,MAAK,IAAI;AAAA,UACV,QAAQ;AAAA,UAER;AACA,iBAAO;AAAA,QACR,CAAC;AAAA,MACF;AAAA,MACA,aAAa,MAAMA,MAAK;AAAA,IACzB;AAAA,EAED,GAAG,IAAI;AAEP,yCAAqB,WAAW,aAAa,WAAW;AACxD,SAAO,KAAK,4BAA4B;AACzC;",
4
+ "sourcesContent": ["/* eslint-disable prefer-rest-params */\nimport { Signal, computed, react } from '@tldraw/state'\nimport { useMemo, useSyncExternalStore } from 'react'\n\n/**\n * Extracts the current value from a signal and subscribes the component to changes.\n *\n * This is the most straightforward way to read signal values in React components.\n * When the signal changes, the component will automatically re-render with the new value.\n *\n * Note: You do not need to use this hook if you are wrapping the component with {@link track},\n * as tracked components automatically subscribe to any signals accessed with `.get()`.\n *\n * @param value - The signal to read the value from\n * @returns The current value of the signal\n *\n * @example\n * ```ts\n * import { atom } from '@tldraw/state'\n * import { useValue } from '@tldraw/state-react'\n *\n * const count = atom('count', 0)\n *\n * function Counter() {\n * const currentCount = useValue(count)\n * return (\n * <button onClick={() => count.set(currentCount + 1)}>\n * Count: {currentCount}\n * </button>\n * )\n * }\n * ```\n *\n * @public\n */\nexport function useValue<Value>(value: Signal<Value>): Value\n\n/**\n * Creates a computed value with automatic dependency tracking and subscribes to changes.\n *\n * This overload allows you to compute a value from one or more signals with automatic\n * memoization. The computed function will only re-execute when its dependencies change,\n * and the component will only re-render when the computed result changes.\n *\n * @param name - A descriptive name for debugging purposes\n * @param fn - Function that computes the value, should call `.get()` on any signals it depends on\n * @param deps - Array of signals that the computed function depends on\n * @returns The computed value\n *\n * @example\n * ```ts\n * import { atom } from '@tldraw/state'\n * import { useValue } from '@tldraw/state-react'\n *\n * const firstName = atom('firstName', 'John')\n * const lastName = atom('lastName', 'Doe')\n *\n * function UserGreeting() {\n * const fullName = useValue('fullName', () => {\n * return `${firstName.get()} ${lastName.get()}`\n * }, [firstName, lastName])\n *\n * return <div>Hello {fullName}!</div>\n * }\n * ```\n *\n * @public\n */\nexport function useValue<Value>(name: string, fn: () => Value, deps: unknown[]): Value\n\n/**\n * Implementation function for useValue hook overloads.\n *\n * Handles both single signal subscription and computed value creation with dependency tracking.\n * Uses React's useSyncExternalStore for efficient subscription management and automatic cleanup.\n *\n * @internal\n */\nexport function useValue() {\n\tconst args = arguments\n\t// deps will be either the computed or the deps array\n\tconst deps = args.length === 3 ? args[2] : [args[0]]\n\tconst name = args.length === 3 ? args[0] : `useValue(${args[0].name})`\n\n\tconst { $val, subscribe, getSnapshot } = useMemo(() => {\n\t\tconst $val =\n\t\t\targs.length === 1 ? (args[0] as Signal<any>) : (computed(name, args[1]) as Signal<any>)\n\n\t\treturn {\n\t\t\t$val,\n\t\t\tsubscribe: (notify: () => void) => {\n\t\t\t\treturn react(`useValue(${name})`, () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$val.get()\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Will be rethrown during render if the component doesn't unmount first.\n\t\t\t\t\t}\n\t\t\t\t\tnotify()\n\t\t\t\t})\n\t\t\t},\n\t\t\tgetSnapshot: () => $val.lastChangedEpoch,\n\t\t}\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, deps)\n\n\tuseSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\treturn $val.__unsafe__getWithoutCapture()\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAwC;AACxC,mBAA8C;AA4EvC,SAAS,WAAW;AAC1B,QAAM,OAAO;AAEb,QAAM,OAAO,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnD,QAAM,OAAO,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,IAAI;AAEnE,QAAM,EAAE,MAAM,WAAW,YAAY,QAAI,sBAAQ,MAAM;AACtD,UAAMA,QACL,KAAK,WAAW,IAAK,KAAK,CAAC,QAAqB,uBAAS,MAAM,KAAK,CAAC,CAAC;AAEvE,WAAO;AAAA,MACN,MAAAA;AAAA,MACA,WAAW,CAAC,WAAuB;AAClC,mBAAO,oBAAM,YAAY,IAAI,KAAK,MAAM;AACvC,cAAI;AACH,YAAAA,MAAK,IAAI;AAAA,UACV,QAAQ;AAAA,UAER;AACA,iBAAO;AAAA,QACR,CAAC;AAAA,MACF;AAAA,MACA,aAAa,MAAMA,MAAK;AAAA,IACzB;AAAA,EAED,GAAG,IAAI;AAEP,yCAAqB,WAAW,aAAa,WAAW;AACxD,SAAO,KAAK,4BAA4B;AACzC;",
6
6
  "names": ["$val"]
7
7
  }