From 093fd71beb643eadc0e1dc959295485a18820c3a Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:36:16 +0100 Subject: [PATCH 01/10] [AI] Always show all nav section headers in left sidebar Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Layout.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Layout.tsx b/src/Layout.tsx index 23fb1d7e..6b4062c5 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -273,7 +273,8 @@ function NavigationContent(props: { const navItemsWithComputed = getNavItemsWithComputed(props.navItems, pageContext.urlPathname) let navItemsRelevant = navItemsWithComputed - if (props.showOnlyRelevant) navItemsRelevant = navItemsRelevant.filter((navItemGroup) => navItemGroup.isRelevant) + if (props.showOnlyRelevant) + navItemsRelevant = navItemsRelevant.filter((navItemGroup) => navItemGroup.isRelevant || navItemGroup.level === 1) const navContent = navItemsRelevant.map((navItem, i) => ) return ( From bdc8589b3b38e4881fbb8f64c6dc99a4d6b1e4a2 Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:42:57 +0100 Subject: [PATCH 02/10] [AI] Fix level-1 nav items stacking in left sidebar Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/NavItemComponent.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NavItemComponent.css b/src/NavItemComponent.css index fe673bf9..08f91e89 100644 --- a/src/NavItemComponent.css +++ b/src/NavItemComponent.css @@ -110,6 +110,7 @@ #nav-left { .nav-item-level-1 { + display: flex; margin-left: min(25px, max(5px, 30 * (1vw - 12.7px) - 5px)); } .nav-head-logo { From 6e84682127d4364ca2da033796384f339ef7cc13 Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:48:55 +0100 Subject: [PATCH 03/10] =?UTF-8?q?[AI]=20Use=20Collapsible=20for=20sidebar?= =?UTF-8?q?=20sections=20=E2=80=94=20proper=20expand/collapse=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Layout.tsx | 51 +++++++++++++++++++++++++++++++++------- src/NavItemComponent.css | 6 +++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/Layout.tsx b/src/Layout.tsx index 6b4062c5..be7d8a04 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -16,6 +16,7 @@ export { scrollFadeMask } import React from 'react' import { getNavItemsWithComputed, NavItem, NavItemComponent } from './NavItemComponent.js' +import { Collapsible } from './MenuModal/Collapsible.js' import { parseMarkdownMini } from './parseMarkdownMini.js' import { usePageContext } from './renderer/usePageContext.js' import { ExternalLinks } from './ExternalLinks.js' @@ -265,25 +266,57 @@ function getStyleNavLeft() { }` } -function NavigationContent(props: { - navItems: NavItem[] - showOnlyRelevant?: true -}) { +function NavigationContent(props: { navItems: NavItem[]; showOnlyRelevant?: true }) { const pageContext = usePageContext() const navItemsWithComputed = getNavItemsWithComputed(props.navItems, pageContext.urlPathname) - let navItemsRelevant = navItemsWithComputed - if (props.showOnlyRelevant) - navItemsRelevant = navItemsRelevant.filter((navItemGroup) => navItemGroup.isRelevant || navItemGroup.level === 1) - const navContent = navItemsRelevant.map((navItem, i) => ) + if (props.showOnlyRelevant) { + const sections = groupBySections(navItemsWithComputed) + return ( +
+ {sections.map((section, i) => ( + } + disabled={false} + collapsedInit={!section.level1.isRelevant} + > + {section.children.map((navItem, j) => ( + + ))} + + ))} +
+ ) + } return (
- {navContent} + {navItemsWithComputed.map((navItem, i) => ( + + ))}
) } +type NavItemsSection = { + level1: ReturnType[number] + children: ReturnType +} +function groupBySections(navItems: ReturnType): NavItemsSection[] { + const sections: NavItemsSection[] = [] + let current: NavItemsSection | null = null + for (const navItem of navItems) { + if (navItem.level === 1) { + current = { level1: navItem, children: [] } + sections.push(current) + } else if (current) { + current.children.push(navItem) + } + } + return sections +} + function isNavLeftAlwaysHidden() { const pageContext = usePageContext() const { isLandingPage, navItemsDetached, pageDesign } = pageContext.resolved diff --git a/src/NavItemComponent.css b/src/NavItemComponent.css index 08f91e89..1369a28e 100644 --- a/src/NavItemComponent.css +++ b/src/NavItemComponent.css @@ -111,6 +111,7 @@ #nav-left { .nav-item-level-1 { display: flex; + cursor: pointer; margin-left: min(25px, max(5px, 30 * (1vw - 12.7px) - 5px)); } .nav-head-logo { @@ -121,4 +122,9 @@ .nav-item-level-4 { padding-left: min(30px, max(8px, 30 * (1vw - 12.7px))); } + .collapsible-icon { + position: static; + margin-left: auto; + padding-left: 6px; + } } From 53bfb722dcb5932705257737c7e4ed9981fc86ac Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:52:51 +0100 Subject: [PATCH 04/10] [AI] Underline level-1 nav item text only, not full sidebar width Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/NavItemComponent.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NavItemComponent.css b/src/NavItemComponent.css index 1369a28e..78aa80ac 100644 --- a/src/NavItemComponent.css +++ b/src/NavItemComponent.css @@ -110,7 +110,7 @@ #nav-left { .nav-item-level-1 { - display: flex; + display: inline-flex; cursor: pointer; margin-left: min(25px, max(5px, 30 * (1vw - 12.7px) - 5px)); } @@ -124,7 +124,6 @@ } .collapsible-icon { position: static; - margin-left: auto; padding-left: 6px; } } From 8c2b0a7db3feb9ba5ac4b19647b33086f9e9ec0f Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:55:09 +0100 Subject: [PATCH 05/10] [AI] Underline only below title text; chevron stays on right Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/NavItemComponent.css | 9 ++++++--- src/NavItemComponent.tsx | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/NavItemComponent.css b/src/NavItemComponent.css index 78aa80ac..405723d7 100644 --- a/src/NavItemComponent.css +++ b/src/NavItemComponent.css @@ -53,10 +53,12 @@ } } #nav-left & { - border-bottom: 3px solid var(--color-category); - padding-bottom: 2px; margin-top: 3px; margin-bottom: 10px; + .nav-item-title { + border-bottom: 3px solid var(--color-category); + padding-bottom: 2px; + } } } .nav-item-level-2 { @@ -110,7 +112,7 @@ #nav-left { .nav-item-level-1 { - display: inline-flex; + display: flex; cursor: pointer; margin-left: min(25px, max(5px, 30 * (1vw - 12.7px) - 5px)); } @@ -124,6 +126,7 @@ } .collapsible-icon { position: static; + margin-left: auto; padding-left: 6px; } } diff --git a/src/NavItemComponent.tsx b/src/NavItemComponent.tsx index 43993908..235a348f 100644 --- a/src/NavItemComponent.tsx +++ b/src/NavItemComponent.tsx @@ -94,7 +94,7 @@ function NavItemComponent({ children = ( <> {icon} - {children} + {children} ) From bc7d82c86515a103672ec42c2b70053ec2d980d2 Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 18:56:56 +0100 Subject: [PATCH 06/10] [AI] Include icon in nav level-1 underline span Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/NavItemComponent.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NavItemComponent.tsx b/src/NavItemComponent.tsx index 235a348f..56ad7da1 100644 --- a/src/NavItemComponent.tsx +++ b/src/NavItemComponent.tsx @@ -93,8 +93,10 @@ function NavItemComponent({ if (navItem.level === 1) { children = ( <> - {icon} - {children} + + {icon} + {children} + ) From 9a9e9bd61096fe602b4e13e2477132350b7e9d7b Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Tue, 10 Mar 2026 19:45:07 +0100 Subject: [PATCH 07/10] =?UTF-8?q?[AI]=20Collapsible:=20display:none=20when?= =?UTF-8?q?=20collapsed=20=E2=80=94=20fix=20layout=20width=20contribution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/MenuModal/Collapsible.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MenuModal/Collapsible.tsx b/src/MenuModal/Collapsible.tsx index d99af5ed..9eaf673a 100644 --- a/src/MenuModal/Collapsible.tsx +++ b/src/MenuModal/Collapsible.tsx @@ -23,9 +23,9 @@ function Collapsible({ const onClick = () => { if (!disabled) { - setIsAnimating(true) if (!collapsed) { - // If expanding, set height to current scroll height before animation + // Collapsing: animate height to 0, then set display:none + setIsAnimating(true) contentRef.current!.style.height = `${contentRef.current!.scrollHeight}px` // Force a reflow contentRef.current!.offsetHeight @@ -49,6 +49,7 @@ function Collapsible({ ref={contentRef} onTransitionEnd={onTransitionEnd} style={{ + display: !showContent && !isAnimating ? 'none' : undefined, height: !showContent ? 0 : isAnimating ? contentRef.current!.scrollHeight : 'auto', overflow: 'hidden', transition: 'none 0.3s ease', From 2a5976c2027c7107852433f92a3a96585b86eaa1 Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Wed, 11 Mar 2026 07:58:20 +0100 Subject: [PATCH 08/10] =?UTF-8?q?Revert=20"[AI]=20Collapsible:=20display:n?= =?UTF-8?q?one=20when=20collapsed=20=E2=80=94=20fix=20layout=20width=20con?= =?UTF-8?q?tribution"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9a9e9bd61096fe602b4e13e2477132350b7e9d7b. --- src/MenuModal/Collapsible.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MenuModal/Collapsible.tsx b/src/MenuModal/Collapsible.tsx index 9eaf673a..d99af5ed 100644 --- a/src/MenuModal/Collapsible.tsx +++ b/src/MenuModal/Collapsible.tsx @@ -23,9 +23,9 @@ function Collapsible({ const onClick = () => { if (!disabled) { + setIsAnimating(true) if (!collapsed) { - // Collapsing: animate height to 0, then set display:none - setIsAnimating(true) + // If expanding, set height to current scroll height before animation contentRef.current!.style.height = `${contentRef.current!.scrollHeight}px` // Force a reflow contentRef.current!.offsetHeight @@ -49,7 +49,6 @@ function Collapsible({ ref={contentRef} onTransitionEnd={onTransitionEnd} style={{ - display: !showContent && !isAnimating ? 'none' : undefined, height: !showContent ? 0 : isAnimating ? contentRef.current!.scrollHeight : 'auto', overflow: 'hidden', transition: 'none 0.3s ease', From ac07ebe0f000167abff0c864a42cc09034d049bf Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Wed, 11 Mar 2026 08:10:39 +0100 Subject: [PATCH 09/10] =?UTF-8?q?[AI]=20Collapsible:=20animate=20width=20l?= =?UTF-8?q?ike=20height=20=E2=80=94=20smooth=20horizontal=20growth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/MenuModal/Collapsible.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/MenuModal/Collapsible.tsx b/src/MenuModal/Collapsible.tsx index d99af5ed..15a5d57e 100644 --- a/src/MenuModal/Collapsible.tsx +++ b/src/MenuModal/Collapsible.tsx @@ -25,8 +25,9 @@ function Collapsible({ if (!disabled) { setIsAnimating(true) if (!collapsed) { - // If expanding, set height to current scroll height before animation + // If collapsing, set height and width to current values before animation contentRef.current!.style.height = `${contentRef.current!.scrollHeight}px` + contentRef.current!.style.width = `${contentRef.current!.scrollWidth}px` // Force a reflow contentRef.current!.offsetHeight } @@ -34,8 +35,8 @@ function Collapsible({ } } - const onTransitionEnd = () => { - setIsAnimating(false) + const onTransitionEnd = (e: React.TransitionEvent) => { + if (e.propertyName === 'height') setIsAnimating(false) } const showContent = disabled ? true : !collapsed @@ -50,14 +51,15 @@ function Collapsible({ onTransitionEnd={onTransitionEnd} style={{ height: !showContent ? 0 : isAnimating ? contentRef.current!.scrollHeight : 'auto', + width: !showContent ? 0 : isAnimating ? contentRef.current!.scrollWidth : 'auto', overflow: 'hidden', transition: 'none 0.3s ease', - transitionProperty: 'height, margin-bottom', + transitionProperty: 'height, width, margin-bottom', marginBottom: (showContent && marginBottomOnExpand) || undefined, }} aria-expanded={showContent} > - {children} +
{children}
) From 0f3f12ddbe627da9baed73af85b4ae6409f088d3 Mon Sep 17 00:00:00 2001 From: Romuald Brillout Date: Wed, 11 Mar 2026 08:14:54 +0100 Subject: [PATCH 10/10] [AI] Sidebar: smooth width transition on #nav-left, not on Collapsible content Collapsed sections use width:0 (instant) to prevent inflating sidebar width. #nav-left transitions its own width smoothly from min-width to full content width. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Layout.tsx | 1 + src/MenuModal/Collapsible.tsx | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Layout.tsx b/src/Layout.tsx index be7d8a04..db4a69c7 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -136,6 +136,7 @@ function LayoutDocsPage({ children }: { children: React.ReactNode }) { } #nav-left { min-width: ${navLeftWidthMax + blockMargin}px; + transition: width 0.3s ease; } } .page-content { diff --git a/src/MenuModal/Collapsible.tsx b/src/MenuModal/Collapsible.tsx index 15a5d57e..06c2a3e5 100644 --- a/src/MenuModal/Collapsible.tsx +++ b/src/MenuModal/Collapsible.tsx @@ -25,9 +25,8 @@ function Collapsible({ if (!disabled) { setIsAnimating(true) if (!collapsed) { - // If collapsing, set height and width to current values before animation + // If collapsing, set height to current scroll height before animation contentRef.current!.style.height = `${contentRef.current!.scrollHeight}px` - contentRef.current!.style.width = `${contentRef.current!.scrollWidth}px` // Force a reflow contentRef.current!.offsetHeight } @@ -51,10 +50,10 @@ function Collapsible({ onTransitionEnd={onTransitionEnd} style={{ height: !showContent ? 0 : isAnimating ? contentRef.current!.scrollHeight : 'auto', - width: !showContent ? 0 : isAnimating ? contentRef.current!.scrollWidth : 'auto', + width: !showContent ? 0 : 'auto', overflow: 'hidden', transition: 'none 0.3s ease', - transitionProperty: 'height, width, margin-bottom', + transitionProperty: 'height, margin-bottom', marginBottom: (showContent && marginBottomOnExpand) || undefined, }} aria-expanded={showContent}