Product updates
The latest features, improvements, and fixes to Doccupine, newest first. Each release links to its own entry so you can share a version directly.
- Stop counting every pageview twice in generated sites with analytics enabled. The client tracker captured a
$pageviewon mount and on every route change, while the middleware independently captured one on every request the client had already counted - including the initial document load and, because only prefetches were filtered, the RSC requests behind client-side navigations. Both events were plain$pageviews distinguished solely by a_server_sideproperty that nothing dedupes on, so default dashboards reported roughly double the real traffic. The two layers now cover disjoint navigation types: the middleware skips any request carrying theRSCheader and so owns document loads, and the client tracker skips its first effect run and so owns soft navigations. Expect pageview and session counts to fall by roughly half after upgrading - that is the correction, not a regression. Historical data before the upgrade remains inflated - Stop inventing a new anonymous person on every request from a reader whose PostHog cookie is missing. The middleware fell back to
crypto.randomUUID()whenever it could not readph_<key>_posthog, which is the case for every ad-blocked reader on every request and for every reader's very first request - so a single person browsing ten pages could appear as ten unique visitors, inflating unique counts and the billable person volume on your PostHog project. Readers now get a stable first-partydcp_anon_idcookie (httpOnly, one year,SameSite=Lax), so one anonymous reader is one person for as long as the cookie survives. The cookie is written on every middleware exit path, including the password gate's rewrite and redirect, so a reader held at the gate cannot loop minting fresh ids. Note this means an analytics-enabled site now sets a first-party cookie where it previously set none; the analytics documentation has been updated to say so - Forward the reader's PostHog session and device ids from the client cookie onto server-captured pageviews, so a server event joins the session the browser already started instead of appearing outside every session
- Point
ui_hostat the PostHog dashboard rather than the ingestion endpoint. It was being handed thehostvalue fromanalytics.json(https://eu.i.posthog.com), so the toolbar and "view in PostHog" links pointed at an ingestion origin that serves no UI; it is now derived by dropping the.i.segment, leaving self-hosted instances (which serve both from one origin) untouched - Correct the analytics documentation, which described the tracking model inaccurately in two directions. The platform analytics page claimed page views are tracked client-side and that "no data is sent directly to PostHog", both of which were false - server-side capture exists, and it posts straight from your server to PostHog, since the
/ingestproxy only ever covered the browser. The CLI analytics page documented both layers but presented the duplication as a feature ("two layers of tracking") without noting counts were doubled, and repeated a "no third-party domains appear in network requests" claim true only of the browser half - Drop
@posthog/reactfrom generated sites' dependencies. It was declared but never imported by any generated file
- Emit a per-page markdown counterpart for the home page, the only doc page that never got one: the generator writes a
.mdfile per page into the generated app'spublic/directory alongsidellms.txtandllms-full.txt, butgenerateSlug()returns an empty string forindex.mdx, which would have produced the filename.md(a dotfile), so the home page was skipped outright. Its counterpart is now namedindex.mdand served at/index.md. Every other page keeps its existing name, so a directory index still collapses to its parent slug (platform/index.mdxstill writespublic/platform.md), and the new file is registered in the stale-file manifest so rename and delete cleanup applies to it like any other page - Document the File Editor's component insertion, live preview, and media directories on the platform documentation page: how the toolbar menu, the
/shortcut, and<autocomplete insert components (and why nested-only components stay out of the menu), how the Code, Split, and Preview modes render components, repository images, and Mermaid diagrams, and how directory browsing, directory-scoped uploads, and copy path work, including the hidden placeholder file Git requires to track an otherwise empty directory - Drop the blank line between the closing frontmatter delimiter and the first heading across all 49 starter MDX documentation templates, so scaffolded docs open directly on their first heading; frontmatter parsing is unaffected, leaving the generated pages and navigation identical
- Update generated app dependencies lucide-react to ^1.25.0, posthog-js to ^1.404.1, posthog-node to ^5.45.2, and styled-components to ^6.4.4
- Render Mermaid diagrams in the generated docs: a
mermaidfenced code block now becomes a themed SVG diagram, with properties set on the opening fence -placementputs the interactive controls in any corner (top-left,top-right,bottom-left,bottom-right; defaultbottom-right) andactionsshows or hides them, overriding the default of appearing only once a diagram is taller than 120px. Readers can zoom, pan, drag the diagram with the mouse, reset the view, and open it full screen. Diagrams are rendered at build time bybeautiful-mermaidinside aserver-onlymodule, so pages stayforce-staticand neither the library nor its elkjs layout engine reaches the browser (they would otherwise add roughly 537 KB gzip to the client bundle); the client ships only the zoom/pan chrome. Diagram colors resolve through new--mermaid-*CSS custom properties derived from the site palette, so one build-time render serves both modes and the theme toggle recolors diagrams through the cascade with no re-render, and any of them can be overridden to restyle diagrams without touching the palette - Support six Mermaid diagram types - flowchart, sequence, state, class, entity relationship, and XY chart - and degrade the rest gracefully:
beautiful-mermaidis an independent implementation rather than a Mermaid wrapper, sopie,gantt,mindmap,journey, andgitGraphare unsupported and throw, as does any syntax error. Because doc pages are statically rendered, an uncaught throw would fail the whole page's build, so an unrenderable diagram now falls back to a plain code block showing its source and logs a warning naming the offending file, letting a build always finish. ELK is the layout engine for every diagram, and a%%{init: ...}%%directive is read as a comment and ignored rather than erroring, so diagrams ported from other tools still render. A new Mermaid documentation page covers the syntax, the supported types, the properties, and the theming variables - Carry a fenced code block's meta string (the text after the language on the opening fence) through to the component that renders it:
mdast-util-to-hastdrops it when building<pre><code>, so a new rehype plugin copies it onto the code element, where MDX passes it through as a plain string that is parsed rather than evaluated - Redesign the generated site's header logo as a white mascot on a solid blue circle, and keep the rest of a logo correctly themed: the header's fill override now targets any element carrying a
fillattribute instead of onlypath, so non-path SVG shapes (circle,rect,g) also pick up the primary theme color, while the mascot artwork is markedignore-filland excluded from that override so it stays white rather than being recolored - Add a "What is Doccupine" page to the Getting Started section of the starter documentation
- Add generated app dependency beautiful-mermaid ^1.1.3, and update @langchain/core to ^1.2.3, posthog-js to ^1.403.0, posthog-node to ^5.45.1, @typescript-eslint/eslint-plugin and @typescript-eslint/parser to ^8.64.0, and tsx to ^4.23.1
- Keep each
<Update>entry's sidebar (its label and description) pinned in view while its content scrolls past: at thelgbreakpoint, where the update renders as a two-column row, the sidebar is nowposition: stickyattop: 80px(matching the site'sscroll-padding-topheader offset) withalign-self: flex-startso the flex row no longer stretches it to full height and leaves it room to travel. Belowlgthe layout still stacks, so the sticky behavior applies only to the desktop two-column view
- Let the
Card,Step, andUpdatecomponents omit their title/description text so no empty element is rendered when it is missing:Card'stitleandStep'stitleare now optional (the title element renders only when a title is present, or for a step when a title or icon is present), andUpdate'sdescriptionis now optional (the description line renders only when provided), so a title-less card or step no longer leaves an empty bold line and a description-less update no longer leaves an empty styled box consuming the sidebar gap. The Cards, Steps, and Update docs pages mark these props optional - Stop a label-less
<Update>from crashing the generated page: the shared heading slugger is now fed only when an<Update>actually has alabel(the injected anchor id falls back toundefinedotherwise), andslugifytolerates nullish input, so the shared helper can no longer throw aTypeErroron an undefined label and blank the whole page
- Let the search modal hand its query straight to the AI assistant: when chat is enabled, the modal shows a desktop "Ask AI" button (advertising an Option+Enter shortcut) that submits the typed query to the assistant and closes the modal, opening the chat panel on the answer. A new
askAssistantbridge on the chat context submits immediately when the assistant is idle, or - if a response is already streaming - opens the panel and pre-fills the input so the question is ready to send the moment the current answer finishes. The greeting is seeded consistently as the first turn (via a sharedINITIAL_GREETING) so a handed-off question always reads against a started conversation - Open search results without leaving the page: Cmd/Ctrl+Enter (or Cmd/Ctrl+click) opens the highlighted result in a new background tab, and Shift+Enter (or Shift+click) opens it in a separate, centered browser window, both with
noopener,noreferrerso the new context can't reach back throughwindow.opener; plain Enter/click still navigates in place within the app - Replace the search results' per-item background fade with a single highlight that glides between rows: the active item is measured in a pre-paint
useLayoutEffectand the indicator is positioned and sized from that measurement, so it slides ontransform/heightas the selection moves while committing its first position without an entrance animation, and the active result's title tints to the primary color.prefers-reduced-motiondisables the glide - Scroll the active result into view in the same pre-paint pass that positions the highlight - adjusting the list's
scrollTopdirectly instead ofscrollIntoView(which could also pan ancestors) - so the highlight and the scrolled row can never desync into a one-frame flicker, and re-filtering on a new query re-scrolls the reset selection into view even when its index is unchanged - Surface the keyboard-shortcut hint on the theme toggle in the sidebar and the password-gate footer via the new cherry-styled-components
ThemeToggle $shortcutprop - Add a dedicated Model Context Protocol (MCP) documentation page covering ready-to-paste connection snippets for Claude, Cursor, and other MCP-compatible apps, and MCP server authentication (the
DOCS_API_KEYBearer-token guard); move the MCP authentication details off the AI Assistant page onto it, leaving the AI Assistant page pointing readers to the new page - Update generated app dependencies: cherry-styled-components to ^0.2.11 (for the
ThemeToggle $shortcutprop), posthog-js to ^1.399.2, and posthog-node to ^5.41.0
- Fix a blank gap appearing above the "Answering..." loader in the generated docs chat between sending a question and the first streamed token: the RAG client (
Chat.ts) used to insert an empty answer bubble ({ text: "", answer: true }) as soon as the response headers arrived, and that bubble'smargin: 20px 0rendered as an empty block sitting on top of the loader while the assistant was still connecting. The answer bubble is now created lazily on the first streamed token (or the done event for an empty response), and the loader shows only while the last message is still the user's question, so "Answering..." sits flush with no gap and is cleanly replaced by the text the moment it starts appearing
- Fix the generated docs chat hanging forever (connecting, streaming keep-alive heartbeats, but never answering) when a deployment's prebuilt embeddings index is missing on a large doc set: 0.0.119 began shipping a precomputed
services/mcp/docs-index.json, but the build-time embed step fails soft, so a build without an embedding API key - or one whose provider/model/dimsno longer match the current LLM config - leaves no usable index, and the MCP server then fell back to embedding the entire doc set on demand inside a single/api/ragrequest. That fallback runs one embedding round trip per batch and, past a few hundred chunks, cannot finish within a serverless function's time limit, so the request stalled until the platform timeout instead of ever answering. The server now caps on-demand embedding in production atRAG_RUNTIME_EMBED_MAX_CHUNKSchunks (default 400) and, above that, fails fast with a newIndexNotBuiltErrorwhose message ("The AI assistant is temporarily unavailable ... redeploy with an embedding API key available at build time") surfaces to the client as an SSEerrorevent, so the chat shows a clear message instead of hanging, and a client-forcedrefreshcan no longer burn embedding quota re-embedding a too-large set.next devhas no time cap, so local development keeps embedding on demand and works without running the build; the newRAG_RUNTIME_EMBED_MAX_CHUNKSvariable is overridable per deployment (0always requires a prebuilt index) and is parsed defensively so a bad value falls back to the default rather than silently disabling the guard - Make a missing or broken index diagnosable without server-log access:
getIndexStatusandGET /api/ragnow return areasonfield (null when healthy) carrying the actionable message about the missing or provider/model/dims-mismatched index, and a failed eager index build at server startup no longer surfaces as anunhandledRejection- the first request retries viaensureDocsIndexand returns a real error to the client
- Stop the generated docs chat from idling on large documentation sets by shrinking the precomputed embeddings index roughly 20x (a 5,000-chunk index drops from ~150 MB to ~8 MB): 0.0.119 shipped
services/mcp/docs-index.jsonwith every chunk's embedding stored as a raw JSON float array, so at the default 1,536-dimension model a few hundred pages produced a 100 MB+ file thatJSON.parse'd into millions of boxed numbers on every serverless cold start; the function then OOM'd or stalled for seconds (the chat connected and emitted its keep-alive but never answered) and the index pressed against Vercel's 250 MB function-size limit because it is bundled into both/api/ragand/api/mcp. A new sharedservices/mcp/vector.tsnow applies the same two transforms at build time and at query time so stored and query vectors can never diverge: each vector is Matryoshka-truncated toLLM_EMBEDDING_DIMS(default 512) and renormalized (text-embedding-3-*andgemini-embedding-001are MRL-trained, so a renormalized prefix is a valid lower-dimension embedding), then quantized to int8 and stored as base64. Because cosine similarity is scale-invariant, the per-vector quantization scale never has to be stored and search scores the float query vector directly against the int8 vector with no dequantization step, so retrieval ranking is preserved: the int8 top result matches the full-precision top result for the overwhelming majority of queries, with negligible score error - Guard the compact index against transform drift and make it tunable:
docs-index.jsonnow records itsdimsandquantizationalongside the existing provider and model, and the MCP server rejects a precomputed index whose transform no longer matches the current config, falling back to on-demand embedding that reduces and quantizes identically so both code paths score the same. A new optionalLLM_EMBEDDING_DIMSenvironment variable (default 512; lower trades a little recall for a smaller index, and values at or above the model's native dimension keep full precision) is documented in.env.example, and the Model Context Protocol docs describe the new compaction step
- Precompute the generated docs' chat embeddings at build time so the AI assistant no longer re-embeds the entire doc set on every serverless cold start, which was the root cause of slow first chats and the proxy timeouts they triggered: a new
scripts/build-docs-index.mtsstep embeds every doc chunk in batches and writesservices/mcp/docs-index.jsonbeforenext build, the MCP server loads that precomputed index at runtime and only falls back to re-embedding when the file is missing or its provider/model no longer match the current LLM config, andnext.config.tsbundles the index into the/api/ragand/api/mcpserverless functions viaoutputFileTracingIncludes(a dynamicfsread is otherwise invisible to Next's file tracing). The embed step fails soft (a missing key or API error exits 0) so the build always proceeds, and the generateddocs-index.jsonartifact is git- and Prettier-ignored. Update the Model Context Protocol docs to describe the new build-time indexing: embeddings are precomputed and shipped with the site and loaded on startup, runtime embedding is only a fallback for a missing index or a provider/model mismatch, and refreshing content is a rebuild or redeploy rather than a server restart - Fix the generated docs chat intermittently failing with an HTTP 524 and an
Unexpected token '<' ... is not valid JSONerror: the RAG route (app/api/rag/route.ts) used to run all indexing, semantic search, and LLM streaming before returning aResponse, so on cold starts it exceeded edge-proxy time-to-first-byte windows and the proxy returned an HTML gateway page the client then tried to parse as JSON. The route now returns the SSE streaming response immediately and moves all slow work inside the stream'sstart()callback, flushing a: connectedheartbeat as the first byte plus a 15s keep-alive so proxies never hit their TTFB timeout; request-body and LLM-config validation stays up front so genuine client and configuration errors still return real 400/500 codes, and errors raised after headers are sent surface as SSEerrorevents. On the client,Chat.tsnow inspects the response content type instead of blindly callingres.json(), showing a friendly "took too long, please try again" retry message for gateway/HTML error pages rather than throwing a JSON parse error
- Let long spaceless inline
code/kbdtokens wrap in the generated docs instead of overflowing the page: a URL or path with no spaces (for example a long AWS console link likehttps://{region}.console.aws.amazon.com/.../catalog-info.yaml) has no legal break point, sohyphens: autocould not break it and the token pushed the document wider than the viewport; the inlinecode/kbdrule now setsoverflow-wrap: anywhereso the browser breaks inside such a token only when it would otherwise overflow. This completes the 0.0.113 fix, which droppedwhite-space: preto allow wrapping at spaces but still left spaceless tokens overflowing
- Fix numbered lists so multi-digit numbers render cleanly: the number marker is now an in-flow element sized to its content with a hanging indent instead of a fixed-width gutter, so
1.,10., and100.keep a consistent gap and never overlap the item's text. Nested ordered lists also number independently now instead of continuing the parent list's count - Align the unordered- and ordered-list indents so bullets and numbers share the same 24px gutter, and recenter the bullet dot within it
- Theme the generated docs code block from the site's palette instead of a fixed GitHub look, so it matches the rest of the app and picks up a custom brand theme in both light and dark modes: the outer frame and window-bar divider now use the same
grayLightborder as the sidebar and footer, the dark-mode surface uses the left sidebar's translucentprimaryLighttint instead of GitHub's#0d1117so the code window shares the nav's background, and the copy button, code tabs, and centered file-name title all draw from theme tokens that swap for dark mode via the theme provider. The.hljssyntax highlighting keeps its fixed GitHub Light and Dark palettes so code stays legible regardless of the brand colors - Fix numbered lists so a wrapped item stays indented: the counter is now pinned to the left with a hanging indent instead of sitting inline, so the second and later lines of a long item align with its text rather than collapsing back under the number
- Render paragraphs inside list items inline so a list item's text sits beside its marker instead of dropping to the next line, and cap list width at 100% so wide list content no longer stretches the page
- Speed up the initial build for large documentation sets by roughly 340x (a 1,000-file build drops from ~239s to ~0.7s): the generator used to regenerate every site-wide file (pages index, root and site layout, sitemap,
llms.txt/llms-full.txt, and section-index redirects) once per MDX file, and each of those re-parsed every file, so a full build scaled quadratically with the number of docs; it now writes each page once and runs the aggregations a single time at the end, sharing a single parse of all pages across them. Output is unchanged - the generated files are byte-identical to before for full builds and for incremental add/change/delete while watching - Fix a latent ordering bug where sections were resolved before the starter sample docs that define them were written, so a first run now applies section navigation correctly in a single pass instead of relying on per-file rediscovery
- Detect pnpm reliably when it is installed as a standalone binary (for example under
PNPM_HOME/~/Library/pnpm): thewatchcommand previously probed for pnpm only through the shellPATH, so launching Doccupine from an IDE terminal or GUI that didn't inherit the interactive shell'sPATHmade it silently fall back to npm even though pnpm was installed. It now also resolves pnpm viaPNPM_HOMEand spawns it by absolute path, trustsnpm_config_user_agentwhen pnpm launched the CLI, and adds a--package-manager <pnpm|npm>flag (plus a matchingpackageManagerfield indoccupine.json) to force the choice
- Size the user's chat message to its content instead of stretching it edge to edge: the user bubble now uses
width: fit-contentand right-aligns within the panel, with roomier padding and rounded corners, while the AI answer keeps its full-width layout, so short questions read as compact right-aligned bubbles rather than full-width blocks
- Let long inline
codeandkbdtokens wrap in the generated docs instead of forcing horizontal overflow: drop thewhite-space: prerule from the inlinecode/kbdstyling so a long token (a file path, URL, or command) breaks onto the next line rather than pushing the document wider than the viewport on narrow screens
- Fix the generated app failing to deploy with an
npm installERESOLVEpeer-dependency error: the 0.0.111 migration to ESLint 10 lefteslint-plugin-react,eslint-plugin-jsx-a11y, andeslint-plugin-importon peer ranges that still cap at ESLint 9 even though they run fine under 10; installing with pnpm only warns on such mismatches, but installing with npm hard-fails. Ship a.npmrc(legacy-peer-deps=true) with the generated app so npm skips the stale peer check and matches pnpm's behavior - Update generated app dependency posthog-js to ^1.398.7
- Smoothly animate the active sidebar link into view instead of snapping to it, and honor
prefers-reduced-motionin both the nav sidebar and the "On this page" table of contents: readers who opt out of motion get instant (auto) scrolling while everyone else gets asmoothscroll - Migrate the generated app's linting to ESLint 10 with a hand-rolled flat config that replaces
eslint-config-next, which crashes under ESLint 10 because itsreact.version: "detect"detection calls the removedcontext.getFilename(); the new config composes the same React, React Hooks, Next.js, jsx-a11y, import, and@typescript-eslintplugins, pinsreact.versionto skip the removed code path, and preserves the project'sno-consoleand@typescript-eslintrules - Add a
type-checkscript (tsgo --noEmit) to the generated app backed by the@typescript/native-previewcompiler, and aliastypescripttonpm:@typescript/typescript6so Next keeps building against the classic compiler while the native preview drives type checking - Update generated app dependencies cherry-styled-components to ^0.2.10 and posthog-js to ^1.398.6
- Upgrade the CLI's TypeScript dev dependency to the native v7 compiler
- Scroll the generated docs sidebar to the active page's link when it starts off-screen (deep pages, in-content links, or search results): the sidebar nav scrolls within its own overflow area so the main document never jumps, treats links hidden behind the sticky theme-toggle footer as off-screen so they are still revealed, and marks the current link with
aria-current="page" - Add the generated Next.js
public/directory to the app's.prettierignoreso generated build output (llms.txt,llms-full.txt, and per-page markdown) is no longer reformatted by the app'sprettier --write . - Use
htmlcode fences instead ofmdxfor the component-usage examples in the starter MDX docs so the sample component tags syntax-highlight consistently in both themes; the JavaScript/JSX highlighter mis-tokenizes bare component tags and colored the first tag differently from the rest - Update generated app dependency posthog-js to ^1.398.1
- Update CLI dev dependencies (
@types/node,vitest)
- Render the search modal's inline result snippets as plain text: snippet source is extracted from the generated page files as raw MDX, so results used to show stray backticks, backslashes, and other Markdown syntax (for example
`Test`instead ofTest); a newtoPlainTextstep now undoes the template-literal escaping and strips inline code, emphasis, links, images, and heading/list markers so snippets read as clean prose - Style
<kbd>elements in the docs like inlinecode, sharing the same tinted background, padding, and rounded corners in both light and dark themes - Document the Cmd/Ctrl+I shortcut for toggling the AI assistant on the AI Assistant page, noting it activates once an LLM provider is configured
- Use "directory" consistently in place of "folder" across the starter MDX documentation
- Strip Next.js route-group segments like
(site)from the search index slug so content search hits map back to real page URLs instead of being silently dropped: the slug MiniSearch stores now matches the URL produced bytoDocPath()and the nav slugs, extending the 0.0.101 URL fix to the client-side search index - Fix the "Images and Embeds" card link on the Components docs page, which pointed to
/images-and-embedsand 404'd; it now uses the canonical/image-and-embedsslug shared by the rest of the site - Update generated app dependency posthog-js to ^1.398.0
- Play the search modal's close animation to completion instead of letting the modal vanish instantly: the backdrop's
onAnimationEndalso fired foranimationendevents bubbling up from descendant elements, which could unmount the modal before its own exit animation finished; it now responds only to the backdrop's own animation via ane.target === e.currentTargetguard - Toggle the AI assistant with Cmd/Ctrl+I when chat is enabled: a global shortcut opens or closes the chat panel (seeding the greeting and focusing the input on open) through a shared
toggleChataction that the "Ask AI" button now reuses as well
- Consolidate the keyboard focus rings added in 0.0.104 into a single global
:focus-visiblerule, so every link shares one consistent, keyboard-only ring instead of each element re-declaring it, and slim the ring from 4px to 2px; the sharedfocusRinghelper is dropped in favor of the global style - Keep the focus ring off buttons that render as links (a Cherry
Buttongiven anhref) so they retain their own button focus treatment instead of picking up the link ring - Draw the section-tab and Tabbed Code Block focus rings inset on a pseudo-element so the horizontally scrolling bars never clip them top or bottom
- Give the search modal's scrollable results list its own inset focus ring so keyboard users can see when it is focused
- Use Cherry's
IconButtonfor the AI chat panel's reset and close buttons - Replace the search modal's desktop-only "Esc" hint with an always-visible close (X) button so the modal can be dismissed by tap on touch devices
- Render the Accordion header as a real
<button>witharia-expandedandaria-controlswired to the contentregion, instead of an<h3>carryingrole="button", so it is fully keyboard- and screen-reader-accessible and no longer injects an out-of-order heading into the page outline - Swap the sample
<Update>label and description on the Update docs page so the version string reads as the entry's sidebar anchor and "Example" as its description
- Fix a generated-app build failure introduced with the keyboard focus rings: the shared
focusRinghelper required a non-optionaltheme, so interpolating it into the sidebar row styles (whose props typethemeas optional) failednext buildwith a styled-components type-variance error; type its generic theme as optional so it composes into both required- and optional-theme styled blocks
- Add accessible keyboard focus indicators throughout the generated docs site: a shared
focusRinghelper draws a soft:focus-visibleglow (matching the existinginteractiveStylestreatment) on header navigation links and the logo, both the left and right sidebars, footer links, and inline document and chat links, so keyboard users get a clear, on-brand focus ring that never shows on mouse clicks - Round the footer's GitHub icon-link focus ring, and draw the header section-tab ring inset so the horizontally scrolling section bar never clips it top or bottom
- Redesign the generated 404 page as a standalone, centered card served from the site root (
app/not-found.tsx) reusing the password-gate box pattern, with an icon, an "Error 404" title, a "This page could not be found." message, and a "Home" button that links back to the home page - Render the Steps component's step title as a bold
divinstead of anh3so step labels no longer inject an out-of-order heading into the page outline
- Add optional sidebar icons: set
navIconin a page's frontmatter for its sidebar link andcategoryIconfor its category header, or add aniconto any category or link innavigation.json, all using Lucide names, with unknown names rendering nothing so a typo never breaks the build - Add an optional
iconprop toTabContentthat renders a Lucide icon before the tab title - Support nested, collapsible navigation groups in
navigation.json: any link can carry its ownlinksarray to become a group that expands and collapses on click and opens automatically when one of its pages is active, nested as deep as needed; a group can be a plain label or a real page when given aslug - Walk nested link groups when computing previous/next page navigation so paging spans every real page in reading order
- Generate unique, document-order heading anchors via a shared slugger so repeated heading text yields stable ids (
setup,setup-1, ...) matching GitHub and rehype-slug, keeping the "On this page" sidebar links in sync with the rendered headings and<Update>labels - Make each
<Update>label a clickable anchor so readers can copy a deep link straight to a changelog entry - Document icons, nested navigation, and the tab
iconprop on the Navigation and Tabs pages
- Surface each
<Update>component'slabelin the generated docs' "On this page" sidebar as a top-level, deep-linkable heading anchor, so changelog entries appear in the page navigation and can be linked to directly - Document the behavior on the Update page and fix two em dashes in its sample content
- Strip Next.js route-group segments like
(site)from generated doc URLs so AI chat answers, chat source links, and MCP search results link to real pages (/code/instead of/(site)/code/) - Instruct the AI chat assistant to never include route-group segments in links as an extra safeguard
- Type the
TabListstyled component inCodewith theThemegeneric so it receives the typed theme prop like the other styled components - Update generated app dependency cherry-styled-components to 0.2.8
- Add an optional
titleprop to theCodecomponent that shows a file name centered in the window bar, styled to match the GitHub-style header in both modes - Add a
CodeTabscomponent for multi-variant snippets (e.g. npm/pnpm/yarn install commands): a keyboard-accessible tablist in the window bar with arrow-key navigation, and a copy button that copies the active tab - Expose
CodeandCodeTabsto MDX authors so docs can use them directly without imports - Render
diffcode blocks GitHub-style with added and removed lines highlighted full-width in green and red - Document the new features on the Code page: Highlighting Diffs, File Names, and Tabbed Code Blocks sections with live examples
- Update generated app dependency baseline-browser-mapping
- Open external links in the generated app in a new tab with
rel="noopener noreferrer":Cardnow detects externalhrefvalues likeButtonalready did, and the "Powered by Doccupine" links in the footer and password gate open in a new tab (the footer's GitHub link also gained the missingrel)
- Apply a slim, theme-aware scrollbar to internal scroll areas in the generated app (tables, code blocks, search results, tab lists, and chat overflow areas) via a shared
thinScrollbarhelper, replacing the chunky native bar that stood out in dark mode
- Adopt cherry-styled-components' theme stack in the generated app:
ThemeToggle,ClientThemeProvider, anduseOnClickOutsidenow come from Cherry instead of bespoke local copies, and the theme is defined as literaltheme/themeDarkobjects swapped on toggle - Keep pages fully static with no dark-mode flash: the blocking theme-init script hides the body and pins a dark background on dark visits until Cherry's provider reconciles, and temporarily disables CSS transitions so the light-to-dark swap on load snaps instantly instead of animating every element
- Rework the
DemoThemepresets to rebuild the swapped theme objects (via the newbuildColorshelper) while still mirroring overrides onto CSS custom properties - Restyle the ActionBar view toggle to match Cherry's
ThemeToggleexactly:interactiveStylesborder highlight and focus/active rings instead of the scale hover, with the knob and icons perfectly centered in both states - Make the code block copy button icon-only (copy icon, check when copied) with an accessible label, using
interactiveStylesinstead of the scale effect - Fix
react-hookslint errors in generated sites: derive search results and the searching flag at render time instead of setting state inside the debounced search effect, and drop the localuseOnClickOutsidewith its non-literal dependency array - Delete obsolete generated files (
ClickOutside.ts,ClientThemeProvider.tsx,ThemeToggle.tsx) on every run so upgraded projects don't keep stale copies that fail lint - Emit Prettier-clean output from the search template
- Update generated app dependency cherry-styled-components to 0.2.5
- Persist the theme preference client-side in the theme toggle and drop the
/api/themeroute, removing a server round-trip on every toggle - Update generated app dependencies (Next.js 16.2.10, cherry-styled-components 0.2.0, PostHog, and others)
- Add optional password protection: set
SITE_PASSWORDto gate the whole generated site behind a shared-password login screen, with a theme toggle and hideable "Powered by Doccupine" branding below the login box - Return
401from the chat (/api/rag) and search (/api/search) APIs while locked so the docs can't be scraped around the login, keeping the MCP endpoint on its ownDOCS_API_KEYauth - Hide password-protected sites from search engines via a
robots.txtdisallow rule, anoindex, nofollowtag, and anX-Robots-Tagheader - Enforce the gate in middleware with a URL-transparent
(site)route-group layout so documentation pages stay statically rendered - Add an Authentication documentation page describing the feature
- Clear the generated
app/directory on start so upgrades never leave stale, conflicting routes behind - Fix disabled buttons crashing by passing the
$errorargument to Cherry'sbuttonStyles - Emit Prettier-formatted output from the layout, button, and sitemap templates so generated sites no longer produce formatting-only diffs
- Disable pnpm's minimum-release-age supply-chain gate in the generated workspace and the CLI repo
- Guard the
Iconcomponent against a missing icon name so it returnsnullinstead of attempting an invalid render, and only render theCallouticon when an icon type is resolved - Update CLI dev dependencies
- Read the initial theme mode via a lazy
useStateinitializer so generated sites apply the stored preference on first render instead of defaulting to light and correcting in an effect - Drop the
esbuild: falseentry from the generated workspaceallowBuilds - Update CLI and generated app dependencies
- Fix
</Update>closing tag indentation in the update MDX template so it renders as a block element instead of being parsed as inline content
- Emit
llms.txt,llms-full.txt, and per-page markdown for LLM-friendly content discovery - Open external links in
Buttonin a new tab by default - Use primary color for footer link hover state
- Ship
pnpm-workspace.yamlwith the package and inherit install stdio so dependency installs stream output to the user - Migrate generated workspace to pnpm's
allowBuildsfor native dependencies - Make starter MDX templates Prettier-conformant and add a
.prettierignorefor generated files - Drop the deprecated
@types/chokidardependency - Update CLI runtime, dev dependencies, and generated app dependencies
- Switch theming to CSS custom properties toggled by a
darkclass on<html>, removing runtimetheme.isDarkbranching across components - Add blocking
theme-initscript in the root layout so the theme is applied before first paint to prevent a flash of incorrect theme - Serve doc, home, and section pages fully statically from the edge cache by removing the theme cookie from middleware and marking pages as
force-static- theme now resolves client-side via thedarkclass set before paint - Derive semantic tokens (
accent,accentStrong,accentMuted,surface) from the brand palette using nativecolor-mix, dropping thepolisheddependency - Add JSON-LD structured data and canonical URLs to generated pages for improved SEO
- Document
sitemap.xmlandrobots.txtgeneration in the README - Document the site URL field in platform site settings
- Fix JSON-LD favicon fallback chain so a configured
config.iconis no longer skipped by an always-falsy override check - Restore type checking in the generated app after the CSS-variable theming refactor
- Derive semantic CSS tokens via
var()so theme preset overrides cascade through to dependent tokens - Lock down semantic tokens and repair filled-button text contrast in dark mode
- Emit Prettier-clean output for JSON-LD declarations and styled-components so generated sites no longer produce formatting-only diffs
- Generate
sitemap.xmlautomatically when a site URL is configured and link it fromrobots.txt - Add
urlfield toconfig.jsonwithNEXT_PUBLIC_SITE_URLenvironment variable override - Update dependencies
- Replace
h3withpelement in Card component to fix heading order accessibility - Fix config command option syntax from single to double dash in README
- Make LLM API key optional to prevent build failures
- Replace manual ref callback with autoFocus in search modal
- Add explicit text color using theme grayDark for field component
- Update icon examples and external links MDX template
- Update dependencies
- Add robots.ts template using Next.js Metadata API
- Improve color contrast across navigation, buttons, links, and primary theme for WCAG AA compliance
- Improve accessibility across sidebar, docs wrapper, and footer
- Code-split PostHogProvider and SearchDocs modal for better performance
- Replace raw script tags with next/script component
- Scope MCP filesystem operations through APP_DIR to fix turbopack warning
- Remove baseUrl from generated tsconfig
- Update dependencies
- Update navigation example with correct slugs and categories
- Rename list-and-tables template to lists-and-tables
- Resolve eslint warnings in generated components
- Update dependencies
- Fix sidebar mobile bar to use light theme color in light mode
- Simplify badge background to use primary color
- Add sticky footer and refine sidebar layout spacing
- Add full-text content search to search modal
- Resolve section labels from slug in search results
- Adjust spacing and shadow in search modal
- Capitalize escape key label in search modal
- Add Cmd+K search modal for docs navigation
- Fix Callout component flex column layout for proper children spacing
- Update to Next.js 16.2
- Update dependencies
- Update dependencies
- Fix
orderfrontmatter values in Components category
- Add Color Swatches card to components index page
- Add ColorSwatch component for documenting color palettes
- Exclude image-wrapping anchors from styled anchor rules
- Fix nested paragraph color inherit rule for buttons
- Move table overflow-x to wrapper div for proper layout
- Self-close img tags in image-and-embeds MDX template
- Add theme-aware visibility classes for light/dark mode content
- Fix button text color inheritance for nested paragraph elements
- Fix public directory watcher to detect directory creation at runtime
- Update dependencies
- Add analytics platform documentation template
- Update template dependencies and migrate PostHog React package
- Add
analytics.jsonto README configuration files table
- Add PostHog analytics integration via
analytics.json - Fix generated layout indentation when PostHog is enabled
- Close chat when tapping source link on mobile
- Replace navigation and sections MDX pages with Navigation Builder guide
- Fix internal link to navigation-settings page
- Fix directory structure in media-and-assets guide
- Show missing component placeholders in MDX pages
- Allow null date in PagesProps type
- Add responsive md breakpoint for 3+ column grids
- Fix dark-mode FOUC on Safari and Firefox
- Rename deployment MDX templates to reduce naming confusion
- Add source links below AI answers in chat
- Add AI internal links prompt and usage budget docs
- Pre-compute page URLs from chunk URIs in RAG context
- Decouple ActionBar from ChatContext
- Extract
useLockBodyScrollhook from Chat - Separate close and reset actions in chat with improved UX
- Use Next.js Link component for source links in chat
- Improve line wrapping in DocsSideBar
- Use
dvhviewport units and adjust sidebar offsets - Show sidebar border-right only on desktop breakpoint
- Update dependencies
- Add components index page and improve content links
- Add welcome greeting when AI chat panel opens
- Add optional
hrefprop for link cards - Overhaul layout and chat UI components
- Improve docs sidebar offset and active item visibility
- Reduce horizontal padding on footer and static links layout
- Fix table scroll, step icon alignment, and table header padding
- Fix inaccuracies and add missing props across MDX templates
- Update dependencies
- Add per-page
nameandimagemetadata overrides for Open Graph - Pass Next.js stdout through in verbose mode
- Refactor CLI into modular
src/lib/structure (types, utils, config, constants, metadata, structures, layout) - Improve footer links responsiveness and chat-aware spacing
- Update dependencies
- Add multi-turn conversation history support for AI chat
- Update dependencies
- Add sections support for organizing docs into multiple areas via
sections.jsonor frontmatter fields (section,sectionOrder,sectionLabel) - Add public directory watching and automatic static asset syncing
- Smooth scrolling for sidebar navigation and heading anchors
- Add branding verification with signature-based key
- Add type safety, security hardening, and stricter linting across generated templates
- Harden SSE streaming, error recovery, and security for AI chat
- Remove
dist/from repository and add vitest testing
- Improve AI system context for better chat responses
- Default AI model to
gpt-4.1-nano - Update chunk sizes and supported OpenAI models
- Add media and asset components
- Add icon support for navigation links
- Add static footer links configuration
- Add footer with version display from
package.json - Use pnpm as package manager when available
- Add MCP server for semantic doc search
- Add AI chat integration with RAG (SSE streaming, LangChain)
- Add AI assistant MDX template
- Add local font support
- Add custom Google Fonts configuration
- Add grayscale default color palette
- Add action bar with copy-to-clipboard for code blocks
- Add code copy button
- Improve theming and dark mode support
- Add theme logo support
- Add theme configuration via
theme.json
- Add Steps component
- Add Columns layout component
- Add SSR theme toggle with system preference detection
- Add custom configuration support (
doccupine.json)
- Switch from
react-markdownto@mdx-js/reactfor MDX rendering - Add custom component templates (Callout, Card, Accordion, Code, etc.)
- Add image, video, and iframe global styles
- Add document index and sidebar navigation
- Initial public release with MDX-to-Next.js generation, file watching, and dev server