@getcoherent/core 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3469,7 +3469,9 @@ declare class PageGenerator {
3469
3469
  /**
3470
3470
  * Generate layout code (for root layout)
3471
3471
  */
3472
- generateLayout(layout: PageLayout, appType?: 'multi-page' | 'spa'): Promise<string>;
3472
+ generateLayout(layout: PageLayout, appType?: 'multi-page' | 'spa', options?: {
3473
+ skipNav?: boolean;
3474
+ }): Promise<string>;
3473
3475
  /**
3474
3476
  * Generate Next.js App Router root layout
3475
3477
  */
@@ -3479,6 +3481,15 @@ declare class PageGenerator {
3479
3481
  * Documentation is part of Design System; also hide on legacy /docs for consistency.
3480
3482
  */
3481
3483
  generateAppNav(): string;
3484
+ /**
3485
+ * Generate shared Header component code for components/shared/header.tsx.
3486
+ * Contains navigation items, theme toggle, and Design System FAB.
3487
+ */
3488
+ generateSharedHeaderCode(): string;
3489
+ /**
3490
+ * Generate shared Footer component code for components/shared/footer.tsx.
3491
+ */
3492
+ generateSharedFooterCode(): string;
3482
3493
  /**
3483
3494
  * Generate React SPA root layout
3484
3495
  */
@@ -3680,8 +3691,9 @@ declare class ProjectScaffolder {
3680
3691
  */
3681
3692
  private generatePages;
3682
3693
  /**
3683
- * Generate root layout (and AppNav when navigation enabled).
3684
- * Public so init can call it after create-next-app to add Design System button and Coherent layout.
3694
+ * Generate root layout with shared Header/Footer components.
3695
+ * Creates layout.tsx (without inline nav), then registers shared Header and Footer
3696
+ * in the manifest and wires them into layout.tsx via integrateSharedLayoutIntoRootLayout.
3685
3697
  */
3686
3698
  generateRootLayout(): Promise<void>;
3687
3699
  private generateDefaultPages;
package/dist/index.js CHANGED
@@ -5552,9 +5552,9 @@ ${sections}
5552
5552
  /**
5553
5553
  * Generate layout code (for root layout)
5554
5554
  */
5555
- async generateLayout(layout, appType = "multi-page") {
5555
+ async generateLayout(layout, appType = "multi-page", options) {
5556
5556
  if (appType === "multi-page") {
5557
- return this.generateNextJSLayout(layout);
5557
+ return this.generateNextJSLayout(layout, options);
5558
5558
  } else {
5559
5559
  return this.generateReactSPALayout(layout);
5560
5560
  }
@@ -5562,9 +5562,9 @@ ${sections}
5562
5562
  /**
5563
5563
  * Generate Next.js App Router root layout
5564
5564
  */
5565
- generateNextJSLayout(_layout) {
5565
+ generateNextJSLayout(_layout, options) {
5566
5566
  const cssVars = buildCssVariables(this.config);
5567
- const navEnabled = this.config.navigation?.enabled;
5567
+ const navEnabled = this.config.navigation?.enabled && !options?.skipNav;
5568
5568
  const navRendered = navEnabled ? "<AppNav />" : "";
5569
5569
  const isDark = this.config.theme?.defaultMode === "dark";
5570
5570
  const htmlClass = isDark ? ' className="dark"' : "";
@@ -5610,7 +5610,7 @@ export default function RootLayout({
5610
5610
  </head>
5611
5611
  <body className="min-h-screen flex flex-col bg-background text-foreground antialiased">
5612
5612
  ${navEnabled ? ` ${navRendered}
5613
- ` : ""}<div className="flex-1 flex flex-col">{children}</div>
5613
+ ` : ""} <div className="flex-1 flex flex-col">{children}</div>
5614
5614
  </body>
5615
5615
  </html>
5616
5616
  )
@@ -5737,6 +5737,111 @@ export function AppNav() {
5737
5737
  </Fragment>
5738
5738
  )
5739
5739
  }
5740
+ `;
5741
+ }
5742
+ /**
5743
+ * Generate shared Header component code for components/shared/header.tsx.
5744
+ * Contains navigation items, theme toggle, and Design System FAB.
5745
+ */
5746
+ generateSharedHeaderCode() {
5747
+ const navItems = this.config.navigation?.items || [];
5748
+ const authRoutes = /* @__PURE__ */ new Set([
5749
+ "/login",
5750
+ "/signin",
5751
+ "/sign-in",
5752
+ "/signup",
5753
+ "/sign-up",
5754
+ "/register",
5755
+ "/forgot-password",
5756
+ "/reset-password"
5757
+ ]);
5758
+ const visibleItems = navItems.filter((item) => !authRoutes.has(item.route) && !item.route.includes("["));
5759
+ const hasMultipleItems = visibleItems.length > 1;
5760
+ const items = visibleItems.map(
5761
+ (item) => `<Link href="${item.route}" className={\`text-sm font-medium px-3 py-2 rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring \${pathname === "${item.route}" ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground hover:bg-muted/50'}\`}>${item.label}</Link>`
5762
+ ).join("\n ");
5763
+ const navItemsBlock = hasMultipleItems ? `
5764
+ ${items}
5765
+ ` : "";
5766
+ const appName = this.escapeString(this.config.name);
5767
+ return `'use client'
5768
+
5769
+ import Link from 'next/link'
5770
+ import { usePathname } from 'next/navigation'
5771
+ import { useEffect, useState } from 'react'
5772
+
5773
+ function ThemeToggle() {
5774
+ const [dark, setDark] = useState(false)
5775
+ useEffect(() => {
5776
+ setDark(document.documentElement.classList.contains('dark'))
5777
+ }, [])
5778
+ const toggle = () => {
5779
+ const next = !dark
5780
+ setDark(next)
5781
+ document.documentElement.classList.toggle('dark', next)
5782
+ }
5783
+ return (
5784
+ <button
5785
+ onClick={toggle}
5786
+ className="flex items-center justify-center w-9 h-9 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
5787
+ title={dark ? 'Switch to light theme' : 'Switch to dark theme'}
5788
+ aria-label="Toggle theme"
5789
+ >
5790
+ {dark ? (
5791
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
5792
+ ) : (
5793
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
5794
+ )}
5795
+ </button>
5796
+ )
5797
+ }
5798
+
5799
+ export function Header() {
5800
+ const pathname = usePathname()
5801
+ return (
5802
+ <>
5803
+ <nav className="sticky top-0 z-50 shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
5804
+ <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
5805
+ <div className="flex items-center gap-6">
5806
+ <Link href="/" className="flex items-center gap-2 text-sm font-semibold text-foreground hover:text-foreground/90 transition-colors shrink-0">
5807
+ ${appName}
5808
+ </Link>
5809
+ <div className="flex items-center gap-1">${navItemsBlock}</div>
5810
+ </div>
5811
+ <div className="flex items-center gap-1">
5812
+ <ThemeToggle />
5813
+ </div>
5814
+ </div>
5815
+ </nav>
5816
+ <Link
5817
+ href="/design-system"
5818
+ className="fixed bottom-4 right-4 z-50 flex items-center gap-2 rounded-full border border-white/20 bg-black/60 backdrop-blur-md text-white px-4 py-2 text-xs shadow-sm hover:bg-black/80 transition-all"
5819
+ title="Design System"
5820
+ >
5821
+ Design System
5822
+ </Link>
5823
+ </>
5824
+ )
5825
+ }
5826
+ `;
5827
+ }
5828
+ /**
5829
+ * Generate shared Footer component code for components/shared/footer.tsx.
5830
+ */
5831
+ generateSharedFooterCode() {
5832
+ const appName = this.escapeString(this.config.name);
5833
+ return `'use client'
5834
+
5835
+ export function Footer() {
5836
+ return (
5837
+ <footer className="border-t">
5838
+ <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 text-sm text-muted-foreground sm:px-6 lg:px-8">
5839
+ <p>\\u00A9 {new Date().getFullYear()} ${appName}</p>
5840
+ <p className="hidden sm:block">Built with Coherent Design Method</p>
5841
+ </div>
5842
+ </footer>
5843
+ )
5844
+ }
5740
5845
  `;
5741
5846
  }
5742
5847
  /**
@@ -6552,17 +6657,33 @@ export function cn(...inputs: ClassValue[]) {
6552
6657
  }
6553
6658
  }
6554
6659
  /**
6555
- * Generate root layout (and AppNav when navigation enabled).
6556
- * Public so init can call it after create-next-app to add Design System button and Coherent layout.
6660
+ * Generate root layout with shared Header/Footer components.
6661
+ * Creates layout.tsx (without inline nav), then registers shared Header and Footer
6662
+ * in the manifest and wires them into layout.tsx via integrateSharedLayoutIntoRootLayout.
6557
6663
  */
6558
6664
  async generateRootLayout() {
6559
6665
  const appType = this.config.settings.appType || "multi-page";
6560
6666
  const layout = this.config.pages[0]?.layout || "centered";
6561
- const code = await this.pageGenerator.generateLayout(layout, appType);
6667
+ const code = await this.pageGenerator.generateLayout(layout, appType, { skipNav: true });
6562
6668
  await this.writeFile("app/layout.tsx", code);
6563
6669
  if (this.config.navigation?.enabled && appType === "multi-page") {
6564
- const appNavCode = this.pageGenerator.generateAppNav();
6565
- await this.writeFile("app/AppNav.tsx", appNavCode);
6670
+ const headerCode = this.pageGenerator.generateSharedHeaderCode();
6671
+ await generateSharedComponent(this.projectRoot, {
6672
+ name: "Header",
6673
+ type: "layout",
6674
+ code: headerCode,
6675
+ description: "Main site header with navigation and theme toggle",
6676
+ usedIn: ["app/layout.tsx"]
6677
+ });
6678
+ const footerCode = this.pageGenerator.generateSharedFooterCode();
6679
+ await generateSharedComponent(this.projectRoot, {
6680
+ name: "Footer",
6681
+ type: "layout",
6682
+ code: footerCode,
6683
+ description: "Site footer",
6684
+ usedIn: ["app/layout.tsx"]
6685
+ });
6686
+ await integrateSharedLayoutIntoRootLayout(this.projectRoot);
6566
6687
  }
6567
6688
  }
6568
6689
  async generateDefaultPages() {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.3",
6
+ "version": "0.3.0",
7
7
  "description": "Core design system engine for Coherent",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",