diff --git a/demos/ai-chat/app/page.tsx b/demos/ai-chat/app/page.tsx index 52025793..ec90dcc5 100644 --- a/demos/ai-chat/app/page.tsx +++ b/demos/ai-chat/app/page.tsx @@ -30,7 +30,10 @@ export default async function Home() { })) ?? []; const groups: RouteGroup[] = [ - { heading: "Chat", routes: staticChatRoutes }, + { + heading: "Chat", + routes: [...staticChatRoutes, { label: "Chat Widget", path: "/widget" }], + }, { heading: "Docs", routes: [ diff --git a/demos/ai-chat/app/pages/layout.tsx b/demos/ai-chat/app/pages/layout.tsx index 6c94754a..5060f63c 100644 --- a/demos/ai-chat/app/pages/layout.tsx +++ b/demos/ai-chat/app/pages/layout.tsx @@ -66,6 +66,12 @@ export default function PagesLayout({ > Chat + + Widget + + typeof window !== "undefined" + ? window.location.origin + : "http://localhost:3000"; + +export default function WidgetPage() { + const [queryClient] = useState(() => new QueryClient()); + const baseURL = getBaseURL(); + + return ( + + + + basePath="" + overrides={{ + "ai-chat": { + apiBaseURL: baseURL, + apiBasePath: "/api/data", + mode: "public", + navigate: () => {}, + chatSuggestions: [ + "What plugins does BTST offer?", + "How do I install @btst/stack?", + "Tell me about the blog plugin", + ], + }, + }} + > +
+ {/* Nav */} + + + {!hasApiKey && ( +
+ Add OPENAI_API_KEY to{" "} + .env.local to enable AI chat. +
+ )} + + {/* Page content */} +
+
+

+ Widget Mode +

+

+ The AI Chat plugin can be embedded as a floating widget on any + page. Click the button in the bottom-right corner to open it. +

+
+ +
+
+

Compact & embeddable

+

+ The widget renders as a fixed-position panel triggered by a + floating button. It does not affect the surrounding page + layout. +

+
+
+

Configurable size

+

+ Pass widgetHeight{" "} + and widgetWidth{" "} + props to control the panel dimensions. +

+
+
+

Page-aware context

+

+ Wrap your layout with{" "} + + PageAIContextProvider + {" "} + and call{" "} + + useRegisterPageAIContext + {" "} + on any page to give the AI awareness of the current view. +

+
+
+

Stateless by default

+

+ In public mode the widget is stateless — no conversation + history is persisted. Switch to{" "} + + mode="authenticated" + {" "} + for persistent history. +

+
+
+ +
+

+ Usage +

+
+									{``}
+								
+
+
+ + {/* Floating widget */} +
+ +
+
+ + + + ); +} diff --git a/demos/ai-chat/package.json b/demos/ai-chat/package.json index cf0ab89e..78097ccf 100644 --- a/demos/ai-chat/package.json +++ b/demos/ai-chat/package.json @@ -11,7 +11,7 @@ "@ai-sdk/openai": "^2.0.68", "@ai-sdk/react": "^2.0.94", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-dialog": "^1.1.15", diff --git a/demos/ai-chat/pnpm-lock.yaml b/demos/ai-chat/pnpm-lock.yaml index fc732402..53325fcf 100644 --- a/demos/ai-chat/pnpm-lock.yaml +++ b/demos/ai-chat/pnpm-lock.yaml @@ -18,8 +18,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(37ea7fbbdb1664cc3e54767443b15e09) + specifier: ^2.6.1 + version: 2.6.1(37ea7fbbdb1664cc3e54767443b15e09) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -355,8 +355,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4188,7 +4188,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(37ea7fbbdb1664cc3e54767443b15e09)': + '@btst/stack@2.6.1(37ea7fbbdb1664cc3e54767443b15e09)': dependencies: '@ai-sdk/react': 2.0.152(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/demos/blog/package.json b/demos/blog/package.json index 6aae5bbd..ed8a0e27 100644 --- a/demos/blog/package.json +++ b/demos/blog/package.json @@ -10,7 +10,7 @@ "dependencies": { "@ai-sdk/react": "^3.0.118", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-dialog": "^1.1.15", diff --git a/demos/blog/pnpm-lock.yaml b/demos/blog/pnpm-lock.yaml index 602b275a..6caef8c7 100644 --- a/demos/blog/pnpm-lock.yaml +++ b/demos/blog/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd) + specifier: ^2.6.1 + version: 2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -342,8 +342,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4169,7 +4169,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd)': + '@btst/stack@2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd)': dependencies: '@ai-sdk/react': 3.0.118(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/demos/cms/package.json b/demos/cms/package.json index 7d61ab51..e39d5849 100644 --- a/demos/cms/package.json +++ b/demos/cms/package.json @@ -10,7 +10,7 @@ "dependencies": { "@ai-sdk/react": "^3.0.118", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/demos/cms/pnpm-lock.yaml b/demos/cms/pnpm-lock.yaml index 26d6e042..d24ca205 100644 --- a/demos/cms/pnpm-lock.yaml +++ b/demos/cms/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd) + specifier: ^2.6.1 + version: 2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -393,8 +393,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4493,7 +4493,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd)': + '@btst/stack@2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd)': dependencies: '@ai-sdk/react': 3.0.118(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/demos/form-builder/package.json b/demos/form-builder/package.json index 461b31ad..48530dbd 100644 --- a/demos/form-builder/package.json +++ b/demos/form-builder/package.json @@ -10,7 +10,7 @@ "dependencies": { "@ai-sdk/react": "^3.0.118", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-checkbox": "^1.3.3", diff --git a/demos/form-builder/pnpm-lock.yaml b/demos/form-builder/pnpm-lock.yaml index 5c006f7b..10f32485 100644 --- a/demos/form-builder/pnpm-lock.yaml +++ b/demos/form-builder/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd) + specifier: ^2.6.1 + version: 2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -351,8 +351,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4178,7 +4178,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd)': + '@btst/stack@2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd)': dependencies: '@ai-sdk/react': 3.0.118(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/demos/kanban/package.json b/demos/kanban/package.json index f4be8d48..4182618b 100644 --- a/demos/kanban/package.json +++ b/demos/kanban/package.json @@ -10,7 +10,7 @@ "dependencies": { "@ai-sdk/react": "^3.0.118", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/demos/kanban/pnpm-lock.yaml b/demos/kanban/pnpm-lock.yaml index b8811872..84aba8ad 100644 --- a/demos/kanban/pnpm-lock.yaml +++ b/demos/kanban/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd) + specifier: ^2.6.1 + version: 2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -330,8 +330,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4179,7 +4179,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd)': + '@btst/stack@2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd)': dependencies: '@ai-sdk/react': 3.0.118(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/demos/ui-builder/package.json b/demos/ui-builder/package.json index 5e264b67..0123d0c1 100644 --- a/demos/ui-builder/package.json +++ b/demos/ui-builder/package.json @@ -10,7 +10,7 @@ "dependencies": { "@ai-sdk/react": "^3.0.118", "@btst/adapter-memory": "^2.0.3", - "@btst/stack": "^2.6.0", + "@btst/stack": "^2.6.1", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/demos/ui-builder/pnpm-lock.yaml b/demos/ui-builder/pnpm-lock.yaml index 4a6edfd3..0cef2a41 100644 --- a/demos/ui-builder/pnpm-lock.yaml +++ b/demos/ui-builder/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.3 version: 2.0.3(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-auth@1.4.5(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)))(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) '@btst/stack': - specifier: ^2.6.0 - version: 2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd) + specifier: ^2.6.1 + version: 2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd) '@btst/yar': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14) @@ -390,8 +390,8 @@ packages: '@btst/db@2.0.3': resolution: {integrity: sha512-ZY3+393oPAsFYHpgdCPewwlBNTTsSep66wNLRZnzbGdAmhVluuII56PmDFECD/4MB6EmZ/yZhUrOiGWUHHSWGg==} - '@btst/stack@2.6.0': - resolution: {integrity: sha512-yCimnTAn4gwIaLss2/0n3si1Ywv0zAMeheojZsmkK9K3DtCi5+sfxsw5Kigt4o6ygQxz8N1vN7hEu+IpxFYMuQ==} + '@btst/stack@2.6.1': + resolution: {integrity: sha512-6DMiOSesm2/WcWWSJNlbkDDNdDbcmJUuVjeNW3ROWr7j+1moiBjwbp44Lb7YNCGWtedOM4zitNL69+CDd9tWPQ==} peerDependencies: '@ai-sdk/react': '>=2.0.0' '@btst/yar': '>=1.2.0' @@ -4503,7 +4503,7 @@ snapshots: - svelte - vue - '@btst/stack@2.6.0(1b6a0a45c8aa5a923a771880a4bfd1bd)': + '@btst/stack@2.6.1(1b6a0a45c8aa5a923a771880a4bfd1bd)': dependencies: '@ai-sdk/react': 3.0.118(react@19.2.3)(zod@4.3.6) '@btst/db': 2.0.3(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@2.0.1(zod@4.3.6))(jose@6.2.0)(kysely@0.28.11)(nanostores@1.1.1)(next@15.4.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.29(typescript@5.9.3)) diff --git a/docs/content/docs/plugins/ai-chat.mdx b/docs/content/docs/plugins/ai-chat.mdx index d9d78cc3..107a7535 100644 --- a/docs/content/docs/plugins/ai-chat.mdx +++ b/docs/content/docs/plugins/ai-chat.mdx @@ -134,12 +134,16 @@ Configure framework-specific overrides in your `StackProvider`: - ```tsx title="app/pages/[[...all]]/layout.tsx" + ```tsx title="app/pages/layout.tsx" + "use client" + import { useState } from "react" import { StackProvider } from "@btst/stack/context" + import { QueryClientProvider } from "@tanstack/react-query" import type { AiChatPluginOverrides } from "@btst/stack/plugins/ai-chat/client" import Link from "next/link" import Image from "next/image" import { useRouter } from "next/navigation" + import { getOrCreateQueryClient } from "@/lib/query-client" const getBaseURL = () => typeof window !== 'undefined' @@ -152,29 +156,32 @@ Configure framework-specific overrides in your `StackProvider`: export default function Layout({ children }) { const router = useRouter() + const [queryClient] = useState(() => getOrCreateQueryClient()) const baseURL = getBaseURL() return ( - - basePath="/pages" - overrides={{ - "ai-chat": { - mode: "authenticated", // Should match backend config - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (path) => router.push(path), - refresh: () => router.refresh(), - uploadFile: async (file) => { - // Implement your file upload logic - return "https://example.com/uploads/file.pdf" - }, - Link: (props) => , - Image: (props) => , - } - }} - > - {children} - + + + basePath="/pages" + overrides={{ + "ai-chat": { + mode: "authenticated", // Should match backend config + apiBaseURL: baseURL, + apiBasePath: "/api/data", + navigate: (path) => router.push(path), + refresh: () => router.refresh(), + uploadFile: async (file) => { + // Implement your file upload logic + return "https://example.com/uploads/file.pdf" + }, + Link: ({ href, ...props }) => , + Image: (props) => , + } + }} + > + {children} + + ) } ``` @@ -182,8 +189,10 @@ Configure framework-specific overrides in your `StackProvider`: ```tsx title="app/routes/pages/_layout.tsx" + import { useState } from "react" import { Outlet, Link, useNavigate } from "react-router" import { StackProvider } from "@btst/stack/context" + import { QueryClientProvider, QueryClient } from "@tanstack/react-query" import type { AiChatPluginOverrides } from "@btst/stack/plugins/ai-chat/client" const getBaseURL = () => @@ -197,30 +206,33 @@ Configure framework-specific overrides in your `StackProvider`: export default function Layout() { const navigate = useNavigate() + const [queryClient] = useState(() => new QueryClient()) const baseURL = getBaseURL() return ( - - basePath="/pages" - overrides={{ - "ai-chat": { - mode: "authenticated", - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => navigate(href), - uploadFile: async (file) => { - return "https://example.com/uploads/file.pdf" - }, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - } - }} - > - - + + + basePath="/pages" + overrides={{ + "ai-chat": { + mode: "authenticated", + apiBaseURL: baseURL, + apiBasePath: "/api/data", + navigate: (href) => navigate(href), + uploadFile: async (file) => { + return "https://example.com/uploads/file.pdf" + }, + Link: ({ href, children, className, ...props }) => ( + + {children} + + ), + } + }} + > + + + ) } ``` @@ -228,7 +240,9 @@ Configure framework-specific overrides in your `StackProvider`: ```tsx title="src/routes/pages/route.tsx" + import { useState } from "react" import { StackProvider } from "@btst/stack/context" + import { QueryClientProvider, QueryClient } from "@tanstack/react-query" import type { AiChatPluginOverrides } from "@btst/stack/plugins/ai-chat/client" import { Link, useRouter, Outlet } from "@tanstack/react-router" @@ -243,30 +257,33 @@ Configure framework-specific overrides in your `StackProvider`: function Layout() { const router = useRouter() + const [queryClient] = useState(() => new QueryClient()) const baseURL = getBaseURL() return ( - - basePath="/pages" - overrides={{ - "ai-chat": { - mode: "authenticated", - apiBaseURL: baseURL, - apiBasePath: "/api/data", - navigate: (href) => router.navigate({ href }), - uploadFile: async (file) => { - return "https://example.com/uploads/file.pdf" - }, - Link: ({ href, children, className, ...props }) => ( - - {children} - - ), - } - }} - > - - + + + basePath="/pages" + overrides={{ + "ai-chat": { + mode: "authenticated", + apiBaseURL: baseURL, + apiBasePath: "/api/data", + navigate: (href) => router.navigate({ href }), + uploadFile: async (file) => { + return "https://example.com/uploads/file.pdf" + }, + Link: ({ href, children, className, ...props }) => ( + + {children} + + ), + } + }} + > + + + ) } ``` @@ -589,7 +606,7 @@ The default widget mode manages its own open/close state and renders a floating ```tsx + typeof window !== "undefined" + ? (process.env.NEXT_PUBLIC_BASE_URL || window.location.origin) + : (process.env.BASE_URL || "http://localhost:3000"); + export default function ChatModal() { const router = useRouter(); + const baseURL = getBaseURL(); return ( {/* Backdrop */}
router.back()}> @@ -656,7 +679,7 @@ export default function ChatModal() { {/* Panel is pre-opened; no trigger button rendered */} { @@ -823,7 +846,7 @@ The default `ToolCallDisplay` component shows: Provide custom UI components for specific tools using the `toolRenderers` override. Each key should match the tool name from your backend configuration: -```tsx title="app/pages/[[...all]]/layout.tsx" +```tsx title="app/pages/layout.tsx" import type { AiChatPluginOverrides, ToolCallProps } from "@btst/stack/plugins/ai-chat/client" // Custom weather card component @@ -978,6 +1001,8 @@ By default, public mode is completely stateless - messages are lost on page refr import { ChatLayout, type UIMessage } from "@btst/stack/plugins/ai-chat/client"; import { useLocalStorage } from "@/hooks/useLocalStorage"; // Your hook +const baseURL = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000"; + export default function PublicChat() { const [messages, setMessages] = useLocalStorage( "public-chat-messages", @@ -986,7 +1011,7 @@ export default function PublicChat() { return (