@farming-labs/svelte-theme 0.0.6 → 0.0.8
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/DocsLayout.svelte +31 -20
- package/src/components/FloatingAIChat.svelte +37 -40
- package/styles/docs.css +40 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/svelte-theme",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
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.
|
|
86
|
-
"@farming-labs/svelte": "0.0.
|
|
85
|
+
"@farming-labs/docs": "0.0.8",
|
|
86
|
+
"@farming-labs/svelte": "0.0.8"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"svelte": ">=5.0.0"
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
let resolvedTitle = $derived(title ?? config?.nav?.title ?? "Docs");
|
|
22
22
|
let resolvedTitleUrl = $derived(titleUrl ?? config?.nav?.url ?? "/docs");
|
|
23
|
+
let staticExport = $derived(!!(config && config.staticExport));
|
|
24
|
+
let showSearch = $derived(!staticExport);
|
|
25
|
+
let showFloatingAI = $derived(!staticExport && config?.ai?.mode === "floating" && !!config?.ai?.enabled);
|
|
23
26
|
|
|
24
27
|
let showThemeToggle = $derived.by(() => {
|
|
25
28
|
const toggle = config?.themeToggle;
|
|
@@ -78,7 +81,7 @@
|
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
function handleKeydown(e) {
|
|
81
|
-
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
84
|
+
if (showSearch && (e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
82
85
|
e.preventDefault();
|
|
83
86
|
searchOpen = !searchOpen;
|
|
84
87
|
}
|
|
@@ -198,6 +201,10 @@
|
|
|
198
201
|
const layout = config?.theme?.ui?.layout;
|
|
199
202
|
return [buildColorsCSS(colorOverrides), buildTypographyCSS(typography), buildLayoutCSS(layout)].filter(Boolean).join("\n");
|
|
200
203
|
});
|
|
204
|
+
|
|
205
|
+
// Build style tag from parts so Vite/Svelte preprocessor doesn't treat it as a real <style> block (PostCSS would then fail on "overrideCSS")
|
|
206
|
+
const styleTagOpen = "<sty" + "le>";
|
|
207
|
+
const styleTagClose = "</sty" + "le>";
|
|
201
208
|
</script>
|
|
202
209
|
|
|
203
210
|
<svelte:window onkeydown={handleKeydown} />
|
|
@@ -205,7 +212,7 @@
|
|
|
205
212
|
<svelte:head>
|
|
206
213
|
{@html `<script>${themeInitScript}</script>`}
|
|
207
214
|
{#if overrideCSS}
|
|
208
|
-
{@html
|
|
215
|
+
{@html `${styleTagOpen}${overrideCSS}${styleTagClose}`}
|
|
209
216
|
{/if}
|
|
210
217
|
</svelte:head>
|
|
211
218
|
|
|
@@ -220,12 +227,14 @@
|
|
|
220
227
|
</svg>
|
|
221
228
|
</button>
|
|
222
229
|
<a href={resolvedTitleUrl} class="fd-header-title">{resolvedTitle}</a>
|
|
223
|
-
|
|
224
|
-
<
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
230
|
+
{#if showSearch}
|
|
231
|
+
<button class="fd-search-trigger-mobile" onclick={openSearch} aria-label="Search">
|
|
232
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
233
|
+
<circle cx="11" cy="11" r="8" />
|
|
234
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
235
|
+
</svg>
|
|
236
|
+
</button>
|
|
237
|
+
{/if}
|
|
229
238
|
</header>
|
|
230
239
|
|
|
231
240
|
{#if sidebarOpen}
|
|
@@ -244,16 +253,18 @@
|
|
|
244
253
|
</a>
|
|
245
254
|
</div>
|
|
246
255
|
|
|
247
|
-
|
|
248
|
-
<
|
|
249
|
-
<
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
{#if showSearch}
|
|
257
|
+
<div class="fd-sidebar-search">
|
|
258
|
+
<button class="fd-sidebar-search-btn" onclick={openSearch}>
|
|
259
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
260
|
+
<circle cx="11" cy="11" r="8" />
|
|
261
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
262
|
+
</svg>
|
|
263
|
+
<span>Search</span>
|
|
264
|
+
<kbd>⌘</kbd><kbd>K</kbd>
|
|
265
|
+
</button>
|
|
266
|
+
</div>
|
|
267
|
+
{/if}
|
|
257
268
|
|
|
258
269
|
{#if sidebarHeader}
|
|
259
270
|
<div class="fd-sidebar-banner">
|
|
@@ -386,7 +397,7 @@
|
|
|
386
397
|
</main>
|
|
387
398
|
</div>
|
|
388
399
|
|
|
389
|
-
{#if
|
|
400
|
+
{#if showFloatingAI}
|
|
390
401
|
<FloatingAIChat
|
|
391
402
|
suggestedQuestions={config.ai.suggestedQuestions ?? []}
|
|
392
403
|
aiLabel={config.ai.aiLabel ?? "AI"}
|
|
@@ -396,6 +407,6 @@
|
|
|
396
407
|
/>
|
|
397
408
|
{/if}
|
|
398
409
|
|
|
399
|
-
{#if searchOpen}
|
|
410
|
+
{#if showSearch && searchOpen}
|
|
400
411
|
<SearchDialog onclose={closeSearch} />
|
|
401
412
|
{/if}
|
|
@@ -207,14 +207,41 @@
|
|
|
207
207
|
|
|
208
208
|
{#if mounted}
|
|
209
209
|
{#if isFullModal}
|
|
210
|
-
<!-- ═══ Full-Modal Mode (better-auth inspired) ═══
|
|
210
|
+
<!-- ═══ Full-Modal Mode (better-auth inspired) ═══
|
|
211
|
+
Trigger is always in a fixed-position wrapper when closed (no morphing).
|
|
212
|
+
When open: overlay + separate fixed input bar for smooth open/close. -->
|
|
211
213
|
|
|
214
|
+
<!-- When closed: fixed trigger only — stays in place, no layout jump -->
|
|
215
|
+
{#if !isOpen}
|
|
216
|
+
<div class="fd-ai-fm-trigger-fixed" style={btnStyle}>
|
|
217
|
+
{#if triggerComponent}
|
|
218
|
+
{@const Trigger = triggerComponent}
|
|
219
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
220
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
221
|
+
<div onclick={() => isOpen = true} class="fd-ai-floating-trigger fd-ai-floating-trigger--static">
|
|
222
|
+
<Trigger aiLabel={label} />
|
|
223
|
+
</div>
|
|
224
|
+
{:else}
|
|
225
|
+
<button
|
|
226
|
+
onclick={() => isOpen = true}
|
|
227
|
+
class="fd-ai-fm-trigger-btn"
|
|
228
|
+
aria-label="Ask {label}"
|
|
229
|
+
>
|
|
230
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
231
|
+
<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"/>
|
|
232
|
+
<path d="M20 3v4"/><path d="M22 5h-4"/>
|
|
233
|
+
</svg>
|
|
234
|
+
<span>Ask {label}</span>
|
|
235
|
+
</button>
|
|
236
|
+
{/if}
|
|
237
|
+
</div>
|
|
238
|
+
{/if}
|
|
239
|
+
|
|
240
|
+
<!-- When open: overlay + input bar (separate elements, no morphing from trigger) -->
|
|
212
241
|
{#if isOpen}
|
|
213
|
-
<!-- Full-screen overlay -->
|
|
214
242
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
215
243
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
216
|
-
<div class="fd-ai-fm-overlay" onclick={(e) => { if (e.target === e.currentTarget) isOpen = false; }}>
|
|
217
|
-
<!-- Close button -->
|
|
244
|
+
<div class="fd-ai-fm-overlay fd-ai-fm-overlay--animate" onclick={(e) => { if (e.target === e.currentTarget) isOpen = false; }}>
|
|
218
245
|
<div class="fd-ai-fm-topbar">
|
|
219
246
|
<button onclick={() => isOpen = false} class="fd-ai-fm-close-btn" aria-label="Close">
|
|
220
247
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -223,7 +250,6 @@
|
|
|
223
250
|
</button>
|
|
224
251
|
</div>
|
|
225
252
|
|
|
226
|
-
<!-- Scrollable message list -->
|
|
227
253
|
<div bind:this={fmListEl} class="fd-ai-fm-messages">
|
|
228
254
|
<div class="fd-ai-fm-messages-inner">
|
|
229
255
|
{#each messages as msg, i}
|
|
@@ -252,38 +278,9 @@
|
|
|
252
278
|
</div>
|
|
253
279
|
</div>
|
|
254
280
|
</div>
|
|
255
|
-
{/if}
|
|
256
281
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
class="fd-ai-fm-input-bar {isOpen ? 'fd-ai-fm-input-bar--open' : 'fd-ai-fm-input-bar--closed'}"
|
|
260
|
-
style={isOpen ? undefined : btnStyle}
|
|
261
|
-
>
|
|
262
|
-
{#if !isOpen}
|
|
263
|
-
{#if triggerComponent}
|
|
264
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
265
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
266
|
-
<div
|
|
267
|
-
onclick={() => isOpen = true}
|
|
268
|
-
class="fd-ai-floating-trigger"
|
|
269
|
-
style={btnStyle}
|
|
270
|
-
>
|
|
271
|
-
<svelte:component this={triggerComponent} aiLabel={label} />
|
|
272
|
-
</div>
|
|
273
|
-
{:else}
|
|
274
|
-
<button
|
|
275
|
-
onclick={() => isOpen = true}
|
|
276
|
-
class="fd-ai-fm-trigger-btn"
|
|
277
|
-
aria-label="Ask {label}"
|
|
278
|
-
>
|
|
279
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
280
|
-
<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"/>
|
|
281
|
-
<path d="M20 3v4"/><path d="M22 5h-4"/>
|
|
282
|
-
</svg>
|
|
283
|
-
<span>Ask {label}</span>
|
|
284
|
-
</button>
|
|
285
|
-
{/if}
|
|
286
|
-
{:else}
|
|
282
|
+
<!-- Input bar when open: fixed at bottom center, never morphs from trigger -->
|
|
283
|
+
<div class="fd-ai-fm-input-bar fd-ai-fm-input-bar--open">
|
|
287
284
|
<div class="fd-ai-fm-input-container">
|
|
288
285
|
<div class="fd-ai-fm-input-wrap">
|
|
289
286
|
<textarea
|
|
@@ -350,9 +347,8 @@
|
|
|
350
347
|
{/if}
|
|
351
348
|
</div>
|
|
352
349
|
</div>
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
350
|
+
</div>
|
|
351
|
+
{/if}
|
|
356
352
|
{:else}
|
|
357
353
|
<!-- ═══ Panel / Modal / Popover Mode ═══ -->
|
|
358
354
|
|
|
@@ -479,6 +475,7 @@
|
|
|
479
475
|
|
|
480
476
|
{#if !isOpen}
|
|
481
477
|
{#if triggerComponent}
|
|
478
|
+
{@const Trigger = triggerComponent}
|
|
482
479
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
483
480
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
484
481
|
<div
|
|
@@ -486,7 +483,7 @@
|
|
|
486
483
|
class="fd-ai-floating-trigger"
|
|
487
484
|
style={btnStyle}
|
|
488
485
|
>
|
|
489
|
-
<
|
|
486
|
+
<Trigger aiLabel={label} />
|
|
490
487
|
</div>
|
|
491
488
|
{:else}
|
|
492
489
|
<button
|
package/styles/docs.css
CHANGED
|
@@ -2262,6 +2262,19 @@ html.dark pre.shiki {
|
|
|
2262
2262
|
transform: scale(0.97);
|
|
2263
2263
|
}
|
|
2264
2264
|
|
|
2265
|
+
.dark .fd-ai-floating-btn,
|
|
2266
|
+
.dark .fd-ai-fm-trigger-btn {
|
|
2267
|
+
background: color-mix(in srgb, var(--color-fd-secondary, rgba(255, 255, 255, 0.08)) 90%, transparent);
|
|
2268
|
+
color: var(--color-fd-foreground, #e4e4e7);
|
|
2269
|
+
border-color: var(--color-fd-border, rgba(255, 255, 255, 0.12));
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
.dark .fd-ai-floating-btn:hover,
|
|
2273
|
+
.dark .fd-ai-fm-trigger-btn:hover {
|
|
2274
|
+
background: var(--color-fd-accent);
|
|
2275
|
+
color: var(--color-fd-accent-foreground);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2265
2278
|
.fd-ai-floating-trigger {
|
|
2266
2279
|
position: fixed;
|
|
2267
2280
|
z-index: 9997;
|
|
@@ -2269,8 +2282,6 @@ html.dark pre.shiki {
|
|
|
2269
2282
|
animation: fd-ai-fade-in 300ms ease-out;
|
|
2270
2283
|
}
|
|
2271
2284
|
|
|
2272
|
-
/* Custom Ask AI trigger (e.g. triggerComponent): same base as .fd-ai-floating-btn
|
|
2273
|
-
so theme overrides for fd-ai-floating-btn can also target .ask-ai-trigger */
|
|
2274
2285
|
.fd-ai-floating-trigger .ask-ai-trigger {
|
|
2275
2286
|
display: flex;
|
|
2276
2287
|
align-items: center;
|
|
@@ -2290,12 +2301,23 @@ html.dark pre.shiki {
|
|
|
2290
2301
|
transition: transform 150ms, background 150ms, color 150ms;
|
|
2291
2302
|
}
|
|
2292
2303
|
|
|
2304
|
+
.dark .fd-ai-floating-trigger .ask-ai-trigger {
|
|
2305
|
+
background: color-mix(in srgb, var(--color-fd-secondary, rgba(255, 255, 255, 0.08)) 90%, transparent);
|
|
2306
|
+
color: var(--color-fd-foreground, #e4e4e7);
|
|
2307
|
+
border-color: var(--color-fd-border, rgba(255, 255, 255, 0.12));
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2293
2310
|
.fd-ai-floating-trigger .ask-ai-trigger:hover {
|
|
2294
2311
|
background: var(--color-fd-accent);
|
|
2295
2312
|
color: var(--color-fd-accent-foreground);
|
|
2296
2313
|
transform: scale(1.03);
|
|
2297
2314
|
}
|
|
2298
2315
|
|
|
2316
|
+
.dark .fd-ai-floating-trigger .ask-ai-trigger:hover {
|
|
2317
|
+
background: var(--color-fd-accent);
|
|
2318
|
+
color: var(--color-fd-accent-foreground);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2299
2321
|
.fd-ai-floating-trigger .ask-ai-trigger:active {
|
|
2300
2322
|
transform: scale(0.97);
|
|
2301
2323
|
}
|
|
@@ -2304,6 +2326,17 @@ html.dark pre.shiki {
|
|
|
2304
2326
|
Full-Modal (better-auth inspired) — fd-ai-fm-*
|
|
2305
2327
|
═══════════════════════════════════════════════════════════════ */
|
|
2306
2328
|
|
|
2329
|
+
/* Fixed wrapper for trigger when closed — never morphs, so no "stuck" feel */
|
|
2330
|
+
.fd-ai-fm-trigger-fixed {
|
|
2331
|
+
position: fixed;
|
|
2332
|
+
z-index: 9997;
|
|
2333
|
+
animation: fd-ai-fade-in 200ms ease-out;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
.fd-ai-floating-trigger--static {
|
|
2337
|
+
position: static;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2307
2340
|
.fd-ai-fm-overlay {
|
|
2308
2341
|
position: fixed;
|
|
2309
2342
|
inset: 0;
|
|
@@ -2313,10 +2346,13 @@ html.dark pre.shiki {
|
|
|
2313
2346
|
background: color-mix(in srgb, var(--color-fd-background, #000) 80%, transparent);
|
|
2314
2347
|
backdrop-filter: blur(8px);
|
|
2315
2348
|
z-index: 9998;
|
|
2316
|
-
animation: fd-ai-fade-in 200ms ease-out;
|
|
2317
2349
|
padding: 0 8px;
|
|
2318
2350
|
}
|
|
2319
2351
|
|
|
2352
|
+
.fd-ai-fm-overlay--animate {
|
|
2353
|
+
animation: fd-ai-fade-in 200ms ease-out;
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2320
2356
|
/* ─── Top bar (close button) ─────────────────────────────────── */
|
|
2321
2357
|
|
|
2322
2358
|
.fd-ai-fm-topbar {
|
|
@@ -2498,14 +2534,6 @@ html.dark pre.shiki {
|
|
|
2498
2534
|
.fd-ai-fm-input-bar {
|
|
2499
2535
|
position: fixed;
|
|
2500
2536
|
z-index: 9999;
|
|
2501
|
-
transition:
|
|
2502
|
-
width 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
2503
|
-
height 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
2504
|
-
transform 200ms ease-out;
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
|
-
.fd-ai-fm-input-bar--closed {
|
|
2508
|
-
/* inherits position from inline btnPosition styles */
|
|
2509
2537
|
}
|
|
2510
2538
|
|
|
2511
2539
|
.fd-ai-fm-input-bar--open {
|
|
@@ -2513,6 +2541,7 @@ html.dark pre.shiki {
|
|
|
2513
2541
|
left: 50%;
|
|
2514
2542
|
transform: translateX(-50%);
|
|
2515
2543
|
width: min(800px, calc(100vw - 32px));
|
|
2544
|
+
animation: fd-ai-fade-in 200ms ease-out;
|
|
2516
2545
|
}
|
|
2517
2546
|
|
|
2518
2547
|
.fd-ai-fm-input-container {
|