diff --git a/web/docs/src/App.svelte b/web/docs/src/App.svelte
index 7727988c..99a4a613 100644
--- a/web/docs/src/App.svelte
+++ b/web/docs/src/App.svelte
@@ -14,6 +14,7 @@
import { bfsFirstRouteKeyUnderDir } from "./lib/manifestBfsDefault";
import { persistExpandedPathForRouteKey } from "./lib/treeSession";
import { DocTreeNav } from "./lib/tree";
+ import { filterTree } from "./lib/filterTree";
const NAV_COLLAPSED_KEY = "fullsend-docs-nav-collapsed";
const WIDTH_STORAGE_KEY = "fullsend-docs-sidebar-width-px";
@@ -34,6 +35,8 @@
let narrowViewport = $state(false);
/** Bumps when outline session keys change outside the tree (e.g. directory hash); keeps DocTreeNav in sync with sessionStorage. */
let outlineSessionEpoch = $state(0);
+ let filterQuery = $state("");
+ let filteredManifest = $derived(filterTree(manifest, filterQuery));
let outlineExpanded = $derived(
narrowViewport ? mobileNavOpen : !navCollapsed,
@@ -422,11 +425,37 @@
+
+
+
+ {#if filterQuery}
+
+ {/if}
+
diff --git a/web/docs/src/app.css b/web/docs/src/app.css
index f917cec1..38db840c 100644
--- a/web/docs/src/app.css
+++ b/web/docs/src/app.css
@@ -260,6 +260,59 @@ body {
display: inline-flex;
}
+.docs-tree-filter-wrap {
+ flex: 0 0 auto;
+ padding: 0.35rem 0.5rem 0;
+ position: relative;
+}
+
+.docs-tree-filter-icon {
+ position: absolute;
+ left: 0.85rem;
+ top: 56%;
+ transform: translateY(-50%);
+ color: var(--docs-muted, #888);
+ pointer-events: none;
+}
+
+.docs-tree-filter {
+ width: 100%;
+ padding: 0.3rem 0.5rem 0.3rem 1.8rem;
+ font: inherit;
+ font-size: 0.85rem;
+ border: 1px solid var(--docs-border);
+ border-radius: 0.3rem;
+ background: var(--docs-surface);
+ color: var(--docs-text);
+ outline: none;
+}
+
+.docs-tree-filter:focus {
+ border-color: var(--docs-link);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--docs-link) 25%, transparent);
+}
+
+.docs-tree-filter-clear {
+ position: absolute;
+ right: 0.75rem;
+ top: 56%;
+ transform: translateY(-50%);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.15rem;
+ border: none;
+ border-radius: 0.2rem;
+ background: transparent;
+ color: var(--docs-muted, #888);
+ cursor: pointer;
+}
+
+.docs-tree-filter-clear:hover {
+ color: var(--docs-text);
+ background: var(--docs-border);
+}
+
.docs-tree-wrap {
flex: 1;
min-height: 0;
@@ -384,6 +437,12 @@ body {
font-weight: 600;
}
+.doc-tree-match {
+ background: color-mix(in srgb, var(--docs-link) 25%, transparent);
+ color: inherit;
+ border-radius: 0.15rem;
+}
+
@media (max-width: 768px) {
.docs-shell-inner {
position: relative;
diff --git a/web/docs/src/lib/DocTreeNav.svelte b/web/docs/src/lib/DocTreeNav.svelte
index 63fb6228..5c19367e 100644
--- a/web/docs/src/lib/DocTreeNav.svelte
+++ b/web/docs/src/lib/DocTreeNav.svelte
@@ -1,6 +1,7 @@