@assistant-ui/react 0.11.48 → 0.11.50
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/client/AssistantClient.d.ts.map +1 -1
- package/dist/client/AssistantClient.js +1 -2
- package/dist/client/AssistantClient.js.map +1 -1
- package/dist/client/ModelContextClient.d.ts +1 -1
- package/dist/client/ThreadMessageClient.d.ts.map +1 -1
- package/dist/client/ThreadMessageClient.js +8 -9
- package/dist/client/ThreadMessageClient.js.map +1 -1
- package/dist/client/util-hooks/tapLookupResources.d.ts +2 -2
- package/dist/client/util-hooks/tapLookupResources.d.ts.map +1 -1
- package/dist/client/util-hooks/tapLookupResources.js +13 -4
- package/dist/client/util-hooks/tapLookupResources.js.map +1 -1
- package/dist/context/providers/MessageProvider.d.ts.map +1 -1
- package/dist/context/providers/MessageProvider.js +1 -2
- package/dist/context/providers/MessageProvider.js.map +1 -1
- package/dist/context/providers/TextMessagePartProvider.d.ts.map +1 -1
- package/dist/context/providers/TextMessagePartProvider.js +1 -2
- package/dist/context/providers/TextMessagePartProvider.js.map +1 -1
- package/dist/context/react/AssistantApiContext.d.ts +1 -2
- package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
- package/dist/context/react/AssistantApiContext.js +1 -12
- package/dist/context/react/AssistantApiContext.js.map +1 -1
- package/dist/legacy-runtime/client/ComposerRuntimeClient.d.ts.map +1 -1
- package/dist/legacy-runtime/client/ComposerRuntimeClient.js +4 -6
- package/dist/legacy-runtime/client/ComposerRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/client/EventManagerRuntimeClient.d.ts +1 -1
- package/dist/legacy-runtime/client/MessageRuntimeClient.d.ts.map +1 -1
- package/dist/legacy-runtime/client/MessageRuntimeClient.js +8 -6
- package/dist/legacy-runtime/client/MessageRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/client/ThreadListRuntimeClient.d.ts.map +1 -1
- package/dist/legacy-runtime/client/ThreadListRuntimeClient.js +4 -3
- package/dist/legacy-runtime/client/ThreadListRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/client/ThreadRuntimeClient.d.ts.map +1 -1
- package/dist/legacy-runtime/client/ThreadRuntimeClient.js +4 -6
- package/dist/legacy-runtime/client/ThreadRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +1 -4
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +34 -12
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +4 -2
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts +36 -0
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +21 -12
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportSlack.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewportSlack.js +4 -1
- package/dist/primitives/thread/ThreadViewportSlack.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts +20 -2
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +21 -2
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/tests/setup.js +7 -7
- package/dist/tests/setup.js.map +1 -1
- package/dist/utils/tap-store/derived-scopes.d.ts +3 -10
- package/dist/utils/tap-store/derived-scopes.d.ts.map +1 -1
- package/dist/utils/tap-store/derived-scopes.js +14 -20
- package/dist/utils/tap-store/derived-scopes.js.map +1 -1
- package/dist/utils/tap-store/index.d.ts +1 -1
- package/dist/utils/tap-store/index.d.ts.map +1 -1
- package/dist/utils/tap-store/index.js.map +1 -1
- package/dist/utils/tap-store/store.d.ts +1 -7
- package/dist/utils/tap-store/store.d.ts.map +1 -1
- package/dist/utils/tap-store/store.js +2 -2
- package/dist/utils/tap-store/store.js.map +1 -1
- package/dist/utils/tap-store/tap-api.d.ts.map +1 -1
- package/dist/utils/tap-store/tap-api.js +13 -7
- package/dist/utils/tap-store/tap-api.js.map +1 -1
- package/package.json +5 -5
- package/src/client/AssistantClient.ts +0 -1
- package/src/client/ThreadMessageClient.tsx +10 -9
- package/src/client/util-hooks/tapLookupResources.ts +26 -11
- package/src/context/providers/MessageProvider.tsx +0 -1
- package/src/context/providers/TextMessagePartProvider.tsx +0 -1
- package/src/context/react/AssistantApiContext.tsx +1 -18
- package/src/legacy-runtime/client/ComposerRuntimeClient.ts +4 -6
- package/src/legacy-runtime/client/MessageRuntimeClient.ts +10 -6
- package/src/legacy-runtime/client/ThreadListRuntimeClient.ts +4 -3
- package/src/legacy-runtime/client/ThreadRuntimeClient.ts +4 -6
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +1 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +40 -17
- package/src/primitives/composer/ComposerInput.tsx +4 -2
- package/src/primitives/thread/ThreadViewport.tsx +49 -18
- package/src/primitives/thread/ThreadViewportSlack.tsx +4 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.tsx +48 -2
- package/src/utils/tap-store/derived-scopes.ts +17 -28
- package/src/utils/tap-store/index.ts +1 -2
- package/src/utils/tap-store/store.ts +2 -7
- package/src/utils/tap-store/tap-api.ts +12 -7
|
@@ -39,8 +39,7 @@ type UseToolInvocationsParams = {
|
|
|
39
39
|
|
|
40
40
|
export type ToolExecutionStatus =
|
|
41
41
|
| { type: "executing" }
|
|
42
|
-
| { type: "interrupt"; payload: { type: "human"; payload: unknown } }
|
|
43
|
-
| { type: "cancelled"; reason: string };
|
|
42
|
+
| { type: "interrupt"; payload: { type: "human"; payload: unknown } };
|
|
44
43
|
|
|
45
44
|
export function useToolInvocations({
|
|
46
45
|
state,
|
|
@@ -71,6 +70,9 @@ export function useToolInvocations({
|
|
|
71
70
|
>(new Map());
|
|
72
71
|
|
|
73
72
|
const acRef = useRef<AbortController>(new AbortController());
|
|
73
|
+
const executingCountRef = useRef(0);
|
|
74
|
+
const settledResolversRef = useRef<Array<() => void>>([]);
|
|
75
|
+
|
|
74
76
|
const [controller] = useState(() => {
|
|
75
77
|
const [stream, controller] = createAssistantStreamController();
|
|
76
78
|
const transform = unstable_toolResultStream(
|
|
@@ -96,6 +98,28 @@ export function useToolInvocations({
|
|
|
96
98
|
}));
|
|
97
99
|
});
|
|
98
100
|
},
|
|
101
|
+
{
|
|
102
|
+
onExecutionStart: (toolCallId: string) => {
|
|
103
|
+
executingCountRef.current++;
|
|
104
|
+
setToolStatuses((prev) => ({
|
|
105
|
+
...prev,
|
|
106
|
+
[toolCallId]: { type: "executing" },
|
|
107
|
+
}));
|
|
108
|
+
},
|
|
109
|
+
onExecutionEnd: (toolCallId: string) => {
|
|
110
|
+
executingCountRef.current--;
|
|
111
|
+
setToolStatuses((prev) => {
|
|
112
|
+
const next = { ...prev };
|
|
113
|
+
delete next[toolCallId];
|
|
114
|
+
return next;
|
|
115
|
+
});
|
|
116
|
+
// Resolve any waiting abort promises when all tools have settled
|
|
117
|
+
if (executingCountRef.current === 0) {
|
|
118
|
+
settledResolversRef.current.forEach((resolve) => resolve());
|
|
119
|
+
settledResolversRef.current = [];
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
},
|
|
99
123
|
);
|
|
100
124
|
stream
|
|
101
125
|
.pipeThrough(transform)
|
|
@@ -116,13 +140,6 @@ export function useToolInvocations({
|
|
|
116
140
|
isError: chunk.isError,
|
|
117
141
|
...(chunk.artifact && { artifact: chunk.artifact }),
|
|
118
142
|
});
|
|
119
|
-
|
|
120
|
-
// Clear status when result is set
|
|
121
|
-
setToolStatuses((prev) => {
|
|
122
|
-
const next = { ...prev };
|
|
123
|
-
delete next[chunk.meta.toolCallId];
|
|
124
|
-
return next;
|
|
125
|
-
});
|
|
126
143
|
}
|
|
127
144
|
},
|
|
128
145
|
}),
|
|
@@ -234,20 +251,27 @@ export function useToolInvocations({
|
|
|
234
251
|
}
|
|
235
252
|
}, [state, controller, onResult]);
|
|
236
253
|
|
|
237
|
-
const abort = () => {
|
|
254
|
+
const abort = (): Promise<void> => {
|
|
238
255
|
humanInputRef.current.forEach(({ reject }) => {
|
|
239
256
|
reject(new Error("Tool execution aborted"));
|
|
240
257
|
});
|
|
241
258
|
humanInputRef.current.clear();
|
|
242
|
-
setToolStatuses({});
|
|
243
259
|
|
|
244
260
|
acRef.current.abort();
|
|
245
261
|
acRef.current = new AbortController();
|
|
262
|
+
|
|
263
|
+
// Return a promise that resolves when all executing tools have settled
|
|
264
|
+
if (executingCountRef.current === 0) {
|
|
265
|
+
return Promise.resolve();
|
|
266
|
+
}
|
|
267
|
+
return new Promise<void>((resolve) => {
|
|
268
|
+
settledResolversRef.current.push(resolve);
|
|
269
|
+
});
|
|
246
270
|
};
|
|
247
271
|
|
|
248
272
|
return {
|
|
249
273
|
reset: () => {
|
|
250
|
-
abort();
|
|
274
|
+
void abort();
|
|
251
275
|
isInitialState.current = true;
|
|
252
276
|
},
|
|
253
277
|
abort,
|
|
@@ -255,11 +279,10 @@ export function useToolInvocations({
|
|
|
255
279
|
const handlers = humanInputRef.current.get(toolCallId);
|
|
256
280
|
if (handlers) {
|
|
257
281
|
humanInputRef.current.delete(toolCallId);
|
|
258
|
-
setToolStatuses((prev) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
282
|
+
setToolStatuses((prev) => ({
|
|
283
|
+
...prev,
|
|
284
|
+
[toolCallId]: { type: "executing" },
|
|
285
|
+
}));
|
|
263
286
|
handlers.resolve(payload);
|
|
264
287
|
} else {
|
|
265
288
|
throw new Error(
|
|
@@ -17,6 +17,7 @@ import TextareaAutosize, {
|
|
|
17
17
|
import { useEscapeKeydown } from "@radix-ui/react-use-escape-keydown";
|
|
18
18
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
|
|
19
19
|
import { useAssistantState, useAssistantApi } from "../../context";
|
|
20
|
+
import { flushSync } from "@assistant-ui/tap";
|
|
20
21
|
|
|
21
22
|
export namespace ComposerPrimitiveInput {
|
|
22
23
|
export type Element = HTMLTextAreaElement;
|
|
@@ -207,8 +208,9 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
207
208
|
disabled={isDisabled}
|
|
208
209
|
onChange={composeEventHandlers(onChange, (e) => {
|
|
209
210
|
if (!api.composer().getState().isEditing) return;
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
flushSync(() => {
|
|
212
|
+
api.composer().setText(e.target.value);
|
|
213
|
+
});
|
|
212
214
|
})}
|
|
213
215
|
onKeyDown={composeEventHandlers(onKeyDown, handleKeyPress)}
|
|
214
216
|
onPaste={composeEventHandlers(onPaste, handlePaste)}
|
|
@@ -30,36 +30,67 @@ export namespace ThreadPrimitiveViewport {
|
|
|
30
30
|
* - "top": New user messages anchor at the top of the viewport for a focused reading experience.
|
|
31
31
|
*/
|
|
32
32
|
turnAnchor?: "top" | "bottom" | undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether to scroll to bottom when a new run starts.
|
|
36
|
+
*
|
|
37
|
+
* Defaults to true.
|
|
38
|
+
*/
|
|
39
|
+
scrollToBottomOnRunStart?: boolean | undefined;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Whether to scroll to bottom when thread history is first loaded.
|
|
43
|
+
*
|
|
44
|
+
* Defaults to true.
|
|
45
|
+
*/
|
|
46
|
+
scrollToBottomOnInitialize?: boolean | undefined;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Whether to scroll to bottom when switching to a different thread.
|
|
50
|
+
*
|
|
51
|
+
* Defaults to true.
|
|
52
|
+
*/
|
|
53
|
+
scrollToBottomOnThreadSwitch?: boolean | undefined;
|
|
33
54
|
};
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
const useViewportSizeRef = () => {
|
|
37
58
|
const register = useThreadViewport((s) => s.registerViewport);
|
|
38
|
-
const getHeight = useCallback(
|
|
39
|
-
(el: HTMLElement) =>
|
|
40
|
-
el.clientHeight - parseFloat(getComputedStyle(el).paddingTop),
|
|
41
|
-
[],
|
|
42
|
-
);
|
|
43
|
-
|
|
59
|
+
const getHeight = useCallback((el: HTMLElement) => el.clientHeight, []);
|
|
44
60
|
return useSizeHandle(register, getHeight);
|
|
45
61
|
};
|
|
46
62
|
|
|
47
63
|
const ThreadPrimitiveViewportScrollable = forwardRef<
|
|
48
64
|
ThreadPrimitiveViewport.Element,
|
|
49
65
|
ThreadPrimitiveViewport.Props
|
|
50
|
-
>(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
>(
|
|
67
|
+
(
|
|
68
|
+
{
|
|
69
|
+
autoScroll,
|
|
70
|
+
scrollToBottomOnRunStart,
|
|
71
|
+
scrollToBottomOnInitialize,
|
|
72
|
+
scrollToBottomOnThreadSwitch,
|
|
73
|
+
children,
|
|
74
|
+
...rest
|
|
75
|
+
},
|
|
76
|
+
forwardedRef,
|
|
77
|
+
) => {
|
|
78
|
+
const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({
|
|
79
|
+
autoScroll,
|
|
80
|
+
scrollToBottomOnRunStart,
|
|
81
|
+
scrollToBottomOnInitialize,
|
|
82
|
+
scrollToBottomOnThreadSwitch,
|
|
83
|
+
});
|
|
84
|
+
const viewportSizeRef = useViewportSizeRef();
|
|
85
|
+
const ref = useComposedRefs(forwardedRef, autoScrollRef, viewportSizeRef);
|
|
56
86
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
87
|
+
return (
|
|
88
|
+
<Primitive.div {...rest} ref={ref}>
|
|
89
|
+
{children}
|
|
90
|
+
</Primitive.div>
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
);
|
|
63
94
|
|
|
64
95
|
ThreadPrimitiveViewportScrollable.displayName =
|
|
65
96
|
"ThreadPrimitive.ViewportScrollable";
|
|
@@ -57,7 +57,10 @@ export const ThreadPrimitiveViewportSlack: FC<ThreadViewportSlackProps> = ({
|
|
|
57
57
|
fillClampThreshold = "10em",
|
|
58
58
|
fillClampOffset = "6em",
|
|
59
59
|
}) => {
|
|
60
|
-
const isLast = useAssistantState(
|
|
60
|
+
const isLast = useAssistantState(
|
|
61
|
+
// only add slack if the message is the last message and we already have at least 3 messages
|
|
62
|
+
({ message }) => message.isLast && message.index >= 2,
|
|
63
|
+
);
|
|
61
64
|
const threadViewportStore = useThreadViewportStore({ optional: true });
|
|
62
65
|
const isNested = useContext(SlackNestingContext);
|
|
63
66
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
4
|
-
import {
|
|
4
|
+
import { useCallback, useRef, type RefCallback } from "react";
|
|
5
5
|
import { useAssistantEvent } from "../../context";
|
|
6
6
|
import { useOnResizeContent } from "../../utils/hooks/useOnResizeContent";
|
|
7
7
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
|
|
@@ -18,11 +18,35 @@ export namespace useThreadViewportAutoScroll {
|
|
|
18
18
|
* Default false if `turnAnchor` is "top", otherwise defaults to true.
|
|
19
19
|
*/
|
|
20
20
|
autoScroll?: boolean | undefined;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether to scroll to bottom when a new run starts.
|
|
24
|
+
*
|
|
25
|
+
* Defaults to true.
|
|
26
|
+
*/
|
|
27
|
+
scrollToBottomOnRunStart?: boolean | undefined;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Whether to scroll to bottom when thread history is first loaded.
|
|
31
|
+
*
|
|
32
|
+
* Defaults to true.
|
|
33
|
+
*/
|
|
34
|
+
scrollToBottomOnInitialize?: boolean | undefined;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether to scroll to bottom when switching to a different thread.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to true.
|
|
40
|
+
*/
|
|
41
|
+
scrollToBottomOnThreadSwitch?: boolean | undefined;
|
|
21
42
|
};
|
|
22
43
|
}
|
|
23
44
|
|
|
24
45
|
export const useThreadViewportAutoScroll = <TElement extends HTMLElement>({
|
|
25
46
|
autoScroll,
|
|
47
|
+
scrollToBottomOnRunStart = true,
|
|
48
|
+
scrollToBottomOnInitialize = true,
|
|
49
|
+
scrollToBottomOnThreadSwitch = true,
|
|
26
50
|
}: useThreadViewportAutoScroll.Options): RefCallback<TElement> => {
|
|
27
51
|
const divRef = useRef<TElement>(null);
|
|
28
52
|
|
|
@@ -62,7 +86,10 @@ export const useThreadViewportAutoScroll = <TElement extends HTMLElement>({
|
|
|
62
86
|
scrollingToBottomBehaviorRef.current = null;
|
|
63
87
|
}
|
|
64
88
|
|
|
65
|
-
|
|
89
|
+
const shouldUpdate =
|
|
90
|
+
newIsAtBottom || scrollingToBottomBehaviorRef.current === null;
|
|
91
|
+
|
|
92
|
+
if (shouldUpdate && newIsAtBottom !== isAtBottom) {
|
|
66
93
|
writableStore(threadViewportStore).setState({
|
|
67
94
|
isAtBottom: newIsAtBottom,
|
|
68
95
|
});
|
|
@@ -96,12 +123,31 @@ export const useThreadViewportAutoScroll = <TElement extends HTMLElement>({
|
|
|
96
123
|
|
|
97
124
|
// autoscroll on run start
|
|
98
125
|
useAssistantEvent("thread.run-start", () => {
|
|
126
|
+
if (!scrollToBottomOnRunStart) return;
|
|
99
127
|
scrollingToBottomBehaviorRef.current = "auto";
|
|
100
128
|
requestAnimationFrame(() => {
|
|
101
129
|
scrollToBottom("auto");
|
|
102
130
|
});
|
|
103
131
|
});
|
|
104
132
|
|
|
133
|
+
// scroll to bottom instantly when thread history is first loaded
|
|
134
|
+
useAssistantEvent("thread.initialize", () => {
|
|
135
|
+
if (!scrollToBottomOnInitialize) return;
|
|
136
|
+
scrollingToBottomBehaviorRef.current = "instant";
|
|
137
|
+
requestAnimationFrame(() => {
|
|
138
|
+
scrollToBottom("instant");
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// scroll to bottom instantly when switching threads
|
|
143
|
+
useAssistantEvent("thread-list-item.switched-to", () => {
|
|
144
|
+
if (!scrollToBottomOnThreadSwitch) return;
|
|
145
|
+
scrollingToBottomBehaviorRef.current = "instant";
|
|
146
|
+
requestAnimationFrame(() => {
|
|
147
|
+
scrollToBottom("instant");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
105
151
|
const autoScrollRef = useComposedRefs<TElement>(resizeRef, scrollRef, divRef);
|
|
106
152
|
return autoScrollRef as RefCallback<TElement>;
|
|
107
153
|
};
|
|
@@ -40,7 +40,7 @@ type AssistantApiFieldNames = {
|
|
|
40
40
|
/**
|
|
41
41
|
* Configuration for a derived scope field - infers types from the actual values provided
|
|
42
42
|
*/
|
|
43
|
-
export type
|
|
43
|
+
export type DerivedConfig<TSource extends string | null, TQuery, TApi> = {
|
|
44
44
|
source: TSource;
|
|
45
45
|
query: TQuery;
|
|
46
46
|
get: () => TApi;
|
|
@@ -59,18 +59,12 @@ export type OnCallbackFn = <TEvent extends AssistantEvent>(
|
|
|
59
59
|
*/
|
|
60
60
|
export type SubscribeCallbackFn = (listener: () => void) => Unsubscribe;
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
* Type for the special `flushSync` callback function
|
|
64
|
-
*/
|
|
65
|
-
export type FlushSyncCallbackFn = () => void;
|
|
66
|
-
|
|
67
62
|
/**
|
|
68
63
|
* Type for special non-field functions in AssistantApi
|
|
69
64
|
*/
|
|
70
65
|
export type SpecialCallbacks = {
|
|
71
66
|
on?: OnCallbackFn;
|
|
72
67
|
subscribe?: SubscribeCallbackFn;
|
|
73
|
-
flushSync?: FlushSyncCallbackFn;
|
|
74
68
|
};
|
|
75
69
|
|
|
76
70
|
/**
|
|
@@ -98,7 +92,7 @@ export type DerivedScopesInput = {
|
|
|
98
92
|
*/
|
|
99
93
|
export const DerivedScope = resource(
|
|
100
94
|
<TSource extends string | null, TQuery, TApi>(
|
|
101
|
-
config:
|
|
95
|
+
config: DerivedConfig<TSource, TQuery, TApi>,
|
|
102
96
|
): AssistantApiField<
|
|
103
97
|
TApi,
|
|
104
98
|
{
|
|
@@ -144,32 +138,28 @@ const ScopeFieldWithNameResource = resource(
|
|
|
144
138
|
*/
|
|
145
139
|
export const DerivedScopes = resource(
|
|
146
140
|
(scopes: DerivedScopesInput): Partial<AssistantApi> => {
|
|
147
|
-
const { on, subscribe,
|
|
148
|
-
const callbacksRef = tapRef({ on, subscribe
|
|
141
|
+
const { on, subscribe, ...scopeFields } = scopes;
|
|
142
|
+
const callbacksRef = tapRef({ on, subscribe });
|
|
149
143
|
tapEffect(() => {
|
|
150
|
-
callbacksRef.current = { on, subscribe
|
|
144
|
+
callbacksRef.current = { on, subscribe };
|
|
151
145
|
});
|
|
152
146
|
|
|
153
147
|
const results = tapResources(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
),
|
|
162
|
-
),
|
|
148
|
+
scopeFields as Record<string, ReturnType<typeof DerivedScope>>,
|
|
149
|
+
(scopeElement, fieldName) =>
|
|
150
|
+
ScopeFieldWithNameResource({
|
|
151
|
+
fieldName,
|
|
152
|
+
scopeElement,
|
|
153
|
+
}),
|
|
154
|
+
[],
|
|
163
155
|
);
|
|
164
156
|
|
|
165
157
|
return tapMemo(() => {
|
|
166
|
-
const result = Object.fromEntries(
|
|
158
|
+
const result = Object.fromEntries(
|
|
159
|
+
Object.values(results),
|
|
160
|
+
) as Partial<AssistantApi>;
|
|
167
161
|
|
|
168
|
-
const {
|
|
169
|
-
on: onCb,
|
|
170
|
-
subscribe: subCb,
|
|
171
|
-
flushSync: flushCb,
|
|
172
|
-
} = callbacksRef.current;
|
|
162
|
+
const { on: onCb, subscribe: subCb } = callbacksRef.current;
|
|
173
163
|
|
|
174
164
|
if (onCb) {
|
|
175
165
|
result.on = <TEvent extends AssistantEvent>(
|
|
@@ -178,9 +168,8 @@ export const DerivedScopes = resource(
|
|
|
178
168
|
) => onCb(selector, callback);
|
|
179
169
|
}
|
|
180
170
|
if (subCb) result.subscribe = (listener) => subCb(listener);
|
|
181
|
-
if (flushCb) result.flushSync = () => flushCb();
|
|
182
171
|
|
|
183
172
|
return result;
|
|
184
|
-
}, [
|
|
173
|
+
}, [results]);
|
|
185
174
|
},
|
|
186
175
|
);
|
|
@@ -2,10 +2,9 @@ export { type Store, asStore } from "./store";
|
|
|
2
2
|
export { tapApi, type ApiObject } from "./tap-api";
|
|
3
3
|
export {
|
|
4
4
|
DerivedScope,
|
|
5
|
-
type
|
|
5
|
+
type DerivedConfig,
|
|
6
6
|
type OnCallbackFn,
|
|
7
7
|
type SubscribeCallbackFn,
|
|
8
|
-
type FlushSyncCallbackFn,
|
|
9
8
|
type SpecialCallbacks,
|
|
10
9
|
type DerivedScopesInput,
|
|
11
10
|
} from "./derived-scopes";
|
|
@@ -17,22 +17,17 @@ export interface Store<TState> {
|
|
|
17
17
|
* Subscribe to the store.
|
|
18
18
|
*/
|
|
19
19
|
subscribe(listener: () => void): Unsubscribe;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Synchronously flush all the updates to the store.
|
|
23
|
-
*/
|
|
24
|
-
flushSync(): void;
|
|
25
20
|
}
|
|
26
21
|
|
|
27
22
|
export const asStore = resource(
|
|
28
23
|
<TState, TProps>(element: ResourceElement<TState, TProps>): Store<TState> => {
|
|
29
24
|
const resource = tapMemo(
|
|
30
|
-
() => createResource(element,
|
|
25
|
+
() => createResource(element, { mount: false }),
|
|
31
26
|
[element.type],
|
|
32
27
|
);
|
|
33
28
|
|
|
34
29
|
tapEffect(() => {
|
|
35
|
-
resource.
|
|
30
|
+
resource.render(element);
|
|
36
31
|
});
|
|
37
32
|
|
|
38
33
|
return resource;
|
|
@@ -5,33 +5,39 @@ export interface ApiObject {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
class ReadonlyApiHandler<TApi extends ApiObject> implements ProxyHandler<TApi> {
|
|
8
|
-
constructor(private readonly
|
|
8
|
+
constructor(private readonly ref: tapRef.RefObject<TApi>) {}
|
|
9
9
|
|
|
10
10
|
get(_: unknown, prop: string | symbol) {
|
|
11
|
-
return this.
|
|
11
|
+
return this.ref.current[prop as keyof TApi];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
ownKeys(): ArrayLike<string | symbol> {
|
|
15
|
-
return Object.keys(this.
|
|
15
|
+
return Object.keys(this.ref.current as object);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
has(_: unknown, prop: string | symbol) {
|
|
19
|
-
return prop in (this.
|
|
19
|
+
return prop in (this.ref.current as object);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
getOwnPropertyDescriptor(_: unknown, prop: string | symbol) {
|
|
23
|
-
return Object.getOwnPropertyDescriptor(this.
|
|
23
|
+
return Object.getOwnPropertyDescriptor(this.ref.current, prop);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
set() {
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
|
+
setPrototypeOf() {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
29
32
|
defineProperty() {
|
|
30
33
|
return false;
|
|
31
34
|
}
|
|
32
35
|
deleteProperty() {
|
|
33
36
|
return false;
|
|
34
37
|
}
|
|
38
|
+
preventExtensions(): boolean {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export const tapApi = <TApi extends ApiObject & { getState: () => any }>(
|
|
@@ -46,8 +52,7 @@ export const tapApi = <TApi extends ApiObject & { getState: () => any }>(
|
|
|
46
52
|
});
|
|
47
53
|
|
|
48
54
|
const apiProxy = tapMemo(
|
|
49
|
-
() =>
|
|
50
|
-
new Proxy<TApi>({} as TApi, new ReadonlyApiHandler(() => ref.current)),
|
|
55
|
+
() => new Proxy<TApi>({} as TApi, new ReadonlyApiHandler(ref)),
|
|
51
56
|
[],
|
|
52
57
|
);
|
|
53
58
|
|