@tanstack/cta-framework-react-cra 0.44.3 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{examples/tanchat/assets/src/components/example-AIAssistant.tsx → add-ons/ai/assets/src/components/demo-AIAssistant.tsx} +6 -8
- package/{examples/tanchat/assets/src/components/example-GuitarRecommendation.tsx → add-ons/ai/assets/src/components/demo-GuitarRecommendation.tsx} +2 -2
- package/{examples/tanchat/assets/src/lib/example.ai-hook.ts → add-ons/ai/assets/src/lib/demo-ai-hook.ts} +9 -9
- package/{examples/tanchat/assets/src/lib/example.guitar-tools.ts → add-ons/ai/assets/src/lib/demo-guitar-tools.ts} +1 -1
- package/{examples/tanchat/assets/src/routes/demo/tanchat.tsx → add-ons/ai/assets/src/routes/demo/ai-chat.tsx} +30 -150
- package/{examples/tanchat/assets/src/routes/demo/image.tsx → add-ons/ai/assets/src/routes/demo/ai-image.tsx} +2 -50
- package/add-ons/ai/assets/src/routes/demo/ai-structured.tsx +310 -0
- package/{examples/tanchat/assets/src/routes/demo/api.tanchat.ts → add-ons/ai/assets/src/routes/demo/api.ai.chat.ts} +16 -6
- package/{examples/tanchat/assets/src/routes/demo/api.image.ts → add-ons/ai/assets/src/routes/demo/api.ai.image.ts} +3 -5
- package/add-ons/ai/assets/src/routes/demo/api.ai.structured.ts +136 -0
- package/add-ons/ai/assets/src/routes/demo/api.ai.transcription.ts +89 -0
- package/{examples/tanchat/assets/src/routes/demo/api.tts.ts → add-ons/ai/assets/src/routes/demo/api.ai.tts.ts} +1 -1
- package/{examples/tanchat/assets/src/routes/example.guitars → add-ons/ai/assets/src/routes/demo/guitars}/$guitarId.tsx +3 -2
- package/{examples/tanchat/assets/src/routes/example.guitars → add-ons/ai/assets/src/routes/demo/guitars}/index.tsx +3 -2
- package/add-ons/ai/info.json +46 -0
- package/{examples/tanchat → add-ons/ai}/package.json +1 -1
- package/examples/events/README.md +110 -0
- package/examples/events/assets/content/speakers/andre-costa.md +22 -0
- package/examples/events/assets/content/speakers/hans-mueller.md +22 -0
- package/examples/events/assets/content/speakers/isabella-martinez.md +22 -0
- package/examples/events/assets/content/speakers/kenji-nakamura.md +22 -0
- package/examples/events/assets/content/speakers/marie-dubois.md +20 -0
- package/examples/events/assets/content/speakers/priya-sharma.md +22 -0
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +39 -0
- package/examples/events/assets/content/talks/french-macaron-mastery.md +39 -0
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md +39 -0
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md +39 -0
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md +36 -0
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md +32 -0
- package/examples/events/assets/content/talks/the-science-of-sugar.md +39 -0
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md +39 -0
- package/examples/events/assets/content-collections.ts +56 -0
- package/examples/events/assets/public/background-1.jpg +0 -0
- package/examples/events/assets/public/background-2.jpg +0 -0
- package/examples/events/assets/public/background-3.jpg +0 -0
- package/examples/events/assets/public/background-4.jpg +0 -0
- package/examples/events/assets/public/conference-logo.png +0 -0
- package/examples/events/assets/public/favicon.ico +0 -0
- package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
- package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
- package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
- package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
- package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
- package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
- package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
- package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
- package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
- package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
- package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
- package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
- package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
- package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
- package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
- package/examples/events/assets/public/tanstack-word-logo-white.svg +1 -0
- package/examples/events/assets/src/components/HeroCarousel.tsx +61 -0
- package/examples/events/assets/src/components/RemyAssistant.tsx +207 -0
- package/examples/events/assets/src/components/RemyButton.tsx +18 -0
- package/examples/events/assets/src/components/SpeakerCard.tsx +67 -0
- package/examples/events/assets/src/components/TalkCard.tsx +77 -0
- package/examples/events/assets/src/components/ui/card.tsx +92 -0
- package/examples/events/assets/src/lib/conference-ai-hook.ts +26 -0
- package/examples/events/assets/src/lib/conference-tools.ts +210 -0
- package/examples/events/assets/src/lib/utils.ts +6 -0
- package/examples/events/assets/src/routes/api.remy-chat.ts +121 -0
- package/examples/events/assets/src/routes/index.tsx +192 -0
- package/examples/events/assets/src/routes/schedule.index.tsx +274 -0
- package/examples/events/assets/src/routes/speakers.$slug.tsx +122 -0
- package/examples/events/assets/src/routes/speakers.index.tsx +40 -0
- package/examples/events/assets/src/routes/talks.$slug.tsx +116 -0
- package/examples/events/assets/src/routes/talks.index.tsx +40 -0
- package/examples/events/assets/src/styles.css +194 -0
- package/examples/events/info.json +63 -0
- package/examples/events/package.json +23 -0
- package/examples/resume/README.md +82 -0
- package/examples/resume/assets/content/education/code-school.md +17 -0
- package/examples/resume/assets/content/jobs/freelance.md +13 -0
- package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
- package/examples/resume/assets/content/jobs/initech-lead.md +29 -0
- package/examples/resume/assets/content/jobs/initrode-senior.md +28 -0
- package/examples/resume/assets/content-collections.ts +36 -0
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
- package/examples/resume/assets/src/components/ResumeAssistantButton.tsx +20 -0
- package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
- package/examples/resume/assets/src/components/ui/card.tsx +92 -0
- package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
- package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
- package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
- package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
- package/examples/resume/assets/src/lib/utils.ts +6 -0
- package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
- package/examples/resume/assets/src/routes/index.tsx +220 -0
- package/examples/resume/assets/src/styles.css +138 -0
- package/examples/resume/info.json +30 -0
- package/examples/resume/package.json +27 -0
- package/package.json +2 -2
- package/examples/tanchat/assets/src/lib/model-selection.ts +0 -78
- package/examples/tanchat/assets/src/lib/vendor-capabilities.ts +0 -55
- package/examples/tanchat/assets/src/routes/demo/api.available-providers.ts +0 -35
- package/examples/tanchat/assets/src/routes/demo/api.structured.ts +0 -168
- package/examples/tanchat/assets/src/routes/demo/api.transcription.ts +0 -89
- package/examples/tanchat/assets/src/routes/demo/structured.tsx +0 -460
- package/examples/tanchat/info.json +0 -46
- /package/{examples/tanchat → add-ons/ai}/README.md +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/_dot_env.local.append +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-flowers.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-motherboard.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-racing.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-steamer-trunk.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-superhero.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-traveling.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-guitar-video-games.jpg +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/public/example-ukelele-tanstack.jpg +0 -0
- /package/{examples/tanchat/assets/src/data/example-guitars.ts → add-ons/ai/assets/src/data/demo-guitars.ts} +0 -0
- /package/{examples/tanchat/assets/src/hooks/useAudioRecorder.ts → add-ons/ai/assets/src/hooks/demo-useAudioRecorder.ts} +0 -0
- /package/{examples/tanchat/assets/src/hooks/useTTS.ts → add-ons/ai/assets/src/hooks/demo-useTTS.ts} +0 -0
- /package/{examples/tanchat → add-ons/ai}/assets/src/lib/ai-devtools.tsx +0 -0
- /package/{examples/tanchat/assets/src/routes/demo/tanchat.css → add-ons/ai/assets/src/routes/demo/ai-chat.css} +0 -0
- /package/{examples/tanchat → add-ons/ai}/small-logo.svg +0 -0
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
-
import type { Provider } from '@/lib/model-selection'
|
|
3
|
-
|
|
4
|
-
export const Route = createFileRoute('/demo/api/available-providers')({
|
|
5
|
-
server: {
|
|
6
|
-
handlers: {
|
|
7
|
-
GET: async () => {
|
|
8
|
-
const available: Provider[] = []
|
|
9
|
-
|
|
10
|
-
if (process.env.OPENAI_API_KEY) {
|
|
11
|
-
available.push('openai')
|
|
12
|
-
}
|
|
13
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
14
|
-
available.push('anthropic')
|
|
15
|
-
}
|
|
16
|
-
if (process.env.GEMINI_API_KEY) {
|
|
17
|
-
available.push('gemini')
|
|
18
|
-
}
|
|
19
|
-
// Ollama is always available (local, no key needed)
|
|
20
|
-
available.push('ollama')
|
|
21
|
-
|
|
22
|
-
return new Response(
|
|
23
|
-
JSON.stringify({
|
|
24
|
-
providers: available,
|
|
25
|
-
hasOpenAI: available.includes('openai'),
|
|
26
|
-
}),
|
|
27
|
-
{
|
|
28
|
-
status: 200,
|
|
29
|
-
headers: { 'Content-Type': 'application/json' },
|
|
30
|
-
},
|
|
31
|
-
)
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
})
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
-
import { chat, createChatOptions } from "@tanstack/ai";
|
|
3
|
-
import { anthropicText } from "@tanstack/ai-anthropic";
|
|
4
|
-
import { geminiText } from "@tanstack/ai-gemini";
|
|
5
|
-
import { openaiText } from "@tanstack/ai-openai";
|
|
6
|
-
import { ollamaText } from "@tanstack/ai-ollama";
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
|
|
9
|
-
import type { Provider } from "@/lib/model-selection";
|
|
10
|
-
|
|
11
|
-
// Schema for structured recipe output
|
|
12
|
-
const RecipeSchema = z.object({
|
|
13
|
-
name: z.string().describe("The name of the recipe"),
|
|
14
|
-
description: z.string().describe("A brief description of the dish"),
|
|
15
|
-
prepTime: z.string().describe('Preparation time (e.g., "15 minutes")'),
|
|
16
|
-
cookTime: z.string().describe('Cooking time (e.g., "30 minutes")'),
|
|
17
|
-
servings: z.number().describe("Number of servings"),
|
|
18
|
-
difficulty: z.enum(["easy", "medium", "hard"]).describe("Difficulty level"),
|
|
19
|
-
ingredients: z
|
|
20
|
-
.array(
|
|
21
|
-
z.object({
|
|
22
|
-
item: z.string().describe("Ingredient name"),
|
|
23
|
-
amount: z.string().describe('Amount needed (e.g., "2 cups")'),
|
|
24
|
-
notes: z.string().optional().describe("Optional preparation notes"),
|
|
25
|
-
})
|
|
26
|
-
)
|
|
27
|
-
.describe("List of ingredients"),
|
|
28
|
-
instructions: z
|
|
29
|
-
.array(z.string())
|
|
30
|
-
.describe("Step-by-step cooking instructions"),
|
|
31
|
-
tips: z.array(z.string()).optional().describe("Optional cooking tips"),
|
|
32
|
-
nutritionPerServing: z
|
|
33
|
-
.object({
|
|
34
|
-
calories: z.number().optional(),
|
|
35
|
-
protein: z.string().optional(),
|
|
36
|
-
carbs: z.string().optional(),
|
|
37
|
-
fat: z.string().optional(),
|
|
38
|
-
})
|
|
39
|
-
.optional()
|
|
40
|
-
.describe("Nutritional information per serving"),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export type Recipe = z.infer<typeof RecipeSchema>;
|
|
44
|
-
|
|
45
|
-
export const Route = createFileRoute("/demo/api/structured")({
|
|
46
|
-
server: {
|
|
47
|
-
handlers: {
|
|
48
|
-
POST: async ({ request }) => {
|
|
49
|
-
const body = await request.json();
|
|
50
|
-
const { recipeName, mode = "structured" } = body;
|
|
51
|
-
const data = body.data || {};
|
|
52
|
-
const provider: Provider = data.provider || body.provider || "openai";
|
|
53
|
-
const model: string = data.model || body.model || "gpt-4o";
|
|
54
|
-
|
|
55
|
-
if (!recipeName || recipeName.trim().length === 0) {
|
|
56
|
-
return new Response(
|
|
57
|
-
JSON.stringify({
|
|
58
|
-
error: "Recipe name is required",
|
|
59
|
-
}),
|
|
60
|
-
{
|
|
61
|
-
status: 400,
|
|
62
|
-
headers: { "Content-Type": "application/json" },
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
// Pre-define typed adapter configurations
|
|
69
|
-
const adapterConfig = {
|
|
70
|
-
anthropic: () =>
|
|
71
|
-
createChatOptions({
|
|
72
|
-
adapter: anthropicText(
|
|
73
|
-
(model || "claude-sonnet-4-5-20250929") as any
|
|
74
|
-
),
|
|
75
|
-
}),
|
|
76
|
-
gemini: () =>
|
|
77
|
-
createChatOptions({
|
|
78
|
-
adapter: geminiText((model || "gemini-2.0-flash-exp") as any),
|
|
79
|
-
}),
|
|
80
|
-
ollama: () =>
|
|
81
|
-
createChatOptions({
|
|
82
|
-
adapter: ollamaText((model || "mistral:7b") as any),
|
|
83
|
-
}),
|
|
84
|
-
openai: () =>
|
|
85
|
-
createChatOptions({
|
|
86
|
-
adapter: openaiText((model || "gpt-4o") as any),
|
|
87
|
-
}),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const options = adapterConfig[provider]();
|
|
91
|
-
|
|
92
|
-
if (mode === "structured") {
|
|
93
|
-
// Structured output mode - returns validated object
|
|
94
|
-
const result = await chat({
|
|
95
|
-
...options,
|
|
96
|
-
messages: [
|
|
97
|
-
{
|
|
98
|
-
role: "user",
|
|
99
|
-
content: `Generate a complete recipe for: ${recipeName}. Include all ingredients with amounts, step-by-step instructions, prep/cook times, and difficulty level.`,
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
outputSchema: RecipeSchema,
|
|
103
|
-
} as any);
|
|
104
|
-
|
|
105
|
-
return new Response(
|
|
106
|
-
JSON.stringify({
|
|
107
|
-
mode: "structured",
|
|
108
|
-
recipe: result,
|
|
109
|
-
provider,
|
|
110
|
-
model,
|
|
111
|
-
}),
|
|
112
|
-
{
|
|
113
|
-
status: 200,
|
|
114
|
-
headers: { "Content-Type": "application/json" },
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
} else {
|
|
118
|
-
// One-shot markdown mode - returns text
|
|
119
|
-
const markdown = await chat({
|
|
120
|
-
...options,
|
|
121
|
-
stream: false,
|
|
122
|
-
messages: [
|
|
123
|
-
{
|
|
124
|
-
role: "user",
|
|
125
|
-
content: `Generate a complete recipe for: ${recipeName}.
|
|
126
|
-
|
|
127
|
-
Format the recipe in beautiful markdown with:
|
|
128
|
-
- A title with the recipe name
|
|
129
|
-
- A brief description
|
|
130
|
-
- Prep time, cook time, and servings
|
|
131
|
-
- Ingredients list with amounts
|
|
132
|
-
- Numbered step-by-step instructions
|
|
133
|
-
- Optional tips section
|
|
134
|
-
- Nutritional info if applicable
|
|
135
|
-
|
|
136
|
-
Make it detailed and easy to follow.`,
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
} as any);
|
|
140
|
-
|
|
141
|
-
return new Response(
|
|
142
|
-
JSON.stringify({
|
|
143
|
-
mode: "oneshot",
|
|
144
|
-
markdown,
|
|
145
|
-
provider,
|
|
146
|
-
model,
|
|
147
|
-
}),
|
|
148
|
-
{
|
|
149
|
-
status: 200,
|
|
150
|
-
headers: { "Content-Type": "application/json" },
|
|
151
|
-
}
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
} catch (error: any) {
|
|
155
|
-
return new Response(
|
|
156
|
-
JSON.stringify({
|
|
157
|
-
error: error.message || "An error occurred",
|
|
158
|
-
}),
|
|
159
|
-
{
|
|
160
|
-
status: 500,
|
|
161
|
-
headers: { "Content-Type": "application/json" },
|
|
162
|
-
}
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
-
import { generateTranscription } from "@tanstack/ai";
|
|
3
|
-
import { openaiTranscription } from "@tanstack/ai-openai";
|
|
4
|
-
|
|
5
|
-
export const Route = createFileRoute("/demo/api/transcription")({
|
|
6
|
-
server: {
|
|
7
|
-
handlers: {
|
|
8
|
-
POST: async ({ request }) => {
|
|
9
|
-
const formData = await request.formData();
|
|
10
|
-
const audioFile = formData.get("audio") as File | null;
|
|
11
|
-
const audioBase64 = formData.get("audioBase64") as string | null;
|
|
12
|
-
const model = (formData.get("model") as string) || "whisper-1";
|
|
13
|
-
const language = formData.get("language") as string | null;
|
|
14
|
-
const responseFormat = formData.get("responseFormat") as string | null;
|
|
15
|
-
|
|
16
|
-
if (!audioFile && !audioBase64) {
|
|
17
|
-
return new Response(
|
|
18
|
-
JSON.stringify({
|
|
19
|
-
error: "Audio file or base64 data is required",
|
|
20
|
-
}),
|
|
21
|
-
{
|
|
22
|
-
status: 400,
|
|
23
|
-
headers: { "Content-Type": "application/json" },
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!process.env.OPENAI_API_KEY) {
|
|
29
|
-
return new Response(
|
|
30
|
-
JSON.stringify({
|
|
31
|
-
error: "OPENAI_API_KEY is not configured",
|
|
32
|
-
}),
|
|
33
|
-
{
|
|
34
|
-
status: 500,
|
|
35
|
-
headers: { "Content-Type": "application/json" },
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const adapter = openaiTranscription(model as any);
|
|
42
|
-
|
|
43
|
-
// Prepare audio data
|
|
44
|
-
let audioData: string | File;
|
|
45
|
-
if (audioFile) {
|
|
46
|
-
audioData = audioFile;
|
|
47
|
-
} else if (audioBase64) {
|
|
48
|
-
audioData = audioBase64;
|
|
49
|
-
} else {
|
|
50
|
-
throw new Error("No audio data provided");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const result = await generateTranscription({
|
|
54
|
-
adapter,
|
|
55
|
-
audio: audioData,
|
|
56
|
-
language: language || undefined,
|
|
57
|
-
responseFormat: (responseFormat as any) || "verbose_json",
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return new Response(
|
|
61
|
-
JSON.stringify({
|
|
62
|
-
id: result.id,
|
|
63
|
-
model: result.model,
|
|
64
|
-
text: result.text,
|
|
65
|
-
language: result.language,
|
|
66
|
-
duration: result.duration,
|
|
67
|
-
segments: result.segments,
|
|
68
|
-
words: result.words,
|
|
69
|
-
}),
|
|
70
|
-
{
|
|
71
|
-
status: 200,
|
|
72
|
-
headers: { "Content-Type": "application/json" },
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
} catch (error: any) {
|
|
76
|
-
return new Response(
|
|
77
|
-
JSON.stringify({
|
|
78
|
-
error: error.message || "An error occurred",
|
|
79
|
-
}),
|
|
80
|
-
{
|
|
81
|
-
status: 500,
|
|
82
|
-
headers: { "Content-Type": "application/json" },
|
|
83
|
-
}
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
});
|