@farming-labs/svelte-theme 0.0.3-beta.2 → 0.0.3-beta.3
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 +3 -3
- package/src/components/DocsContent.svelte +288 -6
- package/src/components/DocsLayout.svelte +2 -6
- package/src/components/FloatingAIChat.svelte +2 -2
- package/src/components/SearchDialog.svelte +354 -55
- package/src/themes/colorful.js +1 -1
- package/styles/colorful.css +72 -0
- package/styles/darksharp.css +71 -0
- package/styles/docs.css +197 -11
- package/styles/greentree.css +50 -0
- package/styles/omni.css +362 -0
- package/styles/pixel-border.css +190 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/svelte-theme",
|
|
3
|
-
"version": "0.0.3-beta.
|
|
3
|
+
"version": "0.0.3-beta.3",
|
|
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.
|
|
86
|
-
"@farming-labs/svelte": "0.0.3-beta.
|
|
85
|
+
"@farming-labs/docs": "0.0.3-beta.3",
|
|
86
|
+
"@farming-labs/svelte": "0.0.3-beta.3"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"svelte": ">=5.0.0"
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import DocsPage from "./DocsPage.svelte";
|
|
3
|
+
import { onMount, onDestroy } from "svelte";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_OPEN_PROVIDERS = [
|
|
6
|
+
{ name: "ChatGPT", urlTemplate: "https://chatgpt.com/?hints=search&q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
7
|
+
{ name: "Claude", urlTemplate: "https://claude.ai/new?q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
8
|
+
];
|
|
3
9
|
|
|
4
10
|
let { data, config = null } = $props();
|
|
5
11
|
|
|
12
|
+
let openDropdownMenu = $state(false);
|
|
13
|
+
let copyLabel = $state("Copy page");
|
|
14
|
+
let copied = $state(false);
|
|
15
|
+
|
|
6
16
|
let titleSuffix = $derived(
|
|
7
17
|
config?.metadata?.titleTemplate
|
|
8
18
|
? config.metadata.titleTemplate.replace("%s", "")
|
|
@@ -14,7 +24,7 @@
|
|
|
14
24
|
);
|
|
15
25
|
|
|
16
26
|
let tocStyle = $derived(
|
|
17
|
-
config?.theme?.ui?.layout?.toc?.style
|
|
27
|
+
(config?.theme?.ui?.layout?.toc?.style === "directional") ? "directional" : "default"
|
|
18
28
|
);
|
|
19
29
|
|
|
20
30
|
let breadcrumbEnabled = $derived.by(() => {
|
|
@@ -37,6 +47,146 @@
|
|
|
37
47
|
if (typeof cfg === "object" && cfg !== null) return cfg.enabled !== false;
|
|
38
48
|
return false;
|
|
39
49
|
});
|
|
50
|
+
|
|
51
|
+
let copyMarkdownEnabled = $derived.by(() => {
|
|
52
|
+
const pa = config?.pageActions;
|
|
53
|
+
if (!pa) return false;
|
|
54
|
+
const cm = pa.copyMarkdown;
|
|
55
|
+
if (cm === true) return true;
|
|
56
|
+
if (typeof cm === "object" && cm !== null) return cm.enabled !== false;
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
let openDocsEnabled = $derived.by(() => {
|
|
61
|
+
const pa = config?.pageActions;
|
|
62
|
+
if (!pa) return false;
|
|
63
|
+
const od = pa.openDocs;
|
|
64
|
+
if (od === true) return true;
|
|
65
|
+
if (typeof od === "object" && od !== null) return od.enabled !== false;
|
|
66
|
+
return false;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
let openDocsProviders = $derived.by(() => {
|
|
70
|
+
const pa = config?.pageActions;
|
|
71
|
+
const od = pa && typeof pa === "object" && pa.openDocs != null ? pa.openDocs : null;
|
|
72
|
+
const list = od && typeof od === "object" && "providers" in od ? od.providers : undefined;
|
|
73
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
74
|
+
const mapped = list
|
|
75
|
+
.map((p) => ({
|
|
76
|
+
name: typeof p?.name === "string" ? p.name : "Open",
|
|
77
|
+
urlTemplate: typeof p?.urlTemplate === "string" ? p.urlTemplate : "",
|
|
78
|
+
}))
|
|
79
|
+
.filter((p) => p.urlTemplate.length > 0);
|
|
80
|
+
if (mapped.length > 0) return mapped;
|
|
81
|
+
}
|
|
82
|
+
return DEFAULT_OPEN_PROVIDERS;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
let pageActionsPosition = $derived(
|
|
86
|
+
typeof config?.pageActions === "object" && config?.pageActions !== null && config?.pageActions.position
|
|
87
|
+
? config.pageActions.position
|
|
88
|
+
: "below-title"
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
let pageActionsAlignment = $derived(
|
|
92
|
+
typeof config?.pageActions === "object" && config?.pageActions !== null && config?.pageActions.alignment
|
|
93
|
+
? config.pageActions.alignment
|
|
94
|
+
: "left"
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
let lastUpdatedConfig = $derived.by(() => {
|
|
98
|
+
const lu = config?.lastUpdated;
|
|
99
|
+
if (lu === false) return { enabled: false, position: "footer" };
|
|
100
|
+
if (lu === true || lu === undefined) return { enabled: true, position: "footer" };
|
|
101
|
+
const o = lu;
|
|
102
|
+
return {
|
|
103
|
+
enabled: o.enabled !== false,
|
|
104
|
+
position: o.position ?? "footer",
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
let showLastUpdatedInFooter = $derived(
|
|
109
|
+
!!data.lastModified && lastUpdatedConfig.enabled && lastUpdatedConfig.position === "footer"
|
|
110
|
+
);
|
|
111
|
+
let showLastUpdatedBelowTitle = $derived(
|
|
112
|
+
!!data.lastModified && lastUpdatedConfig.enabled && lastUpdatedConfig.position === "below-title"
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
let htmlWithoutFirstH1 = $derived(
|
|
116
|
+
(data.html || "").replace(/<h1[^>]*>[\s\S]*?<\/h1>\s*/i, "")
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
let showPageActions = $derived(
|
|
120
|
+
(copyMarkdownEnabled || openDocsEnabled) && openDocsProviders.length >= 0
|
|
121
|
+
);
|
|
122
|
+
let showActionsAbove = $derived(pageActionsPosition === "above-title" && showPageActions);
|
|
123
|
+
let showActionsBelow = $derived(pageActionsPosition === "below-title" && showPageActions);
|
|
124
|
+
|
|
125
|
+
function handleCopyPage() {
|
|
126
|
+
let text = "";
|
|
127
|
+
if (data.rawMarkdown && typeof data.rawMarkdown === "string" && data.rawMarkdown.length > 0) {
|
|
128
|
+
text = data.rawMarkdown;
|
|
129
|
+
} else {
|
|
130
|
+
const article = document.querySelector("#nd-page");
|
|
131
|
+
if (article) text = article.innerText || "";
|
|
132
|
+
}
|
|
133
|
+
if (!text) return;
|
|
134
|
+
navigator.clipboard.writeText(text).then(
|
|
135
|
+
() => {
|
|
136
|
+
copyLabel = "Copied!";
|
|
137
|
+
copied = true;
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
copyLabel = "Copy page";
|
|
140
|
+
copied = false;
|
|
141
|
+
}, 2000);
|
|
142
|
+
},
|
|
143
|
+
() => {
|
|
144
|
+
copyLabel = "Copy failed";
|
|
145
|
+
setTimeout(() => { copyLabel = "Copy page"; }, 2000);
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function toggleDropdown() {
|
|
151
|
+
openDropdownMenu = !openDropdownMenu;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function closeDropdown() {
|
|
155
|
+
openDropdownMenu = false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function openInProvider(provider) {
|
|
159
|
+
const pathname = typeof window !== "undefined" ? window.location.pathname : "";
|
|
160
|
+
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
161
|
+
const mdxUrl = typeof window !== "undefined"
|
|
162
|
+
? window.location.origin + pathname + (pathname.endsWith("/") ? "page.mdx" : ".mdx")
|
|
163
|
+
: "";
|
|
164
|
+
const githubUrl = data.editOnGithub || "";
|
|
165
|
+
const url = provider.urlTemplate
|
|
166
|
+
.replace(/\{url\}/g, encodeURIComponent(pageUrl))
|
|
167
|
+
.replace(/\{mdxUrl\}/g, encodeURIComponent(mdxUrl))
|
|
168
|
+
.replace(/\{githubUrl\}/g, githubUrl);
|
|
169
|
+
if (typeof window !== "undefined") window.open(url, "_blank", "noopener,noreferrer");
|
|
170
|
+
closeDropdown();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function handleClickOutside(e) {
|
|
174
|
+
const target = e.target;
|
|
175
|
+
if (openDropdownMenu && !target?.closest?.(".fd-page-action-dropdown")) {
|
|
176
|
+
closeDropdown();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
onMount(() => {
|
|
181
|
+
if (typeof document !== "undefined") {
|
|
182
|
+
document.addEventListener("click", handleClickOutside);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
onDestroy(() => {
|
|
186
|
+
if (typeof document !== "undefined") {
|
|
187
|
+
document.removeEventListener("click", handleClickOutside);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
40
190
|
</script>
|
|
41
191
|
|
|
42
192
|
<svelte:head>
|
|
@@ -54,13 +204,145 @@
|
|
|
54
204
|
previousPage={data.previousPage}
|
|
55
205
|
nextPage={data.nextPage}
|
|
56
206
|
editOnGithub={showEditOnGithub ? data.editOnGithub : null}
|
|
57
|
-
lastModified={
|
|
207
|
+
lastModified={showLastUpdatedInFooter ? data.lastModified : null}
|
|
58
208
|
{llmsTxtEnabled}
|
|
59
209
|
>
|
|
60
210
|
{#snippet children()}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
211
|
+
<div class="fd-docs-content">
|
|
212
|
+
{#if showActionsAbove}
|
|
213
|
+
<div
|
|
214
|
+
class="fd-page-actions"
|
|
215
|
+
data-page-actions
|
|
216
|
+
data-actions-alignment={pageActionsAlignment}
|
|
217
|
+
>
|
|
218
|
+
{#if copyMarkdownEnabled}
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
class="fd-page-action-btn"
|
|
222
|
+
aria-label="Copy page content"
|
|
223
|
+
data-copied={copied}
|
|
224
|
+
onclick={handleCopyPage}
|
|
225
|
+
>
|
|
226
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
227
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
228
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
229
|
+
</svg>
|
|
230
|
+
<span>{copyLabel}</span>
|
|
231
|
+
</button>
|
|
232
|
+
{/if}
|
|
233
|
+
{#if openDocsEnabled && openDocsProviders.length > 0}
|
|
234
|
+
<div class="fd-page-action-dropdown">
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
class="fd-page-action-btn"
|
|
238
|
+
aria-expanded={openDropdownMenu}
|
|
239
|
+
aria-haspopup="true"
|
|
240
|
+
onclick={toggleDropdown}
|
|
241
|
+
>
|
|
242
|
+
<span>Open in</span>
|
|
243
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
244
|
+
<polyline points="6 9 12 15 18 9" />
|
|
245
|
+
</svg>
|
|
246
|
+
</button>
|
|
247
|
+
<div
|
|
248
|
+
class="fd-page-action-menu"
|
|
249
|
+
role="menu"
|
|
250
|
+
hidden={!openDropdownMenu}
|
|
251
|
+
>
|
|
252
|
+
{#each openDocsProviders as provider}
|
|
253
|
+
<button
|
|
254
|
+
type="button"
|
|
255
|
+
role="menuitem"
|
|
256
|
+
class="fd-page-action-menu-item"
|
|
257
|
+
onclick={() => openInProvider(provider)}
|
|
258
|
+
>
|
|
259
|
+
<span class="fd-page-action-menu-label">Open in {provider.name}</span>
|
|
260
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
261
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
262
|
+
<polyline points="15 3 21 3 21 9" />
|
|
263
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
264
|
+
</svg>
|
|
265
|
+
</button>
|
|
266
|
+
{/each}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
{/if}
|
|
270
|
+
</div>
|
|
271
|
+
{/if}
|
|
272
|
+
|
|
273
|
+
<h1 class="fd-page-title">{data.title}</h1>
|
|
274
|
+
{#if data.description}
|
|
275
|
+
<p class="fd-page-description">{data.description}</p>
|
|
276
|
+
{/if}
|
|
277
|
+
{#if showLastUpdatedBelowTitle && data.lastModified}
|
|
278
|
+
<p class="fd-last-modified fd-last-modified-below-title">
|
|
279
|
+
Last updated: {data.lastModified}
|
|
280
|
+
</p>
|
|
281
|
+
{/if}
|
|
282
|
+
|
|
283
|
+
{#if showActionsBelow}
|
|
284
|
+
<hr class="fd-page-actions-divider" aria-hidden="true" />
|
|
285
|
+
<div
|
|
286
|
+
class="fd-page-actions"
|
|
287
|
+
data-page-actions
|
|
288
|
+
data-actions-alignment={pageActionsAlignment}
|
|
289
|
+
>
|
|
290
|
+
{#if copyMarkdownEnabled}
|
|
291
|
+
<button
|
|
292
|
+
type="button"
|
|
293
|
+
class="fd-page-action-btn"
|
|
294
|
+
aria-label="Copy page content"
|
|
295
|
+
data-copied={copied}
|
|
296
|
+
onclick={handleCopyPage}
|
|
297
|
+
>
|
|
298
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
299
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
300
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
301
|
+
</svg>
|
|
302
|
+
<span>{copyLabel}</span>
|
|
303
|
+
</button>
|
|
304
|
+
{/if}
|
|
305
|
+
{#if openDocsEnabled && openDocsProviders.length > 0}
|
|
306
|
+
<div class="fd-page-action-dropdown">
|
|
307
|
+
<button
|
|
308
|
+
type="button"
|
|
309
|
+
class="fd-page-action-btn"
|
|
310
|
+
aria-expanded={openDropdownMenu}
|
|
311
|
+
aria-haspopup="true"
|
|
312
|
+
onclick={toggleDropdown}
|
|
313
|
+
>
|
|
314
|
+
<span>Open in</span>
|
|
315
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
316
|
+
<polyline points="6 9 12 15 18 9" />
|
|
317
|
+
</svg>
|
|
318
|
+
</button>
|
|
319
|
+
<div
|
|
320
|
+
class="fd-page-action-menu"
|
|
321
|
+
role="menu"
|
|
322
|
+
hidden={!openDropdownMenu}
|
|
323
|
+
>
|
|
324
|
+
{#each openDocsProviders as provider}
|
|
325
|
+
<button
|
|
326
|
+
type="button"
|
|
327
|
+
role="menuitem"
|
|
328
|
+
class="fd-page-action-menu-item"
|
|
329
|
+
onclick={() => openInProvider(provider)}
|
|
330
|
+
>
|
|
331
|
+
<span class="fd-page-action-menu-label">Open in {provider.name}</span>
|
|
332
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
333
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
334
|
+
<polyline points="15 3 21 3 21 9" />
|
|
335
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
336
|
+
</svg>
|
|
337
|
+
</button>
|
|
338
|
+
{/each}
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
{/if}
|
|
342
|
+
</div>
|
|
343
|
+
{/if}
|
|
344
|
+
|
|
345
|
+
{@html htmlWithoutFirstH1}
|
|
346
|
+
</div>
|
|
65
347
|
{/snippet}
|
|
66
348
|
</DocsPage>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import ThemeToggle from "./ThemeToggle.svelte";
|
|
3
|
+
import SearchDialog from "./SearchDialog.svelte";
|
|
3
4
|
import AskAIDialog from "./AskAIDialog.svelte";
|
|
4
5
|
import FloatingAIChat from "./FloatingAIChat.svelte";
|
|
5
6
|
import { page } from "$app/stores";
|
|
@@ -396,10 +397,5 @@
|
|
|
396
397
|
{/if}
|
|
397
398
|
|
|
398
399
|
{#if searchOpen}
|
|
399
|
-
<
|
|
400
|
-
onclose={closeSearch}
|
|
401
|
-
suggestedQuestions={config?.ai?.suggestedQuestions ?? []}
|
|
402
|
-
aiLabel={config?.ai?.aiLabel ?? "AI"}
|
|
403
|
-
hideAITab={config?.ai?.mode === "floating"}
|
|
404
|
-
/>
|
|
400
|
+
<SearchDialog onclose={closeSearch} />
|
|
405
401
|
{/if}
|
|
@@ -268,7 +268,7 @@
|
|
|
268
268
|
class="fd-ai-floating-trigger"
|
|
269
269
|
style={btnStyle}
|
|
270
270
|
>
|
|
271
|
-
<svelte:component this={triggerComponent} />
|
|
271
|
+
<svelte:component this={triggerComponent} aiLabel={label} />
|
|
272
272
|
</div>
|
|
273
273
|
{:else}
|
|
274
274
|
<button
|
|
@@ -486,7 +486,7 @@
|
|
|
486
486
|
class="fd-ai-floating-trigger"
|
|
487
487
|
style={btnStyle}
|
|
488
488
|
>
|
|
489
|
-
<svelte:component this={triggerComponent} />
|
|
489
|
+
<svelte:component this={triggerComponent} aiLabel={label} />
|
|
490
490
|
</div>
|
|
491
491
|
{:else}
|
|
492
492
|
<button
|