@farming-labs/svelte-theme 0.0.2-beta.15 → 0.0.2-beta.18
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 +11 -4
- package/src/components/Breadcrumb.svelte +2 -6
- package/src/components/DocsContent.svelte +8 -0
- package/src/components/DocsLayout.svelte +3 -1
- package/src/components/DocsPage.svelte +2 -1
- package/src/components/FloatingAIChat.svelte +48 -22
- package/src/components/TableOfContents.svelte +182 -15
- package/src/themes/colorful.d.ts +2 -0
- package/src/themes/colorful.js +42 -0
- package/styles/colorful-bundle.css +2 -0
- package/styles/colorful.css +148 -0
- package/styles/docs.css +71 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/svelte-theme",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
3
|
+
"version": "0.0.2-beta.18",
|
|
4
4
|
"description": "Svelte UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./src/index.js",
|
|
@@ -26,12 +26,19 @@
|
|
|
26
26
|
"import": "./src/themes/darksharp.js",
|
|
27
27
|
"default": "./src/themes/darksharp.js"
|
|
28
28
|
},
|
|
29
|
+
"./colorful": {
|
|
30
|
+
"types": "./src/themes/colorful.d.ts",
|
|
31
|
+
"import": "./src/themes/colorful.js",
|
|
32
|
+
"default": "./src/themes/colorful.js"
|
|
33
|
+
},
|
|
29
34
|
"./css": "./styles/docs.css",
|
|
30
35
|
"./fumadocs/css": "./styles/docs.css",
|
|
31
36
|
"./styles/pixel-border.css": "./styles/pixel-border.css",
|
|
32
37
|
"./styles/darksharp.css": "./styles/darksharp.css",
|
|
33
38
|
"./pixel-border/css": "./styles/pixel-border-bundle.css",
|
|
34
|
-
"./darksharp/css": "./styles/darksharp-bundle.css"
|
|
39
|
+
"./darksharp/css": "./styles/darksharp-bundle.css",
|
|
40
|
+
"./styles/colorful.css": "./styles/colorful.css",
|
|
41
|
+
"./colorful/css": "./styles/colorful-bundle.css"
|
|
35
42
|
},
|
|
36
43
|
"typesVersions": {
|
|
37
44
|
"*": {
|
|
@@ -65,8 +72,8 @@
|
|
|
65
72
|
"dependencies": {
|
|
66
73
|
"gray-matter": "^4.0.3",
|
|
67
74
|
"sugar-high": "^0.9.5",
|
|
68
|
-
"@farming-labs/docs": "0.0.2-beta.
|
|
69
|
-
"@farming-labs/svelte": "0.0.2-beta.
|
|
75
|
+
"@farming-labs/docs": "0.0.2-beta.18",
|
|
76
|
+
"@farming-labs/svelte": "0.0.2-beta.18"
|
|
70
77
|
},
|
|
71
78
|
"peerDependencies": {
|
|
72
79
|
"svelte": ">=5.0.0"
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
let { pathname = "", entry = "docs" } = $props();
|
|
6
6
|
|
|
7
7
|
let segments = $derived.by(() => {
|
|
8
|
-
|
|
9
|
-
return all.filter((s) => s.toLowerCase() !== entry.toLowerCase());
|
|
8
|
+
return pathname.split("/").filter(Boolean);
|
|
10
9
|
});
|
|
11
10
|
|
|
12
11
|
let parentLabel = $derived.by(() => {
|
|
@@ -25,10 +24,7 @@
|
|
|
25
24
|
|
|
26
25
|
let parentUrl = $derived.by(() => {
|
|
27
26
|
if (segments.length < 2) return "";
|
|
28
|
-
|
|
29
|
-
const parentSegment = segments[segments.length - 2];
|
|
30
|
-
const parentIndex = all.indexOf(parentSegment);
|
|
31
|
-
return "/" + all.slice(0, parentIndex + 1).join("/");
|
|
27
|
+
return "/" + segments.slice(0, segments.length - 1).join("/");
|
|
32
28
|
});
|
|
33
29
|
</script>
|
|
34
30
|
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
config?.theme?.ui?.layout?.toc?.enabled ?? true
|
|
14
14
|
);
|
|
15
15
|
|
|
16
|
+
let tocStyle = $derived(
|
|
17
|
+
config?.theme?.ui?.layout?.toc?.style ?? "default"
|
|
18
|
+
);
|
|
19
|
+
|
|
16
20
|
let breadcrumbEnabled = $derived.by(() => {
|
|
17
21
|
const bc = config?.breadcrumb;
|
|
18
22
|
if (bc === undefined || bc === true) return true;
|
|
@@ -38,6 +42,7 @@
|
|
|
38
42
|
<DocsPage
|
|
39
43
|
entry={config?.entry ?? "docs"}
|
|
40
44
|
{tocEnabled}
|
|
45
|
+
{tocStyle}
|
|
41
46
|
{breadcrumbEnabled}
|
|
42
47
|
previousPage={data.previousPage}
|
|
43
48
|
nextPage={data.nextPage}
|
|
@@ -45,6 +50,9 @@
|
|
|
45
50
|
lastModified={showLastModified ? data.lastModified : null}
|
|
46
51
|
>
|
|
47
52
|
{#snippet children()}
|
|
53
|
+
{#if data.description}
|
|
54
|
+
<p class="fd-page-description">{data.description}</p>
|
|
55
|
+
{/if}
|
|
48
56
|
{@html data.html}
|
|
49
57
|
{/snippet}
|
|
50
58
|
</DocsPage>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
title = undefined,
|
|
12
12
|
titleUrl = undefined,
|
|
13
13
|
children,
|
|
14
|
+
triggerComponent = null,
|
|
14
15
|
} = $props();
|
|
15
16
|
|
|
16
17
|
let resolvedTitle = $derived(title ?? config?.nav?.title ?? "Docs");
|
|
@@ -143,7 +144,7 @@
|
|
|
143
144
|
vars.push(`${COLOR_MAP[key]}: ${value};`);
|
|
144
145
|
}
|
|
145
146
|
if (vars.length === 0) return "";
|
|
146
|
-
return
|
|
147
|
+
return `.dark {\n ${vars.join("\n ")}\n}`;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
function buildFontStyleVars(prefix, style) {
|
|
@@ -352,6 +353,7 @@
|
|
|
352
353
|
aiLabel={config.ai.aiLabel ?? "AI"}
|
|
353
354
|
position={config.ai.position ?? "bottom-right"}
|
|
354
355
|
floatingStyle={config.ai.floatingStyle ?? "panel"}
|
|
356
|
+
{triggerComponent}
|
|
355
357
|
/>
|
|
356
358
|
{/if}
|
|
357
359
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
8
|
tocEnabled = true,
|
|
9
|
+
tocStyle = "default",
|
|
9
10
|
breadcrumbEnabled = true,
|
|
10
11
|
entry = "docs",
|
|
11
12
|
previousPage = null,
|
|
@@ -143,7 +144,7 @@
|
|
|
143
144
|
|
|
144
145
|
{#if tocEnabled}
|
|
145
146
|
<aside class="fd-toc">
|
|
146
|
-
<TableOfContents items={tocItems} />
|
|
147
|
+
<TableOfContents items={tocItems} {tocStyle} />
|
|
147
148
|
</aside>
|
|
148
149
|
{/if}
|
|
149
150
|
</div>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
aiLabel = "AI",
|
|
9
9
|
position = "bottom-right",
|
|
10
10
|
floatingStyle = "panel",
|
|
11
|
+
triggerComponent = null,
|
|
11
12
|
} = $props();
|
|
12
13
|
|
|
13
14
|
let isOpen = $state(false);
|
|
@@ -254,17 +255,29 @@
|
|
|
254
255
|
style={isOpen ? undefined : btnStyle}
|
|
255
256
|
>
|
|
256
257
|
{#if !isOpen}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
258
|
+
{#if triggerComponent}
|
|
259
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
260
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
261
|
+
<div
|
|
262
|
+
onclick={() => isOpen = true}
|
|
263
|
+
class="fd-ai-floating-trigger"
|
|
264
|
+
style={btnStyle}
|
|
265
|
+
>
|
|
266
|
+
<svelte:component this={triggerComponent} />
|
|
267
|
+
</div>
|
|
268
|
+
{:else}
|
|
269
|
+
<button
|
|
270
|
+
onclick={() => isOpen = true}
|
|
271
|
+
class="fd-ai-fm-trigger-btn"
|
|
272
|
+
aria-label="Ask {label}"
|
|
273
|
+
>
|
|
274
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
275
|
+
<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
|
|
276
|
+
<path d="M20 3v4"/><path d="M22 5h-4"/>
|
|
277
|
+
</svg>
|
|
278
|
+
<span>Ask {label}</span>
|
|
279
|
+
</button>
|
|
280
|
+
{/if}
|
|
268
281
|
{:else}
|
|
269
282
|
<div class="fd-ai-fm-input-container">
|
|
270
283
|
<div class="fd-ai-fm-input-wrap">
|
|
@@ -458,17 +471,30 @@
|
|
|
458
471
|
{/if}
|
|
459
472
|
|
|
460
473
|
{#if !isOpen}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
<
|
|
470
|
-
</
|
|
471
|
-
|
|
474
|
+
{#if triggerComponent}
|
|
475
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
476
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
477
|
+
<div
|
|
478
|
+
onclick={() => isOpen = true}
|
|
479
|
+
class="fd-ai-floating-trigger"
|
|
480
|
+
style={btnStyle}
|
|
481
|
+
>
|
|
482
|
+
<svelte:component this={triggerComponent} />
|
|
483
|
+
</div>
|
|
484
|
+
{:else}
|
|
485
|
+
<button
|
|
486
|
+
onclick={() => isOpen = true}
|
|
487
|
+
aria-label="Ask {label}"
|
|
488
|
+
class="fd-ai-floating-btn"
|
|
489
|
+
style={btnStyle}
|
|
490
|
+
>
|
|
491
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
492
|
+
<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
|
|
493
|
+
<path d="M20 3v4"/><path d="M22 5h-4"/>
|
|
494
|
+
</svg>
|
|
495
|
+
<span>Ask {label}</span>
|
|
496
|
+
</button>
|
|
497
|
+
{/if}
|
|
472
498
|
{/if}
|
|
473
499
|
{/if}
|
|
474
500
|
{/if}
|
|
@@ -1,45 +1,176 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { onMount, onDestroy } from "svelte";
|
|
2
|
+
import { onMount, onDestroy, tick } from "svelte";
|
|
3
3
|
|
|
4
|
-
let { items = [] } = $props();
|
|
5
|
-
let
|
|
4
|
+
let { items = [], tocStyle = "default" } = $props();
|
|
5
|
+
let activeIds = $state(new Set());
|
|
6
6
|
let observer;
|
|
7
|
+
let listEl;
|
|
8
|
+
|
|
9
|
+
let svgPath = $state("");
|
|
10
|
+
let svgWidth = $state(0);
|
|
11
|
+
let svgHeight = $state(0);
|
|
12
|
+
let thumbTop = $state(0);
|
|
13
|
+
let thumbHeight = $state(0);
|
|
14
|
+
|
|
15
|
+
const isDirectional = $derived(tocStyle === "directional");
|
|
16
|
+
|
|
17
|
+
function getItemOffset(depth) {
|
|
18
|
+
if (depth <= 2) return 14;
|
|
19
|
+
if (depth === 3) return 26;
|
|
20
|
+
return 36;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getLineOffset(depth) {
|
|
24
|
+
return depth >= 3 ? 10 : 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildSvgPath() {
|
|
28
|
+
if (!listEl) return;
|
|
29
|
+
const links = listEl.querySelectorAll(".fd-toc-clerk-link");
|
|
30
|
+
if (links.length === 0) { svgPath = ""; return; }
|
|
31
|
+
|
|
32
|
+
let d = [];
|
|
33
|
+
let w = 0, h = 0;
|
|
34
|
+
|
|
35
|
+
links.forEach((el, i) => {
|
|
36
|
+
if (i >= items.length) return;
|
|
37
|
+
const depth = items[i].depth;
|
|
38
|
+
const x = getLineOffset(depth) + 1;
|
|
39
|
+
const styles = getComputedStyle(el);
|
|
40
|
+
const top = el.offsetTop + parseFloat(styles.paddingTop);
|
|
41
|
+
const bottom = el.offsetTop + el.clientHeight - parseFloat(styles.paddingBottom);
|
|
42
|
+
w = Math.max(x, w);
|
|
43
|
+
h = Math.max(h, bottom);
|
|
44
|
+
d.push(`${i === 0 ? "M" : "L"}${x} ${top}`);
|
|
45
|
+
d.push(`L${x} ${bottom}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
svgPath = d.join(" ");
|
|
49
|
+
svgWidth = w + 1;
|
|
50
|
+
svgHeight = h;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function calcThumb() {
|
|
54
|
+
if (!listEl || activeIds.size === 0) {
|
|
55
|
+
thumbTop = 0;
|
|
56
|
+
thumbHeight = 0;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let upper = Infinity, lower = 0;
|
|
61
|
+
for (const id of activeIds) {
|
|
62
|
+
const el = listEl.querySelector(`a[href="#${id}"]`);
|
|
63
|
+
if (!el) continue;
|
|
64
|
+
const styles = getComputedStyle(el);
|
|
65
|
+
upper = Math.min(upper, el.offsetTop + parseFloat(styles.paddingTop));
|
|
66
|
+
lower = Math.max(lower, el.offsetTop + el.clientHeight - parseFloat(styles.paddingBottom));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (upper === Infinity) {
|
|
70
|
+
thumbTop = 0;
|
|
71
|
+
thumbHeight = 0;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
thumbTop = upper;
|
|
76
|
+
thumbHeight = lower - upper;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function maskSvgUrl() {
|
|
80
|
+
if (!svgPath) return "none";
|
|
81
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svgWidth} ${svgHeight}"><path d="${svgPath}" stroke="black" stroke-width="1" fill="none"/></svg>`;
|
|
82
|
+
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
83
|
+
}
|
|
84
|
+
|
|
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
|
+
function isActive(item) {
|
|
95
|
+
return activeIds.has(item.url.slice(1));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasDiagonal(index) {
|
|
99
|
+
if (index === 0) return false;
|
|
100
|
+
return items[index - 1].depth !== items[index].depth;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getDiagonalCoords(index) {
|
|
104
|
+
const upperOffset = getLineOffset(items[index - 1].depth);
|
|
105
|
+
const currentOffset = getLineOffset(items[index].depth);
|
|
106
|
+
return { upperOffset, currentOffset };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function verticalLineStyle(item, index) {
|
|
110
|
+
const prevDepth = index > 0 ? items[index - 1].depth : item.depth;
|
|
111
|
+
const nextDepth = index < items.length - 1 ? items[index + 1].depth : item.depth;
|
|
112
|
+
return {
|
|
113
|
+
position: "absolute",
|
|
114
|
+
left: `${getLineOffset(item.depth)}px`,
|
|
115
|
+
top: prevDepth !== item.depth ? "6px" : "0",
|
|
116
|
+
bottom: nextDepth !== item.depth ? "6px" : "0",
|
|
117
|
+
width: "1px",
|
|
118
|
+
background: "hsla(0, 0%, 50%, 0.1)",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function styleObj(obj) {
|
|
123
|
+
return Object.entries(obj).map(([k, v]) => `${k}:${v}`).join(";");
|
|
124
|
+
}
|
|
7
125
|
|
|
8
126
|
onMount(() => {
|
|
9
127
|
observer = new IntersectionObserver(
|
|
10
128
|
(entries) => {
|
|
11
129
|
for (const entry of entries) {
|
|
12
130
|
if (entry.isIntersecting) {
|
|
13
|
-
|
|
131
|
+
activeIds.add(entry.target.id);
|
|
132
|
+
} else {
|
|
133
|
+
activeIds.delete(entry.target.id);
|
|
14
134
|
}
|
|
15
135
|
}
|
|
136
|
+
activeIds = new Set(activeIds);
|
|
16
137
|
},
|
|
17
138
|
{ rootMargin: "-80px 0px -80% 0px" }
|
|
18
139
|
);
|
|
19
|
-
|
|
20
140
|
observeHeadings();
|
|
141
|
+
|
|
142
|
+
if (isDirectional) {
|
|
143
|
+
tick().then(() => {
|
|
144
|
+
buildSvgPath();
|
|
145
|
+
calcThumb();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
21
148
|
});
|
|
22
149
|
|
|
23
150
|
$effect(() => {
|
|
24
151
|
void items;
|
|
25
152
|
observeHeadings();
|
|
153
|
+
if (isDirectional) {
|
|
154
|
+
tick().then(() => {
|
|
155
|
+
buildSvgPath();
|
|
156
|
+
calcThumb();
|
|
157
|
+
});
|
|
158
|
+
}
|
|
26
159
|
});
|
|
27
160
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const el = document.querySelector(item.url);
|
|
33
|
-
if (el) observer.observe(el);
|
|
161
|
+
$effect(() => {
|
|
162
|
+
void activeIds;
|
|
163
|
+
if (isDirectional) {
|
|
164
|
+
calcThumb();
|
|
34
165
|
}
|
|
35
|
-
}
|
|
166
|
+
});
|
|
36
167
|
|
|
37
168
|
onDestroy(() => {
|
|
38
169
|
observer?.disconnect();
|
|
39
170
|
});
|
|
40
171
|
</script>
|
|
41
172
|
|
|
42
|
-
<div class="fd-toc-inner">
|
|
173
|
+
<div class="fd-toc-inner" class:fd-toc-directional={isDirectional}>
|
|
43
174
|
<h3 class="fd-toc-title">
|
|
44
175
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
45
176
|
<line x1="3" y1="6" x2="21" y2="6" />
|
|
@@ -50,14 +181,14 @@
|
|
|
50
181
|
</h3>
|
|
51
182
|
{#if items.length === 0}
|
|
52
183
|
<p class="fd-toc-empty">No Headings</p>
|
|
53
|
-
{:else}
|
|
184
|
+
{:else if !isDirectional}
|
|
54
185
|
<ul class="fd-toc-list">
|
|
55
186
|
{#each items as item}
|
|
56
187
|
<li class="fd-toc-item">
|
|
57
188
|
<a
|
|
58
189
|
href={item.url}
|
|
59
190
|
class="fd-toc-link"
|
|
60
|
-
class:fd-toc-link-active={
|
|
191
|
+
class:fd-toc-link-active={isActive(item)}
|
|
61
192
|
style:padding-left="{12 + (item.depth - 2) * 12}px"
|
|
62
193
|
>
|
|
63
194
|
{item.title}
|
|
@@ -65,5 +196,41 @@
|
|
|
65
196
|
</li>
|
|
66
197
|
{/each}
|
|
67
198
|
</ul>
|
|
199
|
+
{:else}
|
|
200
|
+
<ul class="fd-toc-list fd-toc-clerk" style="position:relative;" bind:this={listEl}>
|
|
201
|
+
{#each items as item, index}
|
|
202
|
+
<li class="fd-toc-item">
|
|
203
|
+
<a
|
|
204
|
+
href={item.url}
|
|
205
|
+
class="fd-toc-link fd-toc-clerk-link"
|
|
206
|
+
data-active={isActive(item) ? "true" : undefined}
|
|
207
|
+
style="position:relative; padding-left:{getItemOffset(item.depth)}px; padding-top:6px; padding-bottom:6px; font-size:{item.depth <= 2 ? '14' : '13'}px; overflow-wrap:anywhere;"
|
|
208
|
+
>
|
|
209
|
+
<div style={styleObj(verticalLineStyle(item, index))}></div>
|
|
210
|
+
|
|
211
|
+
{#if hasDiagonal(index)}
|
|
212
|
+
{@const d = getDiagonalCoords(index)}
|
|
213
|
+
<svg viewBox="0 0 16 16" width="16" height="16" style="position:absolute; top:-6px; left:0;">
|
|
214
|
+
<line x1={d.upperOffset} y1="0" x2={d.currentOffset} y2="12" stroke="hsla(0, 0%, 50%, 0.1)" stroke-width="1" />
|
|
215
|
+
</svg>
|
|
216
|
+
{/if}
|
|
217
|
+
|
|
218
|
+
{item.title}
|
|
219
|
+
</a>
|
|
220
|
+
</li>
|
|
221
|
+
{/each}
|
|
222
|
+
|
|
223
|
+
{#if svgPath}
|
|
224
|
+
<div
|
|
225
|
+
class="fd-toc-clerk-mask"
|
|
226
|
+
style="position:absolute; left:0; top:0; width:{svgWidth}px; height:{svgHeight}px; pointer-events:none; mask-image:{maskSvgUrl()}; -webkit-mask-image:{maskSvgUrl()}; mask-repeat:no-repeat; -webkit-mask-repeat:no-repeat;"
|
|
227
|
+
>
|
|
228
|
+
<div
|
|
229
|
+
class="fd-toc-clerk-thumb"
|
|
230
|
+
style="margin-top:{thumbTop}px; height:{thumbHeight}px; background:var(--color-fd-primary); transition:all 0.15s; will-change:height,margin-top;"
|
|
231
|
+
></div>
|
|
232
|
+
</div>
|
|
233
|
+
{/if}
|
|
234
|
+
</ul>
|
|
68
235
|
{/if}
|
|
69
236
|
</div>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createTheme } from "@farming-labs/docs";
|
|
2
|
+
|
|
3
|
+
const ColorfulUIDefaults = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "hsl(40, 96%, 40%)",
|
|
6
|
+
background: "#ffffff",
|
|
7
|
+
muted: "#64748b",
|
|
8
|
+
border: "#e5e7eb",
|
|
9
|
+
},
|
|
10
|
+
typography: {
|
|
11
|
+
font: {
|
|
12
|
+
style: {
|
|
13
|
+
sans: "Inter, system-ui, sans-serif",
|
|
14
|
+
mono: "JetBrains Mono, monospace",
|
|
15
|
+
},
|
|
16
|
+
h1: { size: "1.875rem", weight: 700, lineHeight: "1.2", letterSpacing: "-0.02em" },
|
|
17
|
+
h2: { size: "1.5rem", weight: 600, lineHeight: "1.3" },
|
|
18
|
+
h3: { size: "1.25rem", weight: 600, lineHeight: "1.4" },
|
|
19
|
+
h4: { size: "1.125rem", weight: 600, lineHeight: "1.4" },
|
|
20
|
+
body: { size: "1rem", weight: 400, lineHeight: "1.75" },
|
|
21
|
+
small: { size: "0.875rem", weight: 400, lineHeight: "1.5" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
layout: {
|
|
25
|
+
contentWidth: 768,
|
|
26
|
+
sidebarWidth: 260,
|
|
27
|
+
toc: { enabled: true, depth: 3, style: "directional" },
|
|
28
|
+
header: { height: 56, sticky: true },
|
|
29
|
+
},
|
|
30
|
+
components: {
|
|
31
|
+
Callout: { variant: "soft", icon: true },
|
|
32
|
+
CodeBlock: { showCopyButton: true },
|
|
33
|
+
Tabs: { style: "default" },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const colorful = createTheme({
|
|
38
|
+
name: "fumadocs-colorful",
|
|
39
|
+
ui: ColorfulUIDefaults,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export { ColorfulUIDefaults };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* @farming-labs/svelte-theme — colorful theme overrides
|
|
2
|
+
* Fumadocs-inspired theme with warm yellow/amber accent colors.
|
|
3
|
+
* Import AFTER the base theme CSS (docs.css).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* ─── Colorful yellow accent overrides ────────────────────────────── */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--color-fd-primary: hsl(40, 96%, 40%);
|
|
10
|
+
--color-fd-primary-foreground: hsl(0, 0%, 100%);
|
|
11
|
+
--color-fd-ring: hsl(40, 80%, 50%);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.dark {
|
|
15
|
+
--color-fd-primary: hsl(45, 100%, 60%);
|
|
16
|
+
--color-fd-primary-foreground: hsl(0, 0%, 5%);
|
|
17
|
+
--color-fd-ring: hsl(45, 90%, 55%);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ─── Description under title ──────────────────────────────────────── */
|
|
21
|
+
|
|
22
|
+
.fd-page-description {
|
|
23
|
+
margin-bottom: 1rem;
|
|
24
|
+
font-size: 1.125rem;
|
|
25
|
+
line-height: 1.75;
|
|
26
|
+
color: var(--color-fd-muted-foreground);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* ─── Sidebar dark overrides (fumadocs neutral) ────────────────────── */
|
|
30
|
+
|
|
31
|
+
.dark .fd-sidebar {
|
|
32
|
+
--color-fd-muted: hsl(0, 0%, 16%);
|
|
33
|
+
--color-fd-secondary: hsl(0, 0%, 18%);
|
|
34
|
+
--color-fd-muted-foreground: hsl(0, 0%, 72%);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ─── Cards (fumadocs style) ────────────────────────────────────────── */
|
|
38
|
+
|
|
39
|
+
.fd-card {
|
|
40
|
+
display: block;
|
|
41
|
+
border-radius: 0.75rem;
|
|
42
|
+
border: 1px solid var(--color-fd-border);
|
|
43
|
+
background: var(--color-fd-card);
|
|
44
|
+
padding: 1rem;
|
|
45
|
+
font-size: 0.875rem;
|
|
46
|
+
color: var(--color-fd-card-foreground);
|
|
47
|
+
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
|
|
48
|
+
transition: background-color 150ms, border-color 150ms;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.fd-card:hover {
|
|
52
|
+
background: var(--color-fd-accent);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.fd-card-icon {
|
|
56
|
+
margin-bottom: 0.5rem;
|
|
57
|
+
width: fit-content;
|
|
58
|
+
border-radius: 0.375rem;
|
|
59
|
+
border: 1px solid var(--color-fd-border);
|
|
60
|
+
padding: 0.375rem;
|
|
61
|
+
color: var(--color-fd-muted-foreground);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.fd-card-title {
|
|
65
|
+
font-weight: 500;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.fd-card-description {
|
|
69
|
+
color: var(--color-fd-muted-foreground);
|
|
70
|
+
margin-top: 0.25rem;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.fd-cards {
|
|
74
|
+
display: grid;
|
|
75
|
+
grid-template-columns: 1fr;
|
|
76
|
+
gap: 1rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media (min-width: 640px) {
|
|
80
|
+
.fd-cards {
|
|
81
|
+
grid-template-columns: repeat(2, 1fr);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ─── Page nav cards ───────────────────────────────────────────────── */
|
|
86
|
+
|
|
87
|
+
.fd-page-nav-card {
|
|
88
|
+
border-radius: 0.75rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ─── Inline code ──────────────────────────────────────────────────── */
|
|
92
|
+
|
|
93
|
+
.fd-docs-content :not(pre) > code {
|
|
94
|
+
padding: 3px;
|
|
95
|
+
border: 1px solid var(--color-fd-border);
|
|
96
|
+
font-size: 13px;
|
|
97
|
+
border-radius: 5px;
|
|
98
|
+
background: var(--color-fd-muted);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ─── Links in prose ───────────────────────────────────────────────── */
|
|
102
|
+
|
|
103
|
+
.fd-docs-content a:not(.fd-page-nav-card):not([class]) {
|
|
104
|
+
text-decoration: underline;
|
|
105
|
+
text-underline-offset: 3.5px;
|
|
106
|
+
text-decoration-color: var(--color-fd-primary);
|
|
107
|
+
text-decoration-thickness: 1.5px;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ─── Tables (rounded) ─────────────────────────────────────────────── */
|
|
112
|
+
|
|
113
|
+
.fd-docs-content table {
|
|
114
|
+
border-collapse: separate;
|
|
115
|
+
border-spacing: 0;
|
|
116
|
+
background: var(--color-fd-card);
|
|
117
|
+
border-radius: 0.75rem;
|
|
118
|
+
border: 1px solid var(--color-fd-border);
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.fd-docs-content th {
|
|
123
|
+
background: var(--color-fd-muted);
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.fd-docs-content th,
|
|
128
|
+
.fd-docs-content td {
|
|
129
|
+
padding: 0.625rem;
|
|
130
|
+
border-bottom: 1px solid var(--color-fd-border);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.fd-docs-content tr:last-child td {
|
|
134
|
+
border-bottom: none;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* ─── Blockquotes ──────────────────────────────────────────────────── */
|
|
138
|
+
|
|
139
|
+
.fd-docs-content blockquote {
|
|
140
|
+
border-left: 2px solid var(--color-fd-primary);
|
|
141
|
+
padding-left: 1rem;
|
|
142
|
+
color: var(--color-fd-foreground);
|
|
143
|
+
font-style: normal;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.fd-docs-content hr {
|
|
147
|
+
border-color: var(--color-fd-border);
|
|
148
|
+
}
|
package/styles/docs.css
CHANGED
|
@@ -477,6 +477,37 @@ code, kbd, pre, samp {
|
|
|
477
477
|
border-left-color: var(--color-fd-primary);
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
+
/* ─── Clerk TOC (tree-line style) ────────────────────────────────────── */
|
|
481
|
+
|
|
482
|
+
.fd-toc-clerk {
|
|
483
|
+
border-left: none !important;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.fd-toc-clerk .fd-toc-link {
|
|
487
|
+
display: block;
|
|
488
|
+
border-left: none;
|
|
489
|
+
margin-left: 0;
|
|
490
|
+
color: var(--color-fd-muted-foreground);
|
|
491
|
+
text-decoration: none;
|
|
492
|
+
transition: color 0.15s;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.fd-toc-clerk .fd-toc-link:hover {
|
|
496
|
+
color: var(--color-fd-foreground);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.fd-toc-clerk .fd-toc-link[data-active="true"] {
|
|
500
|
+
color: var(--color-fd-primary);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.fd-toc-clerk-mask {
|
|
504
|
+
overflow: hidden;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.fd-toc-clerk-thumb {
|
|
508
|
+
width: 100%;
|
|
509
|
+
}
|
|
510
|
+
|
|
480
511
|
@media (max-width: 1279px) {
|
|
481
512
|
.fd-toc {
|
|
482
513
|
display: none;
|
|
@@ -497,6 +528,15 @@ code, kbd, pre, samp {
|
|
|
497
528
|
}
|
|
498
529
|
}
|
|
499
530
|
|
|
531
|
+
/* ─── Page description (frontmatter) ─────────────────────────────────── */
|
|
532
|
+
|
|
533
|
+
.fd-page-description {
|
|
534
|
+
margin-bottom: 1rem;
|
|
535
|
+
font-size: 1.125rem;
|
|
536
|
+
line-height: 1.75;
|
|
537
|
+
color: var(--color-fd-muted-foreground);
|
|
538
|
+
}
|
|
539
|
+
|
|
500
540
|
/* ─── Breadcrumb ─────────────────────────────────────────────────────── */
|
|
501
541
|
|
|
502
542
|
.fd-breadcrumb {
|
|
@@ -1749,11 +1789,6 @@ html.dark pre.shiki {
|
|
|
1749
1789
|
|
|
1750
1790
|
.fd-ai-floating-btn {
|
|
1751
1791
|
border-radius: 26px;
|
|
1752
|
-
box-shadow: 0 8px 32px rgba(99, 102, 241, 0.3);
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
.fd-ai-floating-btn:hover {
|
|
1756
|
-
box-shadow: 0 10px 40px rgba(99, 102, 241, 0.4);
|
|
1757
1792
|
}
|
|
1758
1793
|
|
|
1759
1794
|
.fd-ai-suggestion {
|
|
@@ -1802,24 +1837,32 @@ html.dark pre.shiki {
|
|
|
1802
1837
|
.fd-ai-floating-btn {
|
|
1803
1838
|
position: fixed;
|
|
1804
1839
|
z-index: 9997;
|
|
1805
|
-
width: 52px;
|
|
1806
|
-
height: 52px;
|
|
1807
|
-
border-radius: var(--radius, 26px);
|
|
1808
|
-
border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
|
|
1809
|
-
background: var(--color-fd-primary, #6366f1);
|
|
1810
|
-
color: var(--color-fd-primary-foreground, #fff);
|
|
1811
|
-
cursor: pointer;
|
|
1812
1840
|
display: flex;
|
|
1813
1841
|
align-items: center;
|
|
1814
1842
|
justify-content: center;
|
|
1815
|
-
|
|
1816
|
-
|
|
1843
|
+
gap: 8px;
|
|
1844
|
+
padding: 8px 12px;
|
|
1845
|
+
height: 40px;
|
|
1846
|
+
border-radius: 16px;
|
|
1847
|
+
border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
|
|
1848
|
+
background: color-mix(in srgb, var(--color-fd-secondary, #f4f4f5) 80%, transparent);
|
|
1849
|
+
backdrop-filter: blur(4px);
|
|
1850
|
+
color: var(--color-fd-muted-foreground, #71717a);
|
|
1851
|
+
cursor: pointer;
|
|
1852
|
+
font-size: 14px;
|
|
1853
|
+
box-shadow: 0 1px 3px color-mix(in srgb, var(--color-fd-background, #000) 20%, transparent);
|
|
1854
|
+
transition: transform 150ms, background 150ms, color 150ms;
|
|
1817
1855
|
animation: fd-ai-fade-in 300ms ease-out;
|
|
1818
1856
|
}
|
|
1819
1857
|
|
|
1820
1858
|
.fd-ai-floating-btn:hover {
|
|
1821
|
-
|
|
1822
|
-
|
|
1859
|
+
background: var(--color-fd-accent);
|
|
1860
|
+
color: var(--color-fd-accent-foreground);
|
|
1861
|
+
transform: scale(1.03);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
.fd-ai-floating-btn:active {
|
|
1865
|
+
transform: scale(0.97);
|
|
1823
1866
|
}
|
|
1824
1867
|
|
|
1825
1868
|
.fd-ai-floating-trigger {
|
|
@@ -2148,24 +2191,28 @@ html.dark pre.shiki {
|
|
|
2148
2191
|
align-items: center;
|
|
2149
2192
|
justify-content: center;
|
|
2150
2193
|
gap: 8px;
|
|
2151
|
-
padding: 8px
|
|
2194
|
+
padding: 8px 12px;
|
|
2152
2195
|
height: 40px;
|
|
2153
|
-
border-radius:
|
|
2196
|
+
border-radius: 16px;
|
|
2154
2197
|
border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
|
|
2155
|
-
background: var(--color-fd-secondary,
|
|
2198
|
+
background: color-mix(in srgb, var(--color-fd-secondary, #f4f4f5) 80%, transparent);
|
|
2199
|
+
backdrop-filter: blur(4px);
|
|
2156
2200
|
color: var(--color-fd-muted-foreground, #71717a);
|
|
2157
2201
|
font-family: inherit;
|
|
2158
2202
|
font-size: 14px;
|
|
2159
2203
|
cursor: pointer;
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
transition: all 200ms;
|
|
2204
|
+
box-shadow: 0 1px 3px color-mix(in srgb, var(--color-fd-background, #000) 20%, transparent);
|
|
2205
|
+
transition: transform 150ms, background 150ms, color 150ms;
|
|
2163
2206
|
animation: fd-ai-fade-in 300ms ease-out;
|
|
2164
2207
|
white-space: nowrap;
|
|
2165
2208
|
}
|
|
2166
2209
|
|
|
2167
2210
|
.fd-ai-fm-trigger-btn:hover {
|
|
2168
|
-
background: var(--color-fd-accent
|
|
2169
|
-
color: var(--color-fd-accent-foreground
|
|
2211
|
+
background: var(--color-fd-accent);
|
|
2212
|
+
color: var(--color-fd-accent-foreground);
|
|
2170
2213
|
transform: scale(1.03);
|
|
2171
2214
|
}
|
|
2215
|
+
|
|
2216
|
+
.fd-ai-fm-trigger-btn:active {
|
|
2217
|
+
transform: scale(0.97);
|
|
2218
|
+
}
|