@farming-labs/svelte-theme 0.0.1 → 0.0.2-beta.13

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.1",
3
+ "version": "0.0.2-beta.13",
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",
@@ -12,21 +12,26 @@
12
12
  "default": "./src/index.js"
13
13
  },
14
14
  "./fumadocs": {
15
+ "types": "./src/themes/default.d.ts",
15
16
  "import": "./src/themes/default.js",
16
17
  "default": "./src/themes/default.js"
17
18
  },
18
19
  "./pixel-border": {
20
+ "types": "./src/themes/pixel-border.d.ts",
19
21
  "import": "./src/themes/pixel-border.js",
20
22
  "default": "./src/themes/pixel-border.js"
21
23
  },
22
24
  "./darksharp": {
25
+ "types": "./src/themes/darksharp.d.ts",
23
26
  "import": "./src/themes/darksharp.js",
24
27
  "default": "./src/themes/darksharp.js"
25
28
  },
26
29
  "./css": "./styles/docs.css",
27
30
  "./fumadocs/css": "./styles/docs.css",
28
31
  "./styles/pixel-border.css": "./styles/pixel-border.css",
29
- "./pixel-border/css": "./styles/pixel-border-bundle.css"
32
+ "./styles/darksharp.css": "./styles/darksharp.css",
33
+ "./pixel-border/css": "./styles/pixel-border-bundle.css",
34
+ "./darksharp/css": "./styles/darksharp-bundle.css"
30
35
  },
31
36
  "files": [
32
37
  "src",
@@ -44,8 +49,8 @@
44
49
  "dependencies": {
45
50
  "gray-matter": "^4.0.3",
46
51
  "sugar-high": "^0.9.5",
47
- "@farming-labs/docs": "0.0.2-beta.4",
48
- "@farming-labs/svelte": "0.0.1"
52
+ "@farming-labs/docs": "0.0.2-beta.13",
53
+ "@farming-labs/svelte": "0.0.2-beta.13"
49
54
  },
50
55
  "peerDependencies": {
51
56
  "svelte": ">=5.0.0"
@@ -174,28 +174,27 @@
174
174
  class="fd-ai-dialog"
175
175
  onclick={(e) => e.stopPropagation()}
176
176
  role="document"
177
- style="left:50%;top:50%;transform:translate(-50%,-50%);width:min(680px,calc(100vw - 32px));max-height:min(560px,calc(100vh - 64px));animation:fd-ai-slide-up 200ms ease-out"
177
+ style="left:50%;top:12%;transform:translateX(-50%);width:min(640px,calc(100vw - 32px));max-height:min(520px,calc(100vh - 120px));animation:fd-ai-slide-up 200ms ease-out"
178
178
  >
179
179
  <!-- Tab bar -->
180
180
  <div class="fd-ai-tab-bar">
181
181
  <button onclick={() => switchTab("search")} class="fd-ai-tab" data-active={tab === "search"}>
182
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
182
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
183
183
  <circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" />
184
184
  </svg>
185
185
  Search
186
186
  </button>
187
187
  {#if !hideAITab}
188
188
  <button onclick={() => switchTab("ai")} class="fd-ai-tab" data-active={tab === "ai"}>
189
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
189
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
190
190
  <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" />
191
- <path d="M20 3v4" /><path d="M22 5h-4" />
192
191
  </svg>
193
192
  Ask {aiLabel}
194
193
  </button>
195
194
  {/if}
196
- <div style="margin-left:auto;display:flex;gap:4px;align-items:center">
195
+ <span style="margin-left:auto;display:flex;align-items:center;padding-right:12px">
197
196
  <kbd class="fd-ai-esc">ESC</kbd>
198
- </div>
197
+ </span>
199
198
  </div>
200
199
 
201
200
  <!-- Search tab -->
@@ -8,6 +8,24 @@
8
8
  ? config.metadata.titleTemplate.replace("%s", "")
9
9
  : " – Docs"
10
10
  );
11
+
12
+ let tocEnabled = $derived(
13
+ config?.theme?.ui?.layout?.toc?.enabled ?? true
14
+ );
15
+
16
+ let breadcrumbEnabled = $derived.by(() => {
17
+ const bc = config?.breadcrumb;
18
+ if (bc === undefined || bc === true) return true;
19
+ if (bc === false) return false;
20
+ if (typeof bc === "object") return bc.enabled !== false;
21
+ return true;
22
+ });
23
+
24
+ let showEditOnGithub = $derived(
25
+ !!config?.github && !!data.editOnGithub
26
+ );
27
+
28
+ let showLastModified = $derived(!!data.lastModified);
11
29
  </script>
12
30
 
13
31
  <svelte:head>
@@ -19,12 +37,12 @@
19
37
 
20
38
  <DocsPage
21
39
  entry={config?.entry ?? "docs"}
22
- tocEnabled={true}
23
- breadcrumbEnabled={config?.breadcrumb?.enabled ?? true}
40
+ {tocEnabled}
41
+ {breadcrumbEnabled}
24
42
  previousPage={data.previousPage}
25
43
  nextPage={data.nextPage}
26
- editOnGithub={data.editOnGithub}
27
- lastModified={data.lastModified}
44
+ editOnGithub={showEditOnGithub ? data.editOnGithub : null}
45
+ lastModified={showLastModified ? data.lastModified : null}
28
46
  >
29
47
  {#snippet children()}
30
48
  {@html data.html}
@@ -3,19 +3,56 @@
3
3
  import AskAIDialog from "./AskAIDialog.svelte";
4
4
  import FloatingAIChat from "./FloatingAIChat.svelte";
5
5
  import { page } from "$app/stores";
6
+ import { onMount } from "svelte";
6
7
 
7
8
  let {
8
9
  tree,
9
10
  config = null,
10
11
  title = undefined,
11
12
  titleUrl = undefined,
12
- themeToggle = true,
13
13
  children,
14
14
  } = $props();
15
15
 
16
16
  let resolvedTitle = $derived(title ?? config?.nav?.title ?? "Docs");
17
17
  let resolvedTitleUrl = $derived(titleUrl ?? config?.nav?.url ?? "/docs");
18
18
 
19
+ let showThemeToggle = $derived.by(() => {
20
+ const toggle = config?.themeToggle;
21
+ if (toggle === undefined || toggle === true) return true;
22
+ if (toggle === false) return false;
23
+ if (typeof toggle === "object") return toggle.enabled !== false;
24
+ return true;
25
+ });
26
+
27
+ let forcedTheme = $derived.by(() => {
28
+ const toggle = config?.themeToggle;
29
+ if (typeof toggle === "object" && toggle.enabled === false && toggle.default && toggle.default !== "system") {
30
+ return toggle.default;
31
+ }
32
+ return null;
33
+ });
34
+
35
+ let themeInitScript = $derived.by(() => {
36
+ if (forcedTheme) {
37
+ return `document.documentElement.classList.remove('light','dark');document.documentElement.classList.add('${forcedTheme}')`;
38
+ }
39
+ return [
40
+ "(function(){",
41
+ "var m=document.cookie.match(/(?:^|;\\s*)theme=(\\w+)/);",
42
+ "var t=m?m[1]:(window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light');",
43
+ "document.documentElement.classList.remove('light','dark');",
44
+ "document.documentElement.classList.add(t);",
45
+ "})()",
46
+ ].join("");
47
+ });
48
+
49
+ onMount(() => {
50
+ if (forcedTheme) {
51
+ document.documentElement.classList.remove("light", "dark");
52
+ document.documentElement.classList.add(forcedTheme);
53
+ }
54
+ });
55
+
19
56
  let sidebarOpen = $state(false);
20
57
  let searchOpen = $state(false);
21
58
 
@@ -82,6 +119,10 @@
82
119
 
83
120
  <svelte:window onkeydown={handleKeydown} />
84
121
 
122
+ <svelte:head>
123
+ {@html `<script>${themeInitScript}</script>`}
124
+ </svelte:head>
125
+
85
126
  <div class="fd-layout">
86
127
  <!-- Mobile header -->
87
128
  <header class="fd-header">
@@ -227,7 +268,7 @@
227
268
  {/if}
228
269
  </nav>
229
270
 
230
- {#if themeToggle}
271
+ {#if showThemeToggle}
231
272
  <div class="fd-sidebar-footer">
232
273
  <ThemeToggle />
233
274
  </div>
@@ -90,51 +90,55 @@
90
90
  {@render children()}
91
91
  </div>
92
92
 
93
- {#if editOnGithub}
94
- <div class="fd-edit-on-github">
95
- <a href={editOnGithub} target="_blank" rel="noopener noreferrer">
96
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
97
- <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" />
98
- <path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" />
99
- </svg>
100
- Edit on GitHub
101
- </a>
102
- {#if lastModified}
103
- <span class="fd-last-modified">Last updated: {lastModified}</span>
104
- {/if}
105
- </div>
106
- {/if}
107
-
108
- {#if previousPage || nextPage}
109
- <nav class="fd-page-nav" aria-label="Page navigation">
110
- {#if previousPage}
111
- <a href={previousPage.url} class="fd-page-nav-card fd-page-nav-prev">
112
- <span class="fd-page-nav-label">
113
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
114
- <polyline points="15 18 9 12 15 6" />
115
- </svg>
116
- Previous
117
- </span>
118
- <span class="fd-page-nav-title">{previousPage.name}</span>
119
- </a>
120
- {:else}
121
- <div></div>
122
- {/if}
123
- {#if nextPage}
124
- <a href={nextPage.url} class="fd-page-nav-card fd-page-nav-next">
125
- <span class="fd-page-nav-label">
126
- Next
93
+ <footer class="fd-page-footer">
94
+ {#if editOnGithub || lastModified}
95
+ <div class="fd-edit-on-github">
96
+ {#if editOnGithub}
97
+ <a href={editOnGithub} target="_blank" rel="noopener noreferrer">
127
98
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
128
- <polyline points="9 18 15 12 9 6" />
99
+ <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" />
100
+ <path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" />
129
101
  </svg>
130
- </span>
131
- <span class="fd-page-nav-title">{nextPage.name}</span>
132
- </a>
133
- {:else}
134
- <div></div>
135
- {/if}
136
- </nav>
137
- {/if}
102
+ Edit on GitHub
103
+ </a>
104
+ {/if}
105
+ {#if lastModified}
106
+ <span class="fd-last-modified">Last updated: {lastModified}</span>
107
+ {/if}
108
+ </div>
109
+ {/if}
110
+
111
+ {#if previousPage || nextPage}
112
+ <nav class="fd-page-nav" aria-label="Page navigation">
113
+ {#if previousPage}
114
+ <a href={previousPage.url} class="fd-page-nav-card fd-page-nav-prev">
115
+ <span class="fd-page-nav-label">
116
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
117
+ <polyline points="15 18 9 12 15 6" />
118
+ </svg>
119
+ Previous
120
+ </span>
121
+ <span class="fd-page-nav-title">{previousPage.name}</span>
122
+ </a>
123
+ {:else}
124
+ <div></div>
125
+ {/if}
126
+ {#if nextPage}
127
+ <a href={nextPage.url} class="fd-page-nav-card fd-page-nav-next">
128
+ <span class="fd-page-nav-label">
129
+ Next
130
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
131
+ <polyline points="9 18 15 12 9 6" />
132
+ </svg>
133
+ </span>
134
+ <span class="fd-page-nav-title">{nextPage.name}</span>
135
+ </a>
136
+ {:else}
137
+ <div></div>
138
+ {/if}
139
+ </nav>
140
+ {/if}
141
+ </footer>
138
142
  </article>
139
143
 
140
144
  {#if tocEnabled}
@@ -8,7 +8,14 @@
8
8
  let theme = $state("light");
9
9
 
10
10
  onMount(() => {
11
- theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
11
+ if (document.documentElement.classList.contains("dark")) {
12
+ theme = "dark";
13
+ } else if (document.documentElement.classList.contains("light")) {
14
+ theme = "light";
15
+ } else {
16
+ theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
17
+ document.documentElement.classList.add(theme);
18
+ }
12
19
  });
13
20
 
14
21
  function toggle() {
@@ -0,0 +1,4 @@
1
+ import type { DocsTheme } from "@farming-labs/docs";
2
+
3
+ export declare const darksharp: (overrides?: Partial<DocsTheme>) => DocsTheme;
4
+ export declare const DarksharpUIDefaults: Record<string, any>;
@@ -0,0 +1,4 @@
1
+ import type { DocsTheme } from "@farming-labs/docs";
2
+
3
+ export declare const fumadocs: (overrides?: Partial<DocsTheme>) => DocsTheme;
4
+ export declare const DefaultUIDefaults: Record<string, any>;
@@ -0,0 +1,4 @@
1
+ import type { DocsTheme } from "@farming-labs/docs";
2
+
3
+ export declare const pixelBorder: (overrides?: Partial<DocsTheme>) => DocsTheme;
4
+ export declare const PixelBorderUIDefaults: Record<string, any>;
@@ -0,0 +1,6 @@
1
+ /* @farming-labs/svelte-theme — darksharp theme (bundled)
2
+ * Single import: includes base docs.css + darksharp overrides.
3
+ * Usage: @import "@farming-labs/svelte-theme/darksharp/css";
4
+ */
5
+ @import "./docs.css";
6
+ @import "./darksharp.css";
@@ -0,0 +1,206 @@
1
+ /* @farming-labs/svelte-theme — darksharp theme overrides
2
+ * All-black, sharp edges, zero-radius look.
3
+ * Applied on top of docs.css base styles.
4
+ */
5
+
6
+ /* ─── Color overrides (light) ─────────────────────────────────────── */
7
+
8
+ :root {
9
+ --color-fd-background: oklch(1 0 0);
10
+ --color-fd-foreground: oklch(0.147 0.004 49.25);
11
+ --color-fd-card: oklch(1 0 0);
12
+ --color-fd-card-foreground: oklch(0.147 0.004 49.25);
13
+ --color-fd-popover: oklch(1 0 0);
14
+ --color-fd-popover-foreground: oklch(0.147 0.004 49.25);
15
+ --color-fd-primary: oklch(0.216 0.006 56.043);
16
+ --color-fd-primary-foreground: oklch(0.985 0.001 106.423);
17
+ --color-fd-secondary: oklch(0.97 0.001 106.424);
18
+ --color-fd-secondary-foreground: oklch(0.216 0.006 56.043);
19
+ --color-fd-muted: oklch(0.97 0.001 106.424);
20
+ --color-fd-muted-foreground: oklch(0.553 0.013 58.071);
21
+ --color-fd-accent: oklch(0.97 0.001 106.424);
22
+ --color-fd-accent-foreground: oklch(0.216 0.006 56.043);
23
+ --color-fd-border: oklch(0.923 0.003 48.717);
24
+ --color-fd-ring: oklch(0.709 0.01 56.259);
25
+ }
26
+
27
+ /* ─── Color overrides (dark) ──────────────────────────────────────── */
28
+
29
+ .dark {
30
+ --color-fd-background: hsl(0 0% 0%);
31
+ --color-fd-foreground: oklch(0.985 0.001 106.423);
32
+ --color-fd-card: hsl(0 0% 0%);
33
+ --color-fd-card-foreground: oklch(0.985 0.001 106.423);
34
+ --color-fd-popover: hsl(0 0% 6%);
35
+ --color-fd-popover-foreground: oklch(0.985 0.001 106.423);
36
+ --color-fd-primary: oklch(0.985 0.001 106.423);
37
+ --color-fd-primary-foreground: oklch(0.216 0.006 56.043);
38
+ --color-fd-secondary: hsl(0 0% 10%);
39
+ --color-fd-secondary-foreground: oklch(0.985 0.001 106.423);
40
+ --color-fd-muted: hsl(0 0% 10%);
41
+ --color-fd-muted-foreground: hsl(0 0% 55%);
42
+ --color-fd-accent: hsl(0 0% 12%);
43
+ --color-fd-accent-foreground: oklch(0.985 0.001 106.423);
44
+ --color-fd-border: hsl(0 0% 14%);
45
+ --color-fd-ring: hsl(0 0% 30%);
46
+ }
47
+
48
+ /* ═══════════════════════════════════════════════════════════════════
49
+ * Sharp radius — override all rounded utilities
50
+ * ═══════════════════════════════════════════════════════════════════ */
51
+
52
+ .fd-sidebar,
53
+ .fd-nav,
54
+ .fd-card,
55
+ .fd-callout,
56
+ .fd-code-block,
57
+ .fd-search-dialog,
58
+ .fd-toc,
59
+ [class*="rounded-lg"] {
60
+ border-radius: 0.2rem !important;
61
+ }
62
+
63
+ [class*="rounded-md"] {
64
+ border-radius: 0.1rem !important;
65
+ }
66
+
67
+ [class*="rounded-xl"],
68
+ [class*="rounded-2xl"] {
69
+ border-radius: 0.2rem !important;
70
+ }
71
+
72
+ /* ─── Tables ──────────────────────────────────────────────────────── */
73
+
74
+ table {
75
+ border-radius: 0 !important;
76
+ border-collapse: collapse;
77
+ }
78
+
79
+ .dark th {
80
+ background: hsl(0 0% 8%);
81
+ color: hsl(0 0% 80%);
82
+ font-weight: 500;
83
+ }
84
+
85
+ .dark td {
86
+ background: hsl(0 0% 2%);
87
+ }
88
+
89
+ .dark tr {
90
+ border-color: hsl(0 0% 14%);
91
+ }
92
+
93
+ /* ─── Code blocks ─────────────────────────────────────────────────── */
94
+
95
+ .fd-code-block,
96
+ .fd-code-block pre {
97
+ border-radius: 0.2rem !important;
98
+ }
99
+
100
+ .fd-code-block button {
101
+ border-radius: 0.1rem !important;
102
+ }
103
+
104
+ /* ─── Inline code ─────────────────────────────────────────────────── */
105
+
106
+ code:not(pre code) {
107
+ border-radius: 0.1rem !important;
108
+ }
109
+
110
+ .dark code:not(pre code) {
111
+ background: hsl(0 0% 10%);
112
+ border: 1px solid hsl(0 0% 16%);
113
+ color: hsl(0 0% 85%);
114
+ }
115
+
116
+ /* ─── Callouts ────────────────────────────────────────────────────── */
117
+
118
+ .dark .fd-card,
119
+ .dark .fd-callout {
120
+ border-color: hsl(0 0% 14%);
121
+ }
122
+
123
+ /* ─── Selection ───────────────────────────────────────────────────── */
124
+
125
+ ::selection {
126
+ background: var(--color-fd-foreground);
127
+ color: var(--color-fd-background);
128
+ }
129
+
130
+ /* ─── Sidebar (dark) ──────────────────────────────────────────────── */
131
+
132
+ .dark .fd-sidebar {
133
+ background: hsl(0 0% 0%);
134
+ border-color: hsl(0 0% 12%);
135
+ }
136
+
137
+ .dark .fd-sidebar a[data-active="true"] {
138
+ background: hsl(0 0% 14%) !important;
139
+ color: hsl(0 0% 96%) !important;
140
+ }
141
+
142
+ .dark .fd-sidebar a:hover {
143
+ background: hsl(0 0% 10%);
144
+ color: hsl(0 0% 90%);
145
+ }
146
+
147
+ /* ─── TOC ─────────────────────────────────────────────────────────── */
148
+
149
+ .dark .fd-toc {
150
+ border-color: hsl(0 0% 12%);
151
+ }
152
+
153
+ /* ─── Separator ───────────────────────────────────────────────────── */
154
+
155
+ .dark hr {
156
+ border-color: hsl(0 0% 14%);
157
+ }
158
+
159
+ /* ─── Nav cards (prev/next) ───────────────────────────────────────── */
160
+
161
+ .dark .fd-nav-card {
162
+ border-color: hsl(0 0% 14%);
163
+ }
164
+
165
+ .dark .fd-nav-card:hover {
166
+ background: hsl(0 0% 8%);
167
+ border-color: hsl(0 0% 20%);
168
+ }
169
+
170
+ /* ─── Search dialog ───────────────────────────────────────────────── */
171
+
172
+ .fd-search-dialog {
173
+ border-radius: 0.2rem !important;
174
+ }
175
+
176
+ /* ─── AI Chat (sharp edges) ───────────────────────────────────────── */
177
+
178
+ .fd-ai-dialog,
179
+ .fd-ai-bubble-user,
180
+ .fd-ai-bubble-ai,
181
+ .fd-ai-input-wrap,
182
+ .fd-ai-send-btn,
183
+ .fd-ai-close-btn,
184
+ .fd-ai-floating-btn,
185
+ .fd-ai-suggestion,
186
+ .fd-ai-result,
187
+ .fd-ai-esc,
188
+ .fd-ai-clear-btn,
189
+ .fd-ai-fm-input-container,
190
+ .fd-ai-fm-send-btn,
191
+ .fd-ai-fm-close-btn,
192
+ .fd-ai-fm-suggestion,
193
+ .fd-ai-fm-trigger-btn,
194
+ .fd-ai-code-block,
195
+ .fd-ai-code-copy {
196
+ border-radius: 2px !important;
197
+ }
198
+
199
+ .fd-ai-floating-btn:hover {
200
+ box-shadow: 0 0 0 1px var(--color-fd-ring);
201
+ transform: none;
202
+ }
203
+
204
+ .fd-ai-fm-trigger-btn:hover {
205
+ transform: none;
206
+ }
package/styles/docs.css CHANGED
@@ -409,6 +409,9 @@ code, kbd, pre, samp {
409
409
  .fd-page-article {
410
410
  min-width: 0;
411
411
  padding: 32px 48px 64px;
412
+ display: flex;
413
+ flex-direction: column;
414
+ min-height: calc(100dvh - var(--fd-nav-height, 56px));
412
415
  }
413
416
 
414
417
  /* ─── Table of Contents (right column, flush to right edge) ──────────── */
@@ -673,6 +676,7 @@ code, kbd, pre, samp {
673
676
  color: var(--color-fd-foreground);
674
677
  line-height: 1.75;
675
678
  font-size: 15px;
679
+ flex: 1;
676
680
  }
677
681
 
678
682
  .fd-page-body a {
@@ -1088,6 +1092,11 @@ html.dark pre.shiki {
1088
1092
 
1089
1093
  /* ─── Edit on GitHub ────────────────────────────────────────────────── */
1090
1094
 
1095
+ .fd-page-footer {
1096
+ margin-top: auto;
1097
+ padding-top: 16px;
1098
+ }
1099
+
1091
1100
  .fd-edit-on-github {
1092
1101
  display: flex;
1093
1102
  align-items: center;
@@ -1212,8 +1221,9 @@ html.dark pre.shiki {
1212
1221
  position: fixed;
1213
1222
  inset: 0;
1214
1223
  z-index: 9998;
1215
- background: color-mix(in srgb, var(--color-fd-background) 70%, transparent);
1216
- backdrop-filter: blur(6px);
1224
+ background: rgba(0, 0, 0, 0.5);
1225
+ backdrop-filter: blur(4px);
1226
+ -webkit-backdrop-filter: blur(4px);
1217
1227
  animation: fd-ai-fade-in 150ms ease-out;
1218
1228
  }
1219
1229
 
@@ -1225,7 +1235,9 @@ html.dark pre.shiki {
1225
1235
  background: var(--color-fd-popover);
1226
1236
  border: 1px solid var(--color-fd-border);
1227
1237
  border-radius: 12px;
1228
- box-shadow: 0 20px 60px color-mix(in srgb, var(--color-fd-background) 60%, transparent);
1238
+ box-shadow:
1239
+ 0 0 0 1px color-mix(in srgb, var(--color-fd-foreground) 5%, transparent),
1240
+ 0 16px 48px -8px rgba(0, 0, 0, 0.35);
1229
1241
  overflow: hidden;
1230
1242
  color: var(--color-fd-foreground);
1231
1243
  font-family: var(--fd-font-sans, system-ui, -apple-system, sans-serif);
@@ -1233,47 +1245,61 @@ html.dark pre.shiki {
1233
1245
 
1234
1246
  .fd-ai-tab-bar {
1235
1247
  display: flex;
1236
- align-items: center;
1237
- gap: 2px;
1238
- padding: 10px 12px 0;
1248
+ align-items: stretch;
1249
+ gap: 0;
1250
+ padding: 0;
1239
1251
  border-bottom: 1px solid var(--color-fd-border);
1252
+ background: var(--color-fd-card);
1240
1253
  }
1241
1254
 
1242
1255
  .fd-ai-tab {
1243
- display: flex;
1256
+ display: inline-flex;
1244
1257
  align-items: center;
1258
+ justify-content: center;
1245
1259
  gap: 6px;
1246
- padding: 8px 14px;
1260
+ padding: 10px 20px;
1247
1261
  font-size: 13px;
1248
1262
  font-weight: 500;
1249
1263
  border: none;
1250
- border-radius: 8px 8px 0 0;
1264
+ border-bottom: 2px solid transparent;
1251
1265
  cursor: pointer;
1252
- transition: all 150ms;
1266
+ transition: color 150ms, border-color 150ms, background-color 150ms;
1253
1267
  background: transparent;
1254
1268
  color: var(--color-fd-muted-foreground);
1255
- border-bottom: 2px solid transparent;
1256
1269
  font-family: inherit;
1270
+ margin-bottom: -1px;
1271
+ position: relative;
1272
+ }
1273
+
1274
+ .fd-ai-tab svg {
1275
+ opacity: 0.7;
1276
+ flex-shrink: 0;
1257
1277
  }
1258
1278
 
1259
1279
  .fd-ai-tab[data-active="true"] {
1260
- background: var(--color-fd-accent);
1261
1280
  color: var(--color-fd-foreground);
1262
1281
  border-bottom-color: var(--color-fd-primary);
1282
+ background: transparent;
1283
+ }
1284
+
1285
+ .fd-ai-tab[data-active="true"] svg {
1286
+ opacity: 1;
1263
1287
  }
1264
1288
 
1265
1289
  .fd-ai-tab:hover:not([data-active="true"]) {
1266
1290
  color: var(--color-fd-foreground);
1291
+ background: var(--color-fd-accent);
1267
1292
  }
1268
1293
 
1269
1294
  .fd-ai-esc {
1270
- padding: 2px 6px;
1271
- border-radius: 4px;
1295
+ padding: 2px 8px;
1296
+ border-radius: 6px;
1272
1297
  border: 1px solid var(--color-fd-border);
1273
- font-size: 10px;
1274
- font-family: inherit;
1275
- margin-right: 4px;
1298
+ font-size: 11px;
1299
+ font-family: var(--fd-font-mono, ui-monospace, monospace);
1276
1300
  color: var(--color-fd-muted-foreground);
1301
+ background: var(--color-fd-secondary);
1302
+ line-height: 1.4;
1277
1303
  }
1278
1304
 
1279
1305
  /* ─── Header ─────────────────────────────────────────────────── */
@@ -1320,6 +1346,11 @@ html.dark pre.shiki {
1320
1346
  color: var(--color-fd-muted-foreground);
1321
1347
  }
1322
1348
 
1349
+ .fd-ai-search-wrap svg {
1350
+ flex-shrink: 0;
1351
+ opacity: 0.5;
1352
+ }
1353
+
1323
1354
  .fd-ai-input {
1324
1355
  flex: 1;
1325
1356
  background: transparent;
@@ -1330,10 +1361,15 @@ html.dark pre.shiki {
1330
1361
  font-family: inherit;
1331
1362
  }
1332
1363
 
1364
+ .fd-ai-input::placeholder {
1365
+ color: var(--color-fd-muted-foreground);
1366
+ opacity: 0.7;
1367
+ }
1368
+
1333
1369
  .fd-ai-results {
1334
1370
  flex: 1;
1335
1371
  overflow-y: auto;
1336
- padding: 4px 8px;
1372
+ padding: 8px;
1337
1373
  max-height: 360px;
1338
1374
  }
1339
1375
 
@@ -1347,23 +1383,34 @@ html.dark pre.shiki {
1347
1383
  border-radius: 8px;
1348
1384
  cursor: pointer;
1349
1385
  text-align: left;
1350
- font-size: 13px;
1386
+ font-size: 14px;
1351
1387
  font-family: inherit;
1352
- transition: background 100ms;
1388
+ transition: background 100ms, color 100ms;
1389
+ color: var(--color-fd-foreground);
1353
1390
  background: transparent;
1354
1391
  color: var(--color-fd-foreground);
1355
1392
  }
1356
1393
 
1394
+ .fd-ai-result svg {
1395
+ flex-shrink: 0;
1396
+ opacity: 0.5;
1397
+ }
1398
+
1357
1399
  .fd-ai-result[data-active="true"] {
1358
1400
  background: var(--color-fd-accent);
1359
- color: var(--color-fd-accent-foreground);
1401
+ color: var(--color-fd-foreground);
1402
+ }
1403
+
1404
+ .fd-ai-result[data-active="true"] svg {
1405
+ opacity: 1;
1360
1406
  }
1361
1407
 
1362
1408
  .fd-ai-result-empty {
1363
- padding: 32px 16px;
1409
+ padding: 48px 16px;
1364
1410
  text-align: center;
1365
1411
  color: var(--color-fd-muted-foreground);
1366
- font-size: 13px;
1412
+ font-size: 14px;
1413
+ line-height: 1.6;
1367
1414
  }
1368
1415
 
1369
1416
  .fd-ai-messages {