seemore 1.7.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -126,7 +126,7 @@ Install **seemore** from the [VS Code Marketplace](https://marketplace.visualstu
126
126
  | --- | --- | --- |
127
127
  | **Open in seemore** | Editor title bar, on any Markdown file | Renders that file's folder as a site |
128
128
  | **Open Folder in seemore** | Explorer, right-click a folder | Renders that folder as a site and pins it as the root |
129
- | **Pin Current Root** | Status bar item, while a site is open | Pins the currently-serving root for this workspace |
129
+ | **Choose Root** | Status bar item, or the command palette | Pins the folder being served, clears a pinned root, or picks another folder to serve |
130
130
 
131
131
  ### Settings
132
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seemore",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Let AI write the Markdown. Let seemore show it better — zero config documentation framework.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -4,18 +4,32 @@ import { useSearchContext } from 'fumadocs-ui/contexts/search';
4
4
  import { SidebarTrigger } from 'fumadocs-ui/components/sidebar/base';
5
5
  import { useTheme } from 'fumadocs-ui/provider/base';
6
6
  import { config } from 'virtual:seemore/config';
7
+ import { useSidebarCollapse } from './Sidebar.js';
7
8
 
8
9
  export function Header() {
9
10
  const search = useSearchContext();
10
11
  const { resolvedTheme, setTheme } = useTheme();
11
12
  const location = useLocation();
13
+ const { collapsed, toggle } = useSidebarCollapse();
12
14
 
13
15
  return (
14
16
  <header className="seemore-header">
17
+ {/* Two triggers, one slot: below `md` the sidebar is a drawer this opens over the page,
18
+ and at `md` and up it is a rail this hides, leaving the article the room. */}
15
19
  <SidebarTrigger className="seemore-sidebar-trigger" aria-label="Toggle navigation">
16
20
  <PanelLeft />
17
21
  </SidebarTrigger>
18
22
 
23
+ <button
24
+ type="button"
25
+ className="seemore-sidebar-collapse"
26
+ aria-label={collapsed ? 'Show navigation' : 'Hide navigation'}
27
+ aria-expanded={!collapsed}
28
+ onClick={toggle}
29
+ >
30
+ <PanelLeft aria-hidden="true" />
31
+ </button>
32
+
19
33
  <Link to="/" className="seemore-brand" viewTransition>
20
34
  {config.title}
21
35
  </Link>
@@ -1,4 +1,4 @@
1
- import { useEffect, type ComponentProps, type ReactNode } from 'react';
1
+ import { useCallback, useEffect, useLayoutEffect, type ComponentProps, type ReactNode } from 'react';
2
2
  import {
3
3
  SidebarFolder as BaseFolder,
4
4
  SidebarFolderContent as BaseFolderContent,
@@ -43,12 +43,55 @@ const styled = {
43
43
 
44
44
  const renderPageTree = createPageTreeRenderer(styled);
45
45
 
46
+ const COLLAPSE_KEY = 'seemore:sidebar-collapsed';
47
+
48
+ /* `useLayoutEffect` does nothing on the server and says so in a warning; the prerender pass
49
+ takes `useEffect`, which it never runs either. */
50
+ const useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect;
51
+
52
+ /**
53
+ * Hiding the rail is a reader's preference, like the theme, so it outlives the page rather
54
+ * than resetting on the next load. fumadocs owns the state itself — every primitive reads
55
+ * `collapsed` from the same context — this only teaches it to persist, and gives the header
56
+ * a trigger to call.
57
+ */
58
+ export function useSidebarCollapse(): { collapsed: boolean; toggle: () => void } {
59
+ const { collapsed, setCollapsed } = useSidebar();
60
+
61
+ const toggle = useCallback(() => {
62
+ const next = !collapsed;
63
+ try {
64
+ localStorage.setItem(COLLAPSE_KEY, String(next));
65
+ } catch {
66
+ // Storage blocked, private window: the choice just does not outlive the page.
67
+ }
68
+ setCollapsed(next);
69
+ }, [collapsed, setCollapsed]);
70
+
71
+ return { collapsed, toggle };
72
+ }
73
+
74
+ /** Applied before paint, so a remembered collapse does not flash the rail open first. */
75
+ function useRestoreCollapse(): void {
76
+ const { setCollapsed } = useSidebar();
77
+
78
+ useIsomorphicLayoutEffect(() => {
79
+ try {
80
+ if (localStorage.getItem(COLLAPSE_KEY) === 'true') setCollapsed(true);
81
+ } catch {
82
+ // Nothing readable is nothing to restore; the rail stays open.
83
+ }
84
+ }, [setCollapsed]);
85
+ }
86
+
46
87
  export function Sidebar({ children }: { children?: ReactNode }) {
47
88
  // Below `md` the sidebar is a drawer, and the header's trigger is what opens it. Without
48
89
  // reading that state the trigger is decorative: the panel is hidden by CSS alone.
49
- const { open, setOpen } = useSidebar();
90
+ const { open, setOpen, collapsed } = useSidebar();
50
91
  const url = useRouteUrl();
51
92
 
93
+ useRestoreCollapse();
94
+
52
95
  // Following a link should not leave the drawer covering the page you asked for.
53
96
  useEffect(() => {
54
97
  setOpen(false);
@@ -79,7 +122,7 @@ export function Sidebar({ children }: { children?: ReactNode }) {
79
122
  />
80
123
  ) : undefined}
81
124
 
82
- <div className="seemore-sidebar-column" data-open={open}>
125
+ <div className="seemore-sidebar-column" data-open={open} data-collapsed={collapsed}>
83
126
  <aside className="seemore-sidebar" aria-label="Documentation navigation">
84
127
  {/* `toc.integrate` passes the table of contents as children: it belongs inside the
85
128
  viewport, so nav and TOC scroll together rather than the TOC sitting below a
@@ -26,8 +26,31 @@
26
26
  @apply truncate;
27
27
  }
28
28
 
29
+ /* One glyph in one slot, whichever trigger is showing: at header scale the panel icon
30
+ reads as "navigation", and which way it will move is the label's job, not a second
31
+ arrow's. Sized and muted to sit with the search and theme buttons rather than shout
32
+ over them. */
33
+ .seemore-sidebar-trigger,
34
+ .seemore-sidebar-collapse {
35
+ @apply shrink-0 rounded-lg p-1.5 text-fd-muted-foreground transition-colors;
36
+ }
37
+
38
+ .seemore-sidebar-trigger:hover,
39
+ .seemore-sidebar-collapse:hover {
40
+ @apply bg-fd-accent text-fd-accent-foreground;
41
+ }
42
+
43
+ .seemore-sidebar-trigger svg,
44
+ .seemore-sidebar-collapse svg {
45
+ @apply size-5;
46
+ }
47
+
29
48
  .seemore-sidebar-trigger {
30
- @apply shrink-0 md:hidden;
49
+ @apply md:hidden;
50
+ }
51
+
52
+ .seemore-sidebar-collapse {
53
+ @apply hidden md:inline-flex;
31
54
  }
32
55
 
33
56
  .seemore-brand {
@@ -152,12 +175,21 @@
152
175
  @apply translate-x-0 rtl:translate-x-0;
153
176
  }
154
177
 
178
+ /* Collapse is the rail's state alone. Below `md` the same column is the drawer, which has
179
+ its own trigger and its own reason to be open, so the collapsed choice must not reach it. */
180
+ .seemore-sidebar-column[data-collapsed='true'] {
181
+ @apply md:hidden;
182
+ }
183
+
155
184
  .seemore-sidebar-backdrop {
156
185
  @apply fixed inset-0 z-30 bg-black/40 md:hidden;
157
186
  }
158
187
 
188
+ /* A flex column, so the site footer can take `mt-auto`: on a page too short to fill the
189
+ viewport it settles at the bottom instead of floating mid-screen, and on a long one the
190
+ `gap-10` is all that separates it from the content above. */
159
191
  .seemore-main {
160
- @apply mx-auto min-w-0 max-w-3xl flex-1 px-4 py-8 md:px-8;
192
+ @apply mx-auto flex min-w-0 max-w-3xl flex-1 flex-col gap-10 px-4 py-8 md:px-8;
161
193
  }
162
194
 
163
195
  /* Docs are full of long identifiers — `EmbeddedPaymentsController`,
@@ -195,7 +227,7 @@
195
227
  }
196
228
 
197
229
  .seemore-page-footer {
198
- @apply mt-10 grid grid-cols-2 gap-4 border-t border-fd-border pt-6 text-sm;
230
+ @apply grid grid-cols-2 gap-4 border-t border-fd-border pt-6 text-sm;
199
231
  }
200
232
 
201
233
  .seemore-next {
@@ -207,11 +239,11 @@
207
239
  }
208
240
 
209
241
  .seemore-edit-link {
210
- @apply mt-8 inline-flex items-center gap-2 text-sm text-fd-muted-foreground;
242
+ @apply inline-flex items-center gap-2 self-start text-sm text-fd-muted-foreground;
211
243
  }
212
244
 
213
245
  .seemore-site-footer {
214
- @apply mt-10 flex flex-wrap gap-4 border-t border-fd-border pt-6 text-sm text-fd-muted-foreground;
246
+ @apply mt-auto flex flex-wrap gap-4 border-t border-fd-border pt-6 text-sm text-fd-muted-foreground;
215
247
  }
216
248
 
217
249
  /* The generated index page: pages as cards, folders as titled sections of cards. */