@farming-labs/nuxt-theme 0.0.2-beta.22 → 0.0.2-beta.24

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/nuxt-theme",
3
- "version": "0.0.2-beta.22",
3
+ "version": "0.0.2-beta.24",
4
4
  "description": "Nuxt/Vue UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
5
5
  "keywords": [
6
6
  "docs",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "sugar-high": "^0.9.5",
56
- "@farming-labs/docs": "0.0.2-beta.22"
56
+ "@farming-labs/docs": "0.0.2-beta.24"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "nuxt": ">=3.0.0",
@@ -36,6 +36,13 @@ const breadcrumbEnabled = computed(() => {
36
36
  const showEditOnGithub = computed(() => !!props.config?.github && !!props.data.editOnGithub);
37
37
  const showLastModified = computed(() => !!props.data.lastModified);
38
38
 
39
+ const llmsTxtEnabled = computed(() => {
40
+ const cfg = props.config?.llmsTxt;
41
+ if (cfg === true) return true;
42
+ if (typeof cfg === "object" && cfg !== null) return (cfg as { enabled?: boolean }).enabled !== false;
43
+ return false;
44
+ });
45
+
39
46
  const entry = computed(() => (props.config?.entry as string) ?? "docs");
40
47
 
41
48
  const metaDescription = computed(
@@ -59,6 +66,7 @@ useHead({
59
66
  :next-page="data.nextPage ?? null"
60
67
  :edit-on-github="showEditOnGithub ? data.editOnGithub : null"
61
68
  :last-modified="showLastModified ? data.lastModified : null"
69
+ :llms-txt-enabled="llmsTxtEnabled"
62
70
  >
63
71
  <p v-if="data.description" class="fd-page-description">{{ data.description }}</p>
64
72
  <div class="fd-docs-content" v-html="data.html" />
@@ -13,6 +13,7 @@ const props = withDefaults(
13
13
  nextPage?: { name: string; url: string } | null;
14
14
  editOnGithub?: string | null;
15
15
  lastModified?: string | null;
16
+ llmsTxtEnabled?: boolean;
16
17
  }>(),
17
18
  {
18
19
  tocEnabled: true,
@@ -23,6 +24,7 @@ const props = withDefaults(
23
24
  nextPage: null,
24
25
  editOnGithub: null,
25
26
  lastModified: null,
27
+ llmsTxtEnabled: false,
26
28
  },
27
29
  );
28
30
 
@@ -106,7 +108,7 @@ watch(
106
108
  </div>
107
109
 
108
110
  <footer class="fd-page-footer">
109
- <div v-if="editOnGithub || lastModified" class="fd-edit-on-github">
111
+ <div v-if="editOnGithub || lastModified || llmsTxtEnabled" class="fd-edit-on-github">
110
112
  <a v-if="editOnGithub" :href="editOnGithub" target="_blank" rel="noopener noreferrer">
111
113
  <svg
112
114
  width="14"
@@ -123,6 +125,10 @@ watch(
123
125
  </svg>
124
126
  Edit on GitHub
125
127
  </a>
128
+ <span v-if="llmsTxtEnabled" class="fd-llms-txt-links">
129
+ <a href="/api/docs?format=llms" target="_blank" rel="noopener noreferrer" class="fd-llms-txt-link">llms.txt</a>
130
+ <a href="/api/docs?format=llms-full" target="_blank" rel="noopener noreferrer" class="fd-llms-txt-link">llms-full.txt</a>
131
+ </span>
126
132
  <span v-if="lastModified" class="fd-last-modified">Last updated: {{ lastModified }}</span>
127
133
  </div>
128
134
 
@@ -213,12 +213,15 @@ function handleFmKeyDown(e: KeyboardEvent) {
213
213
  </div>
214
214
  <div class="fd-ai-fm-msg-content">
215
215
  <template v-if="msg.content">
216
- <div v-html="renderMarkdown(msg.content)" />
216
+ <div :class="isStreaming && i === messages.length - 1 && msg.role === 'assistant' ? 'fd-ai-streaming' : ''" v-html="renderMarkdown(msg.content)" />
217
217
  </template>
218
- <div v-else class="fd-ai-fm-thinking">
219
- <span class="fd-ai-fm-thinking-dot" />
220
- <span class="fd-ai-fm-thinking-dot" />
221
- <span class="fd-ai-fm-thinking-dot" />
218
+ <div v-else class="fd-ai-loader">
219
+ <span class="fd-ai-loader-shimmer-text">Thinking</span>
220
+ <span class="fd-ai-loader-typing-dots">
221
+ <span class="fd-ai-loader-typing-dot" />
222
+ <span class="fd-ai-loader-typing-dot" />
223
+ <span class="fd-ai-loader-typing-dot" />
224
+ </span>
222
225
  </div>
223
226
  </div>
224
227
  </div>
@@ -286,10 +289,10 @@ function handleFmKeyDown(e: KeyboardEvent) {
286
289
  aria-label="Stop"
287
290
  @click="isStreaming = false"
288
291
  >
289
- <span class="fd-ai-loading-dots">
290
- <span class="fd-ai-loading-dot" />
291
- <span class="fd-ai-loading-dot" />
292
- <span class="fd-ai-loading-dot" />
292
+ <span class="fd-ai-loader-typing-dots" style="margin-left:0">
293
+ <span class="fd-ai-loader-typing-dot" />
294
+ <span class="fd-ai-loader-typing-dot" />
295
+ <span class="fd-ai-loader-typing-dot" />
293
296
  </span>
294
297
  </button>
295
298
  <button
@@ -427,16 +430,16 @@ function handleFmKeyDown(e: KeyboardEvent) {
427
430
  <div v-if="msg.role === 'user'" class="fd-ai-bubble-user">{{ msg.content }}</div>
428
431
  <div v-else class="fd-ai-bubble-ai">
429
432
  <template v-if="msg.content">
430
- <div v-html="renderMarkdown(msg.content)" />
433
+ <div :class="isStreaming && i === messages.length - 1 ? 'fd-ai-streaming' : ''" v-html="renderMarkdown(msg.content)" />
431
434
  </template>
432
- <span v-else class="fd-ai-loading">
433
- <span class="fd-ai-loading-text">{{ label }} is thinking</span>
434
- <span class="fd-ai-loading-dots">
435
- <span class="fd-ai-loading-dot" />
436
- <span class="fd-ai-loading-dot" />
437
- <span class="fd-ai-loading-dot" />
435
+ <div v-else class="fd-ai-loader">
436
+ <span class="fd-ai-loader-shimmer-text">Thinking</span>
437
+ <span class="fd-ai-loader-typing-dots">
438
+ <span class="fd-ai-loader-typing-dot" />
439
+ <span class="fd-ai-loader-typing-dot" />
440
+ <span class="fd-ai-loader-typing-dot" />
438
441
  </span>
439
- </span>
442
+ </div>
440
443
  </div>
441
444
  </div>
442
445
  </template>
package/styles/docs.css CHANGED
@@ -1237,6 +1237,29 @@ html.dark pre.shiki {
1237
1237
  font-size: 12px;
1238
1238
  }
1239
1239
 
1240
+ .fd-llms-txt-links {
1241
+ display: inline-flex;
1242
+ align-items: center;
1243
+ gap: 0.5rem;
1244
+ }
1245
+
1246
+ .fd-llms-txt-link {
1247
+ color: var(--color-fd-muted-foreground);
1248
+ font-size: 0.75rem;
1249
+ font-family: var(--fd-font-mono, ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace);
1250
+ text-decoration: none;
1251
+ padding: 0.125rem 0.375rem;
1252
+ border-radius: 0.25rem;
1253
+ border: 1px solid var(--color-fd-border, hsl(0 0% 80% / 50%));
1254
+ transition: color 150ms, border-color 150ms;
1255
+ }
1256
+
1257
+ .fd-llms-txt-link:hover {
1258
+ color: var(--color-fd-foreground);
1259
+ border-color: var(--color-fd-foreground);
1260
+ text-decoration: none;
1261
+ }
1262
+
1240
1263
  /* ─── Previous / Next Navigation ────────────────────────────────────── */
1241
1264
 
1242
1265
  .fd-page-nav {
@@ -1306,19 +1329,6 @@ html.dark pre.shiki {
1306
1329
  * AI Chat & Search Dialog — base styles (fd-ai-*)
1307
1330
  * ═══════════════════════════════════════════════════════════════════════ */
1308
1331
 
1309
- @keyframes fd-ai-dot {
1310
- 0%,
1311
- 80%,
1312
- 100% {
1313
- transform: scale(0);
1314
- opacity: 0.5;
1315
- }
1316
- 40% {
1317
- transform: scale(1);
1318
- opacity: 1;
1319
- }
1320
- }
1321
-
1322
1332
  @keyframes fd-ai-fade-in {
1323
1333
  from {
1324
1334
  opacity: 0;
@@ -1670,6 +1680,12 @@ html.dark pre.shiki {
1670
1680
  line-height: 1.6;
1671
1681
  max-width: 95%;
1672
1682
  word-break: break-word;
1683
+ animation: fd-ai-msg-in 300ms ease-out;
1684
+ }
1685
+
1686
+ @keyframes fd-ai-msg-in {
1687
+ from { opacity: 0; transform: translateY(6px); }
1688
+ to { opacity: 1; transform: translateY(0); }
1673
1689
  }
1674
1690
 
1675
1691
  .fd-ai-chat-footer {
@@ -1722,36 +1738,70 @@ html.dark pre.shiki {
1722
1738
  color: var(--color-fd-primary-foreground);
1723
1739
  }
1724
1740
 
1725
- .fd-ai-loading {
1741
+ .fd-ai-loader {
1726
1742
  display: inline-flex;
1727
- gap: 6px;
1728
1743
  align-items: center;
1744
+ gap: 6px;
1745
+ animation: fd-ai-loader-in 300ms ease-out;
1729
1746
  }
1730
1747
 
1731
- .fd-ai-loading-text {
1732
- font-size: 12px;
1733
- color: var(--color-fd-muted-foreground);
1748
+ @keyframes fd-ai-loader-in {
1749
+ from { opacity: 0; transform: translateY(4px); }
1750
+ to { opacity: 1; transform: translateY(0); }
1751
+ }
1752
+
1753
+ .fd-ai-loader-shimmer-text {
1754
+ font-size: 13px;
1755
+ font-weight: 500;
1756
+ background: linear-gradient(to right, var(--color-fd-muted-foreground, #888) 40%, var(--color-fd-foreground, #fff) 60%, var(--color-fd-muted-foreground, #888) 80%);
1757
+ background-size: 200% auto;
1758
+ background-clip: text;
1759
+ -webkit-background-clip: text;
1760
+ color: transparent;
1761
+ animation: fd-ai-shimmer-text 3s linear infinite;
1734
1762
  }
1735
1763
 
1736
- .fd-ai-loading-dots {
1764
+ @keyframes fd-ai-shimmer-text {
1765
+ 0% { background-position: 150% center; }
1766
+ 100% { background-position: -150% center; }
1767
+ }
1768
+
1769
+ .fd-ai-loader-typing-dots {
1737
1770
  display: inline-flex;
1738
- gap: 3px;
1739
1771
  align-items: center;
1772
+ gap: 2px;
1740
1773
  }
1741
1774
 
1742
- .fd-ai-loading-dot {
1743
- width: 5px;
1744
- height: 5px;
1775
+ .fd-ai-loader-typing-dot {
1776
+ width: 4px;
1777
+ height: 4px;
1745
1778
  border-radius: 50%;
1746
- background: var(--color-fd-muted-foreground);
1747
- animation: fd-ai-dot 1.4s infinite ease-in-out both;
1779
+ background: var(--color-fd-primary, #6366f1);
1780
+ animation: fd-ai-typing 1s infinite;
1748
1781
  }
1749
1782
 
1750
- .fd-ai-loading-dot:nth-child(2) {
1751
- animation-delay: 0.16s;
1783
+ .fd-ai-loader-typing-dot:nth-child(2) { animation-delay: 250ms; }
1784
+ .fd-ai-loader-typing-dot:nth-child(3) { animation-delay: 500ms; }
1785
+
1786
+ @keyframes fd-ai-typing {
1787
+ 0%, 100% { transform: translateY(0); opacity: 0.5; }
1788
+ 50% { transform: translateY(-2px); opacity: 1; }
1789
+ }
1790
+
1791
+ .fd-ai-streaming::after {
1792
+ content: "";
1793
+ display: inline-block;
1794
+ width: 2px;
1795
+ height: 1em;
1796
+ background: var(--color-fd-primary, #6366f1);
1797
+ margin-left: 2px;
1798
+ vertical-align: text-bottom;
1799
+ animation: fd-ai-cursor-blink 0.8s step-end infinite;
1752
1800
  }
1753
- .fd-ai-loading-dot:nth-child(3) {
1754
- animation-delay: 0.32s;
1801
+
1802
+ @keyframes fd-ai-cursor-blink {
1803
+ 0%, 100% { opacity: 1; }
1804
+ 50% { opacity: 0; }
1755
1805
  }
1756
1806
 
1757
1807
  /* ─── Markdown in AI responses ───────────────────────────────── */
@@ -2138,41 +2188,7 @@ html.dark pre.shiki {
2138
2188
  font-size: 12px;
2139
2189
  }
2140
2190
 
2141
- /* ─── Thinking dots ──────────────────────────────────────────── */
2142
-
2143
- .fd-ai-fm-thinking {
2144
- display: flex;
2145
- gap: 4px;
2146
- align-items: center;
2147
- }
2148
-
2149
- .fd-ai-fm-thinking-dot {
2150
- width: 6px;
2151
- height: 6px;
2152
- border-radius: 9999px;
2153
- background: var(--color-fd-primary, #6366f1);
2154
- animation: fd-ai-fm-bounce 1s infinite ease-in-out;
2155
- }
2156
-
2157
- .fd-ai-fm-thinking-dot:nth-child(2) {
2158
- animation-delay: 150ms;
2159
- }
2160
- .fd-ai-fm-thinking-dot:nth-child(3) {
2161
- animation-delay: 300ms;
2162
- }
2163
-
2164
- @keyframes fd-ai-fm-bounce {
2165
- 0%,
2166
- 80%,
2167
- 100% {
2168
- transform: scale(0.6);
2169
- opacity: 0.4;
2170
- }
2171
- 40% {
2172
- transform: scale(1);
2173
- opacity: 1;
2174
- }
2175
- }
2191
+ /* Full-modal now uses the shared .fd-ai-loader indicator */
2176
2192
 
2177
2193
  /* ─── Bottom input bar ───────────────────────────────────────── */
2178
2194
 
@@ -477,13 +477,13 @@
477
477
  letter-spacing: 0.06em;
478
478
  }
479
479
 
480
- .fd-ai-loading-text {
480
+ .fd-ai-loader-shimmer-text {
481
481
  text-transform: uppercase;
482
482
  letter-spacing: 0.04em;
483
483
  font-size: 11px;
484
484
  }
485
485
 
486
- .fd-ai-loading-dot {
486
+ .fd-ai-loader-typing-dot {
487
487
  border-radius: 0;
488
488
  width: 4px;
489
489
  height: 4px;
@@ -561,11 +561,7 @@
561
561
  font-size: 11px;
562
562
  }
563
563
 
564
- .fd-ai-fm-thinking-dot {
565
- border-radius: 0;
566
- width: 5px;
567
- height: 5px;
568
- }
564
+ /* Full-modal now uses .fd-ai-loader-typing-dot (see above) */
569
565
 
570
566
  /* ─── Code blocks in AI chat (pixel-border) ──────────────────────── */
571
567