@tambo-ai/react 0.16.2 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +39 -47
  2. package/dist/hooks/index.d.ts +8 -0
  3. package/dist/hooks/index.d.ts.map +1 -0
  4. package/dist/hooks/index.js +32 -0
  5. package/dist/hooks/index.js.map +1 -0
  6. package/dist/hooks/use-component-state.d.ts +40 -5
  7. package/dist/hooks/use-component-state.d.ts.map +1 -1
  8. package/dist/hooks/use-component-state.js +138 -47
  9. package/dist/hooks/use-component-state.js.map +1 -1
  10. package/dist/hooks/use-streaming-props.d.ts +25 -0
  11. package/dist/hooks/use-streaming-props.d.ts.map +1 -0
  12. package/dist/hooks/use-streaming-props.js +49 -0
  13. package/dist/hooks/use-streaming-props.js.map +1 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +3 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/model/component-metadata.d.ts +7 -1
  19. package/dist/model/component-metadata.d.ts.map +1 -1
  20. package/dist/model/component-metadata.js.map +1 -1
  21. package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
  22. package/dist/providers/tambo-registry-provider.js +24 -2
  23. package/dist/providers/tambo-registry-provider.js.map +1 -1
  24. package/dist/util/registry.d.ts +1 -0
  25. package/dist/util/registry.d.ts.map +1 -1
  26. package/dist/util/registry.js +14 -1
  27. package/dist/util/registry.js.map +1 -1
  28. package/esm/hooks/index.d.ts +8 -0
  29. package/esm/hooks/index.d.ts.map +1 -0
  30. package/esm/hooks/index.js +9 -0
  31. package/esm/hooks/index.js.map +1 -0
  32. package/esm/hooks/use-component-state.d.ts +40 -5
  33. package/esm/hooks/use-component-state.d.ts.map +1 -1
  34. package/esm/hooks/use-component-state.js +139 -48
  35. package/esm/hooks/use-component-state.js.map +1 -1
  36. package/esm/hooks/use-streaming-props.d.ts +25 -0
  37. package/esm/hooks/use-streaming-props.d.ts.map +1 -0
  38. package/esm/hooks/use-streaming-props.js +46 -0
  39. package/esm/hooks/use-streaming-props.js.map +1 -0
  40. package/esm/index.d.ts +1 -0
  41. package/esm/index.d.ts.map +1 -1
  42. package/esm/index.js +1 -0
  43. package/esm/index.js.map +1 -1
  44. package/esm/model/component-metadata.d.ts +7 -1
  45. package/esm/model/component-metadata.d.ts.map +1 -1
  46. package/esm/model/component-metadata.js.map +1 -1
  47. package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
  48. package/esm/providers/tambo-registry-provider.js +21 -2
  49. package/esm/providers/tambo-registry-provider.js.map +1 -1
  50. package/esm/util/registry.d.ts +1 -0
  51. package/esm/util/registry.d.ts.map +1 -1
  52. package/esm/util/registry.js +12 -0
  53. package/esm/util/registry.js.map +1 -1
  54. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"component-metadata.js","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { ComponentType } from \"react\";\nimport z from \"zod\";\nimport type zodToJsonSchema from \"zod-to-json-schema\";\n/** Extension of the ToolParameters interface from Tambo AI to include JSONSchema definition */\nexport type ParameterSpec = TamboAI.ToolParameters & {\n schema?: ReturnType<typeof zodToJsonSchema>;\n};\n\n/**\n * Extends the base ContextTool interface from Tambo AI to include schema information\n * for parameter validation using zod-to-json-schema.\n */\nexport interface ComponentContextToolMetadata\n extends TamboAI.ComponentContextToolMetadata {\n parameters: ParameterSpec[];\n}\n\nexport interface ComponentContextTool {\n getComponentContext: (...args: any[]) => Promise<any>;\n definition: ComponentContextToolMetadata;\n}\n\nexport interface RegisteredComponent extends TamboAI.AvailableComponent {\n component: ComponentType<any>;\n loadingComponent?: ComponentType<any>;\n}\n\nexport type ComponentRegistry = Record<string, RegisteredComponent>;\n\nexport type TamboToolRegistry = Record<string, TamboTool>;\n\nexport interface TamboTool<\n Args extends z.ZodTuple<any, any> = z.ZodTuple<any, any>,\n Returns extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n name: string;\n description: string;\n tool: (...args: z.infer<Args>) => z.infer<Returns>;\n toolSchema: z.ZodFunction<Args, Returns>;\n}\n\nexport type TamboToolAssociations = Record<string, string[]>;\n/**\n * A component that can be registered with the TamboRegistryProvider.\n */\n\nexport interface TamboComponent {\n /** The name of the component */\n name: string;\n /** The description of the component */\n description: string;\n /** The React component to render.\n *\n * Make sure to pass the Component itself, not an instance of the component. For example,\n * if you have a component like this:\n *\n * ```tsx\n * const MyComponent = () => {\n * return <div>My Component</div>;\n * };\n * ```\n *\n * You should pass the `Component`:\n *\n * ```tsx\n * const components = [MyComponent];\n * <TamboRegistryProvider components={components} />\n * ```\n */\n component: ComponentType<any>;\n /** The props definition of the component */\n propsDefinition?: any;\n /** The loading component to render while the component is loading */\n loadingComponent?: ComponentType<any>;\n /** The tools that are associated with the component */\n associatedTools?: TamboTool[];\n}\n"]}
1
+ {"version":3,"file":"component-metadata.js","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { ComponentType } from \"react\";\nimport z from \"zod\";\nimport type zodToJsonSchema from \"zod-to-json-schema\";\n/** Extension of the ToolParameters interface from Tambo AI to include JSONSchema definition */\nexport type ParameterSpec = TamboAI.ToolParameters & {\n schema?: ReturnType<typeof zodToJsonSchema>;\n};\n\n/**\n * Extends the base ContextTool interface from Tambo AI to include schema information\n * for parameter validation using zod-to-json-schema.\n */\nexport interface ComponentContextToolMetadata\n extends TamboAI.ComponentContextToolMetadata {\n parameters: ParameterSpec[];\n}\n\nexport interface ComponentContextTool {\n getComponentContext: (...args: any[]) => Promise<any>;\n definition: ComponentContextToolMetadata;\n}\n\nexport interface RegisteredComponent extends TamboAI.AvailableComponent {\n component: ComponentType<any>;\n loadingComponent?: ComponentType<any>;\n}\n\nexport type ComponentRegistry = Record<string, RegisteredComponent>;\n\nexport type TamboToolRegistry = Record<string, TamboTool>;\n\nexport interface TamboTool<\n Args extends z.ZodTuple<any, any> = z.ZodTuple<any, any>,\n Returns extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n name: string;\n description: string;\n tool: (...args: z.infer<Args>) => z.infer<Returns>;\n toolSchema: z.ZodFunction<Args, Returns>;\n}\n\nexport type TamboToolAssociations = Record<string, string[]>;\n/**\n * A component that can be registered with the TamboRegistryProvider.\n */\n\nexport interface TamboComponent {\n /** The name of the component */\n name: string;\n /** The description of the component */\n description: string;\n /** The React component to render.\n *\n * Make sure to pass the Component itself, not an instance of the component. For example,\n * if you have a component like this:\n *\n * ```tsx\n * const MyComponent = () => {\n * return <div>My Component</div>;\n * };\n * ```\n *\n * You should pass the `Component`:\n *\n * ```tsx\n * const components = [MyComponent];\n * <TamboRegistryProvider components={components} />\n * ```\n */\n component: ComponentType<any>;\n\n /** A zod schema for the component props. (Recommended)\n * Either this or propsDefinition must be provided, but not both.\n */\n propsSchema?: z.ZodTypeAny;\n /** The props definition of the component as a JSON object.\n * Either this or propsSchema must be provided, but not both.\n */\n propsDefinition?: any;\n /** The loading component to render while the component is loading */\n loadingComponent?: ComponentType<any>;\n /** The tools that are associated with the component */\n associatedTools?: TamboTool[];\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-registry-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-registry-provider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAEZ,iBAAiB,EAKlB,MAAM,OAAO,CAAC;AACf,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,SAAS,EACV,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,iBAAiB,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,iBAAiB,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IAC5C,kBAAkB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CACtE;AAYD,MAAM,WAAW,0BAA0B;IACzC,yCAAyC;IACzC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAC1C,iBAAiB,CAAC,0BAA0B,CAAC,CAyG9C,CAAC;AAEF,eAAO,MAAM,gBAAgB,4BAE5B,CAAC"}
1
+ {"version":3,"file":"tambo-registry-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-registry-provider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAEZ,iBAAiB,EAKlB,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,SAAS,EACV,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,iBAAiB,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,iBAAiB,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IAC5C,kBAAkB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CACtE;AAYD,MAAM,WAAW,0BAA0B;IACzC,yCAAyC;IACzC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAC1C,iBAAiB,CAAC,0BAA0B,CAAC,CAqI9C,CAAC;AAEF,eAAO,MAAM,gBAAgB,4BAE5B,CAAC"}
@@ -33,9 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  return result;
34
34
  };
35
35
  })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
36
39
  Object.defineProperty(exports, "__esModule", { value: true });
37
40
  exports.useTamboRegistry = exports.TamboRegistryProvider = void 0;
38
41
  const react_1 = __importStar(require("react"));
42
+ const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
39
43
  const TamboRegistryContext = (0, react_1.createContext)({
40
44
  componentList: {},
41
45
  toolRegistry: {},
@@ -73,7 +77,25 @@ const TamboRegistryProvider = ({ children, components: userComponents }) => {
73
77
  }));
74
78
  }, [componentList]);
75
79
  const registerComponent = (0, react_1.useCallback)((options, warnOnOverwrite = true) => {
76
- const { name, description, component, propsDefinition = {}, loadingComponent, associatedTools, } = options;
80
+ const { name, description, component, propsSchema, propsDefinition, loadingComponent, associatedTools, } = options;
81
+ // Validate that at least one props definition is provided
82
+ if (!propsSchema && !propsDefinition) {
83
+ throw new Error(`Component ${name} must have either propsSchema (recommended) or propsDefinition defined`);
84
+ }
85
+ // Validate that only one props definition is provided
86
+ if (propsSchema && propsDefinition) {
87
+ throw new Error(`Component ${name} cannot have both propsSchema and propsDefinition defined. Use only one. We recommend using propsSchema.`);
88
+ }
89
+ // Convert propsSchema to JSON Schema if it exists
90
+ let props = propsDefinition;
91
+ if (propsSchema) {
92
+ try {
93
+ props = (0, zod_to_json_schema_1.default)(propsSchema);
94
+ }
95
+ catch (error) {
96
+ console.error(`Error converting ${name} props schema to JSON Schema:`, error);
97
+ }
98
+ }
77
99
  setComponentList((prev) => {
78
100
  if (prev[name] && warnOnOverwrite) {
79
101
  console.warn(`overwriting component ${name}`);
@@ -85,7 +107,7 @@ const TamboRegistryProvider = ({ children, components: userComponents }) => {
85
107
  loadingComponent,
86
108
  name,
87
109
  description,
88
- props: propsDefinition,
110
+ props,
89
111
  contextTools: [],
90
112
  },
91
113
  };
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-registry-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-registry-provider.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACb,+CAOe;AAiBf,MAAM,oBAAoB,GAAG,IAAA,qBAAa,EAAuB;IAC/D,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,EAAE;IAChB,yBAAyB,EAAE,EAAE;IAC7B,iBAAiB,EAAE,GAAG,EAAE,GAAE,CAAC;IAC3B,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;IACtB,aAAa,EAAE,GAAG,EAAE,GAAE,CAAC;IACvB,kBAAkB,EAAE,GAAG,EAAE,GAAE,CAAC;CAC7B,CAAC,CAAC;AAOI,MAAM,qBAAqB,GAE9B,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE;IAC/C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,IAAA,gBAAQ,EAAoB,EAAE,CAAC,CAAC;IAC1E,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,IAAA,gBAAQ,EAC9C,EAAE,CACH,CAAC;IACF,MAAM,CAAC,yBAAyB,EAAE,4BAA4B,CAAC,GAAG,IAAA,gBAAQ,EAExE,EAAE,CAAC,CAAC;IAEN,MAAM,YAAY,GAAG,IAAA,mBAAW,EAC9B,CAAC,IAAe,EAAE,eAAe,GAAG,IAAI,EAAE,EAAE;QAC1C,eAAe,CAAC,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;gBACvC,OAAO,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,OAAO;gBACL,GAAG,IAAI;gBACP,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI;aAClB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,aAAa,GAAG,IAAA,mBAAW,EAC/B,CAAC,KAAkB,EAAE,eAAe,GAAG,IAAI,EAAE,EAAE;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;IAC/D,CAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,MAAM,kBAAkB,GAAG,IAAA,mBAAW,EACpC,CAAC,aAAqB,EAAE,IAAe,EAAE,EAAE;QACzC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,aAAa,aAAa,wBAAwB,CAAC,CAAC;QACtE,CAAC;QACD,4BAA4B,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtC,GAAG,IAAI;YACP,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;SAC7D,CAAC,CAAC,CAAC;IACN,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IAEF,MAAM,iBAAiB,GAAG,IAAA,mBAAW,EACnC,CAAC,OAAuB,EAAE,eAAe,GAAG,IAAI,EAAE,EAAE;QAClD,MAAM,EACJ,IAAI,EACJ,WAAW,EACX,SAAS,EACT,eAAe,GAAG,EAAE,EACpB,gBAAgB,EAChB,eAAe,GAChB,GAAG,OAAO,CAAC;QAEZ,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,OAAO;gBACL,GAAG,IAAI;gBACP,CAAC,IAAI,CAAC,EAAE;oBACN,SAAS;oBACT,gBAAgB;oBAChB,IAAI;oBACJ,WAAW;oBACX,KAAK,EAAE,eAAe;oBACtB,YAAY,EAAE,EAAE;iBACjB;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,IAAI,eAAe,EAAE,CAAC;YACpB,aAAa,CAAC,eAAe,CAAC,CAAC;YAC/B,4BAA4B,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACtC,GAAG,IAAI;gBACP,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACjD,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IACF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,cAAc,EAAE,CAAC;YACnB,cAAc,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;gBACnC,iBAAiB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,EAAE,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,CAAC;IAExC,MAAM,KAAK,GAAG;QACZ,aAAa;QACb,YAAY;QACZ,yBAAyB;QACzB,iBAAiB;QACjB,YAAY;QACZ,aAAa;QACb,kBAAkB;KACnB,CAAC;IAEF,OAAO,CACL,8BAAC,oBAAoB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,IACxC,QAAQ,CACqB,CACjC,CAAC;AACJ,CAAC,CAAC;AA1GW,QAAA,qBAAqB,yBA0GhC;AAEK,MAAM,gBAAgB,GAAG,GAAG,EAAE;IACnC,OAAO,IAAA,kBAAU,EAAC,oBAAoB,CAAC,CAAC;AAC1C,CAAC,CAAC;AAFW,QAAA,gBAAgB,oBAE3B","sourcesContent":["\"use client\";\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport {\n ComponentRegistry,\n TamboComponent,\n TamboTool,\n} from \"../model/component-metadata\";\n\nexport interface TamboRegistryContext {\n componentList: ComponentRegistry;\n toolRegistry: Record<string, TamboTool>;\n componentToolAssociations: Record<string, string[]>;\n registerComponent: (options: TamboComponent) => void;\n registerTool: (tool: TamboTool) => void;\n registerTools: (tools: TamboTool[]) => void;\n addToolAssociation: (componentName: string, tool: TamboTool) => void;\n}\n\nconst TamboRegistryContext = createContext<TamboRegistryContext>({\n componentList: {},\n toolRegistry: {},\n componentToolAssociations: {},\n registerComponent: () => {},\n registerTool: () => {},\n registerTools: () => {},\n addToolAssociation: () => {},\n});\n\nexport interface TamboRegistryProviderProps {\n /** The default components to register */\n components?: TamboComponent[];\n}\n\nexport const TamboRegistryProvider: React.FC<\n PropsWithChildren<TamboRegistryProviderProps>\n> = ({ children, components: userComponents }) => {\n const [componentList, setComponentList] = useState<ComponentRegistry>({});\n const [toolRegistry, setToolRegistry] = useState<Record<string, TamboTool>>(\n {},\n );\n const [componentToolAssociations, setComponentToolAssociations] = useState<\n Record<string, string[]>\n >({});\n\n const registerTool = useCallback(\n (tool: TamboTool, warnOnOverwrite = true) => {\n setToolRegistry((prev) => {\n if (prev[tool.name] && warnOnOverwrite) {\n console.warn(`Overwriting tool ${tool.name}`);\n }\n return {\n ...prev,\n [tool.name]: tool,\n };\n });\n },\n [],\n );\n\n const registerTools = useCallback(\n (tools: TamboTool[], warnOnOverwrite = true) => {\n tools.forEach((tool) => registerTool(tool, warnOnOverwrite));\n },\n [registerTool],\n );\n\n const addToolAssociation = useCallback(\n (componentName: string, tool: TamboTool) => {\n if (!componentList[componentName]) {\n throw new Error(`Component ${componentName} not found in registry`);\n }\n setComponentToolAssociations((prev) => ({\n ...prev,\n [componentName]: [...(prev[componentName] || []), tool.name],\n }));\n },\n [componentList],\n );\n\n const registerComponent = useCallback(\n (options: TamboComponent, warnOnOverwrite = true) => {\n const {\n name,\n description,\n component,\n propsDefinition = {},\n loadingComponent,\n associatedTools,\n } = options;\n\n setComponentList((prev) => {\n if (prev[name] && warnOnOverwrite) {\n console.warn(`overwriting component ${name}`);\n }\n return {\n ...prev,\n [name]: {\n component,\n loadingComponent,\n name,\n description,\n props: propsDefinition,\n contextTools: [],\n },\n };\n });\n if (associatedTools) {\n registerTools(associatedTools);\n setComponentToolAssociations((prev) => ({\n ...prev,\n [name]: associatedTools.map((tool) => tool.name),\n }));\n }\n },\n [registerTools],\n );\n useEffect(() => {\n if (userComponents) {\n userComponents.forEach((component) => {\n registerComponent(component, false);\n });\n }\n }, [registerComponent, userComponents]);\n\n const value = {\n componentList,\n toolRegistry,\n componentToolAssociations,\n registerComponent,\n registerTool,\n registerTools,\n addToolAssociation,\n };\n\n return (\n <TamboRegistryContext.Provider value={value}>\n {children}\n </TamboRegistryContext.Provider>\n );\n};\n\nexport const useTamboRegistry = () => {\n return useContext(TamboRegistryContext);\n};\n"]}
1
+ {"version":3,"file":"tambo-registry-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-registry-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport zodToJsonSchema from \"zod-to-json-schema\";\nimport {\n ComponentRegistry,\n TamboComponent,\n TamboTool,\n} from \"../model/component-metadata\";\n\nexport interface TamboRegistryContext {\n componentList: ComponentRegistry;\n toolRegistry: Record<string, TamboTool>;\n componentToolAssociations: Record<string, string[]>;\n registerComponent: (options: TamboComponent) => void;\n registerTool: (tool: TamboTool) => void;\n registerTools: (tools: TamboTool[]) => void;\n addToolAssociation: (componentName: string, tool: TamboTool) => void;\n}\n\nconst TamboRegistryContext = createContext<TamboRegistryContext>({\n componentList: {},\n toolRegistry: {},\n componentToolAssociations: {},\n registerComponent: () => {},\n registerTool: () => {},\n registerTools: () => {},\n addToolAssociation: () => {},\n});\n\nexport interface TamboRegistryProviderProps {\n /** The default components to register */\n components?: TamboComponent[];\n}\n\nexport const TamboRegistryProvider: React.FC<\n PropsWithChildren<TamboRegistryProviderProps>\n> = ({ children, components: userComponents }) => {\n const [componentList, setComponentList] = useState<ComponentRegistry>({});\n const [toolRegistry, setToolRegistry] = useState<Record<string, TamboTool>>(\n {},\n );\n const [componentToolAssociations, setComponentToolAssociations] = useState<\n Record<string, string[]>\n >({});\n\n const registerTool = useCallback(\n (tool: TamboTool, warnOnOverwrite = true) => {\n setToolRegistry((prev) => {\n if (prev[tool.name] && warnOnOverwrite) {\n console.warn(`Overwriting tool ${tool.name}`);\n }\n return {\n ...prev,\n [tool.name]: tool,\n };\n });\n },\n [],\n );\n\n const registerTools = useCallback(\n (tools: TamboTool[], warnOnOverwrite = true) => {\n tools.forEach((tool) => registerTool(tool, warnOnOverwrite));\n },\n [registerTool],\n );\n\n const addToolAssociation = useCallback(\n (componentName: string, tool: TamboTool) => {\n if (!componentList[componentName]) {\n throw new Error(`Component ${componentName} not found in registry`);\n }\n setComponentToolAssociations((prev) => ({\n ...prev,\n [componentName]: [...(prev[componentName] || []), tool.name],\n }));\n },\n [componentList],\n );\n\n const registerComponent = useCallback(\n (options: TamboComponent, warnOnOverwrite = true) => {\n const {\n name,\n description,\n component,\n propsSchema,\n propsDefinition,\n loadingComponent,\n associatedTools,\n } = options;\n\n // Validate that at least one props definition is provided\n if (!propsSchema && !propsDefinition) {\n throw new Error(\n `Component ${name} must have either propsSchema (recommended) or propsDefinition defined`,\n );\n }\n\n // Validate that only one props definition is provided\n if (propsSchema && propsDefinition) {\n throw new Error(\n `Component ${name} cannot have both propsSchema and propsDefinition defined. Use only one. We recommend using propsSchema.`,\n );\n }\n\n // Convert propsSchema to JSON Schema if it exists\n let props = propsDefinition;\n if (propsSchema) {\n try {\n props = zodToJsonSchema(propsSchema);\n } catch (error) {\n console.error(\n `Error converting ${name} props schema to JSON Schema:`,\n error,\n );\n }\n }\n\n setComponentList((prev) => {\n if (prev[name] && warnOnOverwrite) {\n console.warn(`overwriting component ${name}`);\n }\n return {\n ...prev,\n [name]: {\n component,\n loadingComponent,\n name,\n description,\n props,\n contextTools: [],\n },\n };\n });\n if (associatedTools) {\n registerTools(associatedTools);\n setComponentToolAssociations((prev) => ({\n ...prev,\n [name]: associatedTools.map((tool) => tool.name),\n }));\n }\n },\n [registerTools],\n );\n useEffect(() => {\n if (userComponents) {\n userComponents.forEach((component) => {\n registerComponent(component, false);\n });\n }\n }, [registerComponent, userComponents]);\n\n const value = {\n componentList,\n toolRegistry,\n componentToolAssociations,\n registerComponent,\n registerTool,\n registerTools,\n addToolAssociation,\n };\n\n return (\n <TamboRegistryContext.Provider value={value}>\n {children}\n </TamboRegistryContext.Provider>\n );\n};\n\nexport const useTamboRegistry = () => {\n return useContext(TamboRegistryContext);\n};\n"]}
@@ -1,6 +1,7 @@
1
1
  import TamboAI from "@tambo-ai/typescript-sdk";
2
2
  import { ComponentContextToolMetadata, ComponentRegistry, RegisteredComponent, TamboTool, TamboToolAssociations, TamboToolRegistry } from "../model/component-metadata";
3
3
  export declare const getAvailableComponents: (componentRegistry: ComponentRegistry, toolRegistry: TamboToolRegistry, toolAssociations: TamboToolAssociations) => TamboAI.AvailableComponent[];
4
+ export declare const convertPropsToJsonSchema: (component: RegisteredComponent) => any;
4
5
  export declare const getComponentFromRegistry: (componentName: string, componentRegistry: ComponentRegistry) => RegisteredComponent;
5
6
  export declare const getDefaultContextAdditions: () => string[];
6
7
  export declare const getClientContext: () => string;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/util/registry.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAG/C,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EAEjB,mBAAmB,EACnB,SAAS,EACT,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAErC,eAAO,MAAM,sBAAsB,GACjC,mBAAmB,iBAAiB,EACpC,cAAc,iBAAiB,EAC/B,kBAAkB,qBAAqB,KACtC,OAAO,CAAC,kBAAkB,EAuB5B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,eAAe,MAAM,EACrB,mBAAmB,iBAAiB,KACnC,mBAUF,CAAC;AAEF,eAAO,MAAM,0BAA0B,QAAO,MAAM,EAMnD,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,MAGnC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,MAAM,SAAS,KACd,4BAQF,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/util/registry.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAG/C,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EAEjB,mBAAmB,EACnB,SAAS,EACT,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAErC,eAAO,MAAM,sBAAsB,GACjC,mBAAmB,iBAAiB,EACpC,cAAc,iBAAiB,EAC/B,kBAAkB,qBAAqB,KACtC,OAAO,CAAC,kBAAkB,EAuB5B,CAAC;AAGF,eAAO,MAAM,wBAAwB,GACnC,WAAW,mBAAmB,KAC7B,GAYF,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,eAAe,MAAM,EACrB,mBAAmB,iBAAiB,KACnC,mBAUF,CAAC;AAEF,eAAO,MAAM,0BAA0B,QAAO,MAAM,EAMnD,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,MAGnC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,MAAM,SAAS,KACd,4BAQF,CAAC"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mapTamboToolToContextTool = exports.getClientContext = exports.getDefaultContextAdditions = exports.getComponentFromRegistry = exports.getAvailableComponents = void 0;
6
+ exports.mapTamboToolToContextTool = exports.getClientContext = exports.getDefaultContextAdditions = exports.getComponentFromRegistry = exports.convertPropsToJsonSchema = exports.getAvailableComponents = void 0;
7
7
  const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
8
8
  const getAvailableComponents = (componentRegistry, toolRegistry, toolAssociations) => {
9
9
  const availableComponents = [];
@@ -27,6 +27,19 @@ const getAvailableComponents = (componentRegistry, toolRegistry, toolAssociation
27
27
  return availableComponents;
28
28
  };
29
29
  exports.getAvailableComponents = getAvailableComponents;
30
+ // Helper function to convert component props from Zod schema to JSON Schema
31
+ const convertPropsToJsonSchema = (component) => {
32
+ if (!component.props) {
33
+ return component.props;
34
+ }
35
+ // Check if props is a Zod schema (we can't directly check the type, so we check for _def)
36
+ if (component.props._def && typeof component.props.parse === "function") {
37
+ // Use two-step type assertion for safety
38
+ return (0, zod_to_json_schema_1.default)(component.props);
39
+ }
40
+ return component.props;
41
+ };
42
+ exports.convertPropsToJsonSchema = convertPropsToJsonSchema;
30
43
  const getComponentFromRegistry = (componentName, componentRegistry) => {
31
44
  const componentEntry = componentRegistry[componentName];
32
45
  if (!componentEntry) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/util/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { z } from \"zod\";\nimport zodToJsonSchema from \"zod-to-json-schema\";\nimport {\n ComponentContextToolMetadata,\n ComponentRegistry,\n ParameterSpec,\n RegisteredComponent,\n TamboTool,\n TamboToolAssociations,\n TamboToolRegistry,\n} from \"../model/component-metadata\";\n\nexport const getAvailableComponents = (\n componentRegistry: ComponentRegistry,\n toolRegistry: TamboToolRegistry,\n toolAssociations: TamboToolAssociations,\n): TamboAI.AvailableComponent[] => {\n const availableComponents: TamboAI.AvailableComponent[] = [];\n\n for (const [name, componentEntry] of Object.entries(componentRegistry)) {\n const associatedToolNames = toolAssociations[name] || [];\n\n const contextTools = [\n ...associatedToolNames.map((toolName) => {\n const tool = toolRegistry[toolName];\n if (!tool) return null;\n return mapTamboToolToContextTool(tool);\n }),\n ].filter((tool): tool is ComponentContextToolMetadata => tool !== null);\n\n availableComponents.push({\n name: componentEntry.name,\n description: componentEntry.description,\n props: componentEntry.props,\n contextTools,\n });\n }\n\n return availableComponents;\n};\n\nexport const getComponentFromRegistry = (\n componentName: string,\n componentRegistry: ComponentRegistry,\n): RegisteredComponent => {\n const componentEntry = componentRegistry[componentName];\n\n if (!componentEntry) {\n throw new Error(\n `Tambo tried to use Component ${componentName}, but it was not found.`,\n );\n }\n\n return componentEntry;\n};\n\nexport const getDefaultContextAdditions = (): string[] => {\n const utcOffsetHours = new Date().getTimezoneOffset() / 60;\n const utcOffset = `(UTC${utcOffsetHours > 0 ? \"+\" : \"\"}${utcOffsetHours})`;\n return [\n `The current time in user's timezone (${utcOffset}) is: ${new Date().toLocaleString()}`,\n ];\n};\n\nexport const getClientContext = (): string => {\n const contextAdditions = getDefaultContextAdditions();\n return contextAdditions.join(\"\\n\");\n};\n\nexport const mapTamboToolToContextTool = (\n tool: TamboTool,\n): ComponentContextToolMetadata => {\n const parameters = getParametersFromZodFunction(tool.toolSchema);\n\n return {\n name: tool.name,\n description: tool.description,\n parameters,\n };\n};\n\nconst getParametersFromZodFunction = (\n schema: z.ZodFunction<any, any>,\n): ParameterSpec[] => {\n const parameters: z.ZodTuple = schema.parameters();\n return parameters.items.map((param, index): ParameterSpec => {\n const name = `param${index + 1}`;\n const type = getZodBaseType(param);\n const description = param.description ?? \"\";\n const isRequired = !param.isOptional();\n const schema = zodToJsonSchema(param);\n\n return {\n name,\n type,\n description,\n isRequired,\n schema,\n };\n });\n};\n\nconst getZodBaseType = (schema: z.ZodTypeAny): string => {\n const typeName = schema._def.typeName;\n switch (typeName) {\n case \"ZodString\":\n return \"string\";\n case \"ZodNumber\":\n return \"number\";\n case \"ZodBoolean\":\n return \"boolean\";\n case \"ZodArray\":\n return \"array\";\n case \"ZodEnum\":\n return \"enum\";\n case \"ZodDate\":\n return \"date\";\n case \"ZodObject\":\n return \"object\";\n default:\n console.warn(\"falling back to string for\", typeName);\n return \"string\";\n }\n};\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/util/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { z } from \"zod\";\nimport zodToJsonSchema from \"zod-to-json-schema\";\nimport {\n ComponentContextToolMetadata,\n ComponentRegistry,\n ParameterSpec,\n RegisteredComponent,\n TamboTool,\n TamboToolAssociations,\n TamboToolRegistry,\n} from \"../model/component-metadata\";\n\nexport const getAvailableComponents = (\n componentRegistry: ComponentRegistry,\n toolRegistry: TamboToolRegistry,\n toolAssociations: TamboToolAssociations,\n): TamboAI.AvailableComponent[] => {\n const availableComponents: TamboAI.AvailableComponent[] = [];\n\n for (const [name, componentEntry] of Object.entries(componentRegistry)) {\n const associatedToolNames = toolAssociations[name] || [];\n\n const contextTools = [\n ...associatedToolNames.map((toolName) => {\n const tool = toolRegistry[toolName];\n if (!tool) return null;\n return mapTamboToolToContextTool(tool);\n }),\n ].filter((tool): tool is ComponentContextToolMetadata => tool !== null);\n\n availableComponents.push({\n name: componentEntry.name,\n description: componentEntry.description,\n props: componentEntry.props,\n contextTools,\n });\n }\n\n return availableComponents;\n};\n\n// Helper function to convert component props from Zod schema to JSON Schema\nexport const convertPropsToJsonSchema = (\n component: RegisteredComponent,\n): any => {\n if (!component.props) {\n return component.props;\n }\n\n // Check if props is a Zod schema (we can't directly check the type, so we check for _def)\n if (component.props._def && typeof component.props.parse === \"function\") {\n // Use two-step type assertion for safety\n return zodToJsonSchema(component.props as unknown as z.ZodTypeAny);\n }\n\n return component.props;\n};\n\nexport const getComponentFromRegistry = (\n componentName: string,\n componentRegistry: ComponentRegistry,\n): RegisteredComponent => {\n const componentEntry = componentRegistry[componentName];\n\n if (!componentEntry) {\n throw new Error(\n `Tambo tried to use Component ${componentName}, but it was not found.`,\n );\n }\n\n return componentEntry;\n};\n\nexport const getDefaultContextAdditions = (): string[] => {\n const utcOffsetHours = new Date().getTimezoneOffset() / 60;\n const utcOffset = `(UTC${utcOffsetHours > 0 ? \"+\" : \"\"}${utcOffsetHours})`;\n return [\n `The current time in user's timezone (${utcOffset}) is: ${new Date().toLocaleString()}`,\n ];\n};\n\nexport const getClientContext = (): string => {\n const contextAdditions = getDefaultContextAdditions();\n return contextAdditions.join(\"\\n\");\n};\n\nexport const mapTamboToolToContextTool = (\n tool: TamboTool,\n): ComponentContextToolMetadata => {\n const parameters = getParametersFromZodFunction(tool.toolSchema);\n\n return {\n name: tool.name,\n description: tool.description,\n parameters,\n };\n};\n\nconst getParametersFromZodFunction = (\n schema: z.ZodFunction<any, any>,\n): ParameterSpec[] => {\n const parameters: z.ZodTuple = schema.parameters();\n return parameters.items.map((param, index): ParameterSpec => {\n const name = `param${index + 1}`;\n const type = getZodBaseType(param);\n const description = param.description ?? \"\";\n const isRequired = !param.isOptional();\n const schema = zodToJsonSchema(param);\n\n return {\n name,\n type,\n description,\n isRequired,\n schema,\n };\n });\n};\n\nconst getZodBaseType = (schema: z.ZodTypeAny): string => {\n const typeName = schema._def.typeName;\n switch (typeName) {\n case \"ZodString\":\n return \"string\";\n case \"ZodNumber\":\n return \"number\";\n case \"ZodBoolean\":\n return \"boolean\";\n case \"ZodArray\":\n return \"array\";\n case \"ZodEnum\":\n return \"enum\";\n case \"ZodDate\":\n return \"date\";\n case \"ZodObject\":\n return \"object\";\n default:\n console.warn(\"falling back to string for\", typeName);\n return \"string\";\n }\n};\n"]}
@@ -0,0 +1,8 @@
1
+ export * from "./react-query-hooks";
2
+ export { useTamboComponentState } from "./use-component-state";
3
+ export { useTamboCurrentMessage, useTamboMessageContext, } from "./use-current-message";
4
+ export { useTamboStreamingProps } from "./use-streaming-props";
5
+ export * from "./use-suggestions";
6
+ export { useTamboThreads } from "./use-tambo-threads";
7
+ export { useTamboThreadInput } from "./use-thread-input";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Export all hooks from this directory
2
+ export * from "./react-query-hooks";
3
+ export { useTamboComponentState } from "./use-component-state";
4
+ export { useTamboCurrentMessage, useTamboMessageContext, } from "./use-current-message";
5
+ export { useTamboStreamingProps } from "./use-streaming-props";
6
+ export * from "./use-suggestions";
7
+ export { useTamboThreads } from "./use-tambo-threads";
8
+ export { useTamboThreadInput } from "./use-thread-input";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Export all hooks from this directory\nexport * from \"./react-query-hooks\";\nexport { useTamboComponentState } from \"./use-component-state\";\nexport {\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./use-current-message\";\nexport { useTamboStreamingProps } from \"./use-streaming-props\";\nexport * from \"./use-suggestions\";\nexport { useTamboThreads } from \"./use-tambo-threads\";\nexport { useTamboThreadInput } from \"./use-thread-input\";\n"]}
@@ -1,9 +1,44 @@
1
- type StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];
1
+ interface ComponentStateMeta {
2
+ isPending: boolean;
3
+ }
4
+ type StateUpdateResult<T> = [
5
+ currentState: T,
6
+ setState: (newState: T) => void,
7
+ meta: ComponentStateMeta
8
+ ];
2
9
  /**
3
- * Behaves similarly to useState, but the value is stored in the thread
4
- * message, and the state is keyed by the keyName
10
+ * A React hook that provides state management and passes user updates to Tambo.
11
+ * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.
12
+ *
13
+ * @param keyName - The unique key to identify this state within the message's componentState object
14
+ * @param initialValue - Optional initial value for the state, used if no value exists in the message
15
+ * @param debounceTime - Optional debounce time in milliseconds (default: 300ms) to limit API calls
16
+ *
17
+ * @returns A tuple containing:
18
+ * - The current state value
19
+ * - A setter function to update the state (updates UI immediately, debounces server sync)
20
+ * - A metadata object with properties like isPending to track sync status
21
+ *
22
+ * @example
23
+ * // Basic usage
24
+ * const [count, setCount, { isPending }] = useTamboComponentState("counter", 0);
25
+ *
26
+ * // Usage with object state
27
+ * const [formState, setFormState] = useTamboComponentState("myForm", {
28
+ * name: "",
29
+ * email: "",
30
+ * message: ""
31
+ * });
32
+ *
33
+ * // Handling form input
34
+ * const handleChange = (e) => {
35
+ * setFormState({
36
+ * ...formState,
37
+ * [e.target.name]: e.target.value
38
+ * });
39
+ * };
5
40
  */
6
- export declare function useTamboComponentState<S = undefined>(keyName: string): StateUpdateResult<S | undefined>;
7
- export declare function useTamboComponentState<S>(keyName: string, initialValue?: S): StateUpdateResult<S>;
41
+ export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
42
+ export declare function useTamboComponentState<S>(keyName: string, initialValue: S, debounceTime?: number): StateUpdateResult<S>;
8
43
  export {};
9
44
  //# 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":"AAOA,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,GACd,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,GACf,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AASA,UAAU,kBAAkB;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,iBAAiB,CAAC,CAAC,IAAI;IAC1B,YAAY,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI;IAC/B,IAAI,EAAE,kBAAkB;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,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,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
@@ -1,40 +1,134 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { useDebouncedCallback } from "use-debounce";
2
3
  import { useTamboClient, useTamboThread } from "../providers";
3
4
  import { useTamboCurrentMessage, useTamboMessageContext, } from "./use-current-message";
4
- export function useTamboComponentState(keyName, initialValue) {
5
+ export function useTamboComponentState(keyName, initialValue, debounceTime = 300) {
5
6
  const { threadId, messageId } = useTamboMessageContext();
6
7
  const { updateThreadMessage } = useTamboThread();
7
8
  const client = useTamboClient();
8
9
  const message = useTamboCurrentMessage();
10
+ // Initial value management
9
11
  const [cachedInitialValue] = useState(() => initialValue);
10
- const value = useMemo(() => {
11
- if (!message?.componentState)
12
- return cachedInitialValue;
13
- return keyName in message.componentState
14
- ? message.componentState[keyName]
15
- : cachedInitialValue;
16
- }, [cachedInitialValue, keyName, message?.componentState]);
12
+ // UI state management
13
+ const [localState, setLocalState] = useState(cachedInitialValue);
14
+ // Synchronization state
15
+ const [isPending, setIsPending] = useState(false);
16
+ // Track the last user-initiated value instead of a simple boolean flag
17
+ const [lastUserValue, setLastUserValue] = useState(null);
18
+ const [haveInitialized, setHaveInitialized] = useState(false);
19
+ // Determine if we need to initialize state
20
+ const shouldInitialize = !haveInitialized &&
21
+ message &&
22
+ cachedInitialValue !== undefined &&
23
+ (!message.componentState || !(keyName in message.componentState));
24
+ // Helper function to check if two values are deeply equal
25
+ const isEqual = (a, b) => {
26
+ if (a === b)
27
+ return true;
28
+ // Handle primitive types
29
+ if (typeof a !== "object" ||
30
+ typeof b !== "object" ||
31
+ a === null ||
32
+ b === null)
33
+ return false;
34
+ // For objects and arrays, do a shallow comparison for simplicity
35
+ // This could be enhanced with a proper deep equality check if needed
36
+ if (Array.isArray(a) && Array.isArray(b)) {
37
+ if (a.length !== b.length)
38
+ return false;
39
+ return a.every((val, idx) => val === b[idx]);
40
+ }
41
+ const keysA = Object.keys(a);
42
+ const keysB = Object.keys(b);
43
+ if (keysA.length !== keysB.length)
44
+ return false;
45
+ return keysA.every((key) => a[key] === b[key]);
46
+ };
47
+ // Sync local state with message state on initial load and when message changes
48
+ useEffect(() => {
49
+ if (message?.componentState && keyName in message.componentState) {
50
+ const messageState = message.componentState[keyName];
51
+ // If this is a user-initiated state that matches what we're getting from server,
52
+ // we can clear the lastUserValue flag since it's been synchronized
53
+ if (lastUserValue !== null && isEqual(messageState, lastUserValue)) {
54
+ setLastUserValue(null);
55
+ }
56
+ // Update local state with server state unless user has specifically changed this value
57
+ // This allows streaming updates to continue while protecting user edits
58
+ if (lastUserValue === null || !isEqual(localState, lastUserValue)) {
59
+ setLocalState(messageState);
60
+ }
61
+ }
62
+ // Otherwise fall back to initial value if we have one
63
+ else if (cachedInitialValue !== undefined && !localState) {
64
+ setLocalState(cachedInitialValue);
65
+ }
66
+ }, [
67
+ keyName,
68
+ message?.componentState,
69
+ cachedInitialValue,
70
+ lastUserValue,
71
+ localState,
72
+ ]);
73
+ // Create debounced save function for efficient server synchronization
74
+ const debouncedSave = useDebouncedCallback(async (newValue) => {
75
+ if (!message) {
76
+ console.warn(`Cannot update missing message ${messageId} for state key "${keyName}"`);
77
+ setLastUserValue(null);
78
+ return;
79
+ }
80
+ setIsPending(true);
81
+ try {
82
+ const messageUpdate = {
83
+ ...message,
84
+ componentState: {
85
+ ...message.componentState,
86
+ [keyName]: newValue,
87
+ },
88
+ };
89
+ const componentStateUpdate = {
90
+ state: { [keyName]: newValue },
91
+ };
92
+ await Promise.all([
93
+ updateThreadMessage(messageId, messageUpdate, false),
94
+ client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate),
95
+ ]);
96
+ // Only clear the lastUserValue when we've successfully synced this exact value
97
+ if (isEqual(newValue, lastUserValue)) {
98
+ setLastUserValue(null);
99
+ }
100
+ }
101
+ catch (err) {
102
+ console.error(`Failed to save component state for key "${keyName}":`, err);
103
+ }
104
+ finally {
105
+ setIsPending(false);
106
+ }
107
+ }, debounceTime);
108
+ // Initialize state on first render if needed
17
109
  const initializeState = useCallback(async () => {
18
110
  if (!message) {
19
- console.warn(`Cannot initialize state for missing message ${messageId}`);
111
+ console.warn(`Cannot initialize state for missing message ${messageId} with key "${keyName}"`);
20
112
  return;
21
113
  }
22
114
  try {
115
+ const messageUpdate = {
116
+ ...message,
117
+ componentState: {
118
+ ...message.componentState,
119
+ [keyName]: cachedInitialValue,
120
+ },
121
+ };
122
+ const componentStateUpdate = {
123
+ state: { [keyName]: cachedInitialValue },
124
+ };
23
125
  await Promise.all([
24
- updateThreadMessage(messageId, {
25
- ...message,
26
- componentState: {
27
- ...message.componentState,
28
- [keyName]: cachedInitialValue,
29
- },
30
- }, false),
31
- client.beta.threads.messages.updateComponentState(threadId, messageId, {
32
- state: { [keyName]: cachedInitialValue },
33
- }),
126
+ updateThreadMessage(messageId, messageUpdate, false),
127
+ client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate),
34
128
  ]);
35
129
  }
36
130
  catch (err) {
37
- console.warn("Failed to initialize component state:", err);
131
+ console.warn(`Failed to initialize component state for key "${keyName}":`, err);
38
132
  }
39
133
  }, [
40
134
  cachedInitialValue,
@@ -45,39 +139,36 @@ export function useTamboComponentState(keyName, initialValue) {
45
139
  threadId,
46
140
  updateThreadMessage,
47
141
  ]);
48
- const [haveInitialized, setHaveInitialized] = useState(false);
49
- const shouldInitialize = !haveInitialized &&
50
- message &&
51
- cachedInitialValue !== undefined &&
52
- (!message.componentState || !(keyName in message.componentState));
53
- // send initial state
142
+ // Send initial state when component mounts
54
143
  useEffect(() => {
55
144
  if (shouldInitialize) {
56
145
  initializeState();
57
146
  setHaveInitialized(true);
58
147
  }
59
148
  }, [initializeState, shouldInitialize]);
60
- const setValue = useCallback(async (newValue) => {
61
- if (!message) {
62
- console.warn(`Cannot update missing message ${messageId}`);
63
- return;
149
+ // setValue function for updating state
150
+ // Updates local state immediately and schedules debounced server sync
151
+ const setValue = useCallback((newValue) => {
152
+ // Track this as a user-initiated update
153
+ setLastUserValue(newValue);
154
+ setLocalState(newValue);
155
+ // Only trigger server updates if we have a message
156
+ if (message) {
157
+ debouncedSave(newValue);
64
158
  }
65
- await updateThreadMessage(messageId, {
66
- ...message,
67
- componentState: {
68
- ...message.componentState,
69
- [keyName]: newValue,
70
- },
71
- }, false);
72
- await client.beta.threads.messages.updateComponentState(threadId, messageId, { state: { [keyName]: newValue } });
73
- }, [
74
- client.beta.threads.messages,
75
- keyName,
76
- message,
77
- messageId,
78
- threadId,
79
- updateThreadMessage,
80
- ]);
81
- return [value, setValue];
159
+ else {
160
+ console.warn(`Cannot update server for missing message ${messageId} with key "${keyName}"`);
161
+ setLastUserValue(null);
162
+ }
163
+ }, [debouncedSave, message, messageId, keyName]);
164
+ // Ensure pending changes are flushed on unmount
165
+ useEffect(() => {
166
+ return () => {
167
+ debouncedSave.flush();
168
+ setLastUserValue(null);
169
+ };
170
+ }, [debouncedSave]);
171
+ // Return the local state for immediate UI rendering
172
+ return [localState, setValue, { isPending }];
82
173
  }
83
174
  //# sourceMappingURL=use-component-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAe/B,MAAM,UAAU,sBAAsB,CACpC,OAAe,EACf,YAAgB;IAEhB,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,sBAAsB,EAAE,CAAC;IACzD,MAAM,EAAE,mBAAmB,EAAE,GAAG,cAAc,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAEhC,MAAM,OAAO,GAAG,sBAAsB,EAAE,CAAC;IACzC,MAAM,CAAC,kBAAkB,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC;IAE1D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;QACzB,IAAI,CAAC,OAAO,EAAE,cAAc;YAAE,OAAO,kBAAkB,CAAC;QACxD,OAAO,OAAO,IAAI,OAAO,CAAC,cAAc;YACtC,CAAC,CAAE,OAAO,CAAC,cAAc,CAAC,OAAO,CAAO;YACxC,CAAC,CAAC,kBAAkB,CAAC;IACzB,CAAC,EAAE,CAAC,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAE3D,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,+CAA+C,SAAS,EAAE,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChB,mBAAmB,CACjB,SAAS,EACT;oBACE,GAAG,OAAO;oBACV,cAAc,EAAE;wBACd,GAAG,OAAO,CAAC,cAAc;wBACzB,CAAC,OAAO,CAAC,EAAE,kBAAkB;qBAC9B;iBACF,EACD,KAAK,CACN;gBACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,QAAQ,EAAE,SAAS,EAAE;oBACrE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB,EAAE;iBACzC,CAAC;aACH,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,EAAE;QACD,kBAAkB;QAClB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ;QAC5B,OAAO;QACP,OAAO;QACP,SAAS;QACT,QAAQ;QACR,mBAAmB;KACpB,CAAC,CAAC;IACH,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,gBAAgB,GACpB,CAAC,eAAe;QAChB,OAAO;QACP,kBAAkB,KAAK,SAAS;QAChC,CAAC,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IAEpE,qBAAqB;IACrB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,gBAAgB,EAAE,CAAC;YACrB,eAAe,EAAE,CAAC;YAClB,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,EAAE,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAExC,MAAM,QAAQ,GAAG,WAAW,CAC1B,KAAK,EAAE,QAAW,EAAE,EAAE;QACpB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,SAAS,EAAE,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,MAAM,mBAAmB,CACvB,SAAS,EACT;YACE,GAAG,OAAO;YACV,cAAc,EAAE;gBACd,GAAG,OAAO,CAAC,cAAc;gBACzB,CAAC,OAAO,CAAC,EAAE,QAAQ;aACpB;SACF,EACD,KAAK,CACN,CAAC;QACF,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CACrD,QAAQ,EACR,SAAS,EACT,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,EAAE,CACnC,CAAC;IACJ,CAAC,EACD;QACE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ;QAC5B,OAAO;QACP,OAAO;QACP,SAAS;QACT,QAAQ;QACR,mBAAmB;KACpB,CACF,CAAC;IACF,OAAO,CAAC,KAAU,EAAE,QAAQ,CAAC,CAAC;AAChC,CAAC","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useTamboClient, useTamboThread } from \"../providers\";\nimport {\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./use-current-message\";\n\ntype StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];\n\n/**\n * Behaves similarly to useState, but the value is stored in the thread\n * message, and the state is keyed by the keyName\n */\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n): StateUpdateResult<S>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n): StateUpdateResult<S> {\n const { threadId, messageId } = useTamboMessageContext();\n const { updateThreadMessage } = useTamboThread();\n const client = useTamboClient();\n\n const message = useTamboCurrentMessage();\n const [cachedInitialValue] = useState(() => initialValue);\n\n const value = useMemo(() => {\n if (!message?.componentState) return cachedInitialValue;\n return keyName in message.componentState\n ? (message.componentState[keyName] as S)\n : cachedInitialValue;\n }, [cachedInitialValue, keyName, message?.componentState]);\n\n const initializeState = useCallback(async () => {\n if (!message) {\n console.warn(`Cannot initialize state for missing message ${messageId}`);\n return;\n }\n try {\n await Promise.all([\n updateThreadMessage(\n messageId,\n {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: cachedInitialValue,\n },\n },\n false,\n ),\n client.beta.threads.messages.updateComponentState(threadId, messageId, {\n state: { [keyName]: cachedInitialValue },\n }),\n ]);\n } catch (err) {\n console.warn(\"Failed to initialize component state:\", err);\n }\n }, [\n cachedInitialValue,\n client.beta.threads.messages,\n keyName,\n message,\n messageId,\n threadId,\n updateThreadMessage,\n ]);\n const [haveInitialized, setHaveInitialized] = useState(false);\n const shouldInitialize =\n !haveInitialized &&\n message &&\n cachedInitialValue !== undefined &&\n (!message.componentState || !(keyName in message.componentState));\n\n // send initial state\n useEffect(() => {\n if (shouldInitialize) {\n initializeState();\n setHaveInitialized(true);\n }\n }, [initializeState, shouldInitialize]);\n\n const setValue = useCallback(\n async (newValue: S) => {\n if (!message) {\n console.warn(`Cannot update missing message ${messageId}`);\n return;\n }\n await updateThreadMessage(\n messageId,\n {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: newValue,\n },\n },\n false,\n );\n await client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n { state: { [keyName]: newValue } },\n );\n },\n [\n client.beta.threads.messages,\n keyName,\n message,\n messageId,\n threadId,\n updateThreadMessage,\n ],\n );\n return [value as S, setValue];\n}\n"]}
1
+ {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { useTamboClient, useTamboThread } from \"../providers\";\nimport {\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./use-current-message\";\n\n// Define metadata interface for better extensibility\ninterface ComponentStateMeta {\n isPending: boolean;\n}\n\ntype StateUpdateResult<T> = [\n currentState: T,\n setState: (newState: T) => void,\n meta: ComponentStateMeta,\n];\n\n/**\n * A React hook that provides state management and passes user updates to Tambo.\n * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.\n *\n * @param keyName - The unique key to identify this state within the message's componentState object\n * @param initialValue - Optional initial value for the state, used if no value exists in the message\n * @param debounceTime - Optional debounce time in milliseconds (default: 300ms) to limit API calls\n *\n * @returns A tuple containing:\n * - The current state value\n * - A setter function to update the state (updates UI immediately, debounces server sync)\n * - A metadata object with properties like isPending to track sync status\n *\n * @example\n * // Basic usage\n * const [count, setCount, { isPending }] = useTamboComponentState(\"counter\", 0);\n *\n * // Usage with object state\n * const [formState, setFormState] = useTamboComponentState(\"myForm\", {\n * name: \"\",\n * email: \"\",\n * message: \"\"\n * });\n *\n * // Handling form input\n * const handleChange = (e) => {\n * setFormState({\n * ...formState,\n * [e.target.name]: e.target.value\n * });\n * };\n */\n\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n debounceTime?: number,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue: S,\n debounceTime?: number,\n): StateUpdateResult<S>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n debounceTime = 300,\n): StateUpdateResult<S> {\n const { threadId, messageId } = useTamboMessageContext();\n const { updateThreadMessage } = useTamboThread();\n const client = useTamboClient();\n const message = useTamboCurrentMessage();\n\n // Initial value management\n const [cachedInitialValue] = useState(() => initialValue);\n // UI state management\n const [localState, setLocalState] = useState<S | undefined>(\n cachedInitialValue,\n );\n // Synchronization state\n const [isPending, setIsPending] = useState(false);\n // Track the last user-initiated value instead of a simple boolean flag\n const [lastUserValue, setLastUserValue] = useState<S | null>(null);\n const [haveInitialized, setHaveInitialized] = useState(false);\n\n // Determine if we need to initialize state\n const shouldInitialize =\n !haveInitialized &&\n message &&\n cachedInitialValue !== undefined &&\n (!message.componentState || !(keyName in message.componentState));\n\n // Helper function to check if two values are deeply equal\n const isEqual = (a: any, b: any): boolean => {\n if (a === b) return true;\n\n // Handle primitive types\n if (\n typeof a !== \"object\" ||\n typeof b !== \"object\" ||\n a === null ||\n b === null\n )\n return false;\n\n // For objects and arrays, do a shallow comparison for simplicity\n // This could be enhanced with a proper deep equality check if needed\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((val, idx) => val === b[idx]);\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n return keysA.every((key) => a[key] === b[key]);\n };\n\n // Sync local state with message state on initial load and when message changes\n useEffect(() => {\n if (message?.componentState && keyName in message.componentState) {\n const messageState = message.componentState[keyName] as S;\n\n // If this is a user-initiated state that matches what we're getting from server,\n // we can clear the lastUserValue flag since it's been synchronized\n if (lastUserValue !== null && isEqual(messageState, lastUserValue)) {\n setLastUserValue(null);\n }\n\n // Update local state with server state unless user has specifically changed this value\n // This allows streaming updates to continue while protecting user edits\n if (lastUserValue === null || !isEqual(localState, lastUserValue)) {\n setLocalState(messageState);\n }\n }\n // Otherwise fall back to initial value if we have one\n else if (cachedInitialValue !== undefined && !localState) {\n setLocalState(cachedInitialValue);\n }\n }, [\n keyName,\n message?.componentState,\n cachedInitialValue,\n lastUserValue,\n localState,\n ]);\n\n // Create debounced save function for efficient server synchronization\n const debouncedSave = useDebouncedCallback(async (newValue: S) => {\n if (!message) {\n console.warn(\n `Cannot update missing message ${messageId} for state key \"${keyName}\"`,\n );\n setLastUserValue(null);\n return;\n }\n\n setIsPending(true);\n try {\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: newValue,\n },\n };\n\n const componentStateUpdate = {\n state: { [keyName]: newValue },\n };\n\n await Promise.all([\n updateThreadMessage(messageId, messageUpdate, false),\n client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n ),\n ]);\n\n // Only clear the lastUserValue when we've successfully synced this exact value\n if (isEqual(newValue, lastUserValue)) {\n setLastUserValue(null);\n }\n } catch (err) {\n console.error(\n `Failed to save component state for key \"${keyName}\":`,\n err,\n );\n } finally {\n setIsPending(false);\n }\n }, debounceTime);\n\n // Initialize state on first render if needed\n const initializeState = useCallback(async () => {\n if (!message) {\n console.warn(\n `Cannot initialize state for missing message ${messageId} with key \"${keyName}\"`,\n );\n return;\n }\n\n try {\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: cachedInitialValue,\n },\n };\n\n const componentStateUpdate = {\n state: { [keyName]: cachedInitialValue },\n };\n\n await Promise.all([\n updateThreadMessage(messageId, messageUpdate, false),\n client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n ),\n ]);\n } catch (err) {\n console.warn(\n `Failed to initialize component state for key \"${keyName}\":`,\n err,\n );\n }\n }, [\n cachedInitialValue,\n client.beta.threads.messages,\n keyName,\n message,\n messageId,\n threadId,\n updateThreadMessage,\n ]);\n\n // Send initial state when component mounts\n useEffect(() => {\n if (shouldInitialize) {\n initializeState();\n setHaveInitialized(true);\n }\n }, [initializeState, shouldInitialize]);\n\n // setValue function for updating state\n // Updates local state immediately and schedules debounced server sync\n const setValue = useCallback(\n (newValue: S) => {\n // Track this as a user-initiated update\n setLastUserValue(newValue);\n setLocalState(newValue);\n\n // Only trigger server updates if we have a message\n if (message) {\n debouncedSave(newValue);\n } else {\n console.warn(\n `Cannot update server for missing message ${messageId} with key \"${keyName}\"`,\n );\n setLastUserValue(null);\n }\n },\n [debouncedSave, message, messageId, keyName],\n );\n\n // Ensure pending changes are flushed on unmount\n useEffect(() => {\n return () => {\n debouncedSave.flush();\n setLastUserValue(null);\n };\n }, [debouncedSave]);\n\n // Return the local state for immediate UI rendering\n return [localState as S, setValue, { isPending }];\n}\n"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A helper hook that automatically updates Tambo component state when specified props change.
3
+ *
4
+ * This hook streamlines the common pattern of updating component state when receiving new
5
+ * streamed values from Tambo, eliminating the need to write repetitive useEffect code.
6
+ *
7
+ * @param currentState - The current state object from useTamboComponentState
8
+ * @param setState - The setState function from useTamboComponentState
9
+ * @param streamingProps - An object mapping state keys to prop values that should update the state
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // Instead of writing a complex useEffect:
14
+ * const [emailState, setEmailState] = useTamboComponentState("email", initialState);
15
+ *
16
+ * // Simply use:
17
+ * useTamboStreamingProps(emailState, setEmailState, {
18
+ * subject: aiGeneratedSubject,
19
+ * body: aiGeneratedBody,
20
+ * usersEmail: usersEmail
21
+ * });
22
+ * ```
23
+ */
24
+ export declare function useTamboStreamingProps<T extends Record<string, any>>(currentState: T | undefined, setState: (state: T) => void, streamingProps: Partial<T>): void;
25
+ //# sourceMappingURL=use-streaming-props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-streaming-props.d.ts","sourceRoot":"","sources":["../../src/hooks/use-streaming-props.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClE,YAAY,EAAE,CAAC,GAAG,SAAS,EAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC5B,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,QAuB3B"}