Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@
"!**/examples/**",
"!**/packages/ui/**",
"!**/docs/**",
"!**/test-results/**"
"!**/test-results/**",
"!**/.btst-stack-src/**",
"!**/.btst-stack-ui/**"
]
}
}
82 changes: 80 additions & 2 deletions demos/ai-chat/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
export default function Home() {
return null;
import Link from "next/link";
import { getOrCreateQueryClient } from "@/lib/query-client";
import { getStackClient } from "@/lib/stack-client";
import { generateSchema } from "@btst/stack/plugins/route-docs/client";

type RouteItem = { label: string; path: string };
type RouteGroup = { heading: string; routes: RouteItem[] };

const SITE_BASE_PATH = "/pages";

function routeKeyToLabel(key: string): string {
return key
.replace(/([A-Z])/g, " $1")
.replace(/^./, (s) => s.toUpperCase())
.trim();
}

export default async function Home() {
const queryClient = getOrCreateQueryClient();
getStackClient(queryClient);
const schema = await generateSchema();

const aiChatPlugin = schema.plugins.find((p) => p.key === "aiChat");
const staticChatRoutes: RouteItem[] =
aiChatPlugin?.routes
.filter((r) => r.pathParams.length === 0)
.map((r) => ({
label: routeKeyToLabel(r.key),
path: `${SITE_BASE_PATH}${r.path}`,
})) ?? [];

const groups: RouteGroup[] = [
{ heading: "Chat", routes: staticChatRoutes },
{
heading: "Docs",
routes: [
{ label: "Route Docs", path: `${SITE_BASE_PATH}/route-docs` },
{ label: "API Reference", path: "/api/data/reference" },
],
},
].filter((g) => g.routes.length > 0);

return (
<main className="min-h-screen flex items-center justify-center bg-background p-8">
<div className="w-full max-w-lg space-y-8">
<div className="space-y-1">
<h1 className="text-2xl font-semibold tracking-tight">
BTST AI Chat Demo
</h1>
<p className="text-sm text-muted-foreground">
Available routes in this demo
</p>
</div>
<div className="space-y-6">
{groups.map((group) => (
<div key={group.heading} className="space-y-1">
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider px-1 pb-1">
{group.heading}
</p>
<ul className="space-y-1">
{group.routes.map(({ label, path }) => (
<li key={path}>
<Link
href={path}
className="flex items-center justify-between rounded-lg border px-4 py-3 text-sm hover:bg-accent hover:text-accent-foreground transition-colors group"
>
<div className="font-medium truncate mr-4">{label}</div>
<code className="text-xs text-muted-foreground font-mono shrink-0 group-hover:text-accent-foreground/70">
{path}
</code>
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</main>
);
}
22 changes: 15 additions & 7 deletions demos/ai-chat/copy-stack-src.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ const uiSrc = "node_modules/@btst/stack/dist/packages/ui";
const uiDest = "app/.btst-stack-ui";

if (!existsSync(src)) {
// Likely running in the monorepo where the workspace symlink does not expose
// src/ — fall back gracefully; @source paths in globals.css cover this case.
console.log(
"[copy-stack-src] node_modules/@btst/stack/src not found, skipping",
);
Expand All @@ -29,17 +27,27 @@ if (!existsSync(src)) {
await rm(dest, { recursive: true, force: true });
await mkdir(dest, { recursive: true });
await cp(src, dest, { recursive: true });
console.log("[copy-stack-src] copied @btst/stack/src → .btst-stack-src");
console.log(`[copy-stack-src] copied ${src}${dest}`);

if (existsSync(uiSrc)) {
await rm(uiDest, { recursive: true, force: true });
await mkdir(uiDest, { recursive: true });
await cp(uiSrc, uiDest, { recursive: true });
console.log(
"[copy-stack-src] copied @btst/stack/dist/packages/ui → .btst-stack-ui",
);
console.log(`[copy-stack-src] copied ${uiSrc} → ${uiDest}`);
} else {
console.log(`[copy-stack-src] ${uiSrc} not found, skipping`);
}

// When running inside the monorepo, the workspace-built dist/plugins/ has
// @workspace/ui imports already inlined by postbuild.cjs. Copy those files
// over the npm-installed ones so the demo uses the correct, self-contained CSS.
// Outside the monorepo (StackBlitz/WebContainers), this path won't exist and
// the step is silently skipped — the published npm package handles it instead.
const workspacePluginsDist = "../../packages/stack/dist/plugins";
const npmPluginsDist = "node_modules/@btst/stack/dist/plugins";
if (existsSync(workspacePluginsDist)) {
await cp(workspacePluginsDist, npmPluginsDist, { recursive: true });
console.log(
"[copy-stack-src] node_modules/@btst/stack/dist/packages/ui not found, skipping",
`[copy-stack-src] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`,
);
}
1 change: 0 additions & 1 deletion demos/ai-chat/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
3 changes: 0 additions & 3 deletions demos/ai-chat/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ const nextConfig: NextConfig = {
env: {
NEXT_PUBLIC_HAS_OPENAI_KEY: process.env.OPENAI_API_KEY ? "1" : "",
},
async redirects() {
return [{ source: "/", destination: "/pages/chat", permanent: false }];
},
};

export default nextConfig;
2 changes: 1 addition & 1 deletion demos/ai-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@ai-sdk/openai": "^3.0.41",
"@ai-sdk/react": "^3.0.118",
"@btst/adapter-memory": "^2.0.3",
"@btst/stack": "^2.5.2",
"@btst/stack": "^2.5.3",
"@btst/yar": "^1.2.0",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-dialog": "^1.1.15",
Expand Down
2 changes: 1 addition & 1 deletion demos/ai-chat/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"jsx": "preserve",
"incremental": true,
"plugins": [
{
Expand Down
101 changes: 99 additions & 2 deletions demos/blog/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,100 @@
export default function Home() {
return null;
import Link from "next/link";
import { getOrCreateQueryClient } from "@/lib/query-client";
import { getStackClient } from "@/lib/stack-client";
import { generateSchema } from "@btst/stack/plugins/route-docs/client";
import { myStack } from "@/lib/stack";

type RouteItem = { label: string; path: string };
type RouteGroup = { heading: string; routes: RouteItem[] };

const SITE_BASE_PATH = "/pages";

function routeKeyToLabel(key: string): string {
return key
.replace(/([A-Z])/g, " $1")
.replace(/^./, (s) => s.toUpperCase())
.trim();
}

export default async function Home() {
const queryClient = getOrCreateQueryClient();
getStackClient(queryClient);
const schema = await generateSchema();

const blogPlugin = schema.plugins.find((p) => p.key === "blog");
const staticBlogRoutes: RouteItem[] =
blogPlugin?.routes
.filter((r) => r.pathParams.length === 0)
.map((r) => ({
label: routeKeyToLabel(r.key),
path: `${SITE_BASE_PATH}${r.path}`,
})) ?? [];

const { items: posts } = await myStack.api.blog.getAllPosts({
published: true,
});

const groups: RouteGroup[] = [
{ heading: "Blog", routes: staticBlogRoutes },
{
heading: "Posts (seeded)",
routes: posts.map((p) => ({
label: p.title,
path: `${SITE_BASE_PATH}/blog/${p.slug}`,
})),
},
{
heading: "Edit Posts (seeded)",
routes: posts.map((p) => ({
label: p.title,
path: `${SITE_BASE_PATH}/blog/${p.slug}/edit`,
})),
},
{
heading: "Docs",
routes: [
{ label: "Route Docs", path: `${SITE_BASE_PATH}/route-docs` },
{ label: "API Reference", path: "/api/data/reference" },
],
},
].filter((g) => g.routes.length > 0);

return (
<main className="min-h-screen flex items-center justify-center bg-background p-8">
<div className="w-full max-w-lg space-y-8">
<div className="space-y-1">
<h1 className="text-2xl font-semibold tracking-tight">
BTST Blog Demo
</h1>
<p className="text-sm text-muted-foreground">
Available routes in this demo
</p>
</div>
<div className="space-y-6">
{groups.map((group) => (
<div key={group.heading} className="space-y-1">
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider px-1 pb-1">
{group.heading}
</p>
<ul className="space-y-1">
{group.routes.map(({ label, path }) => (
<li key={path}>
<Link
href={path}
className="flex items-center justify-between rounded-lg border px-4 py-3 text-sm hover:bg-accent hover:text-accent-foreground transition-colors group"
>
<div className="font-medium truncate mr-4">{label}</div>
<code className="text-xs text-muted-foreground font-mono shrink-0 group-hover:text-accent-foreground/70">
{path}
</code>
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</main>
);
}
22 changes: 15 additions & 7 deletions demos/blog/copy-stack-src.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ const uiSrc = "node_modules/@btst/stack/dist/packages/ui";
const uiDest = "app/.btst-stack-ui";

if (!existsSync(src)) {
// Likely running in the monorepo where the workspace symlink does not expose
// src/ — fall back gracefully; @source paths in globals.css cover this case.
console.log(
"[copy-stack-src] node_modules/@btst/stack/src not found, skipping",
);
Expand All @@ -29,17 +27,27 @@ if (!existsSync(src)) {
await rm(dest, { recursive: true, force: true });
await mkdir(dest, { recursive: true });
await cp(src, dest, { recursive: true });
console.log("[copy-stack-src] copied @btst/stack/src → .btst-stack-src");
console.log(`[copy-stack-src] copied ${src}${dest}`);

if (existsSync(uiSrc)) {
await rm(uiDest, { recursive: true, force: true });
await mkdir(uiDest, { recursive: true });
await cp(uiSrc, uiDest, { recursive: true });
console.log(
"[copy-stack-src] copied @btst/stack/dist/packages/ui → .btst-stack-ui",
);
console.log(`[copy-stack-src] copied ${uiSrc} → ${uiDest}`);
} else {
console.log(`[copy-stack-src] ${uiSrc} not found, skipping`);
}

// When running inside the monorepo, the workspace-built dist/plugins/ has
// @workspace/ui imports already inlined by postbuild.cjs. Copy those files
// over the npm-installed ones so the demo uses the correct, self-contained CSS.
// Outside the monorepo (StackBlitz/WebContainers), this path won't exist and
// the step is silently skipped — the published npm package handles it instead.
const workspacePluginsDist = "../../packages/stack/dist/plugins";
const npmPluginsDist = "node_modules/@btst/stack/dist/plugins";
if (existsSync(workspacePluginsDist)) {
await cp(workspacePluginsDist, npmPluginsDist, { recursive: true });
console.log(
"[copy-stack-src] node_modules/@btst/stack/dist/packages/ui not found, skipping",
`[copy-stack-src] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`,
);
}
6 changes: 1 addition & 5 deletions demos/blog/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
async redirects() {
return [{ source: "/", destination: "/pages/blog", permanent: false }];
},
};
const nextConfig: NextConfig = {};

export default nextConfig;
2 changes: 1 addition & 1 deletion demos/blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"dependencies": {
"@ai-sdk/react": "^3.0.118",
"@btst/adapter-memory": "^2.0.3",
"@btst/stack": "^2.5.2",
"@btst/stack": "^2.5.3",
"@btst/yar": "^1.2.0",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-dialog": "^1.1.15",
Expand Down
Loading