From 3f7066e4520f378254842755758648704d368d28 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Fri, 6 Mar 2026 18:06:34 -0500 Subject: [PATCH 1/7] feat: enhance demo pages with structured route navigation and update configuration files for improved compatibility --- demos/ai-chat/app/page.tsx | 85 +++++++++++++++- demos/ai-chat/copy-stack-src.mjs | 2 +- demos/ai-chat/next-env.d.ts | 1 - demos/ai-chat/next.config.ts | 3 - demos/ai-chat/tsconfig.json | 2 +- demos/blog/app/page.tsx | 135 +++++++++++++++++++++++++- demos/blog/copy-stack-src.mjs | 2 +- demos/blog/next.config.ts | 6 +- demos/cms/app/page.tsx | 115 +++++++++++++++++++++- demos/cms/copy-stack-src.mjs | 2 +- demos/cms/next-env.d.ts | 1 - demos/cms/next.config.ts | 6 +- demos/cms/tsconfig.json | 2 +- demos/form-builder/app/page.tsx | 105 +++++++++++++++++++- demos/form-builder/copy-stack-src.mjs | 2 +- demos/form-builder/next-env.d.ts | 1 - demos/form-builder/next.config.ts | 6 +- demos/form-builder/tsconfig.json | 2 +- demos/kanban/app/globals.css | 2 +- demos/kanban/app/page.tsx | 100 ++++++++++++++++++- demos/kanban/copy-stack-src.mjs | 2 +- demos/kanban/next-env.d.ts | 1 - demos/kanban/next.config.ts | 6 +- demos/kanban/tsconfig.json | 2 +- demos/ui-builder/app/globals.css | 2 +- demos/ui-builder/app/page.tsx | 105 +++++++++++++++++++- demos/ui-builder/copy-stack-src.mjs | 2 +- demos/ui-builder/next-env.d.ts | 1 - demos/ui-builder/next.config.ts | 8 +- demos/ui-builder/tsconfig.json | 16 ++- 30 files changed, 668 insertions(+), 57 deletions(-) diff --git a/demos/ai-chat/app/page.tsx b/demos/ai-chat/app/page.tsx index 5838f719..40b133cc 100644 --- a/demos/ai-chat/app/page.tsx +++ b/demos/ai-chat/app/page.tsx @@ -1,3 +1,86 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "Chat", + routes: [ + { + label: "Chat", + path: "/pages/chat", + description: "Start a new AI conversation", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST AI Chat Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/ai-chat/copy-stack-src.mjs b/demos/ai-chat/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/ai-chat/copy-stack-src.mjs +++ b/demos/ai-chat/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/ai-chat/next-env.d.ts b/demos/ai-chat/next-env.d.ts index 9edff1c7..1b3be084 100644 --- a/demos/ai-chat/next-env.d.ts +++ b/demos/ai-chat/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -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. diff --git a/demos/ai-chat/next.config.ts b/demos/ai-chat/next.config.ts index 6cf23600..a7f92544 100644 --- a/demos/ai-chat/next.config.ts +++ b/demos/ai-chat/next.config.ts @@ -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; diff --git a/demos/ai-chat/tsconfig.json b/demos/ai-chat/tsconfig.json index 3a13f90a..0352f50e 100644 --- a/demos/ai-chat/tsconfig.json +++ b/demos/ai-chat/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { diff --git a/demos/blog/app/page.tsx b/demos/blog/app/page.tsx index 5838f719..4c8c867e 100644 --- a/demos/blog/app/page.tsx +++ b/demos/blog/app/page.tsx @@ -1,3 +1,136 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "Blog", + routes: [ + { + label: "Blog", + path: "/pages/blog", + description: "All published posts", + }, + { + label: "Drafts", + path: "/pages/blog/drafts", + description: "Unpublished draft posts", + }, + { + label: "New Post", + path: "/pages/blog/new", + description: "Create a new blog post", + }, + ], + }, + { + heading: "Posts (seeded)", + routes: [ + { + label: "Getting Started with BTST Blog", + path: "/pages/blog/getting-started", + description: "An introduction to the BTST blog plugin", + }, + { + label: "Building Full-Stack Apps with Plugins", + path: "/pages/blog/full-stack-plugins", + description: "Plugin-first approach to full-stack development", + }, + { + label: "SEO and Meta Tags in BTST", + path: "/pages/blog/seo-and-meta-tags", + description: "Auto-generated Open Graph and Twitter card meta tags", + }, + ], + }, + { + heading: "Edit Posts (seeded)", + routes: [ + { + label: "Edit: Getting Started with BTST Blog", + path: "/pages/blog/getting-started/edit", + description: "Edit the getting started post", + }, + { + label: "Edit: Building Full-Stack Apps with Plugins", + path: "/pages/blog/full-stack-plugins/edit", + description: "Edit the full-stack plugins post", + }, + { + label: "Edit: SEO and Meta Tags in BTST", + path: "/pages/blog/seo-and-meta-tags/edit", + description: "Edit the SEO post", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST Blog Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/blog/copy-stack-src.mjs b/demos/blog/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/blog/copy-stack-src.mjs +++ b/demos/blog/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/blog/next.config.ts b/demos/blog/next.config.ts index 39272564..cb651cdc 100644 --- a/demos/blog/next.config.ts +++ b/demos/blog/next.config.ts @@ -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; diff --git a/demos/cms/app/page.tsx b/demos/cms/app/page.tsx index 5838f719..dbd3fcd0 100644 --- a/demos/cms/app/page.tsx +++ b/demos/cms/app/page.tsx @@ -1,3 +1,116 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "Articles (public)", + routes: [ + { + label: "Articles", + path: "/pages/articles", + description: "Public-facing article listing", + }, + { + label: "Welcome to BTST CMS", + path: "/pages/articles/welcome-to-btst-cms", + description: "Introduction to managing structured content", + }, + { + label: "Getting Started with Content Types", + path: "/pages/articles/getting-started-with-content-types", + description: "How to define and manage content types", + }, + ], + }, + { + heading: "CMS (admin)", + routes: [ + { + label: "CMS Dashboard", + path: "/pages/cms", + description: "Manage content types and entries", + }, + { + label: "Article List", + path: "/pages/cms/article", + description: "All article content items", + }, + { + label: "New Article", + path: "/pages/cms/article/new", + description: "Create a new article", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST CMS Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/cms/copy-stack-src.mjs b/demos/cms/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/cms/copy-stack-src.mjs +++ b/demos/cms/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/cms/next-env.d.ts b/demos/cms/next-env.d.ts index 9edff1c7..1b3be084 100644 --- a/demos/cms/next-env.d.ts +++ b/demos/cms/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -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. diff --git a/demos/cms/next.config.ts b/demos/cms/next.config.ts index b7155578..cb651cdc 100644 --- a/demos/cms/next.config.ts +++ b/demos/cms/next.config.ts @@ -1,9 +1,5 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = { - async redirects() { - return [{ source: "/", destination: "/pages/cms", permanent: false }]; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/demos/cms/tsconfig.json b/demos/cms/tsconfig.json index 3a13f90a..0352f50e 100644 --- a/demos/cms/tsconfig.json +++ b/demos/cms/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { diff --git a/demos/form-builder/app/page.tsx b/demos/form-builder/app/page.tsx index 5838f719..fe255ee9 100644 --- a/demos/form-builder/app/page.tsx +++ b/demos/form-builder/app/page.tsx @@ -1,3 +1,106 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "Forms (admin)", + routes: [ + { + label: "Forms", + path: "/pages/forms", + description: "Browse and manage all forms", + }, + { + label: "New Form", + path: "/pages/forms/new", + description: "Create a new form", + }, + ], + }, + { + heading: "Public Forms (seeded)", + routes: [ + { + label: "Contact Us", + path: "/submit/contact-us", + description: "Public contact form submission page", + }, + { + label: "Feedback Form", + path: "/submit/feedback", + description: "Public product feedback form", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST Form Builder Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/form-builder/copy-stack-src.mjs b/demos/form-builder/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/form-builder/copy-stack-src.mjs +++ b/demos/form-builder/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/form-builder/next-env.d.ts b/demos/form-builder/next-env.d.ts index 9edff1c7..1b3be084 100644 --- a/demos/form-builder/next-env.d.ts +++ b/demos/form-builder/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -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. diff --git a/demos/form-builder/next.config.ts b/demos/form-builder/next.config.ts index 46abc098..cb651cdc 100644 --- a/demos/form-builder/next.config.ts +++ b/demos/form-builder/next.config.ts @@ -1,9 +1,5 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = { - async redirects() { - return [{ source: "/", destination: "/pages/forms", permanent: false }]; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/demos/form-builder/tsconfig.json b/demos/form-builder/tsconfig.json index 3a13f90a..0352f50e 100644 --- a/demos/form-builder/tsconfig.json +++ b/demos/form-builder/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { diff --git a/demos/kanban/app/globals.css b/demos/kanban/app/globals.css index b6e5c6cd..187ff20f 100644 --- a/demos/kanban/app/globals.css +++ b/demos/kanban/app/globals.css @@ -1,6 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; -@import "@btst/stack/plugins/kanban/css"; +@import "@btst/stack/dist/plugins/kanban/client.css"; @import "@btst/stack/plugins/route-docs/css"; /* WebContainers workaround: the WASM Tailwind scanner cannot traverse node_modules (https://github.com/tailwindlabs/tailwindcss/issues/18418). copy-stack-src.mjs (run via dev/build) copies @btst/stack/src outside node_modules so Tailwind can scan it. Safe to remove once the upstream bug is fixed. */ @source "./.btst-stack-src/**/*.{ts,tsx}"; diff --git a/demos/kanban/app/page.tsx b/demos/kanban/app/page.tsx index 5838f719..e598a650 100644 --- a/demos/kanban/app/page.tsx +++ b/demos/kanban/app/page.tsx @@ -1,3 +1,101 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "Kanban", + routes: [ + { + label: "Boards", + path: "/pages/kanban", + description: "All kanban boards", + }, + { + label: "New Board", + path: "/pages/kanban/new", + description: "Create a new board", + }, + ], + }, + { + heading: "Boards (seeded)", + routes: [ + { + label: "BTST Demo Board", + path: "/pages/kanban/demo-board", + description: "Pre-seeded demo board with sample tasks", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST Kanban Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/kanban/copy-stack-src.mjs b/demos/kanban/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/kanban/copy-stack-src.mjs +++ b/demos/kanban/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/kanban/next-env.d.ts b/demos/kanban/next-env.d.ts index 9edff1c7..1b3be084 100644 --- a/demos/kanban/next-env.d.ts +++ b/demos/kanban/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -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. diff --git a/demos/kanban/next.config.ts b/demos/kanban/next.config.ts index 5b25b592..cb651cdc 100644 --- a/demos/kanban/next.config.ts +++ b/demos/kanban/next.config.ts @@ -1,9 +1,5 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = { - async redirects() { - return [{ source: "/", destination: "/pages/kanban", permanent: false }]; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/demos/kanban/tsconfig.json b/demos/kanban/tsconfig.json index 3a13f90a..0352f50e 100644 --- a/demos/kanban/tsconfig.json +++ b/demos/kanban/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { diff --git a/demos/ui-builder/app/globals.css b/demos/ui-builder/app/globals.css index e1c64943..37ca6683 100644 --- a/demos/ui-builder/app/globals.css +++ b/demos/ui-builder/app/globals.css @@ -1,6 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; -@import "@btst/stack/plugins/ui-builder/css"; +@import "@btst/stack/dist/plugins/ui-builder/client.css"; @import "@btst/stack/plugins/cms/css"; @import "@btst/stack/plugins/route-docs/css"; /* WebContainers workaround: the WASM Tailwind scanner cannot traverse node_modules (https://github.com/tailwindlabs/tailwindcss/issues/18418). copy-stack-src.mjs (run via dev/build) copies @btst/stack/src outside node_modules so Tailwind can scan it. Safe to remove once the upstream bug is fixed. */ diff --git a/demos/ui-builder/app/page.tsx b/demos/ui-builder/app/page.tsx index 5838f719..cf349fc7 100644 --- a/demos/ui-builder/app/page.tsx +++ b/demos/ui-builder/app/page.tsx @@ -1,3 +1,106 @@ +import Link from "next/link"; + +type RouteItem = { + label: string; + path: string; + description: string; +}; + +type RouteGroup = { + heading: string; + routes: RouteItem[]; +}; + +const groups: RouteGroup[] = [ + { + heading: "UI Builder", + routes: [ + { + label: "Pages", + path: "/pages/ui-builder", + description: "All UI builder pages", + }, + { + label: "New Page", + path: "/pages/ui-builder/new", + description: "Create a new visual page", + }, + ], + }, + { + heading: "CMS (admin)", + routes: [ + { + label: "CMS Dashboard", + path: "/pages/cms", + description: "Manage CMS content types and entries", + }, + { + label: "UI Builder Content List", + path: "/pages/cms/ui-builder-page", + description: "CMS list of all UI builder pages", + }, + ], + }, + { + heading: "Docs", + routes: [ + { + label: "Route Docs", + path: "/pages/route-docs", + description: "All client routes in this demo", + }, + { + label: "API Reference", + path: "/api/data/reference", + description: "OpenAPI reference for the backend", + }, + ], + }, +]; + export default function Home() { - return null; + return ( +
+
+
+

+ BTST UI Builder Demo +

+

+ Available routes in this demo +

+
+
+ {groups.map((group) => ( +
+

+ {group.heading} +

+
    + {group.routes.map(({ label, path, description }) => ( +
  • + +
    +
    {label}
    +
    + {description} +
    +
    + + {path} + + +
  • + ))} +
+
+ ))} +
+
+
+ ); } diff --git a/demos/ui-builder/copy-stack-src.mjs b/demos/ui-builder/copy-stack-src.mjs index eca1c4dd..7def84ad 100644 --- a/demos/ui-builder/copy-stack-src.mjs +++ b/demos/ui-builder/copy-stack-src.mjs @@ -19,7 +19,7 @@ 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", ); diff --git a/demos/ui-builder/next-env.d.ts b/demos/ui-builder/next-env.d.ts index 9edff1c7..1b3be084 100644 --- a/demos/ui-builder/next-env.d.ts +++ b/demos/ui-builder/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -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. diff --git a/demos/ui-builder/next.config.ts b/demos/ui-builder/next.config.ts index d31197a4..cb651cdc 100644 --- a/demos/ui-builder/next.config.ts +++ b/demos/ui-builder/next.config.ts @@ -1,11 +1,5 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = { - async redirects() { - return [ - { source: "/", destination: "/pages/ui-builder", permanent: false }, - ]; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/demos/ui-builder/tsconfig.json b/demos/ui-builder/tsconfig.json index 3a13f90a..2627c607 100644 --- a/demos/ui-builder/tsconfig.json +++ b/demos/ui-builder/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, "include": [ @@ -30,5 +36,7 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } From 0a83b77229a7b021e1dd30d1c6337254a72117d8 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 11:10:41 -0500 Subject: [PATCH 2/7] lint: fix --- biome.json | 4 +++- demos/ui-builder/tsconfig.json | 14 +++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/biome.json b/biome.json index 775f1236..2606612a 100644 --- a/biome.json +++ b/biome.json @@ -71,7 +71,9 @@ "!**/examples/**", "!**/packages/ui/**", "!**/docs/**", - "!**/test-results/**" + "!**/test-results/**", + "!**/.btst-stack-src/**", + "!**/.btst-stack-ui/**" ] } } diff --git a/demos/ui-builder/tsconfig.json b/demos/ui-builder/tsconfig.json index 2627c607..0352f50e 100644 --- a/demos/ui-builder/tsconfig.json +++ b/demos/ui-builder/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,9 +19,7 @@ } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, "include": [ @@ -36,7 +30,5 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } From 80827ce7b6d05ff1afe4157146eb612208556a01 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 11:10:56 -0500 Subject: [PATCH 3/7] chore: streamline copy-stack-src script --- demos/ai-chat/copy-stack-src.mjs | 12 +++--------- demos/blog/copy-stack-src.mjs | 12 +++--------- demos/cms/copy-stack-src.mjs | 12 +++--------- demos/form-builder/copy-stack-src.mjs | 12 +++--------- demos/kanban/copy-stack-src.mjs | 12 +++--------- demos/ui-builder/copy-stack-src.mjs | 12 +++--------- 6 files changed, 18 insertions(+), 54 deletions(-) diff --git a/demos/ai-chat/copy-stack-src.mjs b/demos/ai-chat/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/ai-chat/copy-stack-src.mjs +++ b/demos/ai-chat/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } diff --git a/demos/blog/copy-stack-src.mjs b/demos/blog/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/blog/copy-stack-src.mjs +++ b/demos/blog/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } diff --git a/demos/cms/copy-stack-src.mjs b/demos/cms/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/cms/copy-stack-src.mjs +++ b/demos/cms/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } diff --git a/demos/form-builder/copy-stack-src.mjs b/demos/form-builder/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/form-builder/copy-stack-src.mjs +++ b/demos/form-builder/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } diff --git a/demos/kanban/copy-stack-src.mjs b/demos/kanban/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/kanban/copy-stack-src.mjs +++ b/demos/kanban/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } diff --git a/demos/ui-builder/copy-stack-src.mjs b/demos/ui-builder/copy-stack-src.mjs index 7def84ad..407313c3 100644 --- a/demos/ui-builder/copy-stack-src.mjs +++ b/demos/ui-builder/copy-stack-src.mjs @@ -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 - console.log( "[copy-stack-src] node_modules/@btst/stack/src not found, skipping", ); @@ -29,17 +27,13 @@ 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] node_modules/@btst/stack/dist/packages/ui not found, skipping", - ); + console.log(`[copy-stack-src] ${uiSrc} not found, skipping`); } From 5283cdca07d19d778d8158819d7e35ea1e00e040 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 11:40:08 -0500 Subject: [PATCH 4/7] chore: update CSS imports in demo projects to use correct paths --- demos/kanban/app/globals.css | 2 +- demos/ui-builder/app/globals.css | 2 +- packages/stack/scripts/postbuild.cjs | 51 ++++++++++++---------------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/demos/kanban/app/globals.css b/demos/kanban/app/globals.css index 187ff20f..b6e5c6cd 100644 --- a/demos/kanban/app/globals.css +++ b/demos/kanban/app/globals.css @@ -1,6 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; -@import "@btst/stack/dist/plugins/kanban/client.css"; +@import "@btst/stack/plugins/kanban/css"; @import "@btst/stack/plugins/route-docs/css"; /* WebContainers workaround: the WASM Tailwind scanner cannot traverse node_modules (https://github.com/tailwindlabs/tailwindcss/issues/18418). copy-stack-src.mjs (run via dev/build) copies @btst/stack/src outside node_modules so Tailwind can scan it. Safe to remove once the upstream bug is fixed. */ @source "./.btst-stack-src/**/*.{ts,tsx}"; diff --git a/demos/ui-builder/app/globals.css b/demos/ui-builder/app/globals.css index 37ca6683..e1c64943 100644 --- a/demos/ui-builder/app/globals.css +++ b/demos/ui-builder/app/globals.css @@ -1,6 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; -@import "@btst/stack/dist/plugins/ui-builder/client.css"; +@import "@btst/stack/plugins/ui-builder/css"; @import "@btst/stack/plugins/cms/css"; @import "@btst/stack/plugins/route-docs/css"; /* WebContainers workaround: the WASM Tailwind scanner cannot traverse node_modules (https://github.com/tailwindlabs/tailwindcss/issues/18418). copy-stack-src.mjs (run via dev/build) copies @btst/stack/src outside node_modules so Tailwind can scan it. Safe to remove once the upstream bug is fixed. */ diff --git a/packages/stack/scripts/postbuild.cjs b/packages/stack/scripts/postbuild.cjs index e9e054fc..740b0b65 100644 --- a/packages/stack/scripts/postbuild.cjs +++ b/packages/stack/scripts/postbuild.cjs @@ -2,9 +2,9 @@ /* Post-build step for BTST package. - Copies all .css files from src/plugins/** to dist/plugins/** preserving structure - - Resolves @workspace/ui/... CSS imports in dist files by inlining the referenced - content into dist/plugins/shared/ and rewriting the import path — so consumers - outside the monorepo (e.g. npm installs, StackBlitz) never see workspace imports + - Resolves @workspace/ui/... CSS imports by inlining the referenced content directly + into each dist CSS file — producing fully self-contained files with no workspace + references, so npm consumers and StackBlitz never see unresolvable imports - Executes optional per-plugin postbuild scripts if present at: src/plugins//postbuild.(js|cjs|mjs) */ @@ -81,14 +81,15 @@ function inlineCssImports(cssContent, baseDir, seen = new Set()) { /** * After all plugin CSS files are copied to dist, scan them for - * @import "@workspace/ui/..." declarations. For each unique specifier found: - * 1. Resolve the actual file via packages/ui/package.json exports map - * 2. Inline all relative sub-imports recursively into one blob - * 3. Write the blob to dist/plugins/shared/.css - * 4. Rewrite the @workspace/ui/... import in the dist CSS to the relative path + * @import "@workspace/ui/..." declarations and inline the referenced CSS + * content directly — replacing the import with the full CSS text. * - * This keeps source files using proper workspace imports while ensuring the - * published package is self-contained. + * This makes every dist CSS file fully self-contained: no @workspace/ui + * references survive into the published npm package, so consumers outside + * the monorepo (npm installs, StackBlitz) never see unresolvable imports. + * + * Strategy: inline rather than redirect to a shared/ file, so the package + * works correctly with no extra files and no relative-path assumptions. */ function resolveWorkspaceCssImports() { const UI_PKG_DIR = path.resolve(ROOT, "..", "ui"); @@ -112,10 +113,10 @@ function resolveWorkspaceCssImports() { } const WORKSPACE_IMPORT_RE = /@import\s+"(@workspace\/ui[^"]+)";?[ \t]*/g; - const sharedDir = path.join(DIST_PLUGINS_DIR, "shared"); - // specifier → filename written in dist/plugins/shared/ - const generated = new Map(); + // Cache resolved+inlined content per specifier so each unique import is + // only read and processed once even if referenced by multiple dist files. + const cache = new Map(); if (!fs.existsSync(DIST_PLUGINS_DIR)) return; @@ -129,35 +130,27 @@ function resolveWorkspaceCssImports() { let modified = false; content = content.replace(WORKSPACE_IMPORT_RE, (match, specifier) => { - if (!generated.has(specifier)) { + if (!cache.has(specifier)) { const resolvedPath = resolveUiSpecifier(specifier); if (!resolvedPath || !fs.existsSync(resolvedPath)) { console.warn( `@btst/stack: could not resolve workspace import: ${specifier}`, ); + cache.set(specifier, null); return match; } const raw = fs.readFileSync(resolvedPath, "utf8"); + // Recursively inline any relative sub-imports within the resolved file const inlined = inlineCssImports(raw, path.dirname(resolvedPath)); - // Derive a stable filename from the specifier - const slug = specifier - .replace("@workspace/ui/", "") - .replace(/\//g, "-"); - ensureDir(sharedDir); - const destPath = path.join(sharedDir, slug); - fs.writeFileSync(destPath, inlined); - generated.set(specifier, slug); + cache.set(specifier, inlined); console.log( - `@btst/stack: resolved workspace import "${specifier}" → shared/${slug}`, + `@btst/stack: inlined workspace import "${specifier}" into ${path.relative(DIST_PLUGINS_DIR, filePath)}`, ); } - const slug = generated.get(specifier); - const rel = path - .relative(path.dirname(filePath), path.join(sharedDir, slug)) - .replace(/\\/g, "/"); - const relNormalized = rel.startsWith(".") ? rel : `./${rel}`; + const inlined = cache.get(specifier); + if (inlined === null) return match; // could not resolve — keep original modified = true; - return `@import "${relNormalized}";`; + return inlined; }); if (modified) { From db543452a39fba519f4e0dd4e5b6bdb2da8637da Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 11:41:44 -0500 Subject: [PATCH 5/7] chore: enhance copy-stack-src script to conditionally copy workspace-built plugins for demo projects --- demos/ai-chat/copy-stack-src.mjs | 14 ++++++++++++++ demos/blog/copy-stack-src.mjs | 14 ++++++++++++++ demos/cms/copy-stack-src.mjs | 14 ++++++++++++++ demos/form-builder/copy-stack-src.mjs | 14 ++++++++++++++ demos/kanban/copy-stack-src.mjs | 14 ++++++++++++++ demos/ui-builder/copy-stack-src.mjs | 14 ++++++++++++++ 6 files changed, 84 insertions(+) diff --git a/demos/ai-chat/copy-stack-src.mjs b/demos/ai-chat/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/ai-chat/copy-stack-src.mjs +++ b/demos/ai-chat/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} diff --git a/demos/blog/copy-stack-src.mjs b/demos/blog/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/blog/copy-stack-src.mjs +++ b/demos/blog/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} diff --git a/demos/cms/copy-stack-src.mjs b/demos/cms/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/cms/copy-stack-src.mjs +++ b/demos/cms/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} diff --git a/demos/form-builder/copy-stack-src.mjs b/demos/form-builder/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/form-builder/copy-stack-src.mjs +++ b/demos/form-builder/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} diff --git a/demos/kanban/copy-stack-src.mjs b/demos/kanban/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/kanban/copy-stack-src.mjs +++ b/demos/kanban/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} diff --git a/demos/ui-builder/copy-stack-src.mjs b/demos/ui-builder/copy-stack-src.mjs index 407313c3..ca4bc7c3 100644 --- a/demos/ui-builder/copy-stack-src.mjs +++ b/demos/ui-builder/copy-stack-src.mjs @@ -37,3 +37,17 @@ if (existsSync(uiSrc)) { } 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] overlaid ${workspacePluginsDist} → ${npmPluginsDist}`, + ); +} From e185534dfd963e61592ebd672a9a8975fdbe0390 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 12:04:14 -0500 Subject: [PATCH 6/7] feat: refactor demo pages to dynamically generate route groups and improve navigation structure --- demos/ai-chat/app/page.tsx | 83 ++++++++--------- demos/blog/app/page.tsx | 152 ++++++++++++-------------------- demos/cms/app/page.tsx | 138 ++++++++++++++--------------- demos/form-builder/app/page.tsx | 113 ++++++++++-------------- demos/kanban/app/page.tsx | 108 ++++++++++------------- demos/ui-builder/app/page.tsx | 124 +++++++++++++------------- 6 files changed, 319 insertions(+), 399 deletions(-) diff --git a/demos/ai-chat/app/page.tsx b/demos/ai-chat/app/page.tsx index 40b133cc..52025793 100644 --- a/demos/ai-chat/app/page.tsx +++ b/demos/ai-chat/app/page.tsx @@ -1,45 +1,45 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "Chat", - routes: [ - { - label: "Chat", - path: "/pages/chat", - description: "Start a new AI conversation", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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); -export default function Home() { return (
@@ -58,18 +58,13 @@ export default function Home() { {group.heading}

    - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
  • -
    -
    {label}
    -
    - {description} -
    -
    +
    {label}
    {path} diff --git a/demos/blog/app/page.tsx b/demos/blog/app/page.tsx index 4c8c867e..1ce16b65 100644 --- a/demos/blog/app/page.tsx +++ b/demos/blog/app/page.tsx @@ -1,95 +1,64 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "Blog", - routes: [ - { - label: "Blog", - path: "/pages/blog", - description: "All published posts", - }, - { - label: "Drafts", - path: "/pages/blog/drafts", - description: "Unpublished draft posts", - }, - { - label: "New Post", - path: "/pages/blog/new", - description: "Create a new blog post", - }, - ], - }, - { - heading: "Posts (seeded)", - routes: [ - { - label: "Getting Started with BTST Blog", - path: "/pages/blog/getting-started", - description: "An introduction to the BTST blog plugin", - }, - { - label: "Building Full-Stack Apps with Plugins", - path: "/pages/blog/full-stack-plugins", - description: "Plugin-first approach to full-stack development", - }, - { - label: "SEO and Meta Tags in BTST", - path: "/pages/blog/seo-and-meta-tags", - description: "Auto-generated Open Graph and Twitter card meta tags", - }, - ], - }, - { - heading: "Edit Posts (seeded)", - routes: [ - { - label: "Edit: Getting Started with BTST Blog", - path: "/pages/blog/getting-started/edit", - description: "Edit the getting started post", - }, - { - label: "Edit: Building Full-Stack Apps with Plugins", - path: "/pages/blog/full-stack-plugins/edit", - description: "Edit the full-stack plugins post", - }, - { - label: "Edit: SEO and Meta Tags in BTST", - path: "/pages/blog/seo-and-meta-tags/edit", - description: "Edit the SEO post", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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); -export default function Home() { return (
    @@ -108,18 +77,13 @@ export default function Home() { {group.heading}

      - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
    • -
      -
      {label}
      -
      - {description} -
      -
      +
      {label}
      {path} diff --git a/demos/cms/app/page.tsx b/demos/cms/app/page.tsx index dbd3fcd0..bab7b0f0 100644 --- a/demos/cms/app/page.tsx +++ b/demos/cms/app/page.tsx @@ -1,75 +1,70 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "Articles (public)", - routes: [ - { - label: "Articles", - path: "/pages/articles", - description: "Public-facing article listing", - }, - { - label: "Welcome to BTST CMS", - path: "/pages/articles/welcome-to-btst-cms", - description: "Introduction to managing structured content", - }, - { - label: "Getting Started with Content Types", - path: "/pages/articles/getting-started-with-content-types", - description: "How to define and manage content types", - }, - ], - }, - { - heading: "CMS (admin)", - routes: [ - { - label: "CMS Dashboard", - path: "/pages/cms", - description: "Manage content types and entries", - }, - { - label: "Article List", - path: "/pages/cms/article", - description: "All article content items", - }, - { - label: "New Article", - path: "/pages/cms/article/new", - description: "Create a new article", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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 cmsPlugin = schema.plugins.find((p) => p.key === "cms"); + const staticCmsRoutes: RouteItem[] = + cmsPlugin?.routes + .filter((r) => r.pathParams.length === 0) + .map((r) => ({ + label: routeKeyToLabel(r.key), + path: `${SITE_BASE_PATH}${r.path}`, + })) ?? []; + + const [contentTypes, { items: articles }] = await Promise.all([ + myStack.api.cms.getAllContentTypes(), + myStack.api.cms.getAllContentItems("article"), + ]); + + // Expand parameterized CMS admin routes (/cms/:typeSlug, /cms/:typeSlug/new) with real content type slugs + const cmsTypeRoutes: RouteItem[] = contentTypes.flatMap((t) => [ + { label: t.name, path: `${SITE_BASE_PATH}/cms/${t.slug}` }, + { label: `New ${t.name}`, path: `${SITE_BASE_PATH}/cms/${t.slug}/new` }, + ]); + + const groups: RouteGroup[] = [ + { + heading: "Articles (public)", + routes: [ + { label: "Articles", path: `${SITE_BASE_PATH}/articles` }, + ...articles.map((item) => ({ + label: (item.parsedData as { title?: string }).title ?? item.slug, + path: `${SITE_BASE_PATH}/articles/${item.slug}`, + })), + ], + }, + { + heading: "CMS (admin)", + routes: [...staticCmsRoutes, ...cmsTypeRoutes], + }, + { + 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); -export default function Home() { return (
      @@ -88,18 +83,13 @@ export default function Home() { {group.heading}

        - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
      • -
        -
        {label}
        -
        - {description} -
        -
        +
        {label}
        {path} diff --git a/demos/form-builder/app/page.tsx b/demos/form-builder/app/page.tsx index fe255ee9..fac5c354 100644 --- a/demos/form-builder/app/page.tsx +++ b/demos/form-builder/app/page.tsx @@ -1,65 +1,55 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "Forms (admin)", - routes: [ - { - label: "Forms", - path: "/pages/forms", - description: "Browse and manage all forms", - }, - { - label: "New Form", - path: "/pages/forms/new", - description: "Create a new form", - }, - ], - }, - { - heading: "Public Forms (seeded)", - routes: [ - { - label: "Contact Us", - path: "/submit/contact-us", - description: "Public contact form submission page", - }, - { - label: "Feedback Form", - path: "/submit/feedback", - description: "Public product feedback form", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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 formBuilderPlugin = schema.plugins.find((p) => p.key === "formBuilder"); + const staticFormRoutes: RouteItem[] = + formBuilderPlugin?.routes + .filter((r) => r.pathParams.length === 0) + .map((r) => ({ + label: routeKeyToLabel(r.key), + path: `${SITE_BASE_PATH}${r.path}`, + })) ?? []; + + const { items: forms } = await myStack.api.formBuilder.getAllForms(); + + const groups: RouteGroup[] = [ + { heading: "Forms (admin)", routes: staticFormRoutes }, + { + heading: "Public Forms (seeded)", + routes: forms.map((f) => ({ + label: f.name, + path: `/submit/${f.slug}`, + })), + }, + { + 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); -export default function Home() { return (
        @@ -78,18 +68,13 @@ export default function Home() { {group.heading}

          - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
        • -
          -
          {label}
          -
          - {description} -
          -
          +
          {label}
          {path} diff --git a/demos/kanban/app/page.tsx b/demos/kanban/app/page.tsx index e598a650..aa9166a4 100644 --- a/demos/kanban/app/page.tsx +++ b/demos/kanban/app/page.tsx @@ -1,60 +1,55 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "Kanban", - routes: [ - { - label: "Boards", - path: "/pages/kanban", - description: "All kanban boards", - }, - { - label: "New Board", - path: "/pages/kanban/new", - description: "Create a new board", - }, - ], - }, - { - heading: "Boards (seeded)", - routes: [ - { - label: "BTST Demo Board", - path: "/pages/kanban/demo-board", - description: "Pre-seeded demo board with sample tasks", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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 kanbanPlugin = schema.plugins.find((p) => p.key === "kanban"); + const staticKanbanRoutes: RouteItem[] = + kanbanPlugin?.routes + .filter((r) => r.pathParams.length === 0) + .map((r) => ({ + label: routeKeyToLabel(r.key), + path: `${SITE_BASE_PATH}${r.path}`, + })) ?? []; + + const { items: boards } = await myStack.api.kanban.getAllBoards(); + + const groups: RouteGroup[] = [ + { heading: "Kanban", routes: staticKanbanRoutes }, + { + heading: "Boards (seeded)", + routes: boards.map((b) => ({ + label: b.name, + path: `${SITE_BASE_PATH}/kanban/${b.id}`, + })), + }, + { + 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); -export default function Home() { return (
          @@ -73,18 +68,13 @@ export default function Home() { {group.heading}

            - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
          • -
            -
            {label}
            -
            - {description} -
            -
            +
            {label}
            {path} diff --git a/demos/ui-builder/app/page.tsx b/demos/ui-builder/app/page.tsx index cf349fc7..17651d89 100644 --- a/demos/ui-builder/app/page.tsx +++ b/demos/ui-builder/app/page.tsx @@ -1,65 +1,66 @@ 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; - description: string; -}; +type RouteItem = { label: string; path: string }; +type RouteGroup = { heading: string; routes: RouteItem[] }; -type RouteGroup = { - heading: string; - routes: RouteItem[]; -}; +const SITE_BASE_PATH = "/pages"; -const groups: RouteGroup[] = [ - { - heading: "UI Builder", - routes: [ - { - label: "Pages", - path: "/pages/ui-builder", - description: "All UI builder pages", - }, - { - label: "New Page", - path: "/pages/ui-builder/new", - description: "Create a new visual page", - }, - ], - }, - { - heading: "CMS (admin)", - routes: [ - { - label: "CMS Dashboard", - path: "/pages/cms", - description: "Manage CMS content types and entries", - }, - { - label: "UI Builder Content List", - path: "/pages/cms/ui-builder-page", - description: "CMS list of all UI builder pages", - }, - ], - }, - { - heading: "Docs", - routes: [ - { - label: "Route Docs", - path: "/pages/route-docs", - description: "All client routes in this demo", - }, - { - label: "API Reference", - path: "/api/data/reference", - description: "OpenAPI reference for the backend", - }, - ], - }, -]; +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 uiBuilderPlugin = schema.plugins.find((p) => p.key === "uiBuilder"); + const staticUiBuilderRoutes: RouteItem[] = + uiBuilderPlugin?.routes + .filter((r) => r.pathParams.length === 0) + .map((r) => ({ + label: routeKeyToLabel(r.key), + path: `${SITE_BASE_PATH}${r.path}`, + })) ?? []; + + const cmsPlugin = schema.plugins.find((p) => p.key === "cms"); + const staticCmsRoutes: RouteItem[] = + cmsPlugin?.routes + .filter((r) => r.pathParams.length === 0) + .map((r) => ({ + label: routeKeyToLabel(r.key), + path: `${SITE_BASE_PATH}${r.path}`, + })) ?? []; + + // Expand parameterized CMS admin routes with real content type slugs + const contentTypes = await myStack.api.cms.getAllContentTypes(); + const cmsTypeRoutes: RouteItem[] = contentTypes.flatMap((t) => [ + { label: t.name, path: `${SITE_BASE_PATH}/cms/${t.slug}` }, + { label: `New ${t.name}`, path: `${SITE_BASE_PATH}/cms/${t.slug}/new` }, + ]); + + const groups: RouteGroup[] = [ + { heading: "UI Builder", routes: staticUiBuilderRoutes }, + { + heading: "CMS (admin)", + routes: [...staticCmsRoutes, ...cmsTypeRoutes], + }, + { + 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); -export default function Home() { return (
            @@ -78,18 +79,13 @@ export default function Home() { {group.heading}

              - {group.routes.map(({ label, path, description }) => ( + {group.routes.map(({ label, path }) => (
            • -
              -
              {label}
              -
              - {description} -
              -
              +
              {label}
              {path} From b22f97200d268631f87f5048e01c21d79c10b6fc Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Sat, 7 Mar 2026 12:12:05 -0500 Subject: [PATCH 7/7] chore: update @btst/stack dependency to version 2.5.3 across all demo projects --- demos/ai-chat/package.json | 2 +- demos/blog/package.json | 2 +- demos/cms/package.json | 2 +- demos/form-builder/package.json | 2 +- demos/kanban/package.json | 2 +- demos/ui-builder/package.json | 2 +- packages/stack/package.json | 2 +- packages/stack/src/plugins/route-docs/generator.ts | 4 +++- 8 files changed, 10 insertions(+), 8 deletions(-) diff --git a/demos/ai-chat/package.json b/demos/ai-chat/package.json index 3386dc5d..8f92babc 100644 --- a/demos/ai-chat/package.json +++ b/demos/ai-chat/package.json @@ -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", diff --git a/demos/blog/package.json b/demos/blog/package.json index 3ed8d559..724821ac 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.5.2", + "@btst/stack": "^2.5.3", "@btst/yar": "^1.2.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-dialog": "^1.1.15", diff --git a/demos/cms/package.json b/demos/cms/package.json index 0491f309..8b658223 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.5.2", + "@btst/stack": "^2.5.3", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/demos/form-builder/package.json b/demos/form-builder/package.json index dddcda1d..90b616a1 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.5.2", + "@btst/stack": "^2.5.3", "@btst/yar": "^1.2.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-checkbox": "^1.3.3", diff --git a/demos/kanban/package.json b/demos/kanban/package.json index b2cac79c..7fcb31f8 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.5.2", + "@btst/stack": "^2.5.3", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/demos/ui-builder/package.json b/demos/ui-builder/package.json index ca8d856b..a45e7ec8 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.5.2", + "@btst/stack": "^2.5.3", "@btst/yar": "^1.2.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", diff --git a/packages/stack/package.json b/packages/stack/package.json index 749c22ca..de6181b9 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -1,6 +1,6 @@ { "name": "@btst/stack", - "version": "2.5.2", + "version": "2.5.3", "description": "A composable, plugin-based library for building full-stack applications.", "repository": { "type": "git", diff --git a/packages/stack/src/plugins/route-docs/generator.ts b/packages/stack/src/plugins/route-docs/generator.ts index 3e02f260..50ad9bfd 100644 --- a/packages/stack/src/plugins/route-docs/generator.ts +++ b/packages/stack/src/plugins/route-docs/generator.ts @@ -143,7 +143,9 @@ function processZodType(zodType: z.ZodType): Record { // Handle default - unwrap and process inner type if (zodType instanceof z.ZodDefault) { const innerType = (zodType as any)._def?.innerType; - const defaultValue = (zodType as any)._def?.defaultValue?.(); + const rawDefault = (zodType as any)._def?.defaultValue; + const defaultValue = + typeof rawDefault === "function" ? rawDefault() : rawDefault; if (innerType) { const innerSchema = processZodType(innerType); if (defaultValue !== undefined) {