create-kywi-app 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/templates.mjs +58 -7
  2. package/package.json +1 -1
package/lib/templates.mjs CHANGED
@@ -1087,11 +1087,57 @@ export default function RootLayout({ children }: { children: React.ReactNode })
1087
1087
  `
1088
1088
  }
1089
1089
 
1090
+ /**
1091
+ * Client-side wrapper around core's SHARED nav renderer — the same component the
1092
+ * `navigation` module uses — so the site header and footer get identical markup,
1093
+ * CSS (`.kywi-nav-*`) and @kywi-software/js enhancement to any nav placed in a
1094
+ * page body. `items` is whatever app/(site)/layout.tsx resolved server-side (the
1095
+ * owner-managed menu, or the site tree as fallback); nothing is decided here.
1096
+ *
1097
+ * WHY THIS FILE EXISTS — it is `'use client'` on purpose, and the layout that
1098
+ * renders it is a Server Component. `BUILT_IN_MODULE_COMPONENTS` is exported
1099
+ * from one of core's `'use client'` modules, so a Server Component that imports
1100
+ * the map gets a client *reference*, not a real object:
1101
+ * `BUILT_IN_MODULE_COMPONENTS.navigation` reads back `undefined` there and React
1102
+ * throws "Element type is invalid… got: undefined" on every page (kywi-cms#112
1103
+ * follow-up). Crossing the boundary here, in a client module, is what makes the
1104
+ * lookup resolve to the real component — mirrors apps/reference/components/nav.tsx.
1105
+ */
1106
+ function siteNavComponent() {
1107
+ return `'use client'
1108
+
1109
+ import React from 'react'
1110
+ import { BUILT_IN_MODULE_COMPONENTS } from '@kywi-software/core/layout'
1111
+ import type { NavMenuItem } from '@kywi-software/core/nav'
1112
+
1113
+ // BUILT_IN_MODULE_COMPONENTS is a Record<string, …>, so a literal-key lookup is
1114
+ // typed as possibly-undefined under noUncheckedIndexedAccess; 'navigation' is
1115
+ // always present — it's one of core's own built-in modules, not user data.
1116
+ const NavRenderer = BUILT_IN_MODULE_COMPONENTS.navigation!
1117
+
1118
+ export interface SiteNavProps {
1119
+ items: NavMenuItem[]
1120
+ /** \`horizontal\`/\`mega\`/\`vertical\` for the header bar, \`footer\` for the footer columns. */
1121
+ variant: 'horizontal' | 'mega' | 'vertical' | 'footer'
1122
+ ariaLabel: string
1123
+ depth?: number
1124
+ }
1125
+
1126
+ /** Renders one resolved menu (header or footer) through core's shared nav renderer. */
1127
+ export function SiteNav({ items, variant, ariaLabel, depth }: SiteNavProps) {
1128
+ if (items.length === 0) return null
1129
+ return (
1130
+ <NavRenderer props={{ items, variant, ariaLabel, ...(depth ? { depth } : {}) }} />
1131
+ )
1132
+ }
1133
+ `
1134
+ }
1135
+
1090
1136
  /** @param {Answers} a — coupled public site shell. */
1091
1137
  function siteLayout(a) {
1092
1138
  return `import React from 'react'
1093
1139
  import { headers } from 'next/headers'
1094
- import { themeTokenStyleBlock, BUILT_IN_MODULE_COMPONENTS } from '@kywi-software/core/layout'
1140
+ import { themeTokenStyleBlock } from '@kywi-software/core/layout'
1095
1141
  import { navTreeToMenuItems, normalizePath } from '@kywi-software/core/nav'
1096
1142
  // Neutral, token-driven defaults for everything Kywi renders (prose, the layout
1097
1143
  // grid + modules, forms, the edit overlay). Restyle via theme tokens, not by
@@ -1101,6 +1147,7 @@ import '@kywi-software/core/site/styles.css'
1101
1147
  import './site.css'
1102
1148
  import config from '../../kywi.config'
1103
1149
  import { getKywi } from '../../lib/kywi'
1150
+ import { SiteNav } from '../../components/site-nav'
1104
1151
 
1105
1152
  /**
1106
1153
  * Public site shell. Header + footer carry this project's brand; edit them (and
@@ -1113,7 +1160,8 @@ import { getKywi } from '../../lib/kywi'
1113
1160
  * of the box — and the footer renders a \`'footer'\` menu when one exists. NEVER
1114
1161
  * hardcode this list: a menu item's href resolves from its linked page and
1115
1162
  * can never drift the way a pasted URL can (see AGENTS.md → Navigation).
1116
- * Rendered through core's shared nav renderer (\`BUILT_IN_MODULE_COMPONENTS\`)
1163
+ * Rendered through core's shared nav renderer via the generated
1164
+ * \`components/site-nav.tsx\` (a \`'use client'\` wrapper — see that file for why)
1117
1165
  * rather than bespoke markup, so it gets the same CSS and \`@kywi-software/js\`
1118
1166
  * enhancement as a \`navigation\` module placed in the page body.
1119
1167
  *
@@ -1126,8 +1174,6 @@ const siteTheme =
1126
1174
  config.themes.find((t) => t.name === config.sites[0]?.theme) ?? config.themes[0]
1127
1175
  const themeVars = themeTokenStyleBlock(siteTheme?.tokens)
1128
1176
 
1129
- const NavRenderer = BUILT_IN_MODULE_COMPONENTS.navigation
1130
-
1131
1177
  // Content edits (a renamed page, a reordered menu) must show up without a
1132
1178
  // restart — same reasoning as the page route.
1133
1179
  export const dynamic = 'force-dynamic'
@@ -1155,7 +1201,7 @@ export default async function SiteLayout({ children }: { children: React.ReactNo
1155
1201
  <a className="site-brand" href="/">${escapeJsxText(a.projectName)}</a>
1156
1202
  <div className="site-nav">
1157
1203
  {headerItems.length > 0 && (
1158
- <NavRenderer props={{ items: headerItems, variant: 'horizontal', ariaLabel: 'Primary', depth: 2 }} />
1204
+ <SiteNav items={headerItems} variant="horizontal" ariaLabel="Primary" depth={2} />
1159
1205
  )}
1160
1206
  <a className="site-nav__admin" href="/admin">Admin →</a>
1161
1207
  </div>
@@ -1167,7 +1213,7 @@ export default async function SiteLayout({ children }: { children: React.ReactNo
1167
1213
  <footer className="site-footer">
1168
1214
  <div className="site-footer__inner">
1169
1215
  {footerItems && footerItems.length > 0 && (
1170
- <NavRenderer props={{ items: footerItems, variant: 'footer', ariaLabel: 'Footer' }} />
1216
+ <SiteNav items={footerItems} variant="footer" ariaLabel="Footer" />
1171
1217
  )}
1172
1218
  <p className="site-footer__credit">Powered by <a href="https://kywi.dev">Kywi CMS</a></p>
1173
1219
  </div>
@@ -2292,7 +2338,7 @@ lib/config.ts single import path for kywi.config.ts
2292
2338
  app/api/v1/[...kywi]/route.ts the versioned API (delegates to core)
2293
2339
  app/admin/[[...admin]]/page.tsx mounts the FULL core admin (all surfaces) at /admin
2294
2340
  lib/modules.tsx custom (defineModule) module renderers (admin + public)
2295
- app/{llms,robots,sitemap,…} root AX routes (llms.txt, robots.txt, sitemap.xml, …)${a.mode === 'coupled' ? '\napp/(site)/layout.tsx public shell: theme tokens + your header/footer\napp/(site)/site.css your site chrome styles (edit freely)\napp/(site)/[[...slug]]/page.tsx renders published pages (layout + SEO + JSON-LD + i18n)\nlib/site.ts public-render helpers: path/locale resolution, feeds, personalization\ncomponents/personalization-runtime.tsx optional client runtime (self-ID widget, live re-eval)\napp/(site)/kywi-front-edit.tsx front-of-site editor: browse toolbar + in-place Layout editor (?kywi-edit=1, lazy-loaded)' : '\napp/page.tsx returns 404 (no public rendering in this mode)'}
2341
+ app/{llms,robots,sitemap,…} root AX routes (llms.txt, robots.txt, sitemap.xml, …)${a.mode === 'coupled' ? '\napp/(site)/layout.tsx public shell: theme tokens + your header/footer\napp/(site)/site.css your site chrome styles (edit freely)\napp/(site)/[[...slug]]/page.tsx renders published pages (layout + SEO + JSON-LD + i18n)\nlib/site.ts public-render helpers: path/locale resolution, feeds, personalization\ncomponents/site-nav.tsx client wrapper around the shared nav renderer (header + footer)\ncomponents/personalization-runtime.tsx optional client runtime (self-ID widget, live re-eval)\napp/(site)/kywi-front-edit.tsx front-of-site editor: browse toolbar + in-place Layout editor (?kywi-edit=1, lazy-loaded)' : '\napp/page.tsx returns 404 (no public rendering in this mode)'}
2296
2342
  \`\`\`
2297
2343
  `
2298
2344
  }
@@ -2676,6 +2722,11 @@ export function buildFileSet(answers) {
2676
2722
  // page at its slug. More-specific /admin and /api routes take precedence.
2677
2723
  files['app/(site)/layout.tsx'] = siteLayout(answers)
2678
2724
  files['app/(site)/site.css'] = siteStyles(answers)
2725
+ // 'use client' wrapper around core's shared nav renderer — required because
2726
+ // BUILT_IN_MODULE_COMPONENTS is exported from a 'use client' core module, so
2727
+ // the (Server Component) site layout above must render it through here
2728
+ // rather than looking it up directly (see siteNavComponent's doc comment).
2729
+ files['components/site-nav.tsx'] = siteNavComponent()
2679
2730
  files['app/(site)/[[...slug]]/page.tsx'] = siteSlugPage()
2680
2731
  // Public-render helpers: path/locale resolution, feed + component resolvers,
2681
2732
  // personalization + experiments.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kywi-app",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Scaffold a new Kywi CMS project — npx create-kywi-app my-site",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Kywi-Software/kywi-cms#readme",