@farming-labs/svelte-theme 0.0.3-beta.3 → 0.0.3-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/svelte-theme",
3
- "version": "0.0.3-beta.3",
3
+ "version": "0.0.3-beta.4",
4
4
  "description": "Svelte UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
5
5
  "keywords": [
6
6
  "docs",
@@ -82,8 +82,8 @@
82
82
  "dependencies": {
83
83
  "gray-matter": "^4.0.3",
84
84
  "sugar-high": "^0.9.5",
85
- "@farming-labs/docs": "0.0.3-beta.3",
86
- "@farming-labs/svelte": "0.0.3-beta.3"
85
+ "@farming-labs/docs": "0.0.3-beta.4",
86
+ "@farming-labs/svelte": "0.0.3-beta.4"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "svelte": ">=5.0.0"
@@ -1,10 +1,14 @@
1
1
  <script>
2
2
  import { onMount, onDestroy, tick } from "svelte";
3
3
 
4
+ const ACTIVE_ZONE_TOP = 120;
5
+ const HYSTERESIS_PX = 65;
6
+
4
7
  let { items = [], tocStyle = "default" } = $props();
5
8
  let activeIds = $state(new Set());
6
- let observer;
7
- let listEl;
9
+ let listEl = $state(null);
10
+ let lastStableId = null;
11
+ let scrollRafId = 0;
8
12
 
9
13
  let svgPath = $state("");
10
14
  let svgWidth = $state(0);
@@ -14,6 +18,63 @@
14
18
 
15
19
  const isDirectional = $derived(tocStyle === "directional");
16
20
 
21
+ function getDistanceToZone(id) {
22
+ const el = document.getElementById(id);
23
+ if (!el) return Infinity;
24
+ const rect = el.getBoundingClientRect();
25
+ const mid = rect.top + rect.height / 2;
26
+ return Math.abs(mid - ACTIVE_ZONE_TOP);
27
+ }
28
+
29
+ function getClosestId() {
30
+ const ids = items.map((item) => item.url.slice(1));
31
+ let bestId = null;
32
+ let bestDistance = Infinity;
33
+ for (const id of ids) {
34
+ const d = getDistanceToZone(id);
35
+ if (d < bestDistance) {
36
+ bestDistance = d;
37
+ bestId = id;
38
+ }
39
+ }
40
+ return bestId;
41
+ }
42
+
43
+ function isInView(id) {
44
+ const el = document.getElementById(id);
45
+ if (!el) return false;
46
+ const rect = el.getBoundingClientRect();
47
+ return rect.top < window.innerHeight && rect.bottom > 0;
48
+ }
49
+
50
+ function updateActiveFromScroll() {
51
+ scrollRafId = 0;
52
+ const newId = getClosestId();
53
+ if (!newId) {
54
+ activeIds = new Set();
55
+ return;
56
+ }
57
+ if (lastStableId === null) {
58
+ lastStableId = newId;
59
+ activeIds = new Set([newId]);
60
+ return;
61
+ }
62
+ if (newId === lastStableId) {
63
+ activeIds = new Set([newId]);
64
+ return;
65
+ }
66
+ const newDist = getDistanceToZone(newId);
67
+ const currentDist = getDistanceToZone(lastStableId);
68
+ const switchToNew = newDist <= currentDist - HYSTERESIS_PX || !isInView(lastStableId);
69
+ if (switchToNew) lastStableId = newId;
70
+ activeIds = new Set([lastStableId]);
71
+ }
72
+
73
+ function scheduleActiveUpdate() {
74
+ if (scrollRafId !== 0) return;
75
+ scrollRafId = requestAnimationFrame(updateActiveFromScroll);
76
+ }
77
+
17
78
  function getItemOffset(depth) {
18
79
  if (depth <= 2) return 14;
19
80
  if (depth === 3) return 26;
@@ -82,15 +143,6 @@
82
143
  return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
83
144
  }
84
145
 
85
- function observeHeadings() {
86
- if (!observer) return;
87
- observer.disconnect();
88
- for (const item of items) {
89
- const el = document.querySelector(item.url);
90
- if (el) observer.observe(el);
91
- }
92
- }
93
-
94
146
  function isActive(item) {
95
147
  return activeIds.has(item.url.slice(1));
96
148
  }
@@ -124,33 +176,32 @@
124
176
  }
125
177
 
126
178
  onMount(() => {
127
- observer = new IntersectionObserver(
128
- (entries) => {
129
- for (const entry of entries) {
130
- if (entry.isIntersecting) {
131
- activeIds.add(entry.target.id);
132
- } else {
133
- activeIds.delete(entry.target.id);
134
- }
135
- }
136
- activeIds = new Set(activeIds);
137
- },
138
- { rootMargin: "-80px 0px -80% 0px" }
139
- );
140
- observeHeadings();
179
+ const onScroll = () => scheduleActiveUpdate();
180
+ window.addEventListener("scroll", onScroll, { passive: true, capture: true });
181
+ document.addEventListener("scroll", onScroll, { passive: true, capture: true });
141
182
 
142
- if (isDirectional) {
143
- tick().then(() => {
183
+ tick().then(() => {
184
+ const id = getClosestId();
185
+ if (id) {
186
+ lastStableId = id;
187
+ activeIds = new Set([id]);
188
+ }
189
+ if (isDirectional) {
144
190
  buildSvgPath();
145
191
  calcThumb();
146
- });
147
- }
192
+ }
193
+ });
194
+
195
+ return () => {
196
+ window.removeEventListener("scroll", onScroll, { capture: true });
197
+ document.removeEventListener("scroll", onScroll, { capture: true });
198
+ if (scrollRafId) cancelAnimationFrame(scrollRafId);
199
+ };
148
200
  });
149
201
 
150
202
  $effect(() => {
151
203
  void items;
152
- observeHeadings();
153
- if (isDirectional) {
204
+ if (isDirectional && listEl) {
154
205
  tick().then(() => {
155
206
  buildSvgPath();
156
207
  calcThumb();
@@ -165,9 +216,7 @@
165
216
  }
166
217
  });
167
218
 
168
- onDestroy(() => {
169
- observer?.disconnect();
170
- });
219
+ onDestroy(() => {});
171
220
  </script>
172
221
 
173
222
  <div class="fd-toc-inner" class:fd-toc-directional={isDirectional}>
@@ -42,6 +42,13 @@
42
42
  --color-fd-accent: #1a1d1c;
43
43
  --color-fd-accent-foreground: #e5e7e6;
44
44
  --color-fd-border: #2a2d2c;
45
+
46
+ --fd-sidebar-bg: #0a0d0d;
47
+ --fd-sidebar-card-bg: #1a1d1c;
48
+ --fd-sidebar-active-bg: #284d2b;
49
+ --fd-sidebar-active-text: #26bd6c;
50
+ --fd-sidebar-active-line: #26bd6c;
51
+ --fd-sidebar-indent-line: #2a2d2c;
45
52
  }
46
53
 
47
54
  /* ─── Typography — Inter, Mintlify-style values ──────────────── */
@@ -63,79 +70,202 @@ h3 {
63
70
  font-weight: 600;
64
71
  line-height: 1.3;
65
72
  }
66
-
73
+ .fd-docs-content {
74
+ border: none !important;
75
+ }
67
76
  .fd-docs-content p {
68
77
  font-size: 1rem;
69
78
  line-height: 1.7;
70
79
  }
71
80
 
72
81
  /* ═══════════════════════════════════════════════════════════════════
73
- * SIDEBAR — GreenTree: no border, green active background
74
82
  * ═══════════════════════════════════════════════════════════════════ */
75
83
 
76
84
  aside,
77
85
  #nd-sidebar,
78
86
  aside#nd-sidebar,
79
- #nd-docs-layout aside {
87
+ #nd-docs-layout aside,
88
+ .fd-sidebar {
80
89
  border: none;
81
- border-right: none;
90
+ border-right: 1px solid var(--color-fd-border);
82
91
  border-left: none;
83
- border-color: transparent;
84
92
  box-shadow: none;
85
93
  background: var(--color-fd-background);
86
94
  }
87
95
 
88
- aside a[data-active] {
96
+ .dark .fd-sidebar {
97
+ background: var(--color-fd-background);
98
+ }
99
+
100
+ .dark .fd-sidebar .fd-sidebar-title {
101
+ font-size: 1.125rem;
102
+ font-weight: 600;
103
+ color: var(--color-fd-foreground);
104
+ }
105
+
106
+ .dark .fd-sidebar .fd-sidebar-search-btn {
107
+ background: var(--color-fd-secondary) !important;
108
+ border: 1px solid var(--color-fd-border);
109
+ border-radius: 8px;
110
+ color: var(--color-fd-muted-foreground);
111
+ font-size: 0.875rem;
112
+ }
113
+
114
+ .dark .fd-sidebar .fd-sidebar-search-btn:hover {
115
+ background: var(--color-fd-accent) !important;
116
+ }
117
+
118
+ .dark .fd-sidebar .fd-sidebar-search-btn kbd {
119
+ background: var(--color-fd-muted) !important;
120
+ border-color: var(--color-fd-border);
121
+ color: var(--color-fd-muted-foreground);
122
+ border-radius: 4px;
123
+ padding: 2px 6px;
124
+ font-size: 12px;
125
+ line-height: 1;
126
+ }
127
+
128
+ /* Nav padding */
129
+ .fd-sidebar .fd-sidebar-nav {
130
+ padding: 8px 12px;
131
+ }
132
+
133
+ /* All sidebar links — 14px, 500 weight, 6px 10px padding */
134
+ aside a[data-active],
135
+ .fd-sidebar .fd-sidebar-link {
89
136
  font-size: 0.875rem;
90
137
  line-height: 1.5;
91
138
  font-weight: 500;
92
139
  padding: 6px 10px;
93
140
  border-radius: 8px;
94
141
  color: var(--color-fd-muted-foreground);
95
- transition:
96
- color 150ms,
97
- background-color 150ms;
142
+ transition: color 150ms, background-color 150ms;
98
143
  }
99
144
 
100
- aside a[data-active]:hover {
145
+ .dark .fd-sidebar .fd-sidebar-link {
146
+ color: var(--color-fd-muted-foreground);
147
+ }
148
+
149
+ aside a[data-active]:hover,
150
+ .fd-sidebar .fd-sidebar-link:hover {
101
151
  color: var(--color-fd-foreground);
102
152
  background: var(--color-fd-accent);
103
153
  }
104
154
 
105
- aside a[data-active="true"] {
106
- color: var(--color-fd-primary);
107
- font-weight: 600;
108
- background: rgba(13, 147, 115, 0.08);
109
- background-color: rgba(13, 147, 115, 0.08);
155
+ .dark .fd-sidebar .fd-sidebar-link:hover {
156
+ color: var(--color-fd-foreground);
157
+ background: var(--color-fd-accent);
110
158
  }
111
159
 
112
- .dark aside a[data-active="true"] {
113
- background: rgba(38, 189, 108, 0.1);
114
- background-color: rgba(38, 189, 108, 0.1);
115
- color: var(--color-fd-primary);
160
+ aside a[data-active="true"],
161
+ .fd-sidebar .fd-sidebar-link-active,
162
+ .fd-sidebar .fd-sidebar-link[data-active="true"] {
163
+ position: relative !important;
164
+ color: var(--color-fd-primary) !important;
165
+ font-weight: 600 !important;
166
+ background: rgba(13, 147, 115, 0.08) !important;
167
+ background-color: rgba(13, 147, 115, 0.08) !important;
168
+ }
169
+
170
+ .dark .fd-sidebar .fd-sidebar-link-active,
171
+ .dark .fd-sidebar .fd-sidebar-link[data-active="true"] {
172
+ color: var(--color-fd-primary) !important;
173
+ background: rgba(38, 189, 108, 0.1) !important;
174
+ background-color: rgba(38, 189, 108, 0.1) !important;
175
+ }
176
+
177
+ .fd-sidebar-folder-content .fd-sidebar-link-active::before,
178
+ .fd-sidebar-folder-content .fd-sidebar-link[data-active="true"]::before {
179
+ content: "" !important;
180
+ display: block !important;
181
+ position: absolute !important;
182
+ left: -9px !important;
183
+ top: 20% !important;
184
+ bottom: 20% !important;
185
+ width: 3px !important;
186
+ border-radius: 0 2px 2px 0 !important;
187
+ background: var(--color-fd-primary) !important;
188
+ }
189
+
190
+ .fd-sidebar .fd-sidebar-top-link.fd-sidebar-link-active,
191
+ .fd-sidebar .fd-sidebar-top-link[data-active="true"] {
192
+ background: rgba(13, 147, 115, 0.08) !important;
193
+ background-color: rgba(13, 147, 115, 0.08) !important;
194
+ }
195
+
196
+ .dark .fd-sidebar .fd-sidebar-top-link.fd-sidebar-link-active,
197
+ .dark .fd-sidebar .fd-sidebar-top-link[data-active="true"] {
198
+ background: rgba(38, 189, 108, 0.1) !important;
199
+ background-color: rgba(38, 189, 108, 0.1) !important;
200
+ }
201
+
202
+ .dark .fd-sidebar .fd-sidebar-folder {
203
+ margin: 2px 0;
204
+ padding: 0;
205
+ border-radius: 0;
206
+ border: none;
207
+ background: transparent;
208
+ overflow: visible;
209
+ }
210
+ .fd-sidebar .fd-sidebar-folder {
211
+ border: none;
212
+ }
213
+ .fd-page-body .fd-table-wrapper {
214
+ border: none !important;
116
215
  }
117
216
 
118
- aside a[data-active="true"]::before {
119
- background-color: transparent;
120
- width: 0;
217
+ .dark .fd-sidebar .fd-sidebar-folder.fd-sidebar-first-item {
218
+ margin-top: 0;
121
219
  }
122
220
 
123
- aside a[data-active].w-full {
221
+ aside a[data-active].w-full,
222
+ .fd-sidebar .fd-sidebar-folder-trigger {
124
223
  font-weight: 600;
125
224
  font-size: 0.875rem;
126
225
  color: var(--color-fd-foreground);
127
- padding-top: 1rem;
128
- padding-bottom: 0.25rem;
129
- margin-top: 0.5rem;
226
+ padding: 4px 10px 4px 10px;
227
+ margin-top: 0;
228
+ border: none;
130
229
  background: transparent !important;
131
230
  }
132
231
 
133
- aside a[data-active].w-full:first-child {
232
+ .dark .fd-sidebar .fd-sidebar-folder-trigger {
233
+ color: var(--color-fd-muted-foreground);
234
+ border: none;
235
+ }
236
+
237
+ .fd-sidebar .fd-sidebar-folder-trigger:hover {
238
+ background: var(--color-fd-accent) !important;
239
+
240
+ }
241
+
242
+ .dark .fd-sidebar .fd-sidebar-folder-trigger:hover {
243
+ background: var(--color-fd-accent) !important;
244
+ }
245
+
246
+ aside a[data-active].w-full:first-child,
247
+ .fd-sidebar-nav > .fd-sidebar-folder.fd-sidebar-first-item > .fd-sidebar-folder-trigger {
134
248
  margin-top: 0;
135
249
  }
136
250
 
137
- aside a[data-active="true"].w-full {
138
- color: var(--color-fd-primary);
251
+ .fd-sidebar .fd-sidebar-top-link {
252
+ margin-top: 0;
253
+ padding: 6px 10px;
254
+ }
255
+
256
+ .fd-sidebar .fd-sidebar-top-link.fd-sidebar-first-item {
257
+ margin-top: 0;
258
+ }
259
+
260
+ aside a[data-active="true"].w-full,
261
+ .fd-sidebar .fd-sidebar-top-link.fd-sidebar-link-active,
262
+ .fd-sidebar .fd-sidebar-top-link[data-active="true"] {
263
+ color: var(--color-fd-primary) !important;
264
+ }
265
+
266
+ .dark .fd-sidebar .fd-sidebar-top-link.fd-sidebar-link-active,
267
+ .dark .fd-sidebar .fd-sidebar-top-link[data-active="true"] {
268
+ color: var(--color-fd-primary) !important;
139
269
  }
140
270
 
141
271
  aside div.w-full:not([role]) {
@@ -147,7 +277,30 @@ aside div.w-full:not([role]) {
147
277
  margin-top: 0.5rem;
148
278
  }
149
279
 
150
- aside button[class*="bg-fd-secondary"] {
280
+ .fd-sidebar .fd-sidebar-folder-content {
281
+ position: relative;
282
+ padding-left: 16px;
283
+ margin-left: 8px;
284
+ margin-top: -12px;
285
+ }
286
+
287
+ .fd-sidebar .fd-sidebar-folder-content::before {
288
+ content: "";
289
+ position: absolute;
290
+ left: 8px;
291
+ top: 8px;
292
+ height: 90%;
293
+ bottom: 4px;
294
+ width: 1px;
295
+ background: var(--color-fd-border);
296
+ }
297
+
298
+ .dark .fd-sidebar .fd-sidebar-folder-content::before {
299
+ background: var(--fd-sidebar-indent-line, var(--color-fd-border));
300
+ }
301
+
302
+ aside button[class*="bg-fd-secondary"],
303
+ .dark .fd-sidebar .fd-sidebar-search-btn {
151
304
  background: transparent;
152
305
  border: 1px solid var(--color-fd-border);
153
306
  border-radius: 8px;
@@ -155,8 +308,10 @@ aside button[class*="bg-fd-secondary"] {
155
308
  }
156
309
 
157
310
  aside .border-t,
158
- aside [class*="border-t"] {
159
- border-top-color: transparent;
311
+ aside [class*="border-t"],
312
+ .fd-sidebar .fd-sidebar-footer,
313
+ .fd-sidebar .fd-sidebar-footer-custom {
314
+ border-top: 1px solid var(--color-fd-border);
160
315
  }
161
316
 
162
317
  /* ═══════════════════════════════════════════════════════════════════
@@ -236,6 +391,24 @@ nav[class*="header"] {
236
391
  margin: 1rem 0;
237
392
  }
238
393
 
394
+ .fd-page-actions,
395
+ [data-page-actions] {
396
+ gap: 0 !important;
397
+ margin-right: 10px;
398
+ }
399
+ /*
400
+ .fd-page-actions .fd-page-action-btn:nth-child(2) {
401
+ border-top-right-radius: 0 !important;
402
+ border-bottom-right-radius: 0 !important;
403
+ }
404
+
405
+ .fd-page-actions,
406
+ [data-page-actions] .fd-page-action-btn:first-child {
407
+ border-top-left-radius: 0 !important;
408
+ border-bottom-left-radius: 0 !important;
409
+ border-left: none !important;
410
+ } */
411
+
239
412
  .fd-docs-content hr {
240
413
  border-color: var(--color-fd-border);
241
414
  }
@@ -373,21 +546,30 @@ details > :not(summary) {
373
546
  padding: 0.75rem 1rem;
374
547
  }
375
548
 
376
- #nd-toc a[data-active="true"] {
549
+ .fd-toc .fd-toc-link,
550
+ .fd-toc .fd-toc-clerk-link {
551
+ transition: color 0.2s ease;
552
+ }
553
+
554
+ .fd-toc .fd-toc-link-active,
555
+ .fd-toc .fd-toc-link[data-active="true"],
556
+ .fd-toc .fd-toc-clerk-link[data-active="true"] {
377
557
  color: var(--color-fd-primary);
378
558
  font-weight: 500;
379
559
  }
380
560
 
381
- #nd-toc a[data-active="false"] {
561
+ .fd-toc .fd-toc-link:not(.fd-toc-link-active):not([data-active="true"]),
562
+ .fd-toc .fd-toc-clerk-link:not([data-active="true"]) {
382
563
  color: var(--color-fd-muted-foreground);
383
564
  }
384
565
 
385
- #nd-toc a {
566
+ .fd-toc .fd-toc-link,
567
+ .fd-toc .fd-toc-clerk-link {
386
568
  font-size: 0.875rem;
387
569
  line-height: 1.3;
388
570
  }
389
571
 
390
- #nd-toc h3 {
572
+ .fd-toc .fd-toc-title {
391
573
  font-size: 0.875rem;
392
574
  font-weight: 500;
393
575
  }
@@ -540,11 +722,48 @@ details > :not(summary) {
540
722
  filter: brightness(1.1);
541
723
  }
542
724
 
543
- .fd-ai-suggestion,
725
+ .fd-ai-suggestion {
726
+ border-radius: 10px;
727
+ }
728
+ .dark .fd-ai-suggestion:hover {
729
+ border: 1px solid var(--color-fd-primary/50);
730
+ }
731
+
544
732
  .fd-ai-result {
545
733
  border-radius: 10px;
546
734
  }
547
735
 
736
+ .fd-ai-fm-input-container {
737
+ border-radius: 12px;
738
+ }
739
+
740
+ .fd-ai-fm-send-btn {
741
+ border-radius: 9999px;
742
+ }
743
+
744
+ .fd-ai-fm-suggestion {
745
+ border-radius: 9999px;
746
+ }
747
+
748
+ .fd-ai-fm-trigger-btn {
749
+ border-radius: 16px;
750
+ }
751
+
752
+ .fd-ai-fm-close-btn {
753
+ border-radius: 9999px;
754
+ }
755
+
756
+ .fd-ai-code-block {
757
+ border-radius: 20px;
758
+ }
759
+
760
+ .fd-ai-fm-msg-content table {
761
+ border-radius: 20px;
762
+ }
763
+ .fd-ai-code-copy {
764
+ border-radius: 6px;
765
+ }
766
+
548
767
  .fd-sidebar-search-ai-row {
549
768
  display: flex;
550
769
  gap: 8px;
@@ -569,7 +788,7 @@ details > :not(summary) {
569
788
  }
570
789
 
571
790
  .fd-sidebar-search-btn:hover {
572
- background: var(--color-fd-accent);
791
+ background: var(--color-fd-accent) ;
573
792
  }
574
793
 
575
794
  .fd-sidebar-search-btn svg {
@@ -643,7 +862,6 @@ details > :not(summary) {
643
862
 
644
863
  .fd-page-actions .fd-page-action-btn:first-child {
645
864
  border-radius: 8px 0 0 8px;
646
- border-right: none;
647
865
  }
648
866
 
649
867
  .fd-page-actions .fd-page-action-dropdown:last-child .fd-page-action-btn,
@@ -707,6 +707,44 @@ code:not(pre code) {
707
707
  font-family: var(--fd-font-mono, ui-monospace, monospace);
708
708
  }
709
709
 
710
+ /* Light mode: AI code blocks — light bg + dark text + visible pixel border */
711
+ :root:not(.dark) .fd-ai-code-block {
712
+ background: #f4f4f5 !important;
713
+ box-shadow: 3px 3px 0 0 hsl(0 0% 75%);
714
+ --sh-class: #b45309;
715
+ --sh-identifier: #1f2937;
716
+ --sh-keyword: #7c3aed;
717
+ --sh-string: #0d9488;
718
+ --sh-property: #dc2626;
719
+ --sh-entity: #059669;
720
+ --sh-sign: #374151;
721
+ --sh-comment: #6b7280;
722
+ --sh-jsxliterals: #7c3aed;
723
+ }
724
+
725
+ :root:not(.dark) .fd-ai-code-header {
726
+ border-bottom-color: hsl(0 0% 80%);
727
+ background: color-mix(in srgb, #e4e4e7 90%, transparent);
728
+ }
729
+
730
+ :root:not(.dark) .fd-ai-code-lang {
731
+ color: #52525b;
732
+ }
733
+
734
+ :root:not(.dark) .fd-ai-code-copy {
735
+ color: #52525b;
736
+ border-color: hsl(0 0% 75%);
737
+ }
738
+
739
+ :root:not(.dark) .fd-ai-code-copy:hover {
740
+ color: #18181b;
741
+ background: color-mix(in srgb, #d4d4d8 70%, transparent);
742
+ }
743
+
744
+ :root:not(.dark) .fd-ai-code-block code {
745
+ color: #1f2937;
746
+ }
747
+
710
748
  /* ─── Omni Command Palette — pixel-border theme ──────────────────── */
711
749
 
712
750
  .omni-content {