@tambo-ai/react 0.46.2 → 0.46.4
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/README.md +0 -1
- package/dist/hooks/__tests__/use-component-state.test.d.ts +2 -0
- package/dist/hooks/__tests__/use-component-state.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/use-component-state.test.js +324 -0
- package/dist/hooks/__tests__/use-component-state.test.js.map +1 -0
- package/dist/hooks/use-component-state.d.ts +9 -17
- package/dist/hooks/use-component-state.d.ts.map +1 -1
- package/dist/hooks/use-component-state.js +43 -120
- package/dist/hooks/use-component-state.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts +4 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +10 -10
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/esm/hooks/__tests__/use-component-state.test.d.ts +2 -0
- package/esm/hooks/__tests__/use-component-state.test.d.ts.map +1 -0
- package/esm/hooks/__tests__/use-component-state.test.js +322 -0
- package/esm/hooks/__tests__/use-component-state.test.js.map +1 -0
- package/esm/hooks/use-component-state.d.ts +9 -17
- package/esm/hooks/use-component-state.d.ts.map +1 -1
- package/esm/hooks/use-component-state.js +42 -119
- package/esm/hooks/use-component-state.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts +4 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +10 -10
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -187,7 +187,6 @@ function ChatInterface() {
|
|
|
187
187
|
| App | Description |
|
|
188
188
|
| ------------------------------------------------------------------------ | ---------------------------------------------- |
|
|
189
189
|
| [AI Chat with Generative UI](https://github.com/tambo-ai/tambo-template) | Get started with Generative UX, tools, and MCP |
|
|
190
|
-
| [Conversational Form](https://github.com/tambo-ai/conversational-form) | Collect information with generative UX |
|
|
191
190
|
|
|
192
191
|
Check out our UI library [tambo-ui](https://ui.tambo.co) for components that leverage tambo.
|
|
193
192
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-state.test.d.ts","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-component-state.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_1 = require("@testing-library/react");
|
|
4
|
+
const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
|
|
5
|
+
const tambo_thread_provider_1 = require("../../providers/tambo-thread-provider");
|
|
6
|
+
const use_component_state_1 = require("../use-component-state");
|
|
7
|
+
const use_current_message_1 = require("../use-current-message");
|
|
8
|
+
// Mock the required providers
|
|
9
|
+
jest.mock("../../providers/tambo-client-provider", () => ({
|
|
10
|
+
useTamboClient: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
jest.mock("../../providers/tambo-thread-provider", () => ({
|
|
13
|
+
useTamboThread: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
jest.mock("../use-current-message", () => ({
|
|
16
|
+
useTamboCurrentMessage: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
// Create a mock debounced function with flush method
|
|
19
|
+
const createMockDebouncedFunction = (fn) => {
|
|
20
|
+
const debouncedFn = jest.fn((...args) => fn(...args));
|
|
21
|
+
debouncedFn.flush = jest.fn();
|
|
22
|
+
debouncedFn.cancel = jest.fn();
|
|
23
|
+
debouncedFn.isPending = jest.fn(() => false);
|
|
24
|
+
return debouncedFn;
|
|
25
|
+
};
|
|
26
|
+
// Mock use-debounce
|
|
27
|
+
jest.mock("use-debounce", () => ({
|
|
28
|
+
useDebouncedCallback: jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
// Import the mocked useDebouncedCallback
|
|
31
|
+
const use_debounce_1 = require("use-debounce");
|
|
32
|
+
describe("useTamboComponentState", () => {
|
|
33
|
+
// Helper function to create mock TamboThreadMessage
|
|
34
|
+
const createMockMessage = (overrides = {}) => ({
|
|
35
|
+
id: "test-message-id",
|
|
36
|
+
threadId: "test-thread-id",
|
|
37
|
+
componentState: {},
|
|
38
|
+
content: [{ type: "text", text: "Test message" }],
|
|
39
|
+
createdAt: new Date().toISOString(),
|
|
40
|
+
role: "assistant",
|
|
41
|
+
...overrides,
|
|
42
|
+
});
|
|
43
|
+
const mockUpdateThreadMessage = jest.fn();
|
|
44
|
+
const mockUpdateComponentState = jest.fn();
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
// Setup default mock for useDebouncedCallback
|
|
48
|
+
jest
|
|
49
|
+
.mocked(use_debounce_1.useDebouncedCallback)
|
|
50
|
+
.mockImplementation((fn) => createMockDebouncedFunction(fn));
|
|
51
|
+
// Setup default mocks
|
|
52
|
+
jest.mocked(tambo_client_provider_1.useTamboClient).mockReturnValue({
|
|
53
|
+
beta: {
|
|
54
|
+
threads: {
|
|
55
|
+
messages: {
|
|
56
|
+
updateComponentState: mockUpdateComponentState,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
|
|
62
|
+
updateThreadMessage: mockUpdateThreadMessage,
|
|
63
|
+
});
|
|
64
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage());
|
|
65
|
+
});
|
|
66
|
+
describe("Initial State Management", () => {
|
|
67
|
+
it("should initialize with initialValue when no componentState exists", () => {
|
|
68
|
+
const initialValue = "test-initial";
|
|
69
|
+
jest
|
|
70
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
71
|
+
.mockReturnValue(createMockMessage({ componentState: {} }));
|
|
72
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
|
|
73
|
+
expect(result.current[0]).toBe(initialValue);
|
|
74
|
+
});
|
|
75
|
+
it("should use existing componentState value over initialValue", () => {
|
|
76
|
+
const initialValue = "initial";
|
|
77
|
+
const existingValue = "existing";
|
|
78
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
|
|
79
|
+
componentState: { testKey: existingValue },
|
|
80
|
+
}));
|
|
81
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
|
|
82
|
+
expect(result.current[0]).toBe(existingValue);
|
|
83
|
+
});
|
|
84
|
+
it("should handle undefined initialValue gracefully", () => {
|
|
85
|
+
jest
|
|
86
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
87
|
+
.mockReturnValue(createMockMessage({ componentState: {} }));
|
|
88
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey"));
|
|
89
|
+
expect(result.current[0]).toBeUndefined();
|
|
90
|
+
});
|
|
91
|
+
it("should handle different data types correctly", () => {
|
|
92
|
+
const testCases = [
|
|
93
|
+
{ value: "string" },
|
|
94
|
+
{ value: 42 },
|
|
95
|
+
{ value: true },
|
|
96
|
+
{ value: { name: "test" } },
|
|
97
|
+
{ value: [1, 2, 3] },
|
|
98
|
+
];
|
|
99
|
+
testCases.forEach(({ value }) => {
|
|
100
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
|
|
101
|
+
componentState: { testKey: value },
|
|
102
|
+
}));
|
|
103
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", value));
|
|
104
|
+
expect(result.current[0]).toEqual(value);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("State Updates", () => {
|
|
109
|
+
it("should update local state immediately when setValue is called", () => {
|
|
110
|
+
const initialValue = "initial";
|
|
111
|
+
jest
|
|
112
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
113
|
+
.mockReturnValue(createMockMessage({ componentState: { testKey: initialValue } }));
|
|
114
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
|
|
115
|
+
const newValue = "updated";
|
|
116
|
+
(0, react_1.act)(() => {
|
|
117
|
+
result.current[1](newValue);
|
|
118
|
+
});
|
|
119
|
+
expect(result.current[0]).toBe(newValue);
|
|
120
|
+
});
|
|
121
|
+
it("should trigger local thread message update when setValue is called", () => {
|
|
122
|
+
const message = createMockMessage({
|
|
123
|
+
componentState: { testKey: "initial" },
|
|
124
|
+
});
|
|
125
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
126
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
127
|
+
const newValue = "updated";
|
|
128
|
+
(0, react_1.act)(() => {
|
|
129
|
+
result.current[1](newValue);
|
|
130
|
+
});
|
|
131
|
+
expect(mockUpdateThreadMessage).toHaveBeenCalledWith(message.id, {
|
|
132
|
+
threadId: message.threadId,
|
|
133
|
+
componentState: {
|
|
134
|
+
testKey: newValue,
|
|
135
|
+
},
|
|
136
|
+
}, false);
|
|
137
|
+
});
|
|
138
|
+
it("should trigger debounced remote API call when setValue is called", () => {
|
|
139
|
+
const message = createMockMessage({
|
|
140
|
+
componentState: { testKey: "initial" },
|
|
141
|
+
});
|
|
142
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
143
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
144
|
+
const newValue = "updated";
|
|
145
|
+
(0, react_1.act)(() => {
|
|
146
|
+
result.current[1](newValue);
|
|
147
|
+
});
|
|
148
|
+
// The debounced function should be called
|
|
149
|
+
expect(mockUpdateComponentState).toHaveBeenCalledWith(message.threadId, message.id, { state: { testKey: newValue } });
|
|
150
|
+
});
|
|
151
|
+
it("should work with complex objects and arrays", () => {
|
|
152
|
+
const initialValue = { name: "test", items: [1, 2, 3] };
|
|
153
|
+
jest
|
|
154
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
155
|
+
.mockReturnValue(createMockMessage({ componentState: { testKey: initialValue } }));
|
|
156
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
|
|
157
|
+
const newValue = { name: "updated", items: [4, 5, 6] };
|
|
158
|
+
(0, react_1.act)(() => {
|
|
159
|
+
result.current[1](newValue);
|
|
160
|
+
});
|
|
161
|
+
expect(result.current[0]).toEqual(newValue);
|
|
162
|
+
expect(mockUpdateThreadMessage).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
163
|
+
componentState: {
|
|
164
|
+
testKey: newValue,
|
|
165
|
+
},
|
|
166
|
+
}), false);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe("Debouncing Behavior", () => {
|
|
170
|
+
it("should use default debounce time of 500ms", () => {
|
|
171
|
+
(0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
172
|
+
expect(use_debounce_1.useDebouncedCallback).toHaveBeenCalledWith(expect.any(Function), 500);
|
|
173
|
+
});
|
|
174
|
+
it("should use custom debounce time when provided", () => {
|
|
175
|
+
const customDebounceTime = 1000;
|
|
176
|
+
(0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", undefined, customDebounceTime));
|
|
177
|
+
expect(use_debounce_1.useDebouncedCallback).toHaveBeenCalledWith(expect.any(Function), customDebounceTime);
|
|
178
|
+
});
|
|
179
|
+
it("should flush debounced callback on unmount", () => {
|
|
180
|
+
const mockFlush = jest.fn();
|
|
181
|
+
const mockDebouncedFn = createMockDebouncedFunction(jest.fn());
|
|
182
|
+
mockDebouncedFn.flush = mockFlush;
|
|
183
|
+
// Mock the debounced callback to return our specific mock
|
|
184
|
+
jest.mocked(use_debounce_1.useDebouncedCallback).mockReturnValue(mockDebouncedFn);
|
|
185
|
+
const { unmount } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
186
|
+
unmount();
|
|
187
|
+
expect(mockFlush).toHaveBeenCalled();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe("Multi-Hook Scenarios", () => {
|
|
191
|
+
it("should handle multiple hooks with different keyNames independently", () => {
|
|
192
|
+
const message = createMockMessage({
|
|
193
|
+
componentState: {
|
|
194
|
+
key1: "value1",
|
|
195
|
+
key2: "value2",
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
199
|
+
const { result: result1 } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("key1", "default1"));
|
|
200
|
+
const { result: result2 } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("key2", "default2"));
|
|
201
|
+
expect(result1.current[0]).toBe("value1");
|
|
202
|
+
expect(result2.current[0]).toBe("value2");
|
|
203
|
+
// Update first hook
|
|
204
|
+
(0, react_1.act)(() => {
|
|
205
|
+
result1.current[1]("updated1");
|
|
206
|
+
});
|
|
207
|
+
expect(result1.current[0]).toBe("updated1");
|
|
208
|
+
expect(result2.current[0]).toBe("value2"); // Should remain unchanged
|
|
209
|
+
});
|
|
210
|
+
it("should preserve existing componentState when updating another key", () => {
|
|
211
|
+
const message = createMockMessage({
|
|
212
|
+
componentState: {
|
|
213
|
+
existingKey: "existing",
|
|
214
|
+
testKey: "initial",
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
218
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
219
|
+
(0, react_1.act)(() => {
|
|
220
|
+
result.current[1]("updated");
|
|
221
|
+
});
|
|
222
|
+
expect(mockUpdateThreadMessage).toHaveBeenCalledWith(message.id, {
|
|
223
|
+
threadId: message.threadId,
|
|
224
|
+
componentState: {
|
|
225
|
+
existingKey: "existing", // Should preserve existing keys
|
|
226
|
+
testKey: "updated",
|
|
227
|
+
},
|
|
228
|
+
}, false);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe("SetFromProp Feature", () => {
|
|
232
|
+
it("should set value from prop when hasSetFromMessage is false", () => {
|
|
233
|
+
jest
|
|
234
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
235
|
+
.mockReturnValue(createMockMessage({ componentState: {} }));
|
|
236
|
+
const propValue = "from-prop";
|
|
237
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue));
|
|
238
|
+
// Initially, hasSetFromMessage should be false, so prop value should be used
|
|
239
|
+
expect(result.current[0]).toBe(propValue);
|
|
240
|
+
});
|
|
241
|
+
it("should ignore setFromProp when initialized from message state", async () => {
|
|
242
|
+
const existingValue = "existing";
|
|
243
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
|
|
244
|
+
componentState: { testKey: existingValue },
|
|
245
|
+
}));
|
|
246
|
+
const propValue = "from-prop";
|
|
247
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue));
|
|
248
|
+
// Should use existing value from message, not prop value
|
|
249
|
+
await (0, react_1.act)(async () => {
|
|
250
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
251
|
+
});
|
|
252
|
+
expect(result.current[0]).toBe(existingValue);
|
|
253
|
+
});
|
|
254
|
+
it("should update state from setFromProp changes when no message state exists", () => {
|
|
255
|
+
jest
|
|
256
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
257
|
+
.mockReturnValue(createMockMessage({ componentState: {} }));
|
|
258
|
+
const { result, rerender } = (0, react_1.renderHook)(({ propValue }) => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue), { initialProps: { propValue: "prop1" } });
|
|
259
|
+
expect(result.current[0]).toBe("prop1");
|
|
260
|
+
// Change prop value
|
|
261
|
+
rerender({ propValue: "prop2" });
|
|
262
|
+
// Since hasSetFromMessage is still false (no message state),
|
|
263
|
+
// it should update to new prop value
|
|
264
|
+
expect(result.current[0]).toBe("prop2");
|
|
265
|
+
});
|
|
266
|
+
it("should handle undefined setFromProp gracefully", () => {
|
|
267
|
+
jest
|
|
268
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
269
|
+
.mockReturnValue(createMockMessage({ componentState: {} }));
|
|
270
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", undefined));
|
|
271
|
+
expect(result.current[0]).toBe("initial");
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
describe("Message State Sync", () => {
|
|
275
|
+
it("should sync with message.componentState changes", () => {
|
|
276
|
+
const { result, rerender } = (0, react_1.renderHook)(({ message }) => {
|
|
277
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
278
|
+
return (0, use_component_state_1.useTamboComponentState)("testKey", "initial");
|
|
279
|
+
}, {
|
|
280
|
+
initialProps: {
|
|
281
|
+
message: createMockMessage({
|
|
282
|
+
componentState: { testKey: "value1" },
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
// Change the message
|
|
287
|
+
const newMessage = createMockMessage({
|
|
288
|
+
componentState: { testKey: "value2" },
|
|
289
|
+
});
|
|
290
|
+
rerender({ message: newMessage });
|
|
291
|
+
// The hook should sync with the new message state
|
|
292
|
+
expect(result.current[0]).toBe("value2");
|
|
293
|
+
expect(mockUpdateThreadMessage).not.toHaveBeenCalled();
|
|
294
|
+
});
|
|
295
|
+
it("should handle message without componentState gracefully", () => {
|
|
296
|
+
jest
|
|
297
|
+
.mocked(use_current_message_1.useTamboCurrentMessage)
|
|
298
|
+
.mockReturnValue(createMockMessage({ componentState: undefined }));
|
|
299
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
|
|
300
|
+
expect(result.current[0]).toBe("initial");
|
|
301
|
+
});
|
|
302
|
+
it("should preserve state when message updates but componentState[keyName] unchanged", () => {
|
|
303
|
+
const message1 = createMockMessage({
|
|
304
|
+
id: "message1",
|
|
305
|
+
componentState: { testKey: "unchanged" },
|
|
306
|
+
});
|
|
307
|
+
const message2 = createMockMessage({
|
|
308
|
+
id: "message2",
|
|
309
|
+
componentState: { testKey: "unchanged" },
|
|
310
|
+
});
|
|
311
|
+
const { result, rerender } = (0, react_1.renderHook)(({ message }) => {
|
|
312
|
+
jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
|
|
313
|
+
return (0, use_component_state_1.useTamboComponentState)("testKey", "initial");
|
|
314
|
+
}, { initialProps: { message: message1 } });
|
|
315
|
+
// Clear previous calls
|
|
316
|
+
mockUpdateThreadMessage.mockClear();
|
|
317
|
+
// Change message but keep same componentState value
|
|
318
|
+
rerender({ message: message2 });
|
|
319
|
+
// Should preserve the "unchanged" value
|
|
320
|
+
expect(result.current[0]).toBe("unchanged");
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
//# sourceMappingURL=use-component-state.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-state.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-component-state.test.tsx"],"names":[],"mappings":";;AAAA,kDAAyD;AAEzD,iFAAuE;AACvE,iFAAuE;AAEvE,gEAAgE;AAChE,gEAAgE;AAEhE,8BAA8B;AAC9B,IAAI,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE,CAAC,CAAC;IACxD,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;CAC1B,CAAC,CAAC,CAAC;AAEJ,IAAI,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE,CAAC,CAAC;IACxD,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;CAC1B,CAAC,CAAC,CAAC;AAEJ,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,sBAAsB,EAAE,IAAI,CAAC,EAAE,EAAE;CAClC,CAAC,CAAC,CAAC;AAEJ,qDAAqD;AACrD,MAAM,2BAA2B,GAAG,CAAC,EAAO,EAAE,EAAE;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAI1D,CAAC;IACF,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAC9B,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAC/B,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF,oBAAoB;AACpB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,oBAAoB,EAAE,IAAI,CAAC,EAAE,EAAE;CAChC,CAAC,CAAC,CAAC;AAEJ,yCAAyC;AACzC,+CAAoD;AAEpD,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,oDAAoD;IACpD,MAAM,iBAAiB,GAAG,CACxB,YAAyC,EAAE,EACvB,EAAE,CAAC,CAAC;QACxB,EAAE,EAAE,iBAAiB;QACrB,QAAQ,EAAE,gBAAgB;QAC1B,cAAc,EAAE,EAAE;QAClB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;QACjD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,WAAW;QACjB,GAAG,SAAS;KACb,CAAC,CAAC;IAEH,MAAM,uBAAuB,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAC1C,MAAM,wBAAwB,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAE3C,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,8CAA8C;QAC9C,IAAI;aACD,MAAM,CAAC,mCAAoB,CAAC;aAC5B,kBAAkB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC,CAAC;QAE/D,sBAAsB;QACtB,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC,eAAe,CAAC;YAC1C,IAAI,EAAE;gBACJ,OAAO,EAAE;oBACP,QAAQ,EAAE;wBACR,oBAAoB,EAAE,wBAAwB;qBAC/C;iBACF;aACF;SAC8B,CAAC,CAAC;QAEnC,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC,eAAe,CAAC;YAC1C,mBAAmB,EAAE,uBAAuB;SACtC,CAAC,CAAC;QAEV,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;YAC3E,MAAM,YAAY,GAAG,cAAc,CAAC;YACpC,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CAAC,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,YAAY,CAAC,CAChD,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;YACpE,MAAM,YAAY,GAAG,SAAS,CAAC;YAC/B,MAAM,aAAa,GAAG,UAAU,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CACjD,iBAAiB,CAAC;gBAChB,cAAc,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;aAC3C,CAAC,CACH,CAAC;YAEF,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,YAAY,CAAC,CAChD,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CAAC,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CAAC,IAAA,4CAAsB,EAAC,SAAS,CAAC,CAAC,CAAC;YAEvE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,SAAS,GAAG;gBAChB,EAAE,KAAK,EAAE,QAAQ,EAAE;gBACnB,EAAE,KAAK,EAAE,EAAE,EAAE;gBACb,EAAE,KAAK,EAAE,IAAI,EAAE;gBACf,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;gBAC3B,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE;aACrB,CAAC;YAEF,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC9B,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CACjD,iBAAiB,CAAC;oBAChB,cAAc,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;iBACnC,CAAC,CACH,CAAC;gBAEF,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,KAAK,CAAC,CACzC,CAAC;gBAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;YACvE,MAAM,YAAY,GAAG,SAAS,CAAC;YAC/B,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CACd,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,CACjE,CAAC;YAEJ,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,YAAY,CAAC,CAChD,CAAC;YAEF,MAAM,QAAQ,GAAG,SAAS,CAAC;YAC3B,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;YAC5E,MAAM,OAAO,GAAG,iBAAiB,CAAC;gBAChC,cAAc,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;aACvC,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAE7D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAC7C,CAAC;YAEF,MAAM,QAAQ,GAAG,SAAS,CAAC;YAC3B,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,OAAO,CAAC,EAAE,EACV;gBACE,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE;oBACd,OAAO,EAAE,QAAQ;iBAClB;aACF,EACD,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;YAC1E,MAAM,OAAO,GAAG,iBAAiB,CAAC;gBAChC,cAAc,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;aACvC,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAE7D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAC7C,CAAC;YAEF,MAAM,QAAQ,GAAG,SAAS,CAAC;YAC3B,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,0CAA0C;YAC1C,MAAM,CAAC,wBAAwB,CAAC,CAAC,oBAAoB,CACnD,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,EAAE,EACV,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CACjC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,YAAY,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACxD,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CACd,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,CACjE,CAAC;YAEJ,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,YAAY,CAAC,CAChD,CAAC;YAEF,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACvD,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAClB,MAAM,CAAC,gBAAgB,CAAC;gBACtB,cAAc,EAAE;oBACd,OAAO,EAAE,QAAQ;iBAClB;aACF,CAAC,EACF,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,IAAA,kBAAU,EAAC,GAAG,EAAE,CAAC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;YAE/D,MAAM,CAAC,mCAAoB,CAAC,CAAC,oBAAoB,CAC/C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EACpB,GAAG,CACJ,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,MAAM,kBAAkB,GAAG,IAAI,CAAC;YAEhC,IAAA,kBAAU,EAAC,GAAG,EAAE,CACd,IAAA,4CAAsB,EACpB,SAAS,EACT,SAAS,EACT,SAAS,EACT,kBAAkB,CACnB,CACF,CAAC;YAEF,MAAM,CAAC,mCAAoB,CAAC,CAAC,oBAAoB,CAC/C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EACpB,kBAAkB,CACnB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,eAAe,GAAG,2BAA2B,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/D,eAAe,CAAC,KAAK,GAAG,SAAS,CAAC;YAElC,0DAA0D;YAC1D,IAAI,CAAC,MAAM,CAAC,mCAAoB,CAAC,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YAEnE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CAClC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAC7C,CAAC;YAEF,OAAO,EAAE,CAAC;YAEV,MAAM,CAAC,SAAS,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;YAC5E,MAAM,OAAO,GAAG,iBAAiB,CAAC;gBAChC,cAAc,EAAE;oBACd,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,QAAQ;iBACf;aACF,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAE7D,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CAC1C,IAAA,4CAAsB,EAAC,MAAM,EAAE,UAAU,CAAC,CAC3C,CAAC;YACF,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CAC1C,IAAA,4CAAsB,EAAC,MAAM,EAAE,UAAU,CAAC,CAC3C,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE1C,oBAAoB;YACpB,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,0BAA0B;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;YAC3E,MAAM,OAAO,GAAG,iBAAiB,CAAC;gBAChC,cAAc,EAAE;oBACd,WAAW,EAAE,UAAU;oBACvB,OAAO,EAAE,SAAS;iBACnB;aACF,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAE7D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAC7C,CAAC;YAEF,IAAA,WAAG,EAAC,GAAG,EAAE;gBACP,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,OAAO,CAAC,EAAE,EACV;gBACE,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE;oBACd,WAAW,EAAE,UAAU,EAAE,gCAAgC;oBACzD,OAAO,EAAE,SAAS;iBACnB;aACF,EACD,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;YACpE,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CAAC,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,SAAS,GAAG,WAAW,CAAC;YAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CACxD,CAAC;YAEF,6EAA6E;YAC7E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;YAC7E,MAAM,aAAa,GAAG,UAAU,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CACjD,iBAAiB,CAAC;gBAChB,cAAc,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;aAC3C,CAAC,CACH,CAAC;YAEF,MAAM,SAAS,GAAG,WAAW,CAAC;YAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CACxD,CAAC;YAEF,yDAAyD;YACzD,MAAM,IAAA,WAAG,EAAC,KAAK,IAAI,EAAE;gBACnB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;YACnF,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CAAC,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,kBAAU,EACrC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAChB,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,EACzD,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CACzC,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAExC,oBAAoB;YACpB,QAAQ,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEjC,6DAA6D;YAC7D,qCAAqC;YACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CAAC,iBAAiB,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CACxD,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,kBAAU,EACrC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC7D,OAAO,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACtD,CAAC,EACD;gBACE,YAAY,EAAE;oBACZ,OAAO,EAAE,iBAAiB,CAAC;wBACzB,cAAc,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;qBACtC,CAAC;iBACH;aACF,CACF,CAAC;YAEF,qBAAqB;YACrB,MAAM,UAAU,GAAG,iBAAiB,CAAC;gBACnC,cAAc,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;aACtC,CAAC,CAAC;YAEH,QAAQ,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;YAElC,kDAAkD;YAClD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,CAAC,uBAAuB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,IAAI;iBACD,MAAM,CAAC,4CAAsB,CAAC;iBAC9B,eAAe,CACd,iBAAiB,CAAC,EAAE,cAAc,EAAE,SAAgB,EAAE,CAAC,CACxD,CAAC;YAEJ,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAC7C,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;YAC1F,MAAM,QAAQ,GAAG,iBAAiB,CAAC;gBACjC,EAAE,EAAE,UAAU;gBACd,cAAc,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;aACzC,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,iBAAiB,CAAC;gBACjC,EAAE,EAAE,UAAU;gBACd,cAAc,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;aACzC,CAAC,CAAC;YAEH,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,kBAAU,EACrC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,4CAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC7D,OAAO,IAAA,4CAAsB,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACtD,CAAC,EACD,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CACxC,CAAC;YAEF,uBAAuB;YACvB,uBAAuB,CAAC,SAAS,EAAE,CAAC;YAEpC,oDAAoD;YACpD,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAEhC,wCAAwC;YACxC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { act, renderHook } from \"@testing-library/react\";\nimport { TamboThreadMessage } from \"../../model/generate-component-response\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboThread } from \"../../providers/tambo-thread-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboComponentState } from \"../use-component-state\";\nimport { useTamboCurrentMessage } from \"../use-current-message\";\n\n// Mock the required providers\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n}));\n\njest.mock(\"../../providers/tambo-thread-provider\", () => ({\n useTamboThread: jest.fn(),\n}));\n\njest.mock(\"../use-current-message\", () => ({\n useTamboCurrentMessage: jest.fn(),\n}));\n\n// Create a mock debounced function with flush method\nconst createMockDebouncedFunction = (fn: any) => {\n const debouncedFn = jest.fn((...args: any[]) => fn(...args)) as jest.Mock & {\n flush: jest.Mock;\n cancel: jest.Mock;\n isPending: () => boolean;\n };\n debouncedFn.flush = jest.fn();\n debouncedFn.cancel = jest.fn();\n debouncedFn.isPending = jest.fn(() => false);\n return debouncedFn;\n};\n\n// Mock use-debounce\njest.mock(\"use-debounce\", () => ({\n useDebouncedCallback: jest.fn(),\n}));\n\n// Import the mocked useDebouncedCallback\nimport { useDebouncedCallback } from \"use-debounce\";\n\ndescribe(\"useTamboComponentState\", () => {\n // Helper function to create mock TamboThreadMessage\n const createMockMessage = (\n overrides: Partial<TamboThreadMessage> = {},\n ): TamboThreadMessage => ({\n id: \"test-message-id\",\n threadId: \"test-thread-id\",\n componentState: {},\n content: [{ type: \"text\", text: \"Test message\" }],\n createdAt: new Date().toISOString(),\n role: \"assistant\",\n ...overrides,\n });\n\n const mockUpdateThreadMessage = jest.fn();\n const mockUpdateComponentState = jest.fn();\n\n beforeEach(() => {\n jest.clearAllMocks();\n\n // Setup default mock for useDebouncedCallback\n jest\n .mocked(useDebouncedCallback)\n .mockImplementation((fn) => createMockDebouncedFunction(fn));\n\n // Setup default mocks\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n messages: {\n updateComponentState: mockUpdateComponentState,\n },\n },\n },\n } satisfies PartialTamboAI as any);\n\n jest.mocked(useTamboThread).mockReturnValue({\n updateThreadMessage: mockUpdateThreadMessage,\n } as any);\n\n jest.mocked(useTamboCurrentMessage).mockReturnValue(createMockMessage());\n });\n\n describe(\"Initial State Management\", () => {\n it(\"should initialize with initialValue when no componentState exists\", () => {\n const initialValue = \"test-initial\";\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n expect(result.current[0]).toBe(initialValue);\n });\n\n it(\"should use existing componentState value over initialValue\", () => {\n const initialValue = \"initial\";\n const existingValue = \"existing\";\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: existingValue },\n }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n expect(result.current[0]).toBe(existingValue);\n });\n\n it(\"should handle undefined initialValue gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() => useTamboComponentState(\"testKey\"));\n\n expect(result.current[0]).toBeUndefined();\n });\n\n it(\"should handle different data types correctly\", () => {\n const testCases = [\n { value: \"string\" },\n { value: 42 },\n { value: true },\n { value: { name: \"test\" } },\n { value: [1, 2, 3] },\n ];\n\n testCases.forEach(({ value }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: value },\n }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", value),\n );\n\n expect(result.current[0]).toEqual(value);\n });\n });\n });\n\n describe(\"State Updates\", () => {\n it(\"should update local state immediately when setValue is called\", () => {\n const initialValue = \"initial\";\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: { testKey: initialValue } }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n expect(result.current[0]).toBe(newValue);\n });\n\n it(\"should trigger local thread message update when setValue is called\", () => {\n const message = createMockMessage({\n componentState: { testKey: \"initial\" },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n message.id,\n {\n threadId: message.threadId,\n componentState: {\n testKey: newValue,\n },\n },\n false,\n );\n });\n\n it(\"should trigger debounced remote API call when setValue is called\", () => {\n const message = createMockMessage({\n componentState: { testKey: \"initial\" },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n // The debounced function should be called\n expect(mockUpdateComponentState).toHaveBeenCalledWith(\n message.threadId,\n message.id,\n { state: { testKey: newValue } },\n );\n });\n\n it(\"should work with complex objects and arrays\", () => {\n const initialValue = { name: \"test\", items: [1, 2, 3] };\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: { testKey: initialValue } }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n const newValue = { name: \"updated\", items: [4, 5, 6] };\n act(() => {\n result.current[1](newValue);\n });\n\n expect(result.current[0]).toEqual(newValue);\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n expect.any(String),\n expect.objectContaining({\n componentState: {\n testKey: newValue,\n },\n }),\n false,\n );\n });\n });\n\n describe(\"Debouncing Behavior\", () => {\n it(\"should use default debounce time of 500ms\", () => {\n renderHook(() => useTamboComponentState(\"testKey\", \"initial\"));\n\n expect(useDebouncedCallback).toHaveBeenCalledWith(\n expect.any(Function),\n 500,\n );\n });\n\n it(\"should use custom debounce time when provided\", () => {\n const customDebounceTime = 1000;\n\n renderHook(() =>\n useTamboComponentState(\n \"testKey\",\n \"initial\",\n undefined,\n customDebounceTime,\n ),\n );\n\n expect(useDebouncedCallback).toHaveBeenCalledWith(\n expect.any(Function),\n customDebounceTime,\n );\n });\n\n it(\"should flush debounced callback on unmount\", () => {\n const mockFlush = jest.fn();\n const mockDebouncedFn = createMockDebouncedFunction(jest.fn());\n mockDebouncedFn.flush = mockFlush;\n\n // Mock the debounced callback to return our specific mock\n jest.mocked(useDebouncedCallback).mockReturnValue(mockDebouncedFn);\n\n const { unmount } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n unmount();\n\n expect(mockFlush).toHaveBeenCalled();\n });\n });\n\n describe(\"Multi-Hook Scenarios\", () => {\n it(\"should handle multiple hooks with different keyNames independently\", () => {\n const message = createMockMessage({\n componentState: {\n key1: \"value1\",\n key2: \"value2\",\n },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result: result1 } = renderHook(() =>\n useTamboComponentState(\"key1\", \"default1\"),\n );\n const { result: result2 } = renderHook(() =>\n useTamboComponentState(\"key2\", \"default2\"),\n );\n\n expect(result1.current[0]).toBe(\"value1\");\n expect(result2.current[0]).toBe(\"value2\");\n\n // Update first hook\n act(() => {\n result1.current[1](\"updated1\");\n });\n\n expect(result1.current[0]).toBe(\"updated1\");\n expect(result2.current[0]).toBe(\"value2\"); // Should remain unchanged\n });\n\n it(\"should preserve existing componentState when updating another key\", () => {\n const message = createMockMessage({\n componentState: {\n existingKey: \"existing\",\n testKey: \"initial\",\n },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n act(() => {\n result.current[1](\"updated\");\n });\n\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n message.id,\n {\n threadId: message.threadId,\n componentState: {\n existingKey: \"existing\", // Should preserve existing keys\n testKey: \"updated\",\n },\n },\n false,\n );\n });\n });\n\n describe(\"SetFromProp Feature\", () => {\n it(\"should set value from prop when hasSetFromMessage is false\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const propValue = \"from-prop\";\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n );\n\n // Initially, hasSetFromMessage should be false, so prop value should be used\n expect(result.current[0]).toBe(propValue);\n });\n\n it(\"should ignore setFromProp when initialized from message state\", async () => {\n const existingValue = \"existing\";\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: existingValue },\n }),\n );\n\n const propValue = \"from-prop\";\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n );\n\n // Should use existing value from message, not prop value\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n expect(result.current[0]).toBe(existingValue);\n });\n\n it(\"should update state from setFromProp changes when no message state exists\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result, rerender } = renderHook(\n ({ propValue }) =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n { initialProps: { propValue: \"prop1\" } },\n );\n\n expect(result.current[0]).toBe(\"prop1\");\n\n // Change prop value\n rerender({ propValue: \"prop2\" });\n\n // Since hasSetFromMessage is still false (no message state),\n // it should update to new prop value\n expect(result.current[0]).toBe(\"prop2\");\n });\n\n it(\"should handle undefined setFromProp gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", undefined),\n );\n\n expect(result.current[0]).toBe(\"initial\");\n });\n });\n\n describe(\"Message State Sync\", () => {\n it(\"should sync with message.componentState changes\", () => {\n const { result, rerender } = renderHook(\n ({ message }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n return useTamboComponentState(\"testKey\", \"initial\");\n },\n {\n initialProps: {\n message: createMockMessage({\n componentState: { testKey: \"value1\" },\n }),\n },\n },\n );\n\n // Change the message\n const newMessage = createMockMessage({\n componentState: { testKey: \"value2\" },\n });\n\n rerender({ message: newMessage });\n\n // The hook should sync with the new message state\n expect(result.current[0]).toBe(\"value2\");\n expect(mockUpdateThreadMessage).not.toHaveBeenCalled();\n });\n\n it(\"should handle message without componentState gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: undefined as any }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n expect(result.current[0]).toBe(\"initial\");\n });\n\n it(\"should preserve state when message updates but componentState[keyName] unchanged\", () => {\n const message1 = createMockMessage({\n id: \"message1\",\n componentState: { testKey: \"unchanged\" },\n });\n const message2 = createMockMessage({\n id: \"message2\",\n componentState: { testKey: \"unchanged\" },\n });\n\n const { result, rerender } = renderHook(\n ({ message }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n return useTamboComponentState(\"testKey\", \"initial\");\n },\n { initialProps: { message: message1 } },\n );\n\n // Clear previous calls\n mockUpdateThreadMessage.mockClear();\n\n // Change message but keep same componentState value\n rerender({ message: message2 });\n\n // Should preserve the \"unchanged\" value\n expect(result.current[0]).toBe(\"unchanged\");\n });\n });\n});\n"]}
|
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
isPending: boolean;
|
|
3
|
-
}
|
|
4
|
-
type StateUpdateResult<T> = [
|
|
5
|
-
currentState: T,
|
|
6
|
-
setState: (newState: T) => void,
|
|
7
|
-
meta: ComponentStateMeta
|
|
8
|
-
];
|
|
1
|
+
type StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];
|
|
9
2
|
/**
|
|
10
|
-
* A React hook that
|
|
3
|
+
* A React hook that acts like useState, but also automatically updates the thread message's componentState.
|
|
11
4
|
* Benefits: Passes user changes to AI, and when threads are returned, state is preserved.
|
|
12
|
-
* @param keyName - The unique key to identify this state within the message's componentState object
|
|
13
|
-
* @param initialValue - Optional initial value for the state, used if no value exists in the message
|
|
14
|
-
* @param
|
|
5
|
+
* @param keyName - The unique key to identify this state value within the message's componentState object
|
|
6
|
+
* @param initialValue - Optional initial value for the state, used if no componentState value exists in the Tambo message containing this hook usage.
|
|
7
|
+
* @param setFromProp - Optional value used to set the state value, only while no componentState value exists in the Tambo message containing this hook usage. Use this to allow streaming updates from a prop to the state value.
|
|
8
|
+
* @param debounceTime - Optional debounce time in milliseconds (default: 500ms) to limit API calls.
|
|
15
9
|
* @returns A tuple containing:
|
|
16
10
|
* - The current state value
|
|
17
11
|
* - A setter function to update the state (updates UI immediately, debounces server sync)
|
|
18
|
-
* - A metadata object with properties like isPending to track sync status
|
|
19
12
|
* @example
|
|
20
|
-
*
|
|
21
|
-
* const [count, setCount, { isPending }] = useTamboComponentState("counter", 0);
|
|
13
|
+
* const [count, setCount] = useTamboComponentState("counter", 0);
|
|
22
14
|
*
|
|
23
15
|
* // Usage with object state
|
|
24
16
|
* const [formState, setFormState] = useTamboComponentState("myForm", {
|
|
@@ -35,7 +27,7 @@ type StateUpdateResult<T> = [
|
|
|
35
27
|
* });
|
|
36
28
|
* };
|
|
37
29
|
*/
|
|
38
|
-
export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
|
|
39
|
-
export declare function useTamboComponentState<S>(keyName: string, initialValue: S, debounceTime?: number): StateUpdateResult<S>;
|
|
30
|
+
export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
|
|
31
|
+
export declare function useTamboComponentState<S>(keyName: string, initialValue: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S>;
|
|
40
32
|
export {};
|
|
41
33
|
//# sourceMappingURL=use-component-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAMA,
|
|
1
|
+
{"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAMA,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -4,136 +4,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.useTamboComponentState = useTamboComponentState;
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const use_debounce_1 = require("use-debounce");
|
|
7
|
-
const
|
|
7
|
+
const __1 = require("..");
|
|
8
8
|
const use_current_message_1 = require("./use-current-message");
|
|
9
|
-
function useTamboComponentState(keyName, initialValue, debounceTime = 500) {
|
|
9
|
+
function useTamboComponentState(keyName, initialValue, setFromProp, debounceTime = 500) {
|
|
10
10
|
const message = (0, use_current_message_1.useTamboCurrentMessage)();
|
|
11
|
-
const { updateThreadMessage
|
|
12
|
-
const client = (0,
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// Only update local state if we haven't had any user changes yet
|
|
34
|
-
if (lastUserValue === null) {
|
|
35
|
-
setLocalState(messageState);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// Otherwise fall back to initial value if we have one and no user changes
|
|
39
|
-
else if (cachedInitialValue !== undefined &&
|
|
40
|
-
!localState &&
|
|
41
|
-
lastUserValue === null) {
|
|
42
|
-
setLocalState(cachedInitialValue);
|
|
43
|
-
}
|
|
44
|
-
}, [
|
|
45
|
-
keyName,
|
|
46
|
-
message?.componentState,
|
|
47
|
-
cachedInitialValue,
|
|
48
|
-
lastUserValue,
|
|
49
|
-
localState,
|
|
50
|
-
]);
|
|
51
|
-
// Create debounced save function for efficient server synchronization
|
|
52
|
-
const debouncedServerWrite = (0, use_debounce_1.useDebouncedCallback)(async (newValue) => {
|
|
53
|
-
setIsPending(true);
|
|
54
|
-
try {
|
|
55
|
-
const componentStateUpdate = {
|
|
56
|
-
state: { [keyName]: newValue },
|
|
57
|
-
};
|
|
58
|
-
await client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate);
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
console.error(`Failed to save component state for key "${keyName}":`, err);
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
setIsPending(false);
|
|
65
|
-
}
|
|
11
|
+
const { updateThreadMessage } = (0, __1.useTamboThread)();
|
|
12
|
+
const client = (0, __1.useTamboClient)();
|
|
13
|
+
const messageState = message?.componentState?.[keyName];
|
|
14
|
+
const [localState, setLocalState] = (0, react_1.useState)(messageState ?? initialValue);
|
|
15
|
+
const [initializedFromThreadMessage, setInitializedFromThreadMessage] = (0, react_1.useState)(messageState ? true : false);
|
|
16
|
+
// Optimistically update the local thread message's componentState
|
|
17
|
+
const updateLocalThreadMessage = (0, react_1.useCallback)((newState, existingMessage) => {
|
|
18
|
+
const updatedMessage = {
|
|
19
|
+
threadId: existingMessage.threadId,
|
|
20
|
+
componentState: {
|
|
21
|
+
...existingMessage.componentState,
|
|
22
|
+
[keyName]: newState,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
updateThreadMessage(existingMessage.id, updatedMessage, false);
|
|
26
|
+
}, [updateThreadMessage, keyName]);
|
|
27
|
+
// Debounced callback to update the remote thread message's componentState
|
|
28
|
+
const updateRemoteThreadMessage = (0, use_debounce_1.useDebouncedCallback)(async (newState, existingMessage) => {
|
|
29
|
+
const componentStateUpdate = {
|
|
30
|
+
state: { [keyName]: newState },
|
|
31
|
+
};
|
|
32
|
+
await client.beta.threads.messages.updateComponentState(existingMessage.threadId, existingMessage.id, componentStateUpdate);
|
|
66
33
|
}, debounceTime);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
34
|
+
const setValue = (0, react_1.useCallback)((newState) => {
|
|
35
|
+
setLocalState(newState);
|
|
36
|
+
updateLocalThreadMessage(newState, message);
|
|
37
|
+
updateRemoteThreadMessage(newState, message);
|
|
38
|
+
}, [message, updateLocalThreadMessage, updateRemoteThreadMessage]);
|
|
39
|
+
// Mirror the thread message's componentState value to the local state
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
const messageState = message?.componentState?.[keyName];
|
|
42
|
+
if (!messageState) {
|
|
71
43
|
return;
|
|
72
44
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...message.componentState,
|
|
78
|
-
[keyName]: cachedInitialValue,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
const componentStateUpdate = {
|
|
82
|
-
state: { [keyName]: cachedInitialValue },
|
|
83
|
-
};
|
|
84
|
-
await Promise.all([
|
|
85
|
-
updateThreadMessage(messageId, messageUpdate, false),
|
|
86
|
-
client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate),
|
|
87
|
-
]);
|
|
88
|
-
}
|
|
89
|
-
catch (err) {
|
|
90
|
-
console.warn(`Failed to initialize component state for key "${keyName}":`, err);
|
|
91
|
-
}
|
|
92
|
-
}, [
|
|
93
|
-
cachedInitialValue,
|
|
94
|
-
client.beta.threads.messages,
|
|
95
|
-
keyName,
|
|
96
|
-
message,
|
|
97
|
-
messageId,
|
|
98
|
-
threadId,
|
|
99
|
-
updateThreadMessage,
|
|
100
|
-
]);
|
|
101
|
-
// Send initial state when component mounts
|
|
45
|
+
setInitializedFromThreadMessage(true);
|
|
46
|
+
setLocalState(message.componentState?.[keyName]);
|
|
47
|
+
}, [message?.componentState?.[keyName], message, keyName]);
|
|
48
|
+
// For editable fields that are set from a prop to allow streaming updates, don't overwrite a fetched state value set from the thread message with prop value on initial load.
|
|
102
49
|
(0, react_1.useEffect)(() => {
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
setHaveInitialized(true);
|
|
106
|
-
}
|
|
107
|
-
}, [initializeState, shouldInitialize]);
|
|
108
|
-
// setValue function for updating state
|
|
109
|
-
// Updates local state immediately and schedules debounced server sync
|
|
110
|
-
const setValue = (0, react_1.useCallback)((newValue) => {
|
|
111
|
-
// Track this as a user-initiated update
|
|
112
|
-
setLastUserValue(newValue);
|
|
113
|
-
setLocalState(newValue);
|
|
114
|
-
// Only trigger server updates if we have a message
|
|
115
|
-
if (message) {
|
|
116
|
-
debouncedServerWrite(newValue);
|
|
117
|
-
const messageUpdate = {
|
|
118
|
-
...message,
|
|
119
|
-
componentState: {
|
|
120
|
-
...message.componentState,
|
|
121
|
-
[keyName]: newValue,
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
updateThreadMessage(messageId, messageUpdate, false);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
console.warn(`Cannot update server for missing message ${messageId} with key "${keyName}"`);
|
|
50
|
+
if (setFromProp !== undefined && !initializedFromThreadMessage) {
|
|
51
|
+
setLocalState(setFromProp);
|
|
128
52
|
}
|
|
129
|
-
}, [
|
|
53
|
+
}, [setFromProp, initializedFromThreadMessage]);
|
|
130
54
|
// Ensure pending changes are flushed on unmount
|
|
131
55
|
(0, react_1.useEffect)(() => {
|
|
132
56
|
return () => {
|
|
133
|
-
|
|
57
|
+
updateRemoteThreadMessage.flush();
|
|
134
58
|
};
|
|
135
|
-
}, [
|
|
136
|
-
|
|
137
|
-
return [localState, setValue, { isPending }];
|
|
59
|
+
}, [updateRemoteThreadMessage]);
|
|
60
|
+
return [localState, setValue];
|
|
138
61
|
}
|
|
139
62
|
//# sourceMappingURL=use-component-state.js.map
|