@publish.os/nextjs 0.0.1-alpha.81 → 0.0.1-alpha.91

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 (265) hide show
  1. package/compiler-assets/nextjs/defaults/app/globals.css +1 -0
  2. package/compiler-assets/nextjs/defaults/group/index.tsx +50 -0
  3. package/compiler-assets/nextjs/defaults/group/layout.tsx +8 -8
  4. package/compiler-assets/nextjs/defaults/group/page.tsx +44 -43
  5. package/compiler-assets/nextjs/defaults/project/next.config.mjs +33 -13
  6. package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +10 -3
  7. package/compiler-assets/nextjs/defaults/root/css/animation.css +131 -0
  8. package/compiler-assets/nextjs/defaults/root/css/components/code-block.css +18 -0
  9. package/compiler-assets/nextjs/defaults/root/css/components/dialog.css +4 -1
  10. package/compiler-assets/nextjs/defaults/root/css/components/menu.css +1 -1
  11. package/compiler-assets/nextjs/defaults/root/css/components/navigation-menu.css +1 -1
  12. package/compiler-assets/nextjs/defaults/root/css/components/popover.css +1 -1
  13. package/compiler-assets/nextjs/defaults/root/css/components/select.css +1 -1
  14. package/compiler-assets/nextjs/defaults/root/css/components/tooltip.css +1 -1
  15. package/compiler-assets/nextjs/defaults/root/css/shadow.css +6 -25
  16. package/compiler-assets/nextjs/defaults/root/css/typography.css +51 -68
  17. package/compiler-assets/nextjs/defaults/root/globals.css +25 -0
  18. package/compiler-assets/nextjs/defaults/root/{page.tsx → index.tsx} +6 -2
  19. package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -1
  20. package/compiler-assets/nextjs/defaults/root/theme.css +22 -22
  21. package/compiler-assets/nextjs/shims/document.tsx +38 -0
  22. package/compiler-assets/nextjs/shims/generated-group-page.tsx +19 -2
  23. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +18 -2
  24. package/compiler-assets/nextjs/shims/global-layout.tsx +19 -85
  25. package/compiler-assets/nextjs/shims/global-not-found.tsx +69 -0
  26. package/compiler-assets/nextjs/shims/markdown-route.ts +2 -0
  27. package/compiler-assets/nextjs/shims/metadata-icons.tsx +39 -0
  28. package/compiler-assets/nextjs/shims/not-found-content.tsx +53 -0
  29. package/compiler-assets/nextjs/shims/not-found-navigation.tsx +60 -0
  30. package/compiler-assets/nextjs/shims/proxy.ts +143 -65
  31. package/compiler-assets/nextjs/shims/publish-os-theme-provider.tsx +76 -15
  32. package/compiler-assets/nextjs/shims/root-page.tsx +7 -1
  33. package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +30 -0
  34. package/compiler-assets/nextjs/shims/site-heading-level.tsx +14 -0
  35. package/compiler-assets/nextjs/shims/site-mdx-components.tsx +112 -0
  36. package/compiler-assets/nextjs/shims/site-mdx.tsx +17 -0
  37. package/compiler-assets/nextjs/shims/theme-defaults.css +41 -25
  38. package/dist/build-operation-5QMLXF7N.js +89 -0
  39. package/dist/{button-CFdx-ohV.d.ts → button-BdCScEKi.d.ts} +6 -6
  40. package/dist/capture-local-source-WGGDIUU2.js +258 -0
  41. package/dist/chunk-22RSSUNV.js +13 -0
  42. package/dist/chunk-4BC2L2KA.js +620 -0
  43. package/dist/chunk-5J3YUNRY.js +121 -0
  44. package/dist/chunk-5KAD7W53.js +248 -0
  45. package/dist/chunk-6C65KV7O.js +142 -0
  46. package/dist/chunk-6D2E6UMD.js +695 -0
  47. package/dist/chunk-7NY22B5U.js +161 -0
  48. package/dist/chunk-AI2UULNX.js +9 -0
  49. package/dist/chunk-EFO6AWXN.js +19 -0
  50. package/dist/chunk-F3AGNDF4.js +2377 -0
  51. package/dist/chunk-GBPLER5H.js +426 -0
  52. package/dist/chunk-IL4RJOKI.js +186 -0
  53. package/dist/chunk-NNBJZPFM.js +193 -0
  54. package/dist/chunk-ORIUWJBQ.js +58 -0
  55. package/dist/chunk-OXOFRXEI.js +7129 -0
  56. package/dist/chunk-UYGCOWRF.js +1709 -0
  57. package/dist/chunk-WL75GQ5M.js +80 -0
  58. package/dist/compiler.d.ts +54 -7
  59. package/dist/compiler.js +121 -13147
  60. package/dist/components/blocks/agent-actions.d.ts +81 -0
  61. package/dist/components/blocks/agent-actions.js +594 -0
  62. package/dist/components/blocks/toc.d.ts +43 -0
  63. package/dist/components/blocks/toc.js +136 -0
  64. package/dist/components/ui/button.d.ts +5 -0
  65. package/dist/components/ui/button.js +94 -0
  66. package/dist/compose-CVBJRAF6.js +16 -0
  67. package/dist/config/createNextConfig.js +27 -0
  68. package/dist/dev-operation-OSHHWRWK.js +1095 -0
  69. package/dist/framework-selection-ZVO323PK.js +11 -0
  70. package/dist/ide-support-CU6Z5PVR.js +16 -0
  71. package/dist/index.d.ts +55 -4
  72. package/dist/jsx-dev-runtime.d.ts +6 -0
  73. package/dist/jsx-dev-runtime.js +100 -0
  74. package/dist/jsx-runtime.d.ts +7 -0
  75. package/dist/jsx-runtime.js +97 -0
  76. package/dist/kernel/components/blocks/AgentActions.d.ts +35 -6
  77. package/dist/kernel/components/blocks/AgentActions.js +401 -150
  78. package/dist/kernel/components/blocks/agent-actions.d.ts +5 -0
  79. package/dist/kernel/components/blocks/agent-actions.js +594 -0
  80. package/dist/kernel/components/blocks/animated-icon-swap.js +1 -1
  81. package/dist/kernel/components/blocks/color-palette.js +4 -4
  82. package/dist/kernel/components/blocks/logo-block.js +3 -3
  83. package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +10 -2
  84. package/dist/kernel/components/blocks/nav-breadcrumb.js +22 -7
  85. package/dist/kernel/components/blocks/navbar.d.ts +2 -2
  86. package/dist/kernel/components/blocks/navbar.js +406 -65
  87. package/dist/kernel/components/blocks/navigation-progress.js +6 -3
  88. package/dist/kernel/components/blocks/next-prev.d.ts +1 -1
  89. package/dist/kernel/components/blocks/next-prev.js +12 -24
  90. package/dist/kernel/components/blocks/page-list.d.ts +1 -1
  91. package/dist/kernel/components/blocks/page-list.js +7 -6
  92. package/dist/kernel/components/blocks/search.d.ts +83 -58
  93. package/dist/kernel/components/blocks/search.js +554 -428
  94. package/dist/kernel/components/blocks/sidebar-node.d.ts +2 -2
  95. package/dist/kernel/components/blocks/sidebar-node.js +12 -10
  96. package/dist/kernel/components/blocks/sidebar.d.ts +3 -3
  97. package/dist/kernel/components/blocks/sidebar.js +24 -14
  98. package/dist/kernel/components/blocks/theme-toggle.d.ts +2 -2
  99. package/dist/kernel/components/blocks/theme-toggle.js +47 -1
  100. package/dist/kernel/components/blocks/toc-client.d.ts +40 -0
  101. package/dist/kernel/components/blocks/toc-client.js +495 -0
  102. package/dist/kernel/components/blocks/toc.d.ts +19 -1
  103. package/dist/kernel/components/blocks/toc.js +122 -180
  104. package/dist/kernel/components/mdx/components/blockquote.js +2 -2
  105. package/dist/kernel/components/mdx/components/callout.d.ts +5 -7
  106. package/dist/kernel/components/mdx/components/callout.js +67 -36
  107. package/dist/kernel/components/mdx/components/code-block.d.ts +5 -2
  108. package/dist/kernel/components/mdx/components/code-block.js +106 -5
  109. package/dist/kernel/components/mdx/components/copy-code-button.d.ts +8 -0
  110. package/dist/kernel/components/mdx/components/copy-code-button.js +61 -0
  111. package/dist/kernel/components/mdx/components/figure-caption.d.ts +1 -1
  112. package/dist/kernel/components/mdx/components/figure-caption.js +4 -3
  113. package/dist/kernel/components/mdx/components/figure.d.ts +6 -2
  114. package/dist/kernel/components/mdx/components/figure.js +24 -14
  115. package/dist/kernel/components/mdx/components/footnotes.js +1 -1
  116. package/dist/kernel/components/mdx/components/heading-copy-link.js +4 -4
  117. package/dist/kernel/components/mdx/components/heading.js +6 -3
  118. package/dist/kernel/components/mdx/components/hr.js +2 -2
  119. package/dist/kernel/components/mdx/components/image-zoom.js +2 -2
  120. package/dist/kernel/components/mdx/components/image.d.ts +1 -1
  121. package/dist/kernel/components/mdx/components/image.js +94 -68
  122. package/dist/kernel/components/mdx/components/inline-code.js +2 -2
  123. package/dist/kernel/components/mdx/components/kbd.d.ts +2 -2
  124. package/dist/kernel/components/mdx/components/kbd.js +5 -4
  125. package/dist/kernel/components/mdx/components/link.d.ts +35 -4
  126. package/dist/kernel/components/mdx/components/link.js +3 -3
  127. package/dist/kernel/components/mdx/components/paragraph.js +2 -5
  128. package/dist/kernel/components/mdx/components/strikethrough.js +1 -1
  129. package/dist/kernel/components/mdx/components/table.js +2 -2
  130. package/dist/kernel/components/mdx/components/task-list.js +2 -2
  131. package/dist/kernel/components/mdx/components/video-player.d.ts +6 -2
  132. package/dist/kernel/components/mdx/components/video-player.js +8 -4
  133. package/dist/kernel/components/mdx/components/video.d.ts +5 -3
  134. package/dist/kernel/components/mdx/components/video.js +8 -6
  135. package/dist/kernel/components/mdx/error-boundary.js +2 -2
  136. package/dist/kernel/components/mdx/error-fallback.js +1 -1
  137. package/dist/kernel/components/mdx/mdx.d.ts +28 -14
  138. package/dist/kernel/components/mdx/mdx.js +336 -63
  139. package/dist/kernel/components/mdx/rehype-heading-ids.d.ts +1 -0
  140. package/dist/kernel/components/mdx/rehype-heading-ids.js +6 -4
  141. package/dist/kernel/components/next-image.d.ts +12 -0
  142. package/dist/kernel/components/next-image.js +61 -0
  143. package/dist/kernel/components/ui/accordion.js +1 -1
  144. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +6 -23
  145. package/dist/kernel/components/ui/breadcrumb-trail.js +2 -2
  146. package/dist/kernel/components/ui/breadcrumb.d.ts +11 -3
  147. package/dist/kernel/components/ui/breadcrumb.js +10 -8
  148. package/dist/kernel/components/ui/button-group.js +8 -7
  149. package/dist/kernel/components/ui/button.d.ts +58 -6
  150. package/dist/kernel/components/ui/button.js +43 -39
  151. package/dist/kernel/components/ui/card.d.ts +2 -2
  152. package/dist/kernel/components/ui/card.js +1 -1
  153. package/dist/kernel/components/ui/dialog.js +1 -1
  154. package/dist/kernel/components/ui/drawer.d.ts +3 -1
  155. package/dist/kernel/components/ui/drawer.js +6 -2
  156. package/dist/kernel/components/ui/input.d.ts +2 -2
  157. package/dist/kernel/components/ui/input.js +1 -1
  158. package/dist/kernel/components/ui/kbd.js +10 -17
  159. package/dist/kernel/components/ui/link.d.ts +11 -3
  160. package/dist/kernel/components/ui/link.js +12 -11
  161. package/dist/kernel/components/ui/menu.js +6 -6
  162. package/dist/kernel/components/ui/mod-key.d.ts +10 -0
  163. package/dist/kernel/components/ui/mod-key.js +36 -0
  164. package/dist/kernel/components/ui/navigation-menu.js +3 -3
  165. package/dist/kernel/components/ui/popover.js +1 -1
  166. package/dist/kernel/components/ui/progress.js +2 -2
  167. package/dist/kernel/components/ui/scroll-area.js +5 -5
  168. package/dist/kernel/components/ui/select.js +8 -11
  169. package/dist/kernel/components/ui/separator.js +1 -1
  170. package/dist/kernel/components/ui/table.js +1 -1
  171. package/dist/kernel/components/ui/tabs.js +1 -1
  172. package/dist/kernel/components/ui/tag.js +1 -1
  173. package/dist/kernel/components/ui/text.d.ts +2 -2
  174. package/dist/kernel/components/ui/text.js +1 -1
  175. package/dist/kernel/components/ui/toast.js +1 -1
  176. package/dist/kernel/components/ui/tooltip.js +1 -1
  177. package/dist/kernel/defaults.registry.d.ts +11 -24
  178. package/dist/kernel/defaults.registry.js +11 -26
  179. package/dist/kernel/routes/agent/markdown.d.ts +3 -1
  180. package/dist/kernel/routes/agent/markdown.js +219 -48
  181. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  182. package/dist/kernel/routes/layouts/group.js +5 -7
  183. package/dist/kernel/routes/metadata/feed.d.ts +1 -1
  184. package/dist/kernel/routes/metadata/feed.js +111 -81
  185. package/dist/kernel/routes/metadata/llms.d.ts +12 -3
  186. package/dist/kernel/routes/metadata/llms.js +156 -59
  187. package/dist/kernel/routes/metadata/og-image.js +1 -1
  188. package/dist/kernel/routes/metadata/robots.d.ts +1 -1
  189. package/dist/kernel/routes/metadata/robots.js +2 -2
  190. package/dist/kernel/routes/metadata/sitemap.d.ts +26 -4
  191. package/dist/kernel/routes/metadata/sitemap.js +206 -38
  192. package/dist/kernel/routes/not-found.d.ts +34 -0
  193. package/dist/kernel/routes/not-found.js +213 -0
  194. package/dist/kernel/routes/pages/group.d.ts +2 -2
  195. package/dist/kernel/routes/pages/group.js +21 -13
  196. package/dist/kernel/routes/pages/leaf.d.ts +28 -1
  197. package/dist/kernel/routes/pages/leaf.js +25 -16
  198. package/dist/kernel/routes/pages/root.js +19 -13
  199. package/dist/keyboard-shortcuts.js +1 -1
  200. package/dist/resolvers/manifest-resolver.js +29 -6
  201. package/dist/search.d.ts +6 -7
  202. package/dist/search.js +52 -29
  203. package/dist/{sidebar-types-Bgchdfd6.d.ts → sidebar-types-DKpR27II.d.ts} +1 -1
  204. package/dist/theme-scope.js +1 -1
  205. package/dist/{types-DHBm7-j6.d.ts → types-CJWRG05y.d.ts} +20 -1
  206. package/dist/{types-B_b3C0U6.d.ts → types-CUtrppxt.d.ts} +8 -2
  207. package/dist/{types-CIwwieNw.d.ts → types-J2w_puPF.d.ts} +11 -10
  208. package/dist/types.d.ts +15 -6
  209. package/dist/utils/heading-ids.js +30 -11
  210. package/dist/utils/toc-selection.d.ts +7 -0
  211. package/dist/utils/toc-selection.js +14 -0
  212. package/dist/validate-local-source-SV44DBQT.js +16 -0
  213. package/dist/validate-publish-os-source-NZJF3TB2.js +14 -0
  214. package/init/seed/minimal/.gitignore +1 -0
  215. package/init/seed/minimal/publish.os/assets/blog/hello-world.svg +13 -0
  216. package/init/seed/minimal/publish.os/assets/favicon.dark.svg +13 -0
  217. package/init/seed/minimal/publish.os/assets/favicon.svg +13 -0
  218. package/init/seed/minimal/publish.os/blog/content/hello-world.mdx +48 -0
  219. package/init/seed/minimal/publish.os/blog/content/series-a.mdx +11 -0
  220. package/init/seed/minimal/publish.os/blog/index.tsx +82 -0
  221. package/init/seed/minimal/publish.os/blog/layout.tsx +13 -0
  222. package/init/seed/minimal/publish.os/blog/page.tsx +76 -0
  223. package/init/seed/minimal/publish.os/components/blog/copy-link.tsx +64 -0
  224. package/init/seed/minimal/publish.os/docs/content/get-started.mdx +6 -11
  225. package/init/seed/minimal/publish.os/docs/content/groups.mdx +6 -7
  226. package/init/seed/minimal/publish.os/docs/content/{theme.mdx → themes.mdx} +14 -12
  227. package/init/seed/minimal/publish.os/publish.os.config.json +7 -7
  228. package/init/seed/minimal/publish.os/theme.css +52 -24
  229. package/init/seed/minimal/publish.os/tsconfig.json +3 -0
  230. package/init/seed/minimal/seed.json +12 -2
  231. package/init/seed/scratch/publish.os/docs/content/get-started.mdx +6 -0
  232. package/init/seed/scratch/publish.os/publish.os.config.json +4 -0
  233. package/init/seed/scratch/seed.json +5 -0
  234. package/package.json +55 -44
  235. package/release/app/phosphor/package.json +5 -5
  236. package/release/app/phosphor/pnpm-lock.yaml.template +553 -377
  237. package/release/app/phosphor/pnpm-workspace.yaml +3 -0
  238. package/release/compiler/package.json +5 -3
  239. package/release/compiler/pnpm-lock.yaml.template +500 -303
  240. package/release/compiler/pnpm-workspace.yaml +3 -0
  241. package/release/release.json +8 -37
  242. package/compiler-assets/nextjs/defaults/group/leaf.tsx +0 -48
  243. package/dist/icons.d.ts +0 -1
  244. package/dist/icons.js +0 -2
  245. package/dist/kernel/components/blocks/design-system-preview.d.ts +0 -61
  246. package/dist/kernel/components/blocks/design-system-preview.js +0 -648
  247. package/dist/kernel/components/blocks/link-playground.d.ts +0 -5
  248. package/dist/kernel/components/blocks/link-playground.js +0 -744
  249. package/dist/kernel/components/blocks/navigation-progress-playground.d.ts +0 -5
  250. package/dist/kernel/components/blocks/navigation-progress-playground.js +0 -629
  251. package/dist/kernel/components/mdx/components/pre.d.ts +0 -6
  252. package/dist/kernel/components/mdx/components/pre.js +0 -18
  253. package/dist/kernel/components/ui/meta-key.d.ts +0 -17
  254. package/dist/kernel/components/ui/meta-key.js +0 -9
  255. package/dist/kernel/components/ui/theme-toggle.d.ts +0 -24
  256. package/dist/kernel/components/ui/theme-toggle.js +0 -58
  257. package/dist/kernel/framework-icons.d.ts +0 -1
  258. package/dist/kernel/framework-icons.js +0 -39
  259. package/dist/link-CYaCRDb6.d.ts +0 -35
  260. package/release/app/lucide/package.json +0 -30
  261. package/release/app/lucide/pnpm-lock.yaml.template +0 -2905
  262. package/release/app/lucide/pnpm-workspace.yaml +0 -11
  263. package/release/app/tabler/package.json +0 -30
  264. package/release/app/tabler/pnpm-lock.yaml.template +0 -2911
  265. package/release/app/tabler/pnpm-workspace.yaml +0 -11
@@ -0,0 +1,53 @@
1
+ import { Link } from "@components/ui/link";
2
+ import type { NotFoundScope } from "@publish.os/nextjs/kernel/routes/not-found";
3
+
4
+ export default function NotFoundContent({ scope }: { scope: NotFoundScope }) {
5
+ const sections = [
6
+ { title: "Recommendations", links: scope.recommendations },
7
+ { title: "For crawlers and agents", links: scope.indexes },
8
+ ];
9
+ return (
10
+ <section
11
+ className="mx-auto flex w-full max-w-xl flex-1 flex-col items-center justify-center px-6 py-16 font-main sm:px-8 sm:py-24"
12
+ data-publishos-not-found={scope.id}
13
+ >
14
+ <h1 className="text-center font-mono text-8xl leading-none font-medium tracking-tight text-foreground-1 sm:text-9xl">
15
+ 404<span className="sr-only"> — Page not found</span>
16
+ </h1>
17
+ <p className="mt-6 max-w-md text-center text-sm leading-6 text-pretty text-foreground-3">
18
+ This page does not exist. Please check the URL and try again.
19
+ </p>
20
+ <div className="mt-10 w-full space-y-8">
21
+ {sections.map(({ title, links }) =>
22
+ links.length ? (
23
+ <section aria-label={title} key={title}>
24
+ <h2 className="text-xs font-medium text-foreground-3">{title}</h2>
25
+ <ul className="mt-2 space-y-0">
26
+ {links.map((link) => (
27
+ <li className="flex items-baseline gap-3" key={link.href}>
28
+ <span
29
+ aria-hidden="true"
30
+ className="shrink-0 text-sm text-foreground-3"
31
+ >
32
+ -
33
+ </span>
34
+ <Link
35
+ className="inline-flex min-h-10 min-w-0 max-w-full items-center py-1 text-start wrap-anywhere"
36
+ href={link.href}
37
+ prefetch={false}
38
+ size="sm"
39
+ target="_self"
40
+ variant="link.inline"
41
+ >
42
+ {link.title}
43
+ </Link>
44
+ </li>
45
+ ))}
46
+ </ul>
47
+ </section>
48
+ ) : null,
49
+ )}
50
+ </div>
51
+ </section>
52
+ );
53
+ }
@@ -0,0 +1,60 @@
1
+ "use client";
2
+
3
+ import type { MouseEvent, ReactNode } from "react";
4
+
5
+ /** Next's routing-level error document has no app-router tree to navigate from. */
6
+ export default function NotFoundNavigation({
7
+ children,
8
+ }: {
9
+ children: ReactNode;
10
+ }) {
11
+ function navigate(event: MouseEvent<HTMLDivElement>) {
12
+ if (
13
+ event.defaultPrevented ||
14
+ event.button !== 0 ||
15
+ event.altKey ||
16
+ event.ctrlKey ||
17
+ event.metaKey ||
18
+ event.shiftKey
19
+ )
20
+ return;
21
+
22
+ const target = event.target as Node;
23
+ const element =
24
+ target.nodeType === 1 ? (target as Element) : target.parentElement;
25
+ const link = element?.closest<HTMLAnchorElement>("a[href]");
26
+ const view = event.currentTarget.ownerDocument.defaultView;
27
+ if (
28
+ !link ||
29
+ !view ||
30
+ link.hasAttribute("download") ||
31
+ (link.target && link.target !== "_self")
32
+ )
33
+ return;
34
+
35
+ let destination: URL;
36
+ try {
37
+ destination = new URL(link.href, view.location.href);
38
+ } catch {
39
+ return;
40
+ }
41
+ if (
42
+ !["http:", "https:"].includes(destination.protocol) ||
43
+ destination.origin !== view.location.origin ||
44
+ (destination.pathname === view.location.pathname &&
45
+ destination.search === view.location.search &&
46
+ destination.hash)
47
+ )
48
+ return;
49
+
50
+ event.preventDefault();
51
+ event.stopPropagation();
52
+ view.location.assign(destination.href);
53
+ }
54
+
55
+ return (
56
+ <div className="contents" onClickCapture={navigate}>
57
+ {children}
58
+ </div>
59
+ );
60
+ }
@@ -1,3 +1,7 @@
1
+ import {
2
+ createNotFoundTextResponse,
3
+ resolveNotFoundScope,
4
+ } from "@publish.os/nextjs/kernel/routes/not-found";
1
5
  import { agentRouteConfig } from "@source/.publishos/agent-route-config.generated";
2
6
  import type { NextRequest } from "next/server";
3
7
  import { NextResponse } from "next/server";
@@ -5,8 +9,12 @@ import { NextResponse } from "next/server";
5
9
  const AGENT_USER_AGENT_PATTERN =
6
10
  /\b(?:axios|got|node-fetch|undici|curl|wget|python-requests|httpx|aiohttp|claude-code|claudebot|claude|cursor|chatgpt|openai|perplexity|anthropic-ai)\b/i;
7
11
  const INTERNAL_MARKDOWN_PREFIX = "/__publishos/md";
8
- const PUBLIC_FILE_PATTERN = /\.[a-z0-9]{1,12}$/i;
12
+ const FRAMEWORK_PREFIX = "__PUBLISHOS_FRAMEWORK_PREFIX__";
9
13
  const TEXT_METHODS = new Set(["GET", "HEAD"]);
14
+ const PUBLIC_PATHS = new Set<string>(agentRouteConfig.publicPaths);
15
+ const MARKDOWN_PATHS = new Set<string>(agentRouteConfig.markdownPaths);
16
+ const OUTPUT_PATHS = new Set<string>(agentRouteConfig.outputPaths);
17
+ const NOT_FOUND_SCOPE_HEADER = "x-publishos-not-found-scope";
10
18
 
11
19
  function debugAgentRoute(
12
20
  event: string,
@@ -46,29 +54,6 @@ function markdownAlternatePath(pathname: string): string {
46
54
  return normalized.endsWith(".md") ? normalized : `${normalized}.md`;
47
55
  }
48
56
 
49
- function markdownNotFoundBody(): string {
50
- const links = [
51
- agentRouteConfig.discoveryLinks.home
52
- ? `> Home: ${agentRouteConfig.discoveryLinks.home}`
53
- : undefined,
54
- agentRouteConfig.discoveryLinks.sitemap
55
- ? `> Sitemap: ${agentRouteConfig.discoveryLinks.sitemap}`
56
- : undefined,
57
- agentRouteConfig.discoveryLinks.llms
58
- ? `> LLMS index: ${agentRouteConfig.discoveryLinks.llms}`
59
- : undefined,
60
- ].filter((line): line is string => Boolean(line));
61
-
62
- return [
63
- "# Not found",
64
- "",
65
- ...links,
66
- "",
67
- "The requested Markdown page was not found.",
68
- "",
69
- ].join("\n");
70
- }
71
-
72
57
  function explicitAcceptQuality(accept: string, mimeType: string): number {
73
58
  if (!accept) return 0;
74
59
 
@@ -100,8 +85,10 @@ function explicitAcceptQuality(accept: string, mimeType: string): number {
100
85
  return bestQuality;
101
86
  }
102
87
 
103
- function wantsMarkdown(request: NextRequest): boolean {
104
- if (!TEXT_METHODS.has(request.method)) return false;
88
+ function negotiatedTextType(
89
+ request: NextRequest,
90
+ ): "text/markdown; charset=utf-8" | "text/plain; charset=utf-8" | undefined {
91
+ if (!TEXT_METHODS.has(request.method)) return undefined;
105
92
 
106
93
  const accept = request.headers.get("accept") ?? "";
107
94
  const markdownQuality = agentRouteConfig.markdownAccept
@@ -112,12 +99,13 @@ function wantsMarkdown(request: NextRequest): boolean {
112
99
  : 0;
113
100
  const htmlQuality = explicitAcceptQuality(accept, "text/html");
114
101
 
115
- if (markdownQuality > 0 && markdownQuality >= htmlQuality) return true;
102
+ if (markdownQuality > 0 && markdownQuality >= htmlQuality) {
103
+ return "text/markdown; charset=utf-8";
104
+ }
116
105
  if (plainQuality > 0 && htmlQuality === 0 && markdownQuality === 0) {
117
- return true;
106
+ return "text/plain; charset=utf-8";
118
107
  }
119
-
120
- return false;
108
+ return undefined;
121
109
  }
122
110
 
123
111
  function looksLikeAgent(request: NextRequest): boolean {
@@ -139,7 +127,7 @@ function markdownRequestReason(
139
127
  ) {
140
128
  return "extension";
141
129
  }
142
- if (wantsMarkdown(request)) return "accept";
130
+ if (negotiatedTextType(request)) return "accept";
143
131
  if (looksLikeAgent(request)) return "agent";
144
132
  return undefined;
145
133
  }
@@ -152,13 +140,11 @@ function isInternalMarkdownRoute(pathname: string): boolean {
152
140
  }
153
141
 
154
142
  function isStaticOrMetadataRoute(pathname: string): boolean {
143
+ if (OUTPUT_PATHS.has(normalizePagePath(pathname))) return true;
155
144
  if (
156
- pathname.startsWith("/_next/") ||
157
- pathname.startsWith("/_publishos/assets/") ||
158
- pathname.startsWith("/_publishos/favicon/") ||
159
- pathname.startsWith("/_publishos/logo/") ||
160
- pathname.startsWith("/_publishos/og/") ||
161
- pathname.startsWith("/assets/")
145
+ pathname === FRAMEWORK_PREFIX ||
146
+ pathname.startsWith(`${FRAMEWORK_PREFIX}/`) ||
147
+ pathname.startsWith("/_next/")
162
148
  ) {
163
149
  return true;
164
150
  }
@@ -176,21 +162,29 @@ function isStaticOrMetadataRoute(pathname: string): boolean {
176
162
  return true;
177
163
  }
178
164
 
179
- return PUBLIC_FILE_PATTERN.test(pathname) && !pathname.endsWith(".md");
165
+ return false;
180
166
  }
181
167
 
182
- function rewriteToMarkdown(
183
- request: NextRequest,
184
- reason: "accept" | "agent" | "extension",
185
- ): NextResponse {
186
- const url = request.nextUrl.clone();
168
+ function rewriteToMarkdown({
169
+ request,
170
+ reason,
171
+ requestHeaders,
172
+ }: {
173
+ request: NextRequest;
174
+ reason: "accept" | "agent" | "extension";
175
+ requestHeaders: Headers;
176
+ }): NextResponse {
177
+ const url = new URL(request.url);
187
178
  url.pathname =
188
179
  normalizePagePath(url.pathname) === "/"
189
180
  ? "/sitemap.md"
190
181
  : markdownRoutePath(url.pathname);
182
+ const revision = request.nextUrl.searchParams.get("publishos_revision");
183
+ url.search = "";
184
+ url.hash = "";
185
+ if (revision) url.searchParams.set("publishos_revision", revision);
191
186
 
192
- const requestHeaders = new Headers(request.headers);
193
- requestHeaders.set("x-publishos-agent-route", "markdown");
187
+ requestHeaders.set("accept", "text/markdown");
194
188
 
195
189
  debugAgentRoute("md", {
196
190
  accept: request.headers.get("accept") ?? undefined,
@@ -242,18 +236,28 @@ function withDiscoveryHeaders(
242
236
 
243
237
  export function proxy(request: NextRequest) {
244
238
  const { pathname } = request.nextUrl;
239
+ const requestHeaders = new Headers(request.headers);
240
+ requestHeaders.delete(NOT_FOUND_SCOPE_HEADER);
241
+ requestHeaders.delete("x-publishos-agent-route");
242
+
243
+ if (pathname === "/_not-found") {
244
+ requestHeaders.set(NOT_FOUND_SCOPE_HEADER, "none");
245
+ const response = NextResponse.next({
246
+ request: { headers: requestHeaders },
247
+ });
248
+ response.headers.set("x-robots-tag", "noindex");
249
+ response.headers.set("vary", "Accept, User-Agent");
250
+ return response;
251
+ }
245
252
 
246
253
  if (isInternalMarkdownRoute(pathname)) {
247
- if (request.headers.get("x-publishos-agent-route") === "markdown") {
248
- return NextResponse.next();
249
- }
250
254
  if (!agentRouteConfig.blockInternalMarkdownRoute) {
251
255
  debugAgentRoute("pass", {
252
256
  method: request.method,
253
257
  path: pathname,
254
258
  reason: "allowed-internal",
255
259
  });
256
- return NextResponse.next();
260
+ return NextResponse.next({ request: { headers: requestHeaders } });
257
261
  }
258
262
 
259
263
  debugAgentRoute("md", {
@@ -263,15 +267,7 @@ export function proxy(request: NextRequest) {
263
267
  status: 404,
264
268
  });
265
269
 
266
- return new Response(markdownNotFoundBody(), {
267
- status: 404,
268
- headers: {
269
- "cache-control": "public, max-age=60, s-maxage=300",
270
- "content-type": "text/markdown; charset=utf-8",
271
- "x-content-type-options": "nosniff",
272
- "x-robots-tag": "noindex",
273
- },
274
- });
270
+ return createNotFoundTextResponse({ method: request.method });
275
271
  }
276
272
 
277
273
  if (isStaticOrMetadataRoute(pathname)) {
@@ -280,12 +276,93 @@ export function proxy(request: NextRequest) {
280
276
  path: pathname,
281
277
  reason: "static-or-metadata",
282
278
  });
283
- return NextResponse.next();
279
+ return NextResponse.next({ request: { headers: requestHeaders } });
284
280
  }
285
281
 
286
282
  const markdownReason = markdownRequestReason(request);
287
283
  if (markdownReason) {
288
- return rewriteToMarkdown(request, markdownReason);
284
+ let htmlPath: string | undefined;
285
+ try {
286
+ const decoded = normalizePagePath(decodeURIComponent(pathname));
287
+ htmlPath = decoded.endsWith(".md")
288
+ ? decoded.slice(0, -3) || "/"
289
+ : decoded;
290
+ } catch {
291
+ htmlPath = undefined;
292
+ }
293
+ const scope = resolveNotFoundScope({
294
+ scopes: agentRouteConfig.notFoundScopes,
295
+ pathname,
296
+ });
297
+ const hasArtifact =
298
+ htmlPath === "/"
299
+ ? OUTPUT_PATHS.has("/sitemap.md")
300
+ : htmlPath !== undefined && MARKDOWN_PATHS.has(htmlPath);
301
+ if (!scope || !hasArtifact) {
302
+ const response = createNotFoundTextResponse({
303
+ scope,
304
+ pathname,
305
+ method: request.method,
306
+ contentType:
307
+ markdownReason === "accept" ? negotiatedTextType(request) : undefined,
308
+ vary: markdownReason !== "extension",
309
+ });
310
+ if (!agentRouteConfig.enabled || !agentRouteConfig.discoveryHeaders) {
311
+ for (const name of [
312
+ "link",
313
+ "x-publishos-llms",
314
+ "x-publishos-sitemap",
315
+ ]) {
316
+ response.headers.delete(name);
317
+ }
318
+ }
319
+ return response;
320
+ }
321
+ return rewriteToMarkdown({
322
+ request,
323
+ reason: markdownReason,
324
+ requestHeaders,
325
+ });
326
+ }
327
+
328
+ let pagePath: string;
329
+ try {
330
+ pagePath = normalizePagePath(decodeURIComponent(pathname));
331
+ } catch {
332
+ pagePath = pathname;
333
+ }
334
+ if (!PUBLIC_PATHS.has(pagePath)) {
335
+ // Select Next's error route before an unknown catch-all can stream.
336
+ const scope = resolveNotFoundScope({
337
+ scopes: agentRouteConfig.notFoundScopes,
338
+ pathname,
339
+ });
340
+ requestHeaders.set(NOT_FOUND_SCOPE_HEADER, scope?.id ?? "none");
341
+ const url = new URL(request.url);
342
+ url.pathname = "/_not-found";
343
+ url.search = "";
344
+ url.hash = "";
345
+ const response = NextResponse.rewrite(url, {
346
+ status: 404,
347
+ request: { headers: requestHeaders },
348
+ });
349
+ const recoveryHeaders = createNotFoundTextResponse({
350
+ scope,
351
+ pathname,
352
+ method: request.method,
353
+ vary: true,
354
+ }).headers;
355
+ for (const name of [
356
+ "x-robots-tag",
357
+ "vary",
358
+ ...(agentRouteConfig.enabled && agentRouteConfig.discoveryHeaders
359
+ ? ["link", "x-publishos-llms", "x-publishos-sitemap"]
360
+ : []),
361
+ ]) {
362
+ const value = recoveryHeaders.get(name);
363
+ if (value) response.headers.set(name, value);
364
+ }
365
+ return response;
289
366
  }
290
367
 
291
368
  if (!agentRouteConfig.enabled || !agentRouteConfig.discoveryHeaders) {
@@ -296,7 +373,7 @@ export function proxy(request: NextRequest) {
296
373
  ? "discovery-disabled"
297
374
  : "agent-routes-disabled",
298
375
  });
299
- return NextResponse.next();
376
+ return NextResponse.next({ request: { headers: requestHeaders } });
300
377
  }
301
378
 
302
379
  debugAgentRoute("html", {
@@ -307,11 +384,12 @@ export function proxy(request: NextRequest) {
307
384
  userAgent: request.headers.get("user-agent") ?? undefined,
308
385
  });
309
386
 
310
- return withDiscoveryHeaders(NextResponse.next(), pathname);
387
+ return withDiscoveryHeaders(
388
+ NextResponse.next({ request: { headers: requestHeaders } }),
389
+ pathname,
390
+ );
311
391
  }
312
392
 
313
393
  export const config = {
314
- matcher: [
315
- "/((?!_next/static|_next/image|_next/webpack-hmr|_publishos/assets|_publishos/favicon|_publishos/logo|_publishos/og|assets).*)",
316
- ],
394
+ matcher: ["/__PUBLISHOS_MATCHER__"],
317
395
  };
@@ -6,9 +6,9 @@ import {
6
6
  ROOT_FORCED_MODE,
7
7
  routeThemes,
8
8
  } from "@source/.theme.generated";
9
- import { useSelectedLayoutSegments } from "next/navigation";
9
+ import { usePathname } from "next/navigation";
10
10
  import { ThemeProvider as NextThemes } from "next-themes";
11
- import type { ReactNode } from "react";
11
+ import { type ReactNode, Suspense, useLayoutEffect, useState } from "react";
12
12
 
13
13
  type ForcedMode = "dark" | "light" | "none";
14
14
  type RouteTheme = {
@@ -16,24 +16,20 @@ type RouteTheme = {
16
16
  forcedMode?: ForcedMode;
17
17
  };
18
18
 
19
- function selectedPathFromSegments(segments: readonly string[]): string {
20
- const path = segments
21
- .filter((segment) => segment !== "" && !segment.startsWith("("))
22
- .join("/");
23
-
24
- return path ? `/${path}` : "/";
25
- }
26
-
27
19
  function resolveRouteTheme(
28
- segments: readonly string[],
20
+ pathname: string,
21
+ themes: Readonly<Record<string, RouteTheme>>,
29
22
  ): RouteTheme | undefined {
30
- const selectedPath = selectedPathFromSegments(segments);
31
23
  let match: RouteTheme | undefined;
32
24
  let matchLength = 0;
33
25
 
34
- const entries = Object.entries(routeThemes) as Array<[string, RouteTheme]>;
26
+ const entries = Object.entries(themes);
35
27
  for (const [basePath, routeTheme] of entries) {
36
- if (selectedPath === basePath || selectedPath.startsWith(`${basePath}/`)) {
28
+ if (
29
+ basePath === "/" ||
30
+ pathname === basePath ||
31
+ pathname.startsWith(`${basePath}/`)
32
+ ) {
37
33
  if (basePath.length > matchLength) {
38
34
  match = routeTheme;
39
35
  matchLength = basePath.length;
@@ -50,8 +46,57 @@ function effectiveForcedMode(routeTheme: RouteTheme | undefined): ForcedMode {
50
46
  return ROOT_FORCED_MODE ?? "none";
51
47
  }
52
48
 
49
+ function RouteThemeSync({
50
+ onChange,
51
+ }: {
52
+ onChange: (theme: RouteTheme | undefined) => void;
53
+ }) {
54
+ const routeTheme = resolveRouteTheme(usePathname(), routeThemes);
55
+ useLayoutEffect(() => onChange(routeTheme), [onChange, routeTheme]);
56
+ return null;
57
+ }
58
+
59
+ function initialRouteTheme(): RouteTheme | undefined {
60
+ return typeof window === "undefined"
61
+ ? undefined
62
+ : resolveRouteTheme(window.location.pathname, routeThemes);
63
+ }
64
+
65
+ // Both bootstrap and hydration use the same compiler-generated route mapping.
66
+ const initialThemeScript = `{
67
+ const route = (${resolveRouteTheme.toString()})(location.pathname, ${JSON.stringify(routeThemes).replaceAll("<", "\\u003c")});
68
+ document.currentScript.parentElement.setAttribute("data-group", route?.group ?? "");
69
+ const forcedMode = route?.forcedMode ?? ${JSON.stringify(ROOT_FORCED_MODE) ?? "undefined"};
70
+ let mode = ${JSON.stringify(ROOT_DEFAULT_MODE)};
71
+ try { mode = localStorage.getItem("theme") || mode; } catch {}
72
+ if (forcedMode && forcedMode !== "none") mode = forcedMode;
73
+ if (mode === "system") mode = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
74
+ if (mode === "light" || mode === "dark") {
75
+ const root = document.documentElement;
76
+ root.classList.remove("light", "dark");
77
+ root.classList.add(mode);
78
+ root.style.colorScheme = mode;
79
+ }
80
+ }`;
81
+
82
+ export function PublishOSThemeScope({
83
+ children,
84
+ group,
85
+ }: {
86
+ children: ReactNode;
87
+ group: string;
88
+ }) {
89
+ return (
90
+ <ThemeScopeProvider group={group}>
91
+ <div className="contents" data-theme-root="" data-group={group}>
92
+ {children}
93
+ </div>
94
+ </ThemeScopeProvider>
95
+ );
96
+ }
97
+
53
98
  export function PublishOSThemeProvider({ children }: { children: ReactNode }) {
54
- const routeTheme = resolveRouteTheme(useSelectedLayoutSegments());
99
+ const [routeTheme, setRouteTheme] = useState(initialRouteTheme);
55
100
  const forcedMode = effectiveForcedMode(routeTheme);
56
101
  const forcedTheme = forcedMode === "none" ? undefined : forcedMode;
57
102
 
@@ -62,13 +107,29 @@ export function PublishOSThemeProvider({ children }: { children: ReactNode }) {
62
107
  disableTransitionOnChange
63
108
  enableSystem
64
109
  forcedTheme={forcedTheme}
110
+ scriptProps={{
111
+ type: typeof window === "undefined" ? "text/javascript" : "text/plain",
112
+ }}
65
113
  >
66
114
  <ThemeScopeProvider group={routeTheme?.group}>
67
115
  <div
68
116
  className="contents"
117
+ id="publish-os-theme-root"
69
118
  data-theme-root=""
70
119
  data-group={routeTheme?.group ?? ""}
120
+ suppressHydrationWarning
71
121
  >
122
+ <script
123
+ type={
124
+ typeof window === "undefined" ? "text/javascript" : "text/plain"
125
+ }
126
+ suppressHydrationWarning
127
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Compiler-only route metadata escapes HTML; the script must run before paint.
128
+ dangerouslySetInnerHTML={{ __html: initialThemeScript }}
129
+ />
130
+ <Suspense fallback={null}>
131
+ <RouteThemeSync onChange={setRouteTheme} />
132
+ </Suspense>
72
133
  {children}
73
134
  </div>
74
135
  </ThemeScopeProvider>
@@ -4,10 +4,16 @@ import {
4
4
  createRootPage,
5
5
  } from "@publish.os/nextjs/kernel/routes/pages/root";
6
6
  import { ogManifest } from "@source/.publishos/og-manifest.generated";
7
- import RootPage from "@source/page";
7
+ import RootPage from "@source/index";
8
8
  import type { Metadata } from "next";
9
9
 
10
10
  const route = { manifest, ogManifest, RootPage } as const;
11
11
 
12
12
  export default createRootPage(route);
13
13
  export const metadata: Metadata = createRootMetadata(route);
14
+
15
+ export const ensureStatic = "navigation";
16
+
17
+ // Next 16.4 still misclassifies redirect() during instant validation.
18
+ // Keep its HTTP response; ensureStatic independently validates prerendering.
19
+ export const instant = false;
@@ -0,0 +1,30 @@
1
+ import { CodeBlock } from "@components/mdx/code-block";
2
+ import { type HTMLAttributes, isValidElement, type ReactNode } from "react";
3
+
4
+ type CodeChildProps = {
5
+ children?: ReactNode;
6
+ className?: string;
7
+ title?: string;
8
+ };
9
+
10
+ export function FencedCodeBlock({
11
+ children,
12
+ title,
13
+ ...props
14
+ }: HTMLAttributes<HTMLElement>) {
15
+ const codeElement = isValidElement<CodeChildProps>(children)
16
+ ? children
17
+ : null;
18
+ const code = codeElement?.props.children ?? children;
19
+ const language = /(?:^|\s)language-([^\s]+)/.exec(
20
+ codeElement?.props.className ?? "",
21
+ )?.[1];
22
+ return (
23
+ <CodeBlock
24
+ {...props}
25
+ code={typeof code === "string" ? code : ""}
26
+ language={language}
27
+ title={title ?? codeElement?.props.title}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,14 @@
1
+ import type { ComponentType, HTMLAttributes } from "react";
2
+
3
+ type HeadingTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
4
+
5
+ export function headingWithLevel(
6
+ Heading: ComponentType<
7
+ { as: HeadingTag } & HTMLAttributes<HTMLHeadingElement>
8
+ >,
9
+ as: HeadingTag,
10
+ ) {
11
+ return function HeadingLevel(props: HTMLAttributes<HTMLHeadingElement>) {
12
+ return <Heading as={as} {...props} />;
13
+ };
14
+ }