@farming-labs/svelte-theme 0.0.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/package.json +57 -0
- package/src/components/AskAIDialog.svelte +342 -0
- package/src/components/Breadcrumb.svelte +47 -0
- package/src/components/Callout.svelte +28 -0
- package/src/components/DocsContent.svelte +32 -0
- package/src/components/DocsLayout.svelte +258 -0
- package/src/components/DocsPage.svelte +145 -0
- package/src/components/DocsSidebar.svelte +63 -0
- package/src/components/FloatingAIChat.svelte +474 -0
- package/src/components/MobileNav.svelte +14 -0
- package/src/components/SearchDialog.svelte +91 -0
- package/src/components/TableOfContents.svelte +69 -0
- package/src/components/ThemeToggle.svelte +40 -0
- package/src/index.d.ts +23 -0
- package/src/index.js +23 -0
- package/src/lib/renderMarkdown.js +110 -0
- package/src/themes/darksharp.js +42 -0
- package/src/themes/default.js +42 -0
- package/src/themes/pixel-border.js +38 -0
- package/styles/docs.css +2124 -0
- package/styles/pixel-border-bundle.css +6 -0
- package/styles/pixel-border.css +601 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* ThemeToggle — Light/dark mode switch.
|
|
4
|
+
* Uses a cookie-based approach that works with SSR.
|
|
5
|
+
*/
|
|
6
|
+
import { onMount } from "svelte";
|
|
7
|
+
|
|
8
|
+
let theme = $state("light");
|
|
9
|
+
|
|
10
|
+
onMount(() => {
|
|
11
|
+
theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function toggle() {
|
|
15
|
+
theme = theme === "dark" ? "light" : "dark";
|
|
16
|
+
document.documentElement.classList.remove("light", "dark");
|
|
17
|
+
document.documentElement.classList.add(theme);
|
|
18
|
+
document.cookie = `theme=${theme};path=/;max-age=31536000`;
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<button class="fd-theme-toggle" onclick={toggle} aria-label="Toggle theme">
|
|
23
|
+
{#if theme === "dark"}
|
|
24
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
25
|
+
<circle cx="12" cy="12" r="5" />
|
|
26
|
+
<line x1="12" y1="1" x2="12" y2="3" />
|
|
27
|
+
<line x1="12" y1="21" x2="12" y2="23" />
|
|
28
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
|
29
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
|
30
|
+
<line x1="1" y1="12" x2="3" y2="12" />
|
|
31
|
+
<line x1="21" y1="12" x2="23" y2="12" />
|
|
32
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
|
33
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
|
34
|
+
</svg>
|
|
35
|
+
{:else}
|
|
36
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
38
|
+
</svg>
|
|
39
|
+
{/if}
|
|
40
|
+
</button>
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { default as DocsLayout } from "./components/DocsLayout.svelte";
|
|
2
|
+
export { default as DocsContent } from "./components/DocsContent.svelte";
|
|
3
|
+
export { default as DocsSidebar } from "./components/DocsSidebar.svelte";
|
|
4
|
+
export { default as DocsPage } from "./components/DocsPage.svelte";
|
|
5
|
+
export { default as TableOfContents } from "./components/TableOfContents.svelte";
|
|
6
|
+
export { default as Breadcrumb } from "./components/Breadcrumb.svelte";
|
|
7
|
+
export { default as ThemeToggle } from "./components/ThemeToggle.svelte";
|
|
8
|
+
export { default as SearchDialog } from "./components/SearchDialog.svelte";
|
|
9
|
+
export { default as AskAIDialog } from "./components/AskAIDialog.svelte";
|
|
10
|
+
export { default as FloatingAIChat } from "./components/FloatingAIChat.svelte";
|
|
11
|
+
export { default as MobileNav } from "./components/MobileNav.svelte";
|
|
12
|
+
export { default as Callout } from "./components/Callout.svelte";
|
|
13
|
+
|
|
14
|
+
import type { DocsTheme } from "@farming-labs/docs";
|
|
15
|
+
|
|
16
|
+
export declare const fumadocs: (overrides?: Partial<DocsTheme>) => DocsTheme;
|
|
17
|
+
export declare const DefaultUIDefaults: Record<string, any>;
|
|
18
|
+
|
|
19
|
+
export declare const pixelBorder: (overrides?: Partial<DocsTheme>) => DocsTheme;
|
|
20
|
+
export declare const PixelBorderUIDefaults: Record<string, any>;
|
|
21
|
+
|
|
22
|
+
export declare const darksharp: (overrides?: Partial<DocsTheme>) => DocsTheme;
|
|
23
|
+
export declare const DarksharpUIDefaults: Record<string, any>;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @farming-labs/svelte-theme
|
|
3
|
+
*
|
|
4
|
+
* Svelte UI components and theme presets for documentation sites.
|
|
5
|
+
* Port of @farming-labs/theme for SvelteKit.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { default as DocsLayout } from "./components/DocsLayout.svelte";
|
|
9
|
+
export { default as DocsContent } from "./components/DocsContent.svelte";
|
|
10
|
+
export { default as DocsSidebar } from "./components/DocsSidebar.svelte";
|
|
11
|
+
export { default as DocsPage } from "./components/DocsPage.svelte";
|
|
12
|
+
export { default as TableOfContents } from "./components/TableOfContents.svelte";
|
|
13
|
+
export { default as Breadcrumb } from "./components/Breadcrumb.svelte";
|
|
14
|
+
export { default as ThemeToggle } from "./components/ThemeToggle.svelte";
|
|
15
|
+
export { default as SearchDialog } from "./components/SearchDialog.svelte";
|
|
16
|
+
export { default as AskAIDialog } from "./components/AskAIDialog.svelte";
|
|
17
|
+
export { default as FloatingAIChat } from "./components/FloatingAIChat.svelte";
|
|
18
|
+
export { default as MobileNav } from "./components/MobileNav.svelte";
|
|
19
|
+
export { default as Callout } from "./components/Callout.svelte";
|
|
20
|
+
|
|
21
|
+
export { fumadocs, DefaultUIDefaults } from "./themes/default.js";
|
|
22
|
+
export { pixelBorder, PixelBorderUIDefaults } from "./themes/pixel-border.js";
|
|
23
|
+
export { darksharp, DarksharpUIDefaults } from "./themes/darksharp.js";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown renderer for AI chat — exact port of the Next.js fumadocs version.
|
|
3
|
+
*
|
|
4
|
+
* Uses sugar-high for syntax highlighting in code blocks.
|
|
5
|
+
* Supports: fenced code blocks, tables, inline code, bold, italic,
|
|
6
|
+
* links, headings, bullet lists, numbered lists.
|
|
7
|
+
*/
|
|
8
|
+
import { highlight } from "sugar-high";
|
|
9
|
+
|
|
10
|
+
function escapeHtml(s) {
|
|
11
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function buildCodeBlock(lang, code) {
|
|
15
|
+
const trimmed = code.replace(/\n$/, "");
|
|
16
|
+
const highlighted = highlight(trimmed).replace(/<\/span>\n<span/g, "</span><span");
|
|
17
|
+
const langLabel = lang ? `<div class="fd-ai-code-lang">${escapeHtml(lang)}</div>` : "";
|
|
18
|
+
const copyBtn = `<button class="fd-ai-code-copy" onclick="(function(btn){var code=btn.closest('.fd-ai-code-block').querySelector('code').textContent;navigator.clipboard.writeText(code).then(function(){btn.textContent='Copied!';setTimeout(function(){btn.textContent='Copy'},1500)})})(this)">Copy</button>`;
|
|
19
|
+
return `<div class="fd-ai-code-block">`
|
|
20
|
+
+ `<div class="fd-ai-code-header">${langLabel}${copyBtn}</div>`
|
|
21
|
+
+ `<pre><code>${highlighted}</code></pre>`
|
|
22
|
+
+ `</div>`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isTableRow(line) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
return trimmed.startsWith("|") && trimmed.endsWith("|") && trimmed.includes("|");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isTableSeparator(line) {
|
|
31
|
+
return /^\|[\s:]*-+[\s:]*(\|[\s:]*-+[\s:]*)*\|$/.test(line.trim());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderTable(rows) {
|
|
35
|
+
const parseRow = (row) =>
|
|
36
|
+
row.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map(c => c.trim());
|
|
37
|
+
|
|
38
|
+
const headerCells = parseRow(rows[0]);
|
|
39
|
+
const thead = `<thead><tr>${headerCells.map(c => `<th>${c}</th>`).join("")}</tr></thead>`;
|
|
40
|
+
|
|
41
|
+
const bodyRows = rows.slice(1).map(row => {
|
|
42
|
+
const cells = parseRow(row);
|
|
43
|
+
return `<tr>${cells.map(c => `<td>${c}</td>`).join("")}</tr>`;
|
|
44
|
+
}).join("");
|
|
45
|
+
|
|
46
|
+
return `<table>${thead}<tbody>${bodyRows}</tbody></table>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function renderMarkdown(text) {
|
|
50
|
+
if (!text) return "";
|
|
51
|
+
|
|
52
|
+
const codeBlocks = [];
|
|
53
|
+
|
|
54
|
+
// Complete fences: ```lang\n...\n```
|
|
55
|
+
let processed = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
56
|
+
codeBlocks.push(buildCodeBlock(lang, code));
|
|
57
|
+
return `\x00CB${codeBlocks.length - 1}\x00`;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Incomplete fence (streaming): opening ``` with no closing one
|
|
61
|
+
processed = processed.replace(/```(\w*)\n([\s\S]*)$/, (_match, lang, code) => {
|
|
62
|
+
codeBlocks.push(buildCodeBlock(lang, code));
|
|
63
|
+
return `\x00CB${codeBlocks.length - 1}\x00`;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const lines = processed.split("\n");
|
|
67
|
+
const output = [];
|
|
68
|
+
let i = 0;
|
|
69
|
+
|
|
70
|
+
while (i < lines.length) {
|
|
71
|
+
if (isTableRow(lines[i]) && i + 1 < lines.length && isTableSeparator(lines[i + 1])) {
|
|
72
|
+
const tableLines = [lines[i]];
|
|
73
|
+
i++; // separator
|
|
74
|
+
i++; // move past separator
|
|
75
|
+
while (i < lines.length && isTableRow(lines[i])) {
|
|
76
|
+
tableLines.push(lines[i]);
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
output.push(renderTable(tableLines));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
output.push(lines[i]);
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let result = output.join("\n");
|
|
87
|
+
|
|
88
|
+
result = result
|
|
89
|
+
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
90
|
+
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
91
|
+
.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<em>$1</em>")
|
|
92
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
|
|
93
|
+
.replace(/^### (.*$)/gm, "<h4>$1</h4>")
|
|
94
|
+
.replace(/^## (.*$)/gm, "<h3>$1</h3>")
|
|
95
|
+
.replace(/^# (.*$)/gm, "<h2>$1</h2>")
|
|
96
|
+
.replace(
|
|
97
|
+
/^[-*] (.*$)/gm,
|
|
98
|
+
'<div style="display:flex;gap:8px;padding:2px 0"><span style="opacity:0.5">\u2022</span><span>$1</span></div>',
|
|
99
|
+
)
|
|
100
|
+
.replace(
|
|
101
|
+
/^(\d+)\. (.*$)/gm,
|
|
102
|
+
'<div style="display:flex;gap:8px;padding:2px 0"><span style="opacity:0.5">$1.</span><span>$2</span></div>',
|
|
103
|
+
)
|
|
104
|
+
.replace(/\n\n/g, '<div style="height:8px"></div>')
|
|
105
|
+
.replace(/\n/g, "<br>");
|
|
106
|
+
|
|
107
|
+
result = result.replace(/\x00CB(\d+)\x00/g, (_m, idx) => codeBlocks[Number(idx)]);
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createTheme } from "@farming-labs/docs";
|
|
2
|
+
|
|
3
|
+
const DarksharpUIDefaults = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "#fafaf9",
|
|
6
|
+
background: "#000000",
|
|
7
|
+
muted: "#a8a29e",
|
|
8
|
+
border: "#292524",
|
|
9
|
+
},
|
|
10
|
+
typography: {
|
|
11
|
+
font: {
|
|
12
|
+
style: {
|
|
13
|
+
sans: "Geist, system-ui, sans-serif",
|
|
14
|
+
mono: "Geist Mono, monospace",
|
|
15
|
+
},
|
|
16
|
+
h1: { size: "2rem", 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: 280,
|
|
27
|
+
toc: { enabled: true, depth: 3 },
|
|
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 darksharp = createTheme({
|
|
38
|
+
name: "fumadocs-darksharp",
|
|
39
|
+
ui: DarksharpUIDefaults,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export { DarksharpUIDefaults };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createTheme } from "@farming-labs/docs";
|
|
2
|
+
|
|
3
|
+
const DefaultUIDefaults = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "#6366f1",
|
|
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: "2rem", 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: 280,
|
|
27
|
+
toc: { enabled: true, depth: 3 },
|
|
28
|
+
header: { height: 72, 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 fumadocs = createTheme({
|
|
38
|
+
name: "fumadocs-default",
|
|
39
|
+
ui: DefaultUIDefaults,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export { DefaultUIDefaults };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createTheme } from "@farming-labs/docs";
|
|
2
|
+
|
|
3
|
+
const PixelBorderUIDefaults = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "oklch(0.985 0.001 106.423)",
|
|
6
|
+
background: "hsl(0 0% 2%)",
|
|
7
|
+
muted: "hsl(0 0% 55%)",
|
|
8
|
+
border: "hsl(0 0% 15%)",
|
|
9
|
+
},
|
|
10
|
+
typography: {
|
|
11
|
+
font: {
|
|
12
|
+
style: {
|
|
13
|
+
sans: "system-ui, -apple-system, sans-serif",
|
|
14
|
+
mono: "ui-monospace, monospace",
|
|
15
|
+
},
|
|
16
|
+
h1: { size: "2.25rem", weight: 700, lineHeight: "1.2", letterSpacing: "-0.02em" },
|
|
17
|
+
h2: { size: "1.5rem", weight: 600, lineHeight: "1.3", letterSpacing: "-0.01em" },
|
|
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: 860,
|
|
26
|
+
sidebarWidth: 286,
|
|
27
|
+
toc: { enabled: true, depth: 3 },
|
|
28
|
+
header: { height: 56, sticky: true },
|
|
29
|
+
},
|
|
30
|
+
components: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const pixelBorder = createTheme({
|
|
34
|
+
name: "fumadocs-pixel-border",
|
|
35
|
+
ui: PixelBorderUIDefaults,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export { PixelBorderUIDefaults };
|