create-better-t-stack 2.38.1 → 2.39.0-canary.bae99c30
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/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-DlPots7G.js → src-CHXGSisf.js} +49 -71
- package/package.json +3 -4
- package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +22 -0
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +5 -8
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +2 -5
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +2 -5
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +2 -5
- package/templates/frontend/react/web-base/src/{index.css → index.css.hbs} +3 -0
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -261,7 +261,7 @@ declare const router: trpcServer.TRPCBuiltRouter<{
|
|
|
261
261
|
}>>;
|
|
262
262
|
declare function createBtsCli(): trpc_cli0.TrpcCli;
|
|
263
263
|
/**
|
|
264
|
-
* Initialize a new Better-T
|
|
264
|
+
* Initialize a new Better-T-Stack project
|
|
265
265
|
*
|
|
266
266
|
* @example CLI usage:
|
|
267
267
|
* ```bash
|
package/dist/index.js
CHANGED
|
@@ -107,6 +107,7 @@ const dependencyVersionMap = {
|
|
|
107
107
|
"@ai-sdk/vue": "^2.0.9",
|
|
108
108
|
"@ai-sdk/svelte": "^3.0.9",
|
|
109
109
|
"@ai-sdk/react": "^2.0.9",
|
|
110
|
+
streamdown: "^1.1.6",
|
|
110
111
|
"@orpc/server": "^1.8.4",
|
|
111
112
|
"@orpc/client": "^1.8.4",
|
|
112
113
|
"@orpc/tanstack-query": "^1.8.4",
|
|
@@ -502,49 +503,6 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
502
503
|
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") exitWithError("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
503
504
|
if (providedFlags.has("dbSetup") && options.dbSetup === "docker" && config.runtime === "workers") exitWithError("Docker setup (--db-setup docker) is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
504
505
|
}
|
|
505
|
-
function coerceBackendPresets(config) {
|
|
506
|
-
if (config.backend === "convex") {
|
|
507
|
-
config.database = "none";
|
|
508
|
-
config.orm = "none";
|
|
509
|
-
config.api = "none";
|
|
510
|
-
config.runtime = "none";
|
|
511
|
-
config.dbSetup = "none";
|
|
512
|
-
config.examples = ["todo"];
|
|
513
|
-
}
|
|
514
|
-
if (config.backend === "none") {
|
|
515
|
-
config.auth = "none";
|
|
516
|
-
config.database = "none";
|
|
517
|
-
config.orm = "none";
|
|
518
|
-
config.api = "none";
|
|
519
|
-
config.runtime = "none";
|
|
520
|
-
config.dbSetup = "none";
|
|
521
|
-
config.examples = [];
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
function incompatibleFlagsForBackend(backend, providedFlags, options) {
|
|
525
|
-
const list = [];
|
|
526
|
-
if (backend === "convex") {
|
|
527
|
-
if (providedFlags.has("auth") && options.auth && options.auth !== "none" && options.auth !== "clerk") list.push(`--auth ${options.auth}`);
|
|
528
|
-
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
529
|
-
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
530
|
-
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
531
|
-
if (providedFlags.has("runtime") && options.runtime !== "none") list.push(`--runtime ${options.runtime}`);
|
|
532
|
-
if (providedFlags.has("dbSetup") && options.dbSetup !== "none") list.push(`--db-setup ${options.dbSetup}`);
|
|
533
|
-
}
|
|
534
|
-
if (backend === "none") {
|
|
535
|
-
if (providedFlags.has("auth") && options.auth && options.auth !== "none") list.push(`--auth ${options.auth}`);
|
|
536
|
-
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
537
|
-
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
538
|
-
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
539
|
-
if (providedFlags.has("runtime") && options.runtime !== "none") list.push(`--runtime ${options.runtime}`);
|
|
540
|
-
if (providedFlags.has("dbSetup") && options.dbSetup !== "none") list.push(`--db-setup ${options.dbSetup}`);
|
|
541
|
-
if (providedFlags.has("examples") && options.examples) {
|
|
542
|
-
const hasNonNoneExamples = options.examples.some((ex) => ex !== "none");
|
|
543
|
-
if (hasNonNoneExamples) list.push("--examples");
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
return list;
|
|
547
|
-
}
|
|
548
506
|
function validateApiFrontendCompatibility(api, frontends = []) {
|
|
549
507
|
const includesNuxt = frontends.includes("nuxt");
|
|
550
508
|
const includesSvelte = frontends.includes("svelte");
|
|
@@ -1389,7 +1347,7 @@ const getLatestCLIVersion = () => {
|
|
|
1389
1347
|
*/
|
|
1390
1348
|
function isTelemetryEnabled() {
|
|
1391
1349
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1392
|
-
const BTS_TELEMETRY = "
|
|
1350
|
+
const BTS_TELEMETRY = "0";
|
|
1393
1351
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1394
1352
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1395
1353
|
return true;
|
|
@@ -1397,8 +1355,8 @@ function isTelemetryEnabled() {
|
|
|
1397
1355
|
|
|
1398
1356
|
//#endregion
|
|
1399
1357
|
//#region src/utils/analytics.ts
|
|
1400
|
-
const POSTHOG_API_KEY = "
|
|
1401
|
-
const POSTHOG_HOST = "
|
|
1358
|
+
const POSTHOG_API_KEY = "random";
|
|
1359
|
+
const POSTHOG_HOST = "random";
|
|
1402
1360
|
function generateSessionId() {
|
|
1403
1361
|
const rand = Math.random().toString(36).slice(2);
|
|
1404
1362
|
const now = Date.now().toString(36);
|
|
@@ -1751,14 +1709,9 @@ function validateBackendConstraints(config, providedFlags, options) {
|
|
|
1751
1709
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
|
|
1752
1710
|
if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.");
|
|
1753
1711
|
}
|
|
1754
|
-
if (backend === "convex"
|
|
1755
|
-
const
|
|
1756
|
-
if (
|
|
1757
|
-
if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
|
|
1758
|
-
const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
|
|
1759
|
-
if (incompatibleFrontends.length > 0) exitWithError(`The following frontends are not compatible with '--backend convex': ${incompatibleFrontends.join(", ")}. Please choose a different frontend or backend.`);
|
|
1760
|
-
}
|
|
1761
|
-
coerceBackendPresets(config);
|
|
1712
|
+
if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
|
|
1713
|
+
const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
|
|
1714
|
+
if (incompatibleFrontends.length > 0) exitWithError(`The following frontends are not compatible with '--backend convex': ${incompatibleFrontends.join(", ")}. Please choose a different frontend or backend.`);
|
|
1762
1715
|
}
|
|
1763
1716
|
}
|
|
1764
1717
|
function validateFrontendConstraints(config, providedFlags) {
|
|
@@ -1825,6 +1778,25 @@ function extractAndValidateProjectName(projectName, projectDirectory, throwOnErr
|
|
|
1825
1778
|
|
|
1826
1779
|
//#endregion
|
|
1827
1780
|
//#region src/validation.ts
|
|
1781
|
+
const CORE_STACK_FLAGS = new Set([
|
|
1782
|
+
"database",
|
|
1783
|
+
"orm",
|
|
1784
|
+
"backend",
|
|
1785
|
+
"runtime",
|
|
1786
|
+
"frontend",
|
|
1787
|
+
"addons",
|
|
1788
|
+
"examples",
|
|
1789
|
+
"auth",
|
|
1790
|
+
"dbSetup",
|
|
1791
|
+
"api",
|
|
1792
|
+
"webDeploy",
|
|
1793
|
+
"serverDeploy"
|
|
1794
|
+
]);
|
|
1795
|
+
function validateYesFlagCombination(options, providedFlags) {
|
|
1796
|
+
if (!options.yes) return;
|
|
1797
|
+
const coreStackFlagsProvided = Array.from(providedFlags).filter((flag) => CORE_STACK_FLAGS.has(flag));
|
|
1798
|
+
if (coreStackFlagsProvided.length > 0) exitWithError(`Cannot combine --yes with core stack configuration flags: ${coreStackFlagsProvided.map((f) => `--${f}`).join(", ")}. The --yes flag uses default configuration. Remove these flags or use --yes without them.`);
|
|
1799
|
+
}
|
|
1828
1800
|
function processAndValidateFlags(options, providedFlags, projectName) {
|
|
1829
1801
|
if (options.yolo) {
|
|
1830
1802
|
const cfg = processFlags(options, projectName);
|
|
@@ -1832,6 +1804,7 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
1832
1804
|
if (validatedProjectName$1) cfg.projectName = validatedProjectName$1;
|
|
1833
1805
|
return cfg;
|
|
1834
1806
|
}
|
|
1807
|
+
validateYesFlagCombination(options, providedFlags);
|
|
1835
1808
|
try {
|
|
1836
1809
|
validateArrayOptions(options);
|
|
1837
1810
|
} catch (error) {
|
|
@@ -1844,6 +1817,10 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
1844
1817
|
return config;
|
|
1845
1818
|
}
|
|
1846
1819
|
function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
1820
|
+
if (!options.yolo) {
|
|
1821
|
+
const providedFlags = getProvidedFlags(options);
|
|
1822
|
+
validateYesFlagCombination(options, providedFlags);
|
|
1823
|
+
}
|
|
1847
1824
|
const config = processFlags(options, projectName);
|
|
1848
1825
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
1849
1826
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
@@ -2369,6 +2346,10 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
2369
2346
|
if (hasReactWeb) {
|
|
2370
2347
|
const exampleWebSrc = path.join(exampleBaseDir, "web/react");
|
|
2371
2348
|
if (await fs.pathExists(exampleWebSrc)) {
|
|
2349
|
+
if (example === "ai") {
|
|
2350
|
+
const exampleWebBaseSrc = path.join(exampleWebSrc, "base");
|
|
2351
|
+
if (await fs.pathExists(exampleWebBaseSrc)) await processAndCopyFiles("**/*", exampleWebBaseSrc, webAppDir, context, false);
|
|
2352
|
+
}
|
|
2372
2353
|
const reactFramework = context.frontend.find((f) => [
|
|
2373
2354
|
"next",
|
|
2374
2355
|
"react-router",
|
|
@@ -2972,9 +2953,9 @@ async function addAddonsToProject(input) {
|
|
|
2972
2953
|
try {
|
|
2973
2954
|
const projectDir = input.projectDir || process.cwd();
|
|
2974
2955
|
const isBetterTStack = await isBetterTStackProject(projectDir);
|
|
2975
|
-
if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T
|
|
2956
|
+
if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
|
|
2976
2957
|
const detectedConfig = await detectProjectConfig(projectDir);
|
|
2977
|
-
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T
|
|
2958
|
+
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
2978
2959
|
const config = {
|
|
2979
2960
|
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
2980
2961
|
projectDir,
|
|
@@ -3742,9 +3723,9 @@ async function addDeploymentToProject(input) {
|
|
|
3742
3723
|
try {
|
|
3743
3724
|
const projectDir = input.projectDir || process.cwd();
|
|
3744
3725
|
const isBetterTStack = await isBetterTStackProject(projectDir);
|
|
3745
|
-
if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T
|
|
3726
|
+
if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
|
|
3746
3727
|
const detectedConfig = await detectProjectConfig(projectDir);
|
|
3747
|
-
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T
|
|
3728
|
+
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
3748
3729
|
if (input.webDeploy && detectedConfig.webDeploy === input.webDeploy) exitWithError(`${input.webDeploy} web deployment is already configured for this project.`);
|
|
3749
3730
|
if (input.serverDeploy && detectedConfig.serverDeploy === input.serverDeploy) exitWithError(`${input.serverDeploy} server deployment is already configured for this project.`);
|
|
3750
3731
|
const config = {
|
|
@@ -3854,7 +3835,7 @@ async function setupExamples(config) {
|
|
|
3854
3835
|
const dependencies = ["ai"];
|
|
3855
3836
|
if (hasNuxt) dependencies.push("@ai-sdk/vue");
|
|
3856
3837
|
else if (hasSvelte) dependencies.push("@ai-sdk/svelte");
|
|
3857
|
-
else if (hasReactWeb) dependencies.push("@ai-sdk/react");
|
|
3838
|
+
else if (hasReactWeb) dependencies.push("@ai-sdk/react", "streamdown");
|
|
3858
3839
|
await addPackageDependency({
|
|
3859
3840
|
dependencies,
|
|
3860
3841
|
projectDir: webClientDir
|
|
@@ -5768,7 +5749,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
5768
5749
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
5769
5750
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
5770
5751
|
output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
|
|
5771
|
-
output += `${pc.bold("Like Better-T
|
|
5752
|
+
output += `${pc.bold("Like Better-T-Stack?")} Please consider giving us a star\n on GitHub:\n`;
|
|
5772
5753
|
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
|
|
5773
5754
|
consola$1.box(output);
|
|
5774
5755
|
}
|
|
@@ -6096,7 +6077,7 @@ async function createProjectHandler(input) {
|
|
|
6096
6077
|
const startTime = Date.now();
|
|
6097
6078
|
const timeScaffolded = (/* @__PURE__ */ new Date()).toISOString();
|
|
6098
6079
|
if (input.renderTitle !== false) renderTitle();
|
|
6099
|
-
intro(pc.magenta("Creating a new Better-T
|
|
6080
|
+
intro(pc.magenta("Creating a new Better-T-Stack project"));
|
|
6100
6081
|
if (input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
|
|
6101
6082
|
let currentPathInput;
|
|
6102
6083
|
if (input.yes && input.projectName) currentPathInput = input.projectName;
|
|
@@ -6170,10 +6151,7 @@ async function createProjectHandler(input) {
|
|
|
6170
6151
|
projectDir: finalResolvedPath,
|
|
6171
6152
|
relativePath: finalPathInput
|
|
6172
6153
|
};
|
|
6173
|
-
coerceBackendPresets(config);
|
|
6174
6154
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
6175
|
-
if (config.backend === "convex") log.info(`Due to '--backend convex' flag, the following options have been automatically set: database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`);
|
|
6176
|
-
else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth none, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none");
|
|
6177
6155
|
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
6178
6156
|
log.message(displayConfig(config));
|
|
6179
6157
|
log.message("");
|
|
@@ -6246,7 +6224,7 @@ async function addAddonsHandler(input) {
|
|
|
6246
6224
|
try {
|
|
6247
6225
|
const projectDir = input.projectDir || process.cwd();
|
|
6248
6226
|
const detectedConfig = await detectProjectConfig(projectDir);
|
|
6249
|
-
if (!detectedConfig) exitWithError("Could not detect project configuration. Please ensure this is a valid Better-T
|
|
6227
|
+
if (!detectedConfig) exitWithError("Could not detect project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
6250
6228
|
if (!input.addons || input.addons.length === 0) {
|
|
6251
6229
|
const addonsPrompt = await getAddonsToAdd(detectedConfig.frontend || [], detectedConfig.addons || []);
|
|
6252
6230
|
if (addonsPrompt.length > 0) input.addons = addonsPrompt;
|
|
@@ -6370,7 +6348,7 @@ function displaySponsors(sponsors$1) {
|
|
|
6370
6348
|
const t = trpcServer.initTRPC.create();
|
|
6371
6349
|
const router = t.router({
|
|
6372
6350
|
init: t.procedure.meta({
|
|
6373
|
-
description: "Create a new Better-T
|
|
6351
|
+
description: "Create a new Better-T-Stack project",
|
|
6374
6352
|
default: true,
|
|
6375
6353
|
negateBooleans: true
|
|
6376
6354
|
}).input(z.tuple([ProjectNameSchema.optional(), z.object({
|
|
@@ -6404,7 +6382,7 @@ const router = t.router({
|
|
|
6404
6382
|
const result = await createProjectHandler(combinedInput);
|
|
6405
6383
|
if (options.verbose) return result;
|
|
6406
6384
|
}),
|
|
6407
|
-
add: t.procedure.meta({ description: "Add addons or deployment configurations to an existing Better-T
|
|
6385
|
+
add: t.procedure.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z.tuple([z.object({
|
|
6408
6386
|
addons: z.array(AddonsSchema).optional().default([]),
|
|
6409
6387
|
webDeploy: WebDeploySchema.optional(),
|
|
6410
6388
|
serverDeploy: ServerDeploySchema.optional(),
|
|
@@ -6415,17 +6393,17 @@ const router = t.router({
|
|
|
6415
6393
|
const [options] = input;
|
|
6416
6394
|
await addAddonsHandler(options);
|
|
6417
6395
|
}),
|
|
6418
|
-
sponsors: t.procedure.meta({ description: "Show Better-T
|
|
6396
|
+
sponsors: t.procedure.meta({ description: "Show Better-T-Stack sponsors" }).mutation(async () => {
|
|
6419
6397
|
try {
|
|
6420
6398
|
renderTitle();
|
|
6421
|
-
intro(pc.magenta("Better-T
|
|
6399
|
+
intro(pc.magenta("Better-T-Stack Sponsors"));
|
|
6422
6400
|
const sponsors$1 = await fetchSponsors();
|
|
6423
6401
|
displaySponsors(sponsors$1);
|
|
6424
6402
|
} catch (error) {
|
|
6425
6403
|
handleError(error, "Failed to display sponsors");
|
|
6426
6404
|
}
|
|
6427
6405
|
}),
|
|
6428
|
-
docs: t.procedure.meta({ description: "Open Better-T
|
|
6406
|
+
docs: t.procedure.meta({ description: "Open Better-T-Stack documentation" }).mutation(async () => {
|
|
6429
6407
|
const DOCS_URL = "https://better-t-stack.dev/docs";
|
|
6430
6408
|
try {
|
|
6431
6409
|
await openUrl(DOCS_URL);
|
|
@@ -6453,7 +6431,7 @@ function createBtsCli() {
|
|
|
6453
6431
|
});
|
|
6454
6432
|
}
|
|
6455
6433
|
/**
|
|
6456
|
-
* Initialize a new Better-T
|
|
6434
|
+
* Initialize a new Better-T-Stack project
|
|
6457
6435
|
*
|
|
6458
6436
|
* @example CLI usage:
|
|
6459
6437
|
* ```bash
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.39.0-canary.bae99c30",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Aman Varshney",
|
|
8
8
|
"bin": {
|
|
9
|
-
"create-better-t-stack": "
|
|
9
|
+
"create-better-t-stack": "./src/cli.ts"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"templates",
|
|
@@ -59,8 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"exports": {
|
|
61
61
|
".": {
|
|
62
|
-
"
|
|
63
|
-
"import": "./dist/index.js"
|
|
62
|
+
"import": "./src/index.ts"
|
|
64
63
|
}
|
|
65
64
|
},
|
|
66
65
|
"dependencies": {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { type ComponentProps, memo } from "react";
|
|
4
|
+
import { Streamdown } from "streamdown";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
type ResponseProps = ComponentProps<typeof Streamdown>;
|
|
8
|
+
|
|
9
|
+
export const Response = memo(
|
|
10
|
+
({ className, ...props }: ResponseProps) => (
|
|
11
|
+
<Streamdown
|
|
12
|
+
className={cn(
|
|
13
|
+
"size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
),
|
|
19
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
Response.displayName = "Response";
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { useChat } from "@ai-sdk/react";
|
|
4
4
|
import { DefaultChatTransport } from "ai";
|
|
5
|
-
import { Input } from "@/components/ui/input";
|
|
6
|
-
import { Button } from "@/components/ui/button";
|
|
7
5
|
import { Send } from "lucide-react";
|
|
8
|
-
import {
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Response } from "@/components/response";
|
|
8
|
+
import { Button } from "@/components/ui/button";
|
|
9
|
+
import { Input } from "@/components/ui/input";
|
|
9
10
|
|
|
10
11
|
export default function AIPage() {
|
|
11
12
|
const [input, setInput] = useState("");
|
|
@@ -51,11 +52,7 @@ export default function AIPage() {
|
|
|
51
52
|
</p>
|
|
52
53
|
{message.parts?.map((part, index) => {
|
|
53
54
|
if (part.type === "text") {
|
|
54
|
-
return
|
|
55
|
-
<div key={index} className="whitespace-pre-wrap">
|
|
56
|
-
{part.text}
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
55
|
+
return <Response key={index}>{part.text}</Response>;
|
|
59
56
|
}
|
|
60
57
|
return null;
|
|
61
58
|
})}
|
|
@@ -4,6 +4,7 @@ import { DefaultChatTransport } from "ai";
|
|
|
4
4
|
import { Input } from "@/components/ui/input";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { Send } from "lucide-react";
|
|
7
|
+
import { Response } from "@/components/response";
|
|
7
8
|
|
|
8
9
|
const AI: React.FC = () => {
|
|
9
10
|
const [input, setInput] = useState("");
|
|
@@ -49,11 +50,7 @@ const AI: React.FC = () => {
|
|
|
49
50
|
</p>
|
|
50
51
|
{message.parts?.map((part, index) => {
|
|
51
52
|
if (part.type === "text") {
|
|
52
|
-
return
|
|
53
|
-
<div key={index} className="whitespace-pre-wrap">
|
|
54
|
-
{part.text}
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
53
|
+
return <Response key={index}>{part.text}</Response>;
|
|
57
54
|
}
|
|
58
55
|
return null;
|
|
59
56
|
})}
|
|
@@ -5,6 +5,7 @@ import { Input } from "@/components/ui/input";
|
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { Send } from "lucide-react";
|
|
7
7
|
import { useRef, useEffect, useState } from "react";
|
|
8
|
+
import { Response } from "@/components/response";
|
|
8
9
|
|
|
9
10
|
export const Route = createFileRoute("/ai")({
|
|
10
11
|
component: RouteComponent,
|
|
@@ -54,11 +55,7 @@ function RouteComponent() {
|
|
|
54
55
|
</p>
|
|
55
56
|
{message.parts?.map((part, index) => {
|
|
56
57
|
if (part.type === "text") {
|
|
57
|
-
return
|
|
58
|
-
<div key={index} className="whitespace-pre-wrap">
|
|
59
|
-
{part.text}
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
58
|
+
return <Response key={index}>{part.text}</Response>;
|
|
62
59
|
}
|
|
63
60
|
return null;
|
|
64
61
|
})}
|
|
@@ -5,6 +5,7 @@ import { Input } from "@/components/ui/input";
|
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { Send } from "lucide-react";
|
|
7
7
|
import { useRef, useEffect, useState } from "react";
|
|
8
|
+
import { Response } from "@/components/response";
|
|
8
9
|
|
|
9
10
|
export const Route = createFileRoute("/ai")({
|
|
10
11
|
component: RouteComponent,
|
|
@@ -54,11 +55,7 @@ function RouteComponent() {
|
|
|
54
55
|
</p>
|
|
55
56
|
{message.parts?.map((part, index) => {
|
|
56
57
|
if (part.type === "text") {
|
|
57
|
-
return
|
|
58
|
-
<div key={index} className="whitespace-pre-wrap">
|
|
59
|
-
{part.text}
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
58
|
+
return <Response key={index}>{part.text}</Response>;
|
|
62
59
|
}
|
|
63
60
|
return null;
|
|
64
61
|
})}
|