sdocs 0.0.59 → 0.0.60

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/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.60] - 2026-07-06
11
+
12
+ ### Added
13
+
14
+ - **The page table of contents highlights the current section.** As the
15
+ page scrolls, the "On this page" entry for the section in view lights up;
16
+ clicking an entry highlights it immediately and holds it through the
17
+ smooth scroll.
18
+
10
19
  ## [0.0.59] - 2026-07-05
11
20
 
12
21
  ### Changed
@@ -37,7 +37,69 @@
37
37
  };
38
38
  });
39
39
 
40
+ // Scrollspy: the TOC highlights the section currently in view. The active
41
+ // heading is the last one above the threshold line near the viewport top.
42
+ // The scroll listener is capturing because the scrolling element is an
43
+ // ancestor container, not the window.
44
+ const SPY_OFFSET = 120;
45
+ let activeId = $state('');
46
+ let spyLockUntil = 0;
47
+
48
+ $effect(() => {
49
+ if (!container || toc.length === 0) return;
50
+ void PageComponent; // re-run once the page body has mounted
51
+ const ids = toc.map((h) => h.id);
52
+ let frame = 0;
53
+ const update = () => {
54
+ frame = 0;
55
+ if (performance.now() < spyLockUntil) return;
56
+ // At the bottom the last sections can never reach the threshold
57
+ // line — snap to the final entry so it is reachable at all.
58
+ let scroller: HTMLElement | null = container ?? null;
59
+ while (scroller) {
60
+ const o = getComputedStyle(scroller).overflowY;
61
+ if (
62
+ scroller.scrollHeight > scroller.clientHeight + 1 &&
63
+ (o === 'auto' || o === 'scroll')
64
+ ) {
65
+ break;
66
+ }
67
+ scroller = scroller.parentElement;
68
+ }
69
+ const atBottom = scroller
70
+ ? scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2
71
+ : window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2;
72
+ if (atBottom) {
73
+ activeId = ids[ids.length - 1];
74
+ return;
75
+ }
76
+ let current = ids[0] ?? '';
77
+ for (const id of ids) {
78
+ const el = container?.querySelector(`#${CSS.escape(id)}`);
79
+ if (!el) continue;
80
+ if (el.getBoundingClientRect().top > SPY_OFFSET) break;
81
+ current = id;
82
+ }
83
+ activeId = current;
84
+ };
85
+ const onScroll = () => {
86
+ if (!frame) frame = requestAnimationFrame(update);
87
+ };
88
+ update();
89
+ document.addEventListener('scroll', onScroll, { capture: true, passive: true });
90
+ window.addEventListener('resize', onScroll);
91
+ return () => {
92
+ document.removeEventListener('scroll', onScroll, { capture: true });
93
+ window.removeEventListener('resize', onScroll);
94
+ if (frame) cancelAnimationFrame(frame);
95
+ };
96
+ });
97
+
40
98
  function scrollToHeading(id: string) {
99
+ // Highlight the target right away and hold it while the smooth scroll
100
+ // passes intermediate sections.
101
+ activeId = id;
102
+ spyLockUntil = performance.now() + 800;
41
103
  container?.querySelector(`#${CSS.escape(id)}`)?.scrollIntoView({ behavior: 'smooth' });
42
104
  }
43
105
 
@@ -102,6 +164,8 @@
102
164
  <li class="sdocs-toc-item" style:padding-left="{(heading.level - 2) * 12}px">
103
165
  <button
104
166
  class="sdocs-toc-link"
167
+ class:is-active={heading.id === activeId}
168
+ aria-current={heading.id === activeId ? 'true' : undefined}
105
169
  onclick={() => scrollToHeading(heading.id)}
106
170
  >
107
171
  {heading.text}
@@ -372,8 +436,10 @@
372
436
  cursor: pointer;
373
437
  text-align: left;
374
438
  width: 100%;
439
+ transition: color 0.15s;
375
440
  }
376
- .sdocs-toc-link:hover {
441
+ .sdocs-toc-link:hover,
442
+ .sdocs-toc-link.is-active {
377
443
  color: var(--color-action-500);
378
444
  }
379
445
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdocs",
3
- "version": "0.0.59",
3
+ "version": "0.0.60",
4
4
  "description": "A lightweight documentation tool for Svelte 5 components",
5
5
  "type": "module",
6
6
  "license": "MIT",