You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 04e1c04
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: CHANGELOG.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,17 @@ All notable changes to ShellDocs land here. Format follows [Keep a Changelog](ht
4
4
5
5
## [Unreleased]
6
6
7
+
## [0.1.15-alpha] — 2026-10-11
8
+
9
+
Search now works in static exports, the last piece of chrome that needed a running Blazor app.
10
+
11
+
### Changed
12
+
13
+
-**Search runs in the browser.** The app serves its search index at `/_shelldocs/search.json` (no `Program.cs` change), `shelldocs build` copies it into the export, and `shelldocs.js` fetches it on first use and ranks it with the same scoring as before: title, section, description and body, with body snippets, scoped to the current version. Search is the same on GitHub Pages or any static host as on a running app, including under `--base-href`. Results are links, so they open in a new tab like any other.
14
+
-**Search buttons are `[data-search-open]` buttons** handled by `shelldocs.js`; add the attribute to any element to open search from it. `shelldocsSearch.open()` still works from script, and there's a matching `shelldocsSearch.close()`.
15
+
-`SearchState` is no longer used and is kept only so existing code compiles. `shelldocsSearch.init` is gone.
16
+
-`shelldocs build` warns when the site's `ShellDocs.Components` is too old to serve an index, since search would not work in that export.
17
+
7
18
## [0.1.14-alpha] — 2026-10-10
8
19
9
20
Static sites get the last piece of interactive chrome apart from search, the desktop sidebar collapse, and unknown pages finally answer 404, with a matching `404.html` in static exports.
Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,15 +49,15 @@ Already have a Blazor project? Run `shelldocs init --attach` inside it: it adds
49
49
50
50
-**Markdown-first authoring.** YAML frontmatter, Shiki-highlighted code fences, live `razor:preview` examples, inline component tags mid-prose. Set `o.RenderPageTitle = true` to render the frontmatter `title` and `description` as the page header instead of writing `# Title`.
51
51
-**File-based navigation.** Drop a `.md` in `content/docs/` and it's a page. Sidebar, breadcrumb, prev/next and TOC come from the folder tree; `meta.json` controls order, dividers, subsections and hidden pages.
52
-
-**`Cmd+K` search** across titles, headings and page text, with body snippets. The index is built in memory at startup, so there's no external service; search needs a running Blazor app (it isn't available in the static export).
52
+
-**`Cmd+K` search** across titles, headings and page text, with body snippets. The index is built at startup and searched in the browser, so there's no external service and it works in the static export too.
53
53
-**Blazor-native.** Your components render as real Razor components, not iframes.
54
54
-**Composable.** Bring your own component library (ShellUI, MudBlazor, Radzen, hand-rolled) and register it in one line:
55
55
```csharp
56
56
o.RegisterComponentsFromAssembly<MyLib.Button>();
57
57
```
58
58
Your components win name collisions with ShellDocs' built-ins, which stay available as `<DocsCard>`, `<DocsCallout>`, `<DocsTabs>`, and so on.
-**Static export.**`shelldocs build` prerenders every page to static HTML for GitHub Pages, Cloudflare Pages, Netlify or S3. Optional flags rewrite `<base href>` (`--base-href`), add a SPA `404.html` (`--spa-fallback`), and write sitemap / robots / `og:` meta (`--site-url`). Navigation, sidebar sections and collapse, the mobile menu, selectors, tabs, preview toolbars, the TOC, code copy and the theme toggle work there through `shelldocs.js`. Only search and stateful demos need a running Blazor app. Unknown pages answer 404 (`<DocsNotFound />` in the docs page) and `build` writes the same view as `404.html`.
60
+
-**Static export.**`shelldocs build` prerenders every page to static HTML for GitHub Pages, Cloudflare Pages, Netlify or S3. Optional flags rewrite `<base href>` (`--base-href`), add a SPA `404.html` (`--spa-fallback`), and write sitemap / robots / `og:` meta (`--site-url`). Navigation, sidebar sections and collapse, the mobile menu, selectors, tabs, preview toolbars, the TOC, code copy, the theme toggle and search work there through `shelldocs.js`. Only stateful demos need a running Blazor app. Unknown pages answer 404 (`<DocsNotFound />` in the docs page) and `build` writes the same view as `404.html`.
Copy file name to clipboardExpand all lines: docs/ARCHITECTURE.md
+7-5Lines changed: 7 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -189,7 +189,9 @@ All answers are computed at render time and emitted as plain `<a href>`, so prer
189
189
190
190
`SearchIndex.FromGraph` runs once at startup and keeps everything in memory: one entry per visible page (title, description, section, plain-text body) and one per h2/h3 heading (headings are read from the source if the page hasn't rendered yet).
191
191
192
-
`SearchDialog` (Cmd/Ctrl+K, or the header / sidebar buttons) filters the entries to the current version and scores them in .NET: whole-query and per-token matches against title (strongest), section, description and body; every token must match somewhere. Body-only matches get a ~150-character snippet. It's an interactive Blazor component, so search needs a running Blazor app; a static `shelldocs build` site has no search.
192
+
Searching happens in the browser, so it's the same on a static host. `DocsSearchIndexMiddleware` (added by an `IStartupFilter`, like the redirects) serves the index at `/_shelldocs/search.json`: `{ versions: [{ id, root, latest }], entries: [{ u, t, d, s, k, b, v }] }`, where `k` is `p` (page) or `h` (heading) and `v` is the id of the version that owns the entry. `shelldocs build` copies that file into the export.
193
+
194
+
`SearchDialog` renders a hidden template. On Cmd/Ctrl+K or a `[data-search-open]` button, `shelldocs.js` clones it onto `<body>` (outside the DOM Blazor owns, so a circuit starting doesn't close it), fetches the index once, keeps the current version's entries plus those outside every version, and scores them: whole-query and per-token matches against title (strongest), section, description and body; every token must match somewhere. Body-only matches get a ~150-character snippet. Results are links, cloned from the template's `[data-search-row]` rows. `shelldocsSearch.open()` / `.close()` do the same from script.
193
195
194
196
---
195
197
@@ -211,10 +213,9 @@ Chrome that must work without a Blazor runtime follows one contract:
211
213
| Mobile nav | page shell `[data-mobile-open]`| hamburger toggles; backdrop, Escape, link or navigation closes; page scroll locked on `<html>`|
212
214
| Sidebar collapse | page shell `[data-sidebar-collapsed]`, `[data-sidebar-collapse-toggle]`| toggles collapse; remembered in `localStorage` and restored as soon as `shelldocs.js` runs, so the sidebar doesn't flash open |
213
215
| TOC |`[data-toc-list][data-toc-ids]`| IntersectionObserver scroll-spy, re-attached on `enhancedload`|
216
+
| Search |`[data-search-dialog]` template, `[data-search-open]`| Cmd/Ctrl+K or a button opens a clone of the template; ranks `/_shelldocs/search.json`; arrows, Enter, Escape |
214
217
| Theme |`<html class="dark">`, `[data-theme-toggle]`| inline head script applies the saved / system theme; the toggle flips the class and CSS picks its icon; a `MutationObserver` saves any change (including another library's toggle) and pushes it to `ThemeState`; re-applied after enhanced navigation |
215
218
216
-
Still Blazor-only: search.
217
-
218
219
**Not found.**`<DocsNotFound />` is the docs page's not-found view. It answers 404 by setting the status in `Response.OnStarting`: .NET 10 sends an empty body for a page whose status is already 404 unless the app has a `NotFoundPage` or status-code re-execution, so the status has to change only as the headers go out. It carries `data-shelldocs-not-found`, which `shelldocs build` checks for.
219
220
220
221
`enhancedload` is a Blazor event (`Blazor.addEventListener`), not a DOM event; `shelldocsOnEnhancedLoad(fn)` attaches once `blazor.web.js` has run.
@@ -246,8 +247,9 @@ One neutral, shadcn-shaped palette of CSS variables in `ShellDocs.Tokens/tokens.
246
247
3.`PrerenderRunner` starts the published app on a free port, requests each URL and writes `<output>/<path>/index.html`. A URL that answers 404, or renders `<DocsNotFound>`, fails the build.
247
248
4. Reads the app's redirect map and writes a redirect page for each source URL without a prerendered page (meta refresh plus `location.replace`, relative to `<base href>`).
248
249
5. Requests `/docs/__shelldocs-not-found__` and, if it renders `<DocsNotFound>` with a 404, writes it as `404.html`.
249
-
6. Merges the published `wwwroot/` into the output without overwriting prerendered HTML.
250
-
7. Optionally rewrites `<base href>` in every HTML file, copies `index.html` to `404.html` (replacing the not-found page), and with `--site-url` writes `sitemap.xml`, `robots.txt` and `og:*` meta.
250
+
6. Copies the app's search index to `_shelldocs/search.json`.
251
+
7. Merges the published `wwwroot/` into the output without overwriting prerendered HTML.
252
+
8. Optionally rewrites `<base href>` in every HTML file, copies `index.html` to `404.html` (replacing the not-found page), and with `--site-url` writes `sitemap.xml`, `robots.txt` and `og:*` meta.
Ships to `ShellDocs.Core` + `ShellDocs.Components`.
148
148
149
-
*As built:*`SearchIndex.FromGraph` builds the index in memory at startup (no `search-index.json`), and `SearchDialog` is its own Blazor modal with token scoring and body snippets, not ShellUI's `<CommandPalette>`. It needs a running Blazor app. The original plan:
149
+
*As built:*`SearchIndex.FromGraph` builds the index in memory at startup (no `search-index.json`), and `SearchDialog` is its own modal with token scoring and body snippets, not ShellUI's `<CommandPalette>`. It first needed a running Blazor app; `feat/static-search` moved the ranking into `shelldocs.js`, with the index served at `/_shelldocs/search.json` and copied into `shelldocs build` exports. The original plan:
150
150
151
151
-`SearchIndexBuilder` in `ShellDocs.Core` — walks nav graph, emits `search-index.json`
152
152
- Wired into `shelldocs build` — index emitted alongside published site
-`SlotExtractor.ReplaceComponentTags` no longer `.Trim()`s the raw child content of inline component tags. The Trim was stripping the first line's indent and defeating `SlotRenderer.Dedent` — Markdig then interpreted the remaining 4-space-indented lines as an indented code block. Symptom was the same "literal `<pre>` around placeholder divs" bug that had already been fixed for `razor:preview` fences; the inline-tag code path was still hitting it.
Driven by building the ShellUI docs on ShellDocs. Highlights (details in the CHANGELOG):
214
214
215
215
-`feat/build-static-prerender` (0.1.5) — `shelldocs build` prerenders every URL into a static site.
@@ -221,6 +221,7 @@ Driven by building the ShellUI docs on ShellDocs. Highlights (details in the CHA
221
221
-`fix/fence-scanner-inline-attrs` (0.1.12) — CommonMark fences (any length, tildes), inline and named-slot tags read by the Razor tag scanner so `>` in a quoted value is safe, `<TypeTable>` rows that share a name.
222
222
-`feat/preview-code-variants` (0.1.13) — code tabs on previews with sync and a missing-tab note, page switch (`AddSyncSwitch`), captions, `<IframePreview>`, XAML highlighting; one-line component bodies stay inline.
223
223
-`fix/not-found-and-sidebar-collapse` (0.1.14) — `<DocsNotFound>` answers 404, `build` fails on not-found pages and writes `404.html`, desktop sidebar collapse in `shelldocs.js`.
224
+
-`feat/static-search` (0.1.15) — search runs in `shelldocs.js` against `/_shelldocs/search.json`, which `build` copies into the export, so static sites have search.
224
225
-`fix/preview-fidelity`, `feat/generics-redirects-page-title` (0.1.10) — `not-prose` previews, Razor child content, stretch layout, generic components in markdown, redirects (middleware + static pages), `RenderPageTitle`, theme sync and a static-host `ThemeToggle`.
if(redirects>0)AnsiConsole.MarkupLine($"[dim]redirects:[/] wrote [cyan]{redirects}[/] redirect page(s)");
126
126
if(WriteNotFoundPage(http,outputDir))AnsiConsole.MarkupLine("[dim]not found:[/] wrote [cyan]404.html[/]");
127
+
varindexBytes=WriteSearchIndex(http,outputDir);
128
+
if(indexBytes>0)
129
+
AnsiConsole.MarkupLine($"[dim]search:[/] wrote [cyan]{SearchIndexPath.TrimStart('/')}[/] ({Math.Max(1,indexBytes/1024)} KB)");
130
+
elseif(indexBytes==0)
131
+
AnsiConsole.MarkupLine(" [yellow]warn:[/] the site serves no search index (its ShellDocs.Components predates static search), so search won't work in this export");
0 commit comments