@tanstack/cta-framework-react-cra 0.43.1 → 0.44.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 (41) hide show
  1. package/add-ons/apollo-client/README.md +150 -0
  2. package/add-ons/apollo-client/assets/src/routes/demo.apollo-client.tsx +75 -0
  3. package/add-ons/apollo-client/info.json +19 -0
  4. package/add-ons/apollo-client/package.json +8 -0
  5. package/add-ons/apollo-client/small-logo.svg +11 -0
  6. package/add-ons/convex/package.json +2 -2
  7. package/add-ons/db/assets/src/hooks/demo.useChat.ts +1 -1
  8. package/add-ons/db/assets/src/routes/demo/db-chat-api.ts +4 -1
  9. package/add-ons/db/package.json +1 -1
  10. package/add-ons/mcp/package.json +1 -1
  11. package/add-ons/neon/package.json +1 -1
  12. package/add-ons/sentry/assets/instrument.server.mjs +16 -9
  13. package/add-ons/sentry/assets/src/routes/demo/sentry.testing.tsx +42 -2
  14. package/add-ons/shadcn/package.json +1 -1
  15. package/add-ons/start/assets/src/router.tsx.ejs +34 -10
  16. package/add-ons/start/package.json +2 -2
  17. package/add-ons/store/package.json +3 -3
  18. package/add-ons/storybook/package.json +2 -2
  19. package/examples/tanchat/assets/src/hooks/useAudioRecorder.ts +85 -0
  20. package/examples/tanchat/assets/src/hooks/useTTS.ts +78 -0
  21. package/examples/tanchat/assets/src/lib/model-selection.ts +78 -0
  22. package/examples/tanchat/assets/src/lib/vendor-capabilities.ts +55 -0
  23. package/examples/tanchat/assets/src/routes/demo/api.available-providers.ts +35 -0
  24. package/examples/tanchat/assets/src/routes/demo/api.image.ts +74 -0
  25. package/examples/tanchat/assets/src/routes/demo/api.structured.ts +168 -0
  26. package/examples/tanchat/assets/src/routes/demo/api.tanchat.ts +89 -0
  27. package/examples/tanchat/assets/src/routes/demo/api.transcription.ts +89 -0
  28. package/examples/tanchat/assets/src/routes/demo/api.tts.ts +81 -0
  29. package/examples/tanchat/assets/src/routes/demo/image.tsx +257 -0
  30. package/examples/tanchat/assets/src/routes/demo/structured.tsx +460 -0
  31. package/examples/tanchat/assets/src/routes/demo/tanchat.css +14 -7
  32. package/examples/tanchat/assets/src/routes/demo/tanchat.tsx +301 -81
  33. package/examples/tanchat/info.json +10 -7
  34. package/examples/tanchat/package.json +8 -5
  35. package/package.json +2 -2
  36. package/project/base/src/routes/__root.tsx.ejs +14 -6
  37. package/tests/react-cra.test.ts +14 -0
  38. package/tests/snapshots/react-cra/cr-ts-start-apollo-client-npm.json +31 -0
  39. package/tests/snapshots/react-cra/cr-ts-start-npm.json +2 -2
  40. package/tests/snapshots/react-cra/cr-ts-start-tanstack-query-npm.json +2 -2
  41. package/examples/tanchat/assets/src/routes/demo/api.tanchat.ts.ejs +0 -72
@@ -0,0 +1,150 @@
1
+ # Apollo Client Integration
2
+
3
+ This add-on integrates Apollo Client with TanStack Start to provide modern streaming SSR support for GraphQL data fetching.
4
+
5
+ ## Dependencies
6
+
7
+ The following packages are automatically installed:
8
+
9
+ - `@apollo/client` - Apollo Client core
10
+ - `@apollo/client-integration-tanstack-start` - TanStack Start integration
11
+ - `graphql` - GraphQL implementation
12
+
13
+ ## Configuration
14
+
15
+ ### 1. GraphQL Endpoint
16
+
17
+ Configure your GraphQL API endpoint in `src/router.tsx`:
18
+
19
+ ```tsx
20
+ // Configure Apollo Client
21
+ const apolloClient = new ApolloClient({
22
+ cache: new InMemoryCache(),
23
+ link: new HttpLink({
24
+ uri: 'https://your-graphql-api.example.com/graphql', // Update this!
25
+ }),
26
+ })
27
+ ```
28
+
29
+ You can use environment variables by creating a `.env.local` file:
30
+
31
+ ```bash
32
+ VITE_GRAPHQL_ENDPOINT=https://your-api.com/graphql
33
+ ```
34
+
35
+ The default configuration already uses this pattern:
36
+
37
+ ```tsx
38
+ uri: import.meta.env.VITE_GRAPHQL_ENDPOINT ||
39
+ 'https://your-graphql-api.example.com/graphql'
40
+ ```
41
+
42
+ ## Usage Patterns
43
+
44
+ ### Pattern 1: Loader with preloadQuery (Recommended for SSR)
45
+
46
+ Use `preloadQuery` in route loaders for optimal streaming SSR performance:
47
+
48
+ ```tsx
49
+ import { gql, TypedDocumentNode } from '@apollo/client'
50
+ import { useReadQuery } from '@apollo/client/react'
51
+ import { createFileRoute } from '@tanstack/react-router'
52
+
53
+ const MY_QUERY: TypedDocumentNode<{
54
+ posts: { id: string; title: string; content: string }[]
55
+ }> = gql`
56
+ query GetData {
57
+ posts {
58
+ id
59
+ title
60
+ content
61
+ }
62
+ }
63
+ `
64
+
65
+ export const Route = createFileRoute('/my-route')({
66
+ component: RouteComponent,
67
+ loader: ({ context: { preloadQuery } }) => {
68
+ const queryRef = preloadQuery(MY_QUERY, {
69
+ variables: {},
70
+ })
71
+ return { queryRef }
72
+ },
73
+ })
74
+
75
+ function RouteComponent() {
76
+ const { queryRef } = Route.useLoaderData()
77
+ const { data } = useReadQuery(queryRef)
78
+
79
+ return <div>{/* render your data */}</div>
80
+ }
81
+ ```
82
+
83
+ ### Pattern 2: useSuspenseQuery
84
+
85
+ Use `useSuspenseQuery` directly in components with automatic suspense support:
86
+
87
+ ```tsx
88
+ import { gql, TypedDocumentNode } from '@apollo/client'
89
+ import { useSuspenseQuery } from '@apollo/client/react'
90
+ import { createFileRoute } from '@tanstack/react-router'
91
+
92
+ const MY_QUERY: TypedDocumentNode<{
93
+ posts: { id: string; title: string }[]
94
+ }> = gql`
95
+ query GetData {
96
+ posts {
97
+ id
98
+ title
99
+ }
100
+ }
101
+ `
102
+
103
+ export const Route = createFileRoute('/my-route')({
104
+ component: RouteComponent,
105
+ })
106
+
107
+ function RouteComponent() {
108
+ const { data } = useSuspenseQuery(MY_QUERY)
109
+
110
+ return <div>{/* render your data */}</div>
111
+ }
112
+ ```
113
+
114
+ ### Pattern 3: Manual Refetching
115
+
116
+ ```tsx
117
+ import { useQueryRefHandlers, useReadQuery } from '@apollo/client/react'
118
+
119
+ function MyComponent() {
120
+ const { queryRef } = Route.useLoaderData()
121
+ const { refetch } = useQueryRefHandlers(queryRef)
122
+ const { data } = useReadQuery(queryRef)
123
+
124
+ return (
125
+ <div>
126
+ <button onClick={() => refetch()}>Refresh</button>
127
+ {/* render data */}
128
+ </div>
129
+ )
130
+ }
131
+ ```
132
+
133
+ ## Important Notes
134
+
135
+ ### SSR Optimization
136
+
137
+ The integration automatically handles:
138
+
139
+ - Query deduplication across server and client
140
+ - Streaming SSR with `@defer` directive support
141
+ - Proper cache hydration
142
+
143
+ ## Learn More
144
+
145
+ - [Apollo Client Documentation](https://www.apollographql.com/docs/react)
146
+ - [@apollo/client-integration-tanstack-start](https://www.npmjs.com/package/@apollo/client-integration-tanstack-start)
147
+
148
+ ## Demo
149
+
150
+ Visit `/demo/apollo-client` in your application to see a working example of Apollo Client integration.
@@ -0,0 +1,75 @@
1
+ import { gql, TypedDocumentNode } from '@apollo/client'
2
+ import { useReadQuery } from '@apollo/client/react'
3
+ import { createFileRoute } from '@tanstack/react-router'
4
+ import React from 'react'
5
+
6
+ // Example GraphQL query - replace with your own schema
7
+ const EXAMPLE_QUERY: TypedDocumentNode<{
8
+ continents: { __typename: string; code: string; name: string }
9
+ }> = gql`
10
+ query ExampleQuery {
11
+ continents {
12
+ code
13
+ name
14
+ }
15
+ }
16
+ `
17
+
18
+ export const Route = createFileRoute('/demo/apollo-client')({
19
+ component: RouteComponent,
20
+ loader: ({ context: { preloadQuery } }) => {
21
+ // Preload the query in the loader for optimal performance
22
+ const queryRef = preloadQuery(EXAMPLE_QUERY, {
23
+ variables: {},
24
+ })
25
+ return {
26
+ queryRef,
27
+ }
28
+ },
29
+ })
30
+
31
+ function RouteComponent() {
32
+ const { queryRef } = Route.useLoaderData()
33
+ const { data } = useReadQuery(queryRef)
34
+
35
+ return (
36
+ <div className="p-4">
37
+ <h2 className="text-2xl font-bold mb-4">Apollo Client Demo</h2>
38
+ <div className="bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded mb-4">
39
+ <p className="font-bold">Apollo Client is configured!</p>
40
+ <p className="text-sm mt-2">
41
+ This demo uses{' '}
42
+ <code className="bg-blue-200 px-1 rounded">preloadQuery</code> in the
43
+ loader and{' '}
44
+ <code className="bg-blue-200 px-1 rounded">useReadQuery</code> in the
45
+ component for optimal streaming SSR performance.
46
+ </p>
47
+ </div>
48
+ <div className="bg-gray-100 p-4 rounded">
49
+ <h3 className="font-bold mb-2">Query Result:</h3>
50
+ <pre className="text-sm overflow-x-auto">
51
+ {JSON.stringify(data, null, 2)}
52
+ </pre>
53
+ </div>
54
+ <div className="mt-4 text-sm text-gray-600">
55
+ <p>📝 Next steps:</p>
56
+ <ul className="list-disc ml-6 mt-2">
57
+ <li>
58
+ Configure your GraphQL endpoint in{' '}
59
+ <code className="bg-gray-200 px-1 rounded">src/router.tsx</code>
60
+ </li>
61
+ <li>Replace the example query with your actual GraphQL schema</li>
62
+ <li>
63
+ Learn more:{' '}
64
+ <a
65
+ href="https://www.apollographql.com/docs/react"
66
+ className="text-blue-600 hover:underline"
67
+ >
68
+ Apollo Client Docs
69
+ </a>
70
+ </li>
71
+ </ul>
72
+ </div>
73
+ </div>
74
+ )
75
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "Apollo Client",
3
+ "description": "Integrate Apollo Client with streaming SSR support for GraphQL data fetching.",
4
+ "phase": "add-on",
5
+ "modes": ["file-router"],
6
+ "type": "add-on",
7
+ "priority": 15,
8
+ "link": "https://github.com/apollographql/apollo-client-integrations/tree/main/packages/tanstack-start",
9
+ "dependsOn": ["start"],
10
+ "routes": [
11
+ {
12
+ "icon": "Network",
13
+ "url": "/demo/apollo-client",
14
+ "name": "Apollo Client",
15
+ "path": "src/routes/demo.apollo-client.tsx",
16
+ "jsName": "ApolloClientDemo"
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "dependencies": {
3
+ "@apollo/client": "^4.0.0",
4
+ "@apollo/client-integration-tanstack-start": "^0.14.2-rc.0",
5
+ "graphql": "^16.10.0",
6
+ "rxjs": "^7.8.2"
7
+ }
8
+ }
@@ -0,0 +1,11 @@
1
+ <svg width="238" height="238" viewBox="0 0 238 238" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_3682_2767)">
3
+ <path d="M169.918 151.249L164.506 135.869L157.379 115.617L152.223 100.96L145.474 81.779L137.633 59.5H99.6029L93.5324 76.7516L88.1203 92.1321L61.2402 168.518H86.3228L94.1915 146.222H132.229L125.479 127.04H100.913L106.069 112.384L116.837 81.779L118.618 76.7134L120.398 81.779L150.906 168.5L150.913 168.518H175.995L169.918 151.249Z" fill="#15252D"/>
4
+ <path d="M222.007 60.4327C221.377 59.3152 220.328 58.4938 219.092 58.1493C217.857 57.8048 216.534 57.9653 215.417 58.5956C214.299 59.2259 213.478 60.2743 213.133 61.5102C212.789 62.7461 212.949 64.0682 213.58 65.1858C226.408 87.9957 230.592 114.663 225.366 140.306C220.141 165.948 205.854 188.851 185.122 204.822C164.39 220.792 138.6 228.761 112.472 227.271C86.3451 225.78 61.6284 214.93 42.8471 196.706C24.0658 178.481 12.4764 154.102 10.2003 128.032C7.92431 101.961 15.1139 75.9426 30.4531 54.7395C45.7923 33.5363 68.2548 18.567 93.7287 12.5717C119.203 6.57632 145.984 9.95607 169.169 22.0923C168.426 25.5125 168.92 29.0854 170.561 32.1766C172.202 35.2678 174.886 37.6774 178.136 38.9777C181.385 40.278 184.99 40.3849 188.311 39.2794C191.632 38.1739 194.453 35.9275 196.275 32.9389C198.097 29.9504 198.801 26.4131 198.262 22.9548C197.723 19.4966 195.976 16.3412 193.331 14.0486C190.687 11.7561 187.315 10.4748 183.815 10.4321C180.316 10.3895 176.914 11.5883 174.214 13.8157C149.014 0.453404 119.841 -3.37605 92.0453 3.02956C64.2498 9.43517 39.6953 25.6465 22.8848 48.6905C6.07431 71.7345 -1.86527 100.066 0.522193 128.49C2.90965 156.914 15.4641 183.525 35.8831 203.442C56.3021 223.359 83.2168 235.247 111.691 236.926C140.166 238.605 168.291 229.963 190.91 212.584C213.528 195.205 229.123 170.255 234.835 142.308C240.547 114.362 235.992 85.2934 222.007 60.4333V60.4327Z" fill="#15252D"/>
5
+ </g>
6
+ <defs>
7
+ <clipPath id="clip0_3682_2767">
8
+ <rect width="237.236" height="237.236" fill="white"/>
9
+ </clipPath>
10
+ </defs>
11
+ </svg>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "@convex-dev/react-query": "0.0.0-alpha.11",
3
+ "@convex-dev/react-query": "0.1.0",
4
4
  "convex": "^1.27.3",
5
- "lucide-react": "^0.544.0"
5
+ "lucide-react": "^0.561.0"
6
6
  }
7
7
  }
@@ -3,7 +3,7 @@ import { useLiveQuery } from '@tanstack/react-db'
3
3
 
4
4
  import { messagesCollection, type Message } from '@/db-collections'
5
5
 
6
- import type { Collection } from '@tanstack/db'
6
+ import type { Collection } from '@tanstack/react-db'
7
7
 
8
8
  function useStreamConnection(
9
9
  url: string,
@@ -1,7 +1,10 @@
1
1
  import { createFileRoute } from '@tanstack/react-router'
2
2
  import { json } from '@tanstack/react-start'
3
3
 
4
- import { createCollection, localOnlyCollectionOptions } from '@tanstack/db'
4
+ import {
5
+ createCollection,
6
+ localOnlyCollectionOptions,
7
+ } from '@tanstack/react-db'
5
8
  import { z } from 'zod'
6
9
 
7
10
  const IncomingMessageSchema = z.object({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "dependencies": {
3
- "@tanstack/query-db-collection": "^0.2.0",
3
+ "@tanstack/query-db-collection": "^1.0.8",
4
4
  "@tanstack/react-db": "^0.1.1",
5
5
  "zod": "^4.0.14"
6
6
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "@modelcontextprotocol/sdk": "^1.17.0",
4
- "zod": "4.1.11"
4
+ "zod": "4.2.1"
5
5
  }
6
6
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "@neondatabase/serverless": "^1.0.0",
4
- "@neondatabase/vite-plugin-postgres": "^0.2.0"
4
+ "@neondatabase/vite-plugin-postgres": "^0.5.0"
5
5
  }
6
6
  }
@@ -1,10 +1,17 @@
1
1
  import * as Sentry from '@sentry/tanstackstart-react'
2
- Sentry.init({
3
- dsn: import.meta.env.VITE_SENTRY_DSN,
4
- // Adds request headers and IP for users, for more info visit:
5
- // https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/configuration/options/#sendDefaultPii
6
- sendDefaultPii: true,
7
- tracesSampleRate: 1.0,
8
- replaysSessionSampleRate: 1.0,
9
- replaysOnErrorSampleRate: 1.0,
10
- })
2
+
3
+ const sentryDsn = import.meta.env?.VITE_SENTRY_DSN ?? process.env.VITE_SENTRY_DSN
4
+
5
+ if (!sentryDsn) {
6
+ console.warn('VITE_SENTRY_DSN is not defined. Sentry is not running.')
7
+ } else {
8
+ Sentry.init({
9
+ dsn: sentryDsn,
10
+ // Adds request headers and IP for users, for more info visit:
11
+ // https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/configuration/options/#sendDefaultPii
12
+ sendDefaultPii: true,
13
+ tracesSampleRate: 1.0,
14
+ replaysSessionSampleRate: 1.0,
15
+ replaysOnErrorSampleRate: 1.0,
16
+ })
17
+ }
@@ -285,6 +285,16 @@ function RouteComponent() {
285
285
  const [results, setResults] = useState<
286
286
  Record<string, { type: 'success' | 'error'; spanOp: string }>
287
287
  >({})
288
+ const [sentryConfigured, setSentryConfigured] = useState<boolean | null>(null)
289
+
290
+ useEffect(() => {
291
+ // Check if Sentry DSN environment variable is set
292
+ const hasDsn = !!import.meta.env.VITE_SENTRY_DSN
293
+ setSentryConfigured(hasDsn)
294
+ }, [])
295
+
296
+ // Don't show warning until we've checked on the client
297
+ const showWarning = sentryConfigured === false
288
298
 
289
299
  const handleClientError = async () => {
290
300
  setIsLoading((prev) => ({ ...prev, clientError: true }))
@@ -394,8 +404,8 @@ function RouteComponent() {
394
404
  </div>
395
405
  </div>
396
406
  <p className="text-lg text-[#A49FB5] max-w-xl mx-auto leading-relaxed">
397
- Click the buttons below to trigger errors and traces, then view
398
- them in your{' '}
407
+ Click the buttons below to trigger errors and traces, then view them
408
+ in your{' '}
399
409
  <a
400
410
  href="https://sentry.io"
401
411
  target="_blank"
@@ -408,6 +418,32 @@ function RouteComponent() {
408
418
  </p>
409
419
  </div>
410
420
 
421
+ {/* Sentry Not Initialized Warning */}
422
+ {showWarning && (
423
+ <div className="mb-8 flex items-center gap-3 bg-[#E5A000]/10 border border-[#E5A000]/30 rounded-xl px-6 py-4">
424
+ <svg
425
+ className="w-6 h-6 text-[#E5A000] flex-shrink-0"
426
+ fill="none"
427
+ strokeLinecap="round"
428
+ strokeLinejoin="round"
429
+ strokeWidth="2"
430
+ viewBox="0 0 24 24"
431
+ stroke="currentColor"
432
+ >
433
+ <title>Warning</title>
434
+ <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
435
+ </svg>
436
+ <div>
437
+ <p className="text-[#E5A000] font-medium">
438
+ Sentry is not initialized
439
+ </p>
440
+ <p className="text-[#A49FB5] text-sm mt-1">
441
+ Set the <code className="bg-[#1C1825] px-1.5 py-0.5 rounded text-[#B3A1FF]">VITE_SENTRY_DSN</code> environment variable to enable error tracking and performance monitoring.
442
+ </p>
443
+ </div>
444
+ </div>
445
+ )}
446
+
411
447
  {/* Features Grid */}
412
448
  <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-12">
413
449
  <FeatureCard
@@ -463,6 +499,7 @@ function RouteComponent() {
463
499
  variant="error"
464
500
  onClick={handleClientError}
465
501
  loading={isLoading.clientError}
502
+ disabled={sentryConfigured === false}
466
503
  >
467
504
  Trigger Client Error
468
505
  </SentryButton>
@@ -483,6 +520,7 @@ function RouteComponent() {
483
520
  variant="primary"
484
521
  onClick={handleClientTrace}
485
522
  loading={isLoading.clientTrace}
523
+ disabled={sentryConfigured === false}
486
524
  >
487
525
  Test Client Trace
488
526
  </SentryButton>
@@ -513,6 +551,7 @@ function RouteComponent() {
513
551
  variant="error"
514
552
  onClick={handleServerError}
515
553
  loading={isLoading.serverError}
554
+ disabled={sentryConfigured === false}
516
555
  >
517
556
  Trigger Server Error
518
557
  </SentryButton>
@@ -533,6 +572,7 @@ function RouteComponent() {
533
572
  variant="primary"
534
573
  onClick={handleServerTrace}
535
574
  loading={isLoading.serverTrace}
575
+ disabled={sentryConfigured === false}
536
576
  >
537
577
  Test Server Trace
538
578
  </SentryButton>
@@ -2,7 +2,7 @@
2
2
  "dependencies": {
3
3
  "class-variance-authority": "^0.7.1",
4
4
  "clsx": "^2.1.1",
5
- "lucide-react": "^0.544.0",
5
+ "lucide-react": "^0.561.0",
6
6
  "tailwind-merge": "^3.0.2",
7
7
  "tw-animate-css": "^1.3.6"
8
8
  }
@@ -1,6 +1,13 @@
1
1
  import { createRouter } from '@tanstack/react-router'<% if (addOnEnabled['tanstack-query']) { %>
2
2
  import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
3
3
  import * as TanstackQuery from "./integrations/tanstack-query/root-provider";
4
+ <% } %><% if (addOnEnabled['apollo-client']) { %>
5
+ import {
6
+ routerWithApolloClient,
7
+ ApolloClient,
8
+ InMemoryCache,
9
+ } from "@apollo/client-integration-tanstack-start";
10
+ import { HttpLink } from "@apollo/client";
4
11
  <% } %>
5
12
  <% if (addOnEnabled.sentry) { %>
6
13
  import * as Sentry from "@sentry/tanstackstart-react";
@@ -11,22 +18,39 @@ import { routeTree } from './routeTree.gen'
11
18
 
12
19
  // Create a new router instance
13
20
  export const getRouter = () => {
21
+ <% if (addOnEnabled['apollo-client']) { %>
22
+ // Configure Apollo Client
23
+ const apolloClient = new ApolloClient({
24
+ cache: new InMemoryCache(),
25
+ link: new HttpLink({
26
+ uri: import.meta.env.VITE_GRAPHQL_ENDPOINT || "https://countries.trevorblades.com/"
27
+ }),
28
+ });
29
+ <% } %>
14
30
  <% if (addOnEnabled['tanstack-query']) { %>
15
- const rqContext = TanstackQuery.getContext();
16
-
17
- const router = createRouter({
18
- routeTree,
19
- context: { ...rqContext },
20
- defaultPreload: "intent",
21
- })
31
+ const rqContext = TanstackQuery.getContext();
32
+ <% } %>
22
33
 
23
- setupRouterSsrQueryIntegration({router, queryClient: rqContext.queryClient})
24
- <% } else { %>
25
34
  const router = createRouter({
26
35
  routeTree,
36
+ context: {
37
+ <% if (addOnEnabled['apollo-client']) { %>
38
+ ...routerWithApolloClient.defaultContext,
39
+ <% } %>
40
+ <% if (addOnEnabled['tanstack-query']) { %>
41
+ ...rqContext,
42
+ <% } %>
43
+ },
44
+ <% if (addOnEnabled['tanstack-query'] || addOnEnabled['apollo-client']) { %>
45
+ defaultPreload: "intent",
46
+ <% } else { %>
27
47
  scrollRestoration: true,
28
48
  defaultPreloadStaleTime: 0,
49
+ <% } %>
29
50
  })
51
+
52
+ <% if (addOnEnabled['tanstack-query']) { %>
53
+ setupRouterSsrQueryIntegration({router, queryClient: rqContext.queryClient})
30
54
  <% } %>
31
55
 
32
56
  <% if (addOnEnabled.sentry) { %>
@@ -40,5 +64,5 @@ export const getRouter = () => {
40
64
  }
41
65
  <% } %>
42
66
 
43
- return router;
67
+ return <% if (addOnEnabled['apollo-client']) { %>routerWithApolloClient(router, apolloClient)<% } else { %>router<% } %>;
44
68
  }
@@ -6,7 +6,7 @@
6
6
  "dependencies": {
7
7
  "@tanstack/react-router-ssr-query": "^1.131.7",
8
8
  "@tanstack/react-start": "^1.132.0",
9
- "vite-tsconfig-paths": "^5.1.4",
10
- "lucide-react": "^0.544.0"
9
+ "vite-tsconfig-paths": "^6.0.2",
10
+ "lucide-react": "^0.561.0"
11
11
  }
12
12
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "dependencies": {
3
- "@tanstack/store": "^0.7.0",
4
- "@tanstack/react-store": "^0.7.0"
3
+ "@tanstack/store": "^0.8.0",
4
+ "@tanstack/react-store": "^0.8.0"
5
5
  },
6
6
  "devDependencies": {
7
- "@tanstack/devtools-event-client": "^0.3.2"
7
+ "@tanstack/devtools-event-client": "^0.4.0"
8
8
  }
9
9
  }
@@ -4,7 +4,7 @@
4
4
  "build-storybook": "storybook build"
5
5
  },
6
6
  "dependencies": {
7
- "@storybook/react-vite": "^9.1.9",
8
- "storybook": "^9.1.9"
7
+ "@storybook/react-vite": "^10.1.10",
8
+ "storybook": "^10.1.10"
9
9
  }
10
10
  }
@@ -0,0 +1,85 @@
1
+ import { useCallback, useRef, useState } from 'react'
2
+
3
+ /**
4
+ * Hook for recording audio and transcribing it via the transcription API.
5
+ */
6
+ export function useAudioRecorder() {
7
+ const [isRecording, setIsRecording] = useState(false)
8
+ const [isTranscribing, setIsTranscribing] = useState(false)
9
+ const mediaRecorderRef = useRef<MediaRecorder | null>(null)
10
+ const chunksRef = useRef<Blob[]>([])
11
+
12
+ const startRecording = useCallback(async () => {
13
+ try {
14
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
15
+ const mediaRecorder = new MediaRecorder(stream, {
16
+ mimeType: 'audio/webm;codecs=opus',
17
+ })
18
+ mediaRecorderRef.current = mediaRecorder
19
+ chunksRef.current = []
20
+
21
+ mediaRecorder.ondataavailable = (e) => {
22
+ if (e.data.size > 0) {
23
+ chunksRef.current.push(e.data)
24
+ }
25
+ }
26
+
27
+ mediaRecorder.start()
28
+ setIsRecording(true)
29
+ } catch (error) {
30
+ console.error('Failed to start recording:', error)
31
+ alert('Could not access microphone. Please check permissions.')
32
+ }
33
+ }, [])
34
+
35
+ const stopRecording = useCallback(async (): Promise<string | null> => {
36
+ return new Promise((resolve) => {
37
+ const mediaRecorder = mediaRecorderRef.current
38
+ if (!mediaRecorder) {
39
+ resolve(null)
40
+ return
41
+ }
42
+
43
+ mediaRecorder.onstop = async () => {
44
+ setIsRecording(false)
45
+ setIsTranscribing(true)
46
+
47
+ const audioBlob = new Blob(chunksRef.current, { type: 'audio/webm' })
48
+
49
+ // Stop all tracks
50
+ mediaRecorder.stream.getTracks().forEach((track) => track.stop())
51
+
52
+ try {
53
+ const formData = new FormData()
54
+ formData.append(
55
+ 'audio',
56
+ new File([audioBlob], 'recording.webm', { type: 'audio/webm' }),
57
+ )
58
+ formData.append('model', 'whisper-1')
59
+
60
+ const response = await fetch('/demo/api/transcription', {
61
+ method: 'POST',
62
+ body: formData,
63
+ })
64
+
65
+ if (!response.ok) {
66
+ const errorData = await response.json()
67
+ throw new Error(errorData.error || 'Transcription failed')
68
+ }
69
+
70
+ const result = await response.json()
71
+ setIsTranscribing(false)
72
+ resolve(result.text || null)
73
+ } catch (error) {
74
+ console.error('Transcription error:', error)
75
+ setIsTranscribing(false)
76
+ resolve(null)
77
+ }
78
+ }
79
+
80
+ mediaRecorder.stop()
81
+ })
82
+ }, [])
83
+
84
+ return { isRecording, isTranscribing, startRecording, stopRecording }
85
+ }