fix left sidebar expand toggle

This commit is contained in:
Wayne Sutton
2025-12-30 21:11:21 -08:00
parent 86e9c4cf5b
commit db960ec163
10 changed files with 71 additions and 18 deletions

View File

@@ -22,7 +22,7 @@ Your content is instantly available to browsers, LLMs, and AI agents.. Write mar
- **Total Posts**: 17
- **Total Pages**: 5
- **Latest Post**: 2025-12-29
- **Last Updated**: 2025-12-30T23:27:44.143Z
- **Last Updated**: 2025-12-31T01:30:04.561Z
## Tech stack

View File

@@ -5,7 +5,7 @@ Project instructions for Claude Code.
## Project context
<!-- Auto-updated by sync:discovery -->
<!-- Site: markdown | Posts: 17 | Pages: 5 | Updated: 2025-12-30T23:27:44.146Z -->
<!-- Site: markdown | Posts: 17 | Pages: 5 | Updated: 2025-12-31T01:30:04.562Z -->
Markdown sync framework. Write markdown in `content/`, run sync commands, content appears instantly via Convex real-time database. Built for developers and AI agents.

View File

@@ -7,6 +7,7 @@ published: true
tags: ["tutorial", "markdown", "cursor", "IDE", "publishing"]
readTime: "3 min read"
featured: false
layout: "sidebar"
featuredOrder: 3
authorName: "Markdown"
blogFeatured: true

View File

@@ -5,6 +5,7 @@ date: "2025-12-14"
slug: "using-images-in-posts"
published: true
featured: false
layout: "sidebar"
featuredOrder: 4
tags: ["images", "tutorial", "markdown", "open-graph"]
readTime: "4 min read"

View File

@@ -122,7 +122,7 @@ Content here...
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) |
| `showFooter` | No | Show footer on this post (overrides siteConfig default) |
| `footer` | No | Footer markdown content (overrides siteConfig.defaultContent) |
| `footer` | No | Per-post footer markdown (overrides `footer.md` and siteConfig.defaultContent) |
| `showSocialFooter` | No | Show social footer on this post (overrides siteConfig default) |
| `aiChat` | No | Enable AI chat in right sidebar. Set `true` to enable (requires `rightSidebar: true` and `siteConfig.aiChat.enabledOnContent: true`). Set `false` to explicitly hide even if global config is enabled. |
| `blogFeatured` | No | Show as featured on blog page (first becomes hero, rest in 2-column row) |
@@ -165,7 +165,7 @@ Content here...
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) |
| `showFooter` | No | Show footer on this page (overrides siteConfig default) |
| `footer` | No | Footer markdown content (overrides siteConfig.defaultContent) |
| `footer` | No | Per-page footer markdown (overrides `footer.md` and siteConfig.defaultContent) |
| `showSocialFooter` | No | Show social footer on this page (overrides siteConfig default) |
| `aiChat` | No | Enable AI chat in right sidebar. Set `true` to enable (requires `rightSidebar: true` and `siteConfig.aiChat.enabledOnContent: true`). Set `false` to explicitly hide even if global config is enabled. |
| `newsletter` | No | Override newsletter signup display (`true` to show, `false` to hide) |

View File

@@ -1,6 +1,6 @@
# llms.txt - Information for AI assistants and LLMs
# Learn more: https://llmstxt.org/
# Last updated: 2025-12-30T23:27:44.147Z
# Last updated: 2025-12-31T01:30:04.563Z
> Your content is instantly available to browsers, LLMs, and AI agents.

View File

@@ -119,7 +119,7 @@ Content here...
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) |
| `showFooter` | No | Show footer on this post (overrides siteConfig default) |
| `footer` | No | Footer markdown content (overrides siteConfig.defaultContent) |
| `footer` | No | Per-post footer markdown (overrides `footer.md` and siteConfig.defaultContent) |
| `showSocialFooter` | No | Show social footer on this post (overrides siteConfig default) |
| `aiChat` | No | Enable AI chat in right sidebar. Set `true` to enable (requires `rightSidebar: true` and `siteConfig.aiChat.enabledOnContent: true`). Set `false` to explicitly hide even if global config is enabled. |
| `blogFeatured` | No | Show as featured on blog page (first becomes hero, rest in 2-column row) |
@@ -162,7 +162,7 @@ Content here...
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) |
| `showFooter` | No | Show footer on this page (overrides siteConfig default) |
| `footer` | No | Footer markdown content (overrides siteConfig.defaultContent) |
| `footer` | No | Per-page footer markdown (overrides `footer.md` and siteConfig.defaultContent) |
| `showSocialFooter` | No | Show social footer on this page (overrides siteConfig default) |
| `aiChat` | No | Enable AI chat in right sidebar. Set `true` to enable (requires `rightSidebar: true` and `siteConfig.aiChat.enabledOnContent: true`). Set `false` to explicitly hide even if global config is enabled. |
| `newsletter` | No | Override newsletter signup display (`true` to show, `false` to hide) |

View File

@@ -38,22 +38,54 @@ function buildHeadingTree(headings: Heading[]): HeadingNode[] {
return tree;
}
// Get ID of first root-level heading that has children (for default expanded state)
function getFirstExpandableId(headings: Heading[]): Set<string> {
if (headings.length === 0) return new Set();
// Find minimum level (usually h2)
let minLevel = Infinity;
for (const h of headings) {
if (h.level < minLevel) minLevel = h.level;
}
// Find first root heading that has a child (next heading with higher level)
for (let i = 0; i < headings.length; i++) {
if (headings[i].level === minLevel) {
// Check if this heading has children (next heading has higher level number)
if (i + 1 < headings.length && headings[i + 1].level > minLevel) {
return new Set([headings[i].id]);
}
}
}
return new Set();
}
// Load expanded state from localStorage
function loadExpandedState(headings: Heading[]): Set<string> {
const stored = localStorage.getItem("page-sidebar-expanded-state");
if (stored) {
try {
const storedIds = new Set(JSON.parse(stored));
// Only return stored IDs that still exist in headings
return new Set(
headings.filter((h) => storedIds.has(h.id)).map((h) => h.id),
);
const parsed = JSON.parse(stored) as string[];
// If stored array has items, use it (filter to valid IDs)
if (parsed.length > 0) {
const storedIds = new Set(parsed);
const validIds = new Set(
headings.filter((h) => storedIds.has(h.id)).map((h) => h.id),
);
// If valid IDs exist, use them; otherwise fall through to default
if (validIds.size > 0) {
return validIds;
}
}
} catch {
// If parse fails, return empty (collapsed)
// If parse fails, use default
}
// Clear stale/empty localStorage
localStorage.removeItem("page-sidebar-expanded-state");
}
// Default: all headings collapsed
return new Set();
// Default: expand only the first expandable heading
return getFirstExpandableId(headings);
}
// Save expanded state to localStorage
@@ -159,6 +191,23 @@ export default function PageSidebar({ headings, activeId }: PageSidebarProps) {
// Build tree structure from headings
const headingTree = useMemo(() => buildHeadingTree(headings), [headings]);
// Reset expanded state when headings change (new page) to expand root items
useEffect(() => {
if (headings.length > 0) {
setExpanded((prev) => {
const currentIds = new Set(headings.map((h) => h.id));
const hasMatchingIds = Array.from(prev).some((id) =>
currentIds.has(id),
);
// If no current expanded IDs match new headings, reset to first expanded
if (!hasMatchingIds) {
return getFirstExpandableId(headings);
}
return prev;
});
}
}, [headings]);
// Get all heading IDs for scroll tracking
const allHeadingIds = useMemo(() => headings.map((h) => h.id), [headings]);

View File

@@ -388,7 +388,7 @@ export const siteConfig: SiteConfig = {
},
],
position: "above-footer",
speed: 30,
speed: 20,
title: "Built with",
scrolling: false, // Set to false for static grid showing first maxItems logos
maxItems: 4, // Number of logos to show when scrolling is false

View File

@@ -1742,7 +1742,9 @@ body {
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease;
transition:
background-color 0.2s ease,
border-color 0.2s ease;
z-index: 10001;
}
@@ -4534,7 +4536,7 @@ body {
}
.logo-marquee-image {
height: 24px;
height: 50px;
max-width: 80px;
}
}