Changelog
Neko is under active development and follows a calendar-versioning (vYY.M) scheme. The most recent release is listed first.
New components
Version badge
A compact, theme-agnostic package pill — a muted label, a bold monospace
version, an optional link and a one-click copy button:
Curiosity.FrontEndv26.6.1753.
Place several next to each other and they flow inline, wrapping onto the same
line: Tesseraev2026.6.67285 Curiosity.CLIv26.6.1718. Documented at components/version-badge.md, with a version-badge skill and a neko-version-badge snippet.
Link card
A ```links fenced block renders a titled card of links — each row is a
labelled link on the left and a version pill on the right, one row per line as
text | url | version:
Documented at components/link-card.md, with a link-card skill and a
neko-link-card snippet.
Folder-based changelogs
Changelogs are now built from a folder instead of a single hand-maintained
file. Drop a folder anywhere in your project, mark it with changelog: true in
its index.yml (plus a title/description), and add one Markdown file per
release named after its version (v26.6.md, 1.2.0.md, …). Neko parses each
file name as a version, sorts the entries newest-first, and renders a single
timeline page at the folder URL. The folder collapses to one sidebar entry, the
version files are not emitted as standalone pages, and the aggregated page is
what gets indexed for search and listed in the sitemap. The Neko changelog you
are reading now uses this model.
Quiz component
Added a ```quiz fenced block whose YAML body defines a self-scoring
multiple-choice comprehension check. Questions render radio buttons for a single
correct answer or checkboxes for multiple answers: [..], with an optional
per-question explain. Check answers grades client-side, highlights
correct/incorrect options, and reveals explanations; score and answered-state
persist per-browser in localStorage (no backend). Documented at
components/quiz.md, with a quiz skill and a neko-quiz snippet.
Improvements
Redesigned changelog timeline
The changelog timeline now renders as sections with entries. Each release's
section headings (#) become labelled headers with an icon, and each entry —
authored as a ::: change {badge="…" title="…"} block — renders its badge in a
left column, vertically aligned, with the title and description next to it. Every
version header is sticky and links to the closest NuGet package for that
month, so the version you are reading stays pinned until the next release scrolls
into view.
Fixes
gen-images skips commented directives
neko gen-images and neko gen-dark-images now skip [!img-gen] directives
(and assets/img-gen/*.png references) that sit inside an HTML comment. After
the first generation pass the original directive is preserved as a <!-- ... -->
block above the rendered image — re-running the command was matching the
commented-out directive again, burning API tokens and producing nested comments
/ orphan PNGs. The discovery regex is now paired with a comment-span filter so
commented directives are detected and ignored.
Features
redirectSlug frontmatter key
Added a redirectSlug page frontmatter key that exposes a page at a short, stable URL of the form /redirect/<slug>. When set, Neko writes a tiny HTML file at redirect/<slug>.html whose only job is to redirect the visitor to the page's actual URL via <meta http-equiv="refresh"> (with a JavaScript fallback and a visible link). The redirect page is marked noindex and carries a <link rel="canonical"> back to the real page, so it doesn't compete with the original in search engines. Useful for short, shareable, stable links — printed material, QR codes, emails, social posts — that survive future page re-organisation. Folder index pages collapse their target to the folder URL (docs/index.md → /docs/), and the route prefix is applied automatically in multi-repo builds. Slugs are a flat namespace under /redirect/ and must not contain / or \; duplicate slugs across pages are detected at build time (first wins, the rest logged as warnings). Docs at configuration/page.md#redirectslug, and the frontmatter skill in the starter template has been updated.
pageLinks navigation in neko.yml
Added a pageLinks configuration in neko.yml for rendering site-wide links at the top of every page's "On this page" navigation. Each entry takes a label, icon, url, and optional target; the url template supports three URL-encoded placeholders resolved at click time — ${page} (page title), ${url} (absolute page URL), and ${selection} (the visitor's current text selection, or empty). Useful for "Report an issue", "Suggest an edit", and "Quote this page" actions. Links only render on pages where the TOC is visible. Docs live at configuration/core/project.md#pagelinks, and the neko-yml skill in the starter template has been updated.
neko gen-dark-images command
Added a neko gen-dark-images command that backfills missing dark-mode variants for images previously created by [!img-gen] (or any assets/img-gen/*.png reference authored manually). The command walks every .md file under --input, finds image references that don't already carry a {src-dark="…"} attribute, calls the OpenAI image-edit endpoint to regenerate each in dark mode using the configured imageGen.darkModePrompt, saves the result as <name>-dark.png next to the original, and rewrites the Markdown attribute — preserving any other attributes already on the image. Idempotent: paired images and <name>-dark.png files themselves are skipped, and dark variants already present on disk are relinked into the Markdown without a second API call. Where possible the dark generation matches the PNG header's pixel dimensions of the source image so the pair lines up visually; otherwise it falls back to imageGen.size. Docs at components/img-gen.md#backfilling-dark-variants-for-existing-images.
Expanded img-gen with project defaults and dark mode
Expanded [!img-gen] with project-level defaults, a sensible landscape default size, and automatic dark-mode variants. A new imageGen: section in neko.yml exposes a global systemPrompt appended to every image prompt, a size default (now 1536x1024 landscape out of the box, instead of the model's square default), and lightMode / darkMode toggles (both on by default). When lightMode is on, a "render for a light theme" instruction is appended to every prompt; when darkMode is on, Neko follows the light generation with a second call to the OpenAI image-edit endpoint using the freshly generated light image plus a "redo this in dark mode" prompt, producing a paired name-dark.png. The rewritten Markdown now carries a src-dark="…" attribute that the image renderer expands into two <img> tags (dark:hidden / hidden dark:inline-block) so the active theme picks the right variant automatically. The size attribute on a directive now accepts every TornadoImageSizes value (1024x1024, 1536x1024, 1024x1536, 2048x2048 / 2048x1152, 3840x2160 / 2160x3840, auto, plus any explicit <width>x<height> going through Custom), and two new per-directive overrides — light= and dark= — let an individual image opt out of the light-mode hint or the dark-variant pass. Docs at components/img-gen.md, snippets refreshed in templates.json, and both the img-gen and neko-yml skills updated accordingly.
sitemap.xml generated by default
sitemap.xml is now generated by default as part of every neko build / neko watch. The default for the sitemap configuration key in neko.yml flipped from false to true, so no opt-in is required. Each entry uses the page's clean (extensionless) URL — directory index.md pages collapse to their folder URL — and includes a <lastmod> derived from the source file's last-write time. Password-protected pages are excluded. Generation is skipped automatically when url is unset (or left at the placeholder localhost), since the resulting sitemap would not contain valid absolute URLs. In multi-repo / sub-project mode, only the root project writes the sitemap (sub-projects share the output directory and would otherwise clobber it).
searchExclude configuration option
Added a searchExclude configuration option for opting pages and folders out of the in-site search index. Set searchExclude: true in a page's frontmatter (or sibling .yml) to omit that single page from search.json, or set it in a folder's index.yml / <foldername>.yml to exclude every page under that folder recursively. Excluded pages are still built and reachable by direct link — they just do not appear in search results. Documentation lives at configuration/page.md#searchexclude and configuration/folder.md#searchexclude, and the page/folder skills (frontmatter, folder-index) have been updated accordingly.
neko update-skills command
Added a new neko update-skills command that refreshes the Neko-managed skills under an existing project's .claude/skills/ folder to match the versions bundled with the running CLI. Pass --path to point at a project (default: current directory) and --dry-run to preview the changes. The command requires a .claude/ folder to exist (otherwise it errors out and suggests neko new), replaces every Neko-shipped skill folder in place, and leaves any custom (non-Neko) skills untouched — reporting both the count of skills replaced/added and the names of the custom skills preserved.
img-gen component and neko gen-images
Added a new [!img-gen] component and a matching neko gen-images command that uses the LlmTornado NuGet package to generate images from inline prompts via OpenAI. Authors describe an image inside a [!img-gen ...] block; running neko gen-images --api-key sk-... [--image-model gpt-image-1] [--llm-model gpt-4o-mini] walks every Markdown file under --input, asks the chat model for a slug and alt-text (strict JSON), generates the PNG with the image model, saves it into the page's assets/img-gen/ folder, and rewrites the directive into a regular Markdown image with the original directive preserved as an HTML comment so it can be re-generated later. Only OpenAI is supported for now. The directive renders nothing in HTML until you run the command, and supports size, quality, background, and style attributes. Documentation lives at components/img-gen.md, with two neko-img-gen* snippets in templates.json and a new img-gen skill in the starter template.
neko new scaffolding command
Added a new neko new command that scaffolds a fresh hello-world documentation project (with neko.yml, three sample pages, and a .claude/ folder of skills) into the current directory or a directory passed via --path. The starter lives under .template/ in the repository and is zipped at build time and embedded as a resource in the CLI assembly. Pass --force to overwrite a non-empty target.
Roadmap component
Added a new Roadmap component that renders a kanban-style product roadmap board. Lanes (:::: lane) carry a title, count badge and accent colour; items (::: roadmap-item) carry a title, tag pill, optional date, vote count, and optional clickable link. Lane count badges reuse the icon-badge tint+ring style from grid cards, item tag pills follow the soft bg-{color}-100 / text-{color}-800 palette from [!badge], and lanes/items use rounded-2xl / rounded-xl with hover:border-primary-* to match Neko cards. Documentation lives at components/roadmap.md, with three neko-roadmap* snippets in templates.json.
Drag-and-drop sidebar reordering
Added drag-and-drop reordering of sidebar items while running neko watch. Items can be reordered within their parent group; on drop, the corresponding .md frontmatter order (or folder index.yml order) values are rewritten as multiples of 10, and the site reloads automatically. A new POST /api/neko/reorder endpoint handles the update.
neko snap screenshot command
Split screenshot capture into a dedicated neko snap command. neko build and neko watch no longer call Playwright; instead, run neko snap to capture missing screenshots, or neko snap --all to re-capture everything.
Lesson component
Added a new [!lesson] Markdown component that renders a curriculum-style track. Steps are auto-discovered from sibling .md files in the folder, ordered by their order frontmatter. User progress is persisted to localStorage per lesson.
Lesson step navigation block
Pages inside a [!lesson] folder now render a dedicated Go back: … / Next step: … navigation block at the bottom, with chevron icons and links to the previous and next siblings in the same curriculum order as the parent lesson page. Detected automatically — no per-page configuration required.
Learn Neko sample track
Added a Learn Neko sample track under lesson/ showcasing the new component end-to-end.
Improvements
HtmlGenerator split into partial classes
Split HtmlGenerator into focused partial-class files by concern (HtmlGenerator.Head.cs, HtmlGenerator.Navbar.cs, HtmlGenerator.Sidebar.cs, HtmlGenerator.Content.cs, HtmlGenerator.WatchMode.cs, HtmlGenerator.Scripts.cs). The 1,345-line Generate method is now a ~50-line orchestrator that delegates to single-purpose render methods (RenderBanner, RenderNavbar, RenderBreadcrumbs, RenderArticleBody, RenderPageNavigation, RenderFooter, RenderTocSidebar, RenderPageScripts, etc.), and the 327-line GenerateHead follows the same shape (RenderHeadMeta, RenderHeadTailwindAndTheme, RenderHeadMermaid, RenderHeadHighlightJs, …). Pure code reorganization with no behavioral changes — the emitted HTML for every page in Neko.Documentation is byte-identical to the previous output (only the intentionally-random encrypted-page payloads and tab GUIDs differ).
Search works across multi-repo builds
Search now works across every sub-project in a multi-repo build. Each sub-project's search.json is still written next to its own output, but at the end of the build (and every watch-mode rebuild) every sub-project's entries are merged into a single aggregated search.json at the root output. Document ids are now route-prefixed (workspace/core-concepts/graph-model.html instead of bare core-concepts/graph-model.html), and the client always fetches the root index — so a search from /workspace-deployment/ can find pages under /workspace/ and vice versa. The route-prefix segments also become slug tokens, so a query like workspace graph model boosts the right pages. Results from the sub-site the visitor is currently browsing also get a 1.6× score boost, so "local" hits surface first while other sub-sites stay reachable below them.
Friendlier search result breadcrumbs
Search results now show a friendly, extension-less breadcrumb under each hit (.../workspace/core-concepts/graph-model) instead of the raw .html URL, and a trailing index segment collapses to its folder. Pages living under any folder whose name starts with . or _ (e.g. _helpers, _reference-material, .template) are no longer indexed — these are treated as private scaffolding.
More prominent search result titles
Search results now make the page title visually more prominent — the title renders at text-base font-semibold (up from text-sm font-medium) so it dominates the snippet/breadcrumb beneath it. The indexed title also now falls back to the frontmatter label: (when title: is absent) and then to the first heading at any level (not just H1), so a page that opens with ## Graph Model and only declares label: "Graph Model" is indexed as Graph Model instead of the bare filename graph-model. Finally, the search slug drops trailing index segments (blog/index.html → slug blog, root index.html → empty), so a query for index no longer blanket-matches every section landing page via the slug. Pages named index.md remain indexed and findable through their title and content.
Deeper, more relevant search results
Overhauled the in-page search to surface deeper, more relevant results. The build-time indexer now emits one document per H2/H3 in addition to the page-level document, so search results deep-link directly to a section anchor instead of just the page. Each page document carries new slug, headings, type, parentTitle, and parentId fields. The slug splits the path into separate tokens (e.g. blog/index.html → blog index), so queries like index now reliably return index.md even when the page title is something else. The client (MiniSearch) was upgraded to AND-combine multi-word queries, boost slug and title above body content, apply fuzzy matching only to longer terms, and de-duplicate page+section hits in favour of the deeper link. The results UI now shows a highlighted snippet around the first match, a parent-page breadcrumb on section hits, a loading state while the index is fetched on first use, and a recent-searches list (stored in localStorage) when the input is empty.
csharp-docs preserves accessor declarations
The csharp-docs block now preserves property and indexer accessor declarations ({ get; }, { get; set; }, { get; init; }, { get; private set; }, etc.) in the rendered signature. Accessor bodies and expression bodies are stripped but the accessor keywords themselves are kept, so the rendered API doc shows what is actually readable / writable at a glance.
Cleaner DocFx-style csharp-docs layout
The csharp-docs block now renders a much cleaner DocFx-style layout and stops leaking the parent class body into the type signature. Class declarations are rebuilt from their modifiers, keyword, identifier, type-parameter list, base list, and constraint clauses, so the body — fields, private members, etc. — never appears in the signature. The parent type now renders as a sticky header (kind badge, name with a small unobtrusive anchor link after it, signature, summary) that floats at the top of the csharp-docs section while the visitor scrolls through the constructors / properties / methods listed below it. Members are grouped by kind (Constructors → Properties → Methods → Events → Fields), each prefixed with a kind badge (Constructor, Property, Method, etc.) and qualified with the parent class name (e.g. DetailsList.OnColumnClick). The link anchor was moved to after the name, shrunk, and stripped of its underline. Signatures are now whitespace-normalized at render time, so column-aligned source code like public void OnColumnClick() renders as public void OnColumnClick(). Standalone members (fragments without an enclosing class) still render via SourceCodeKind.Script parsing so the legacy snippet form on components/csharp-docs.md keeps working.
Refreshed default light / dark themes
Refreshed the default light / dark themes to match the look-and-feel of docs.curiosity.ai. Introduced a new curiosity default theme (deep navy #050914 background in dark mode), a paired accent palette, and a neko-text-gradient utility used by the hero accent word.
Redesigned grid card variant
Redesigned the grid card variant to match the curiosity card style — rounded icon badge with palette-coloured tint, hover glow, no image required. Added a new palette attribute and a deterministic palette fallback.
Redesigned hero component
Redesigned the [!hero] component with an eyebrow label, gradient accent word (title-accent), and subtle radial glow accents. Default alignment is now left to match the reference.
Removed gradient card options
Removed all gradient card options (gradient, gradient-mode, gradient-colors, gradient-noise, gradient-speed) and the bundled makegradient.js asset. Use the new icon-badge style instead.
Removed --disable-snapframe flag
Removed the --disable-snapframe build/watch flag — capturing is now opt-in via neko snap.
Fixes
label honored consistently
The frontmatter label: is now honored consistently wherever a page's display name is derived. Previously only the left sidebar fell back to label; the top navigation entry and the page <title> tag used title only and ignored label. Now the top-nav entry resolves label → title → file name (matching the sidebar), and the <title> tag falls back to label when no title is set. This means pages that follow the recommended pattern of setting label: (instead of the discouraged title:) get a meaningful title and nav label everywhere, not just in the sidebar.
Heading anchor link spacing
The hover # anchor link next to headings no longer abuts the heading text. It's now positioned by its own width (-translate-x-full) with a fixed pr-2 gap, so the spacing stays consistent regardless of heading size — previously the fixed -left-6 offset was too small for larger headings, leaving the icon touching the text.
Header logos resolve route prefix
Header logos (branding.logo / branding.logoDark) on multi-site builds are now resolved with the project's route prefix, so a sub-project at /tesserae emits <img src="/tesserae/assets/tesserae-logo.png"> instead of /assets/tesserae-logo.png. Previously the root-relative path generated by ResolveLogoPath was written out verbatim, which only happened to work when the same asset filename also existed at the site root.
Favicon path HTML-escaping and auto-detection
Favicon injection now HTML-escapes the configured path before writing it into the <link rel="icon" href="..."> tag, so values containing &, ", <, or > (e.g. favicon.ico?v=1&cache=2) no longer produce broken markup. In addition, the long-documented default behavior is now actually implemented: when branding.favicon is not set, Neko auto-detects favicon.ico or favicon.png at the input root, sets the favicon link to /favicon.<ext>, and copies the file to the output root so the link resolves.
neko.yml link normalization
Links in neko.yml (top-navigation links, dropdown items / footerItems, and banner.link) are now normalized at load time the same way Markdown links are — a trailing .md or .html is stripped so authors can paste the on-disk filename (e.g. link: /workspace/core-concepts/graph-model.md) and get the clean URL (/workspace/core-concepts/graph-model) in the rendered nav. Fragments (#section), query strings (?x=1) and external :// URLs are preserved unchanged.
Centered-alignment pipe tables
Pipe tables whose delimiter row used centered-alignment markers (:---:) were rendered as a paragraph instead of a table. The EmojiParser was greedily matching :---: as an emoji named ---, consuming the alignment markers before Markdig's pipe-table parser could see them. Emoji names now require at least one alphanumeric character, so alignment markers are left intact. This restores the rendering of the All Icons table on the Icon component documentation page.
Search shows H1 for untitled pages
Search results now display the page's first # H1 heading when the page has no title: set in its frontmatter, falling back to the file name only if neither is present. Previously, untitled pages always showed the bare file name in the search modal.
Search indexes rendered content
Search now indexes the rendered page content instead of the raw markdown source. Auto-injected blog and changelog listings, callouts, tabs, and other component bodies are searchable, while YAML frontmatter is no longer mixed into the indexed text. Password-protected pages are skipped entirely (previously their frontmatter password and body leaked into search.json). The client honours --route-prefix for both the search.json fetch and result links.
Documentation
Styled inline code
Inline code (text wrapped in single backticks) is now styled with a subtle background, border, and rounded corners in both light and dark mode, matching standard documentation rendering. The Tailwind Typography default of showing literal backtick characters around inline code has been replaced with this visual style.
Features
Inline PDF component
Added a new component for rendering PDF files inline in the text. You can now use the standard markdown image syntax pointing to a .pdf file to automatically render it in an iframe using pdf.js.
Features
Global password protection
Added support for configuring a global password in neko.yml. You can now protect the entire documentation by defining password: "my-secret" in your global configuration. Individual pages can bypass this global protection by setting password: none in their frontmatter.
Documentation
Tesserae TODO sample app
Updated the Tesserae component documentation (components/tesserae.md) to include a full interactive TODO sample application that demonstrates building UI components and persisting state via window.localStorage.
Features
csharp-docs code block
Added csharp-docs code block language mode which leverages Roslyn to parse C# code blocks containing XML comments and beautifully renders them with DocFx-like layouts detailing the summary, parameters, remarks, return types, and exceptions.
sitemap.xml generation
Added a sitemap boolean configuration option in neko.yml to automatically generate a sitemap.xml file containing all generated HTML pages, utilizing the configured url as the base address.
Monaco template auto-completion
Added Monaco Editor support for auto-completing templates for all valid components of neko starting with the "neko-" prefix. The template list is loaded dynamically from templates.json on the first render of the editor.
Improvements
Sticky sidebar search box
Made the sidebar search box sticky when scrolling the sidebar, allowing quick access to the filter functionality. This was achieved by updating the HTML generation in Neko/Builder/HtmlGenerator.cs to wrap the search input in a sticky container while maintaining the proper layout for the rest of the navigation list.
Documentation
Live Editing guide
Added a new guide for the Live Editing feature in Watch mode, including details on auto-completing templates.
Snapframe Component
Features
Snapframe component
Added a new [!snapframe] Markdown extension that automatically generates screenshots of external websites using the SnapFrame .NET tool during the build process.
Improvements
Multi-line snapframe commands
Extended the [!snapframe] Markdown extension to support multi-line command execution, allowing interaction with the page before taking the screenshot.
Documentation
Merged image alignment docs
Merged the image alignment documentation into the main image.md file.
Initial Release
Highlights
Initial Release of Neko v26.3
We are excited to announce the initial release of Neko, a powerful static site generator designed to help you create beautiful, documentation-first websites with ease.
Key Features
- Markdown First: Write your documentation in standard Markdown. Neko handles the rest.
- Rich Components: Enhance your docs with built-in components like Alerts, Badges, Tabs, and more.
- Theming: Dynamic Tailwind themes configurable via
neko.ymlunder thethemekey. Users can specify a built-in palette (e.g.,name: violet) and override specific shades. - Blog Support: Neko supports a 'Blog Mode' where files in a
blog/directory are processed as posts, sorted by date (descending), and displayed in a responsive grid layout of cards. - Changelog Support: Neko supports a 'Changelog Mode' where a folder marked with
changelog: truecollects version-named files, sorts them newest-first, and displays them in a vertical timeline layout. - Watch Mode: The CLI supports a
watchcommand that serves the site on localhost and auto-reloads on file changes, including a built-in Monaco editor for quick edits. - Multi-Repo Mode: Simultaneously build, watch, and serve multiple sub-projects located in immediate subdirectories containing a
neko.ymlfile. - Tesserae Support: Write and compile Tesserae C# code blocks directly in your Markdown, generating live interactive components.
Other Important Features
- Markdown Custom Containers: Support for custom Markdown syntax like Icons, Badges, Alerts, Tabs, Columns, Steps, Generic Components, Code Snippets, Panels, Emojis, and Cards.
- Navigation History: Tracks the last visited pages in browser
localStorage, with a flyover popup UI. - Built-in Search: Full-text client-side search across your documentation using Minisearch.
- Dynamic Card Backgrounds:
makegradient.jsintegration for beautiful, dynamic card backgrounds. - Mathematical Formulas & Diagrams: Integrated support for KaTeX math formulas and Mermaid diagrams.
Features
Icon Search in Watch Mode Editor
- Added a searchable list of icons in the watch mode editor modal.
- Accessible via the
Ctrl+IorCmd+Ikeyboard shortcut. - Allows inserting the selected icon's name directly into the editor at the current cursor position.
Mermaid Diagram Zoom Controls
- Added built-in zoom controls to Mermaid diagrams.
- Hover over any Mermaid diagram to access Zoom In, Zoom Out, and Reset buttons.
- Applied a minimum height of 400px to all Mermaid diagrams to give ample space for interacting with diagrams.
Improvements
Workflow LeaderLines Clipping Improvements
- Modified the workflow component javascript to appropriately clip connection lines within the workflow container.