@farming-labs/theme 0.0.2-beta.23 → 0.0.2-beta.26

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.
@@ -1,10 +1,12 @@
1
1
  //#region src/ai-search-dialog.d.ts
2
+ type LoaderVariant = "shimmer-dots" | "circular" | "dots" | "typing" | "wave" | "bars" | "pulse" | "pulse-dot" | "terminal" | "text-blink" | "text-shimmer" | "loading-dots";
2
3
  declare function DocsSearchDialog({
3
4
  open,
4
5
  onOpenChange,
5
6
  api,
6
7
  suggestedQuestions,
7
8
  aiLabel,
9
+ loaderVariant,
8
10
  loadingComponentHtml
9
11
  }: {
10
12
  open: boolean;
@@ -12,6 +14,7 @@ declare function DocsSearchDialog({
12
14
  api?: string;
13
15
  suggestedQuestions?: string[];
14
16
  aiLabel?: string;
17
+ loaderVariant?: LoaderVariant;
15
18
  loadingComponentHtml?: string;
16
19
  }): any;
17
20
  type FloatingPosition = "bottom-right" | "bottom-left" | "bottom-center";
@@ -23,6 +26,7 @@ declare function FloatingAIChat({
23
26
  triggerComponentHtml,
24
27
  suggestedQuestions,
25
28
  aiLabel,
29
+ loaderVariant,
26
30
  loadingComponentHtml
27
31
  }: {
28
32
  api?: string;
@@ -31,6 +35,7 @@ declare function FloatingAIChat({
31
35
  triggerComponentHtml?: string;
32
36
  suggestedQuestions?: string[];
33
37
  aiLabel?: string;
38
+ loaderVariant?: LoaderVariant;
34
39
  loadingComponentHtml?: string;
35
40
  }): any;
36
41
  declare function AIModalDialog({
@@ -39,6 +44,7 @@ declare function AIModalDialog({
39
44
  api,
40
45
  suggestedQuestions,
41
46
  aiLabel,
47
+ loaderVariant,
42
48
  loadingComponentHtml
43
49
  }: {
44
50
  open: boolean;
@@ -46,6 +52,7 @@ declare function AIModalDialog({
46
52
  api?: string;
47
53
  suggestedQuestions?: string[];
48
54
  aiLabel?: string;
55
+ loaderVariant?: LoaderVariant;
49
56
  loadingComponentHtml?: string;
50
57
  }): any;
51
58
  //#endregion
@@ -143,33 +143,145 @@ function XIcon() {
143
143
  children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
144
144
  });
145
145
  }
146
- function DefaultLoadingIndicator({ label }) {
147
- return /* @__PURE__ */ jsxs("span", {
148
- className: "fd-ai-loading",
149
- children: [/* @__PURE__ */ jsxs("span", {
150
- className: "fd-ai-loading-text",
151
- children: [label, " is thinking"]
152
- }), /* @__PURE__ */ jsxs("span", {
153
- className: "fd-ai-loading-dots",
154
- children: [
155
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" }),
156
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" }),
157
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" })
158
- ]
159
- })]
160
- });
146
+ function LoaderIndicator({ variant = "shimmer-dots" }) {
147
+ const text = "Thinking";
148
+ switch (variant) {
149
+ case "circular": return /* @__PURE__ */ jsxs("div", {
150
+ className: "fd-ai-loader",
151
+ children: [/* @__PURE__ */ jsx("div", { className: "fd-ai-loader-circular" }), /* @__PURE__ */ jsx("span", {
152
+ className: "sr-only",
153
+ children: "Loading"
154
+ })]
155
+ });
156
+ case "dots": return /* @__PURE__ */ jsx("div", {
157
+ className: "fd-ai-loader",
158
+ children: /* @__PURE__ */ jsxs("span", {
159
+ className: "fd-ai-loader-dots",
160
+ children: [
161
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bounce-dot" }),
162
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bounce-dot" }),
163
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bounce-dot" })
164
+ ]
165
+ })
166
+ });
167
+ case "typing": return /* @__PURE__ */ jsx("div", {
168
+ className: "fd-ai-loader",
169
+ children: /* @__PURE__ */ jsxs("span", {
170
+ className: "fd-ai-loader-typing-dots",
171
+ children: [
172
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
173
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
174
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" })
175
+ ]
176
+ })
177
+ });
178
+ case "wave": return /* @__PURE__ */ jsx("div", {
179
+ className: "fd-ai-loader",
180
+ children: /* @__PURE__ */ jsxs("span", {
181
+ className: "fd-ai-loader-wave",
182
+ children: [
183
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-wave-bar" }),
184
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-wave-bar" }),
185
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-wave-bar" }),
186
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-wave-bar" }),
187
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-wave-bar" })
188
+ ]
189
+ })
190
+ });
191
+ case "bars": return /* @__PURE__ */ jsx("div", {
192
+ className: "fd-ai-loader",
193
+ children: /* @__PURE__ */ jsxs("span", {
194
+ className: "fd-ai-loader-bars",
195
+ children: [
196
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bar" }),
197
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bar" }),
198
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-bar" })
199
+ ]
200
+ })
201
+ });
202
+ case "pulse": return /* @__PURE__ */ jsx("div", {
203
+ className: "fd-ai-loader",
204
+ children: /* @__PURE__ */ jsx("div", { className: "fd-ai-loader-pulse" })
205
+ });
206
+ case "pulse-dot": return /* @__PURE__ */ jsx("div", {
207
+ className: "fd-ai-loader",
208
+ children: /* @__PURE__ */ jsx("div", { className: "fd-ai-loader-pulse-dot" })
209
+ });
210
+ case "terminal": return /* @__PURE__ */ jsx("div", {
211
+ className: "fd-ai-loader",
212
+ children: /* @__PURE__ */ jsxs("span", {
213
+ className: "fd-ai-loader-terminal",
214
+ children: [/* @__PURE__ */ jsx("span", {
215
+ className: "fd-ai-loader-terminal-prompt",
216
+ children: ">"
217
+ }), /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-terminal-cursor" })]
218
+ })
219
+ });
220
+ case "text-blink": return /* @__PURE__ */ jsx("div", {
221
+ className: "fd-ai-loader",
222
+ children: /* @__PURE__ */ jsx("span", {
223
+ className: "fd-ai-loader-text-blink",
224
+ children: text
225
+ })
226
+ });
227
+ case "text-shimmer": return /* @__PURE__ */ jsx("div", {
228
+ className: "fd-ai-loader",
229
+ children: /* @__PURE__ */ jsx("span", {
230
+ className: "fd-ai-loader-shimmer-text",
231
+ children: text
232
+ })
233
+ });
234
+ case "loading-dots": return /* @__PURE__ */ jsxs("div", {
235
+ className: "fd-ai-loader",
236
+ children: [/* @__PURE__ */ jsx("span", {
237
+ className: "fd-ai-loader-text",
238
+ children: text
239
+ }), /* @__PURE__ */ jsxs("span", {
240
+ className: "fd-ai-loader-text-dots",
241
+ children: [
242
+ /* @__PURE__ */ jsx("span", {
243
+ className: "fd-ai-loader-text-dot",
244
+ children: "."
245
+ }),
246
+ /* @__PURE__ */ jsx("span", {
247
+ className: "fd-ai-loader-text-dot",
248
+ children: "."
249
+ }),
250
+ /* @__PURE__ */ jsx("span", {
251
+ className: "fd-ai-loader-text-dot",
252
+ children: "."
253
+ })
254
+ ]
255
+ })]
256
+ });
257
+ default: return /* @__PURE__ */ jsxs("div", {
258
+ className: "fd-ai-loader",
259
+ children: [/* @__PURE__ */ jsx("span", {
260
+ className: "fd-ai-loader-shimmer-text",
261
+ children: text
262
+ }), /* @__PURE__ */ jsxs("span", {
263
+ className: "fd-ai-loader-typing-dots",
264
+ children: [
265
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
266
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
267
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" })
268
+ ]
269
+ })]
270
+ });
271
+ }
161
272
  }
162
- function LoadingDots() {
273
+ function InlineLoaderDots() {
163
274
  return /* @__PURE__ */ jsxs("span", {
164
- className: "fd-ai-loading-dots",
275
+ className: "fd-ai-loader-typing-dots",
276
+ style: { marginLeft: 0 },
165
277
  children: [
166
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" }),
167
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" }),
168
- /* @__PURE__ */ jsx("span", { className: "fd-ai-loading-dot" })
278
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
279
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" }),
280
+ /* @__PURE__ */ jsx("span", { className: "fd-ai-loader-typing-dot" })
169
281
  ]
170
282
  });
171
283
  }
172
- function AIChat({ api, messages, setMessages, aiInput, setAiInput, isStreaming, setIsStreaming, suggestedQuestions, aiLabel, loadingComponentHtml }) {
284
+ function AIChat({ api, messages, setMessages, aiInput, setAiInput, isStreaming, setIsStreaming, suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
173
285
  const label = aiLabel || "AI";
174
286
  const aiInputRef = useRef(null);
175
287
  const messagesEndRef = useRef(null);
@@ -311,7 +423,13 @@ function AIChat({ api, messages, setMessages, aiInput, setAiInput, isStreaming,
311
423
  children: msg.content
312
424
  }) : /* @__PURE__ */ jsx("div", {
313
425
  className: "fd-ai-bubble-ai",
314
- children: msg.content ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderMarkdown(msg.content) } }) : loadingComponentHtml ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: loadingComponentHtml } }) : /* @__PURE__ */ jsx(DefaultLoadingIndicator, { label })
426
+ children: msg.content ? /* @__PURE__ */ jsx("div", {
427
+ className: isStreaming && i === messages.length - 1 ? "fd-ai-streaming" : void 0,
428
+ dangerouslySetInnerHTML: { __html: renderMarkdown(msg.content) }
429
+ }) : loadingComponentHtml ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: loadingComponentHtml } }) : /* @__PURE__ */ jsx(LoaderIndicator, {
430
+ variant: loaderVariant,
431
+ label
432
+ })
315
433
  })]
316
434
  }, i)), /* @__PURE__ */ jsx("div", { ref: messagesEndRef })]
317
435
  }), /* @__PURE__ */ jsxs("div", {
@@ -353,7 +471,7 @@ function AIChat({ api, messages, setMessages, aiInput, setAiInput, isStreaming,
353
471
  })]
354
472
  });
355
473
  }
356
- function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestions, aiLabel, loadingComponentHtml }) {
474
+ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
357
475
  const [tab, setTab] = useState("search");
358
476
  const [searchQuery, setSearchQuery] = useState("");
359
477
  const [searchResults, setSearchResults] = useState([]);
@@ -496,7 +614,7 @@ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", suggestedQues
496
614
  onKeyDown: handleSearchKeyDown,
497
615
  className: "fd-ai-input"
498
616
  }),
499
- isSearching && /* @__PURE__ */ jsx(LoadingDots, {})
617
+ isSearching && /* @__PURE__ */ jsx(InlineLoaderDots, {})
500
618
  ]
501
619
  }), /* @__PURE__ */ jsx("div", {
502
620
  className: "fd-ai-results",
@@ -528,6 +646,7 @@ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", suggestedQues
528
646
  setIsStreaming,
529
647
  suggestedQuestions,
530
648
  aiLabel,
649
+ loaderVariant,
531
650
  loadingComponentHtml
532
651
  })
533
652
  ]
@@ -602,7 +721,7 @@ function getContainerStyles(style, position) {
602
721
  function getAnimation(style) {
603
722
  return style === "modal" ? "fd-ai-float-center-in 200ms ease-out" : "fd-ai-float-in 200ms ease-out";
604
723
  }
605
- function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floatingStyle = "panel", triggerComponentHtml, suggestedQuestions, aiLabel, loadingComponentHtml }) {
724
+ function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floatingStyle = "panel", triggerComponentHtml, suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
606
725
  const [mounted, setMounted] = useState(false);
607
726
  const [isOpen, setIsOpen] = useState(false);
608
727
  const [messages, setMessages] = useState([]);
@@ -640,6 +759,7 @@ function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floating
640
759
  setIsStreaming,
641
760
  suggestedQuestions,
642
761
  aiLabel,
762
+ loaderVariant,
643
763
  loadingComponentHtml,
644
764
  triggerComponentHtml,
645
765
  position
@@ -688,6 +808,7 @@ function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floating
688
808
  setIsStreaming,
689
809
  suggestedQuestions,
690
810
  aiLabel,
811
+ loaderVariant,
691
812
  loadingComponentHtml
692
813
  })]
693
814
  }),
@@ -705,7 +826,7 @@ function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floating
705
826
  }))
706
827
  ] }), document.body);
707
828
  }
708
- function FullModalAIChat({ api, isOpen, setIsOpen, messages, setMessages, aiInput, setAiInput, isStreaming, setIsStreaming, suggestedQuestions, aiLabel, loadingComponentHtml, triggerComponentHtml, position }) {
829
+ function FullModalAIChat({ api, isOpen, setIsOpen, messages, setMessages, aiInput, setAiInput, isStreaming, setIsStreaming, suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml, triggerComponentHtml, position }) {
709
830
  const label = aiLabel || "AI";
710
831
  const inputRef = useRef(null);
711
832
  const listRef = useRef(null);
@@ -831,13 +952,12 @@ function FullModalAIChat({ api, isOpen, setIsOpen, messages, setMessages, aiInpu
831
952
  children: msg.role === "user" ? "you" : label
832
953
  }), /* @__PURE__ */ jsx("div", {
833
954
  className: "fd-ai-fm-msg-content",
834
- children: msg.content ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderMarkdown(msg.content) } }) : loadingComponentHtml ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: loadingComponentHtml } }) : /* @__PURE__ */ jsxs("div", {
835
- className: "fd-ai-fm-thinking",
836
- children: [
837
- /* @__PURE__ */ jsx("span", { className: "fd-ai-fm-thinking-dot" }),
838
- /* @__PURE__ */ jsx("span", { className: "fd-ai-fm-thinking-dot" }),
839
- /* @__PURE__ */ jsx("span", { className: "fd-ai-fm-thinking-dot" })
840
- ]
955
+ children: msg.content ? /* @__PURE__ */ jsx("div", {
956
+ className: isStreaming && i === messages.length - 1 ? "fd-ai-streaming" : void 0,
957
+ dangerouslySetInnerHTML: { __html: renderMarkdown(msg.content) }
958
+ }) : loadingComponentHtml ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: loadingComponentHtml } }) : /* @__PURE__ */ jsx(LoaderIndicator, {
959
+ variant: loaderVariant,
960
+ label
841
961
  })
842
962
  })]
843
963
  }, i))
@@ -871,7 +991,7 @@ function FullModalAIChat({ api, isOpen, setIsOpen, messages, setMessages, aiInpu
871
991
  }), isStreaming ? /* @__PURE__ */ jsx("button", {
872
992
  className: "fd-ai-fm-send-btn",
873
993
  onClick: () => setIsStreaming(false),
874
- children: /* @__PURE__ */ jsx(LoadingDots, {})
994
+ children: /* @__PURE__ */ jsx(InlineLoaderDots, {})
875
995
  }) : /* @__PURE__ */ jsx("button", {
876
996
  className: "fd-ai-fm-send-btn",
877
997
  "data-active": canSend,
@@ -932,7 +1052,7 @@ function TrashIcon() {
932
1052
  ]
933
1053
  });
934
1054
  }
935
- function AIModalDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestions, aiLabel, loadingComponentHtml }) {
1055
+ function AIModalDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
936
1056
  const [messages, setMessages] = useState([]);
937
1057
  const [aiInput, setAiInput] = useState("");
938
1058
  const [isStreaming, setIsStreaming] = useState(false);
@@ -999,6 +1119,7 @@ function AIModalDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestio
999
1119
  setIsStreaming,
1000
1120
  suggestedQuestions,
1001
1121
  aiLabel,
1122
+ loaderVariant,
1002
1123
  loadingComponentHtml
1003
1124
  }),
1004
1125
  /* @__PURE__ */ jsx("div", {
@@ -8,6 +8,7 @@ interface DocsAIFeaturesProps {
8
8
  triggerComponentHtml?: string;
9
9
  suggestedQuestions?: string[];
10
10
  aiLabel?: string;
11
+ loaderVariant?: string;
11
12
  loadingComponentHtml?: string;
12
13
  }
13
14
  declare function DocsAIFeatures({
@@ -17,6 +18,7 @@ declare function DocsAIFeatures({
17
18
  triggerComponentHtml,
18
19
  suggestedQuestions,
19
20
  aiLabel,
21
+ loaderVariant,
20
22
  loadingComponentHtml
21
23
  }: DocsAIFeaturesProps): react_jsx_runtime0.JSX.Element;
22
24
  //#endregion
@@ -19,15 +19,17 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
19
  * This component is rendered inside the docs layout so the user's root layout
20
20
  * never needs to be modified — AI features work purely from `docs.config.tsx`.
21
21
  */
22
- function DocsAIFeatures({ mode, position = "bottom-right", floatingStyle = "panel", triggerComponentHtml, suggestedQuestions, aiLabel, loadingComponentHtml }) {
22
+ function DocsAIFeatures({ mode, position = "bottom-right", floatingStyle = "panel", triggerComponentHtml, suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
23
23
  if (mode === "search") return /* @__PURE__ */ jsx(SearchModeAI, {
24
24
  suggestedQuestions,
25
25
  aiLabel,
26
+ loaderVariant,
26
27
  loadingComponentHtml
27
28
  });
28
29
  if (mode === "sidebar-icon") return /* @__PURE__ */ jsx(SidebarIconModeAI, {
29
30
  suggestedQuestions,
30
31
  aiLabel,
32
+ loaderVariant,
31
33
  loadingComponentHtml
32
34
  });
33
35
  return /* @__PURE__ */ jsx(FloatingAIChat, {
@@ -37,15 +39,11 @@ function DocsAIFeatures({ mode, position = "bottom-right", floatingStyle = "pane
37
39
  triggerComponentHtml,
38
40
  suggestedQuestions,
39
41
  aiLabel,
42
+ loaderVariant,
40
43
  loadingComponentHtml
41
44
  });
42
45
  }
43
- /**
44
- * Search mode: intercepts Cmd+K / Ctrl+K globally and opens the
45
- * custom search dialog (with Search + Ask AI tabs) instead of
46
- * fumadocs' built-in search dialog.
47
- */
48
- function SearchModeAI({ suggestedQuestions, aiLabel, loadingComponentHtml }) {
46
+ function SearchModeAI({ suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
49
47
  const [open, setOpen] = useState(false);
50
48
  useEffect(() => {
51
49
  function handler(e) {
@@ -80,15 +78,11 @@ function SearchModeAI({ suggestedQuestions, aiLabel, loadingComponentHtml }) {
80
78
  api: "/api/docs",
81
79
  suggestedQuestions,
82
80
  aiLabel,
81
+ loaderVariant,
83
82
  loadingComponentHtml
84
83
  });
85
84
  }
86
- /**
87
- * Sidebar-icon mode: injects a sparkle icon button next to the search bar
88
- * in the sidebar header. The search button opens the Cmd+K search dialog,
89
- * and the AI sparkle button opens a pure AI modal (no search tabs).
90
- */
91
- function SidebarIconModeAI({ suggestedQuestions, aiLabel, loadingComponentHtml }) {
85
+ function SidebarIconModeAI({ suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml }) {
92
86
  const [searchOpen, setSearchOpen] = useState(false);
93
87
  const [aiOpen, setAiOpen] = useState(false);
94
88
  useEffect(() => {
@@ -123,6 +117,7 @@ function SidebarIconModeAI({ suggestedQuestions, aiLabel, loadingComponentHtml }
123
117
  api: "/api/docs",
124
118
  suggestedQuestions,
125
119
  aiLabel,
120
+ loaderVariant,
126
121
  loadingComponentHtml
127
122
  }), /* @__PURE__ */ jsx(AIModalDialog, {
128
123
  open: aiOpen,
@@ -130,6 +125,7 @@ function SidebarIconModeAI({ suggestedQuestions, aiLabel, loadingComponentHtml }
130
125
  api: "/api/docs",
131
126
  suggestedQuestions,
132
127
  aiLabel,
128
+ loaderVariant,
133
129
  loadingComponentHtml
134
130
  })] });
135
131
  }
@@ -229,7 +229,7 @@ function resolveSidebar(sidebar) {
229
229
  if (sidebar === false) return { enabled: false };
230
230
  return {
231
231
  enabled: sidebar.enabled !== false,
232
- component: sidebar.component,
232
+ componentFn: typeof sidebar.component === "function" ? sidebar.component : void 0,
233
233
  footer: sidebar.footer,
234
234
  banner: sidebar.banner,
235
235
  collapsible: sidebar.collapsible,
@@ -343,7 +343,8 @@ function createDocsLayout(config) {
343
343
  const forcedTheme = themeSwitch.enabled === false && toggleConfig?.default && toggleConfig.default !== "system" ? toggleConfig.default : void 0;
344
344
  const resolvedSidebar = resolveSidebar(config.sidebar);
345
345
  const sidebarFlat = resolvedSidebar.flat;
346
- const { flat: _sidebarFlat, ...sidebarProps } = resolvedSidebar;
346
+ const sidebarComponentFn = resolvedSidebar.componentFn;
347
+ const { flat: _sidebarFlat, componentFn: _componentFn, ...sidebarProps } = resolvedSidebar;
347
348
  const breadcrumbConfig = config.breadcrumb;
348
349
  const breadcrumbEnabled = breadcrumbConfig === void 0 || breadcrumbConfig === true || typeof breadcrumbConfig === "object" && breadcrumbConfig.enabled !== false;
349
350
  const colors = config.theme?._userColorOverrides;
@@ -375,18 +376,26 @@ function createDocsLayout(config) {
375
376
  const aiTriggerComponentHtml = aiConfig?.triggerComponent ? serializeIcon(aiConfig.triggerComponent) : void 0;
376
377
  const aiSuggestedQuestions = aiConfig?.suggestedQuestions;
377
378
  const aiLabel = aiConfig?.aiLabel;
379
+ const aiLoaderVariant = aiConfig?.loader;
378
380
  const aiLoadingComponentHtml = typeof aiConfig?.loadingComponent === "function" ? serializeIcon(aiConfig.loadingComponent({ name: aiLabel || "AI" })) : void 0;
379
381
  const lastModifiedMap = buildLastModifiedMap(config.entry);
380
382
  const descriptionMap = buildDescriptionMap(config.entry);
381
383
  return function DocsLayoutWrapper({ children }) {
384
+ const tree = buildTree(config, !!sidebarFlat);
385
+ const finalSidebarProps = { ...sidebarProps };
386
+ if (sidebarComponentFn) finalSidebarProps.component = sidebarComponentFn({
387
+ tree,
388
+ collapsible: sidebarProps.collapsible !== false,
389
+ flat: !!sidebarFlat
390
+ });
382
391
  return /* @__PURE__ */ jsxs(DocsLayout, {
383
- tree: buildTree(config, !!sidebarFlat),
392
+ tree,
384
393
  nav: {
385
394
  title: navTitle,
386
395
  url: navUrl
387
396
  },
388
397
  themeSwitch,
389
- sidebar: sidebarProps,
398
+ sidebar: finalSidebarProps,
390
399
  ...aiMode === "sidebar-icon" && aiEnabled ? { searchToggle: { components: { lg: /* @__PURE__ */ jsx(SidebarSearchWithAI, {}) } } } : {},
391
400
  children: [
392
401
  /* @__PURE__ */ jsx(ColorStyle, { colors }),
@@ -401,6 +410,7 @@ function createDocsLayout(config) {
401
410
  triggerComponentHtml: aiTriggerComponentHtml,
402
411
  suggestedQuestions: aiSuggestedQuestions,
403
412
  aiLabel,
413
+ loaderVariant: aiLoaderVariant,
404
414
  loadingComponentHtml: aiLoadingComponentHtml
405
415
  }),
406
416
  /* @__PURE__ */ jsx(DocsPageClient, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.0.2-beta.23",
3
+ "version": "0.0.2-beta.26",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -98,7 +98,7 @@
98
98
  "next": ">=14.0.0",
99
99
  "tsdown": "^0.20.3",
100
100
  "typescript": "^5.9.3",
101
- "@farming-labs/docs": "0.0.2-beta.23"
101
+ "@farming-labs/docs": "0.0.2-beta.26"
102
102
  },
103
103
  "peerDependencies": {
104
104
  "@farming-labs/docs": ">=0.0.1",
package/styles/ai.css CHANGED
@@ -7,19 +7,6 @@
7
7
 
8
8
  /* ─── Animations ─────────────────────────────────────────────────── */
9
9
 
10
- @keyframes fd-ai-dot {
11
- 0%,
12
- 80%,
13
- 100% {
14
- transform: scale(0);
15
- opacity: 0.5;
16
- }
17
- 40% {
18
- transform: scale(1);
19
- opacity: 1;
20
- }
21
- }
22
-
23
10
  @keyframes fd-ai-fade-in {
24
11
  from {
25
12
  opacity: 0;
@@ -343,6 +330,12 @@
343
330
  line-height: 1.6;
344
331
  max-width: 95%;
345
332
  word-break: break-word;
333
+ animation: fd-ai-msg-in 300ms ease-out;
334
+ }
335
+
336
+ @keyframes fd-ai-msg-in {
337
+ from { opacity: 0; transform: translateY(6px); }
338
+ to { opacity: 1; transform: translateY(0); }
346
339
  }
347
340
 
348
341
  /* ─── Chat input ─────────────────────────────────────────────────── */
@@ -397,38 +390,274 @@
397
390
  color: var(--color-fd-primary-foreground, #fff);
398
391
  }
399
392
 
400
- /* ─── Loading indicator ──────────────────────────────────────────── */
393
+ /* ═══════════════════════════════════════════════════════════════════
394
+ * AI Loader variants — fd-ai-loader-*
395
+ * Default: "shimmer-dots" (shimmer text + typing dots in a row)
396
+ * ═══════════════════════════════════════════════════════════════════ */
401
397
 
402
- .fd-ai-loading {
398
+ .fd-ai-loader {
403
399
  display: inline-flex;
400
+ align-items: center;
404
401
  gap: 6px;
402
+ animation: fd-ai-loader-in 300ms ease-out;
403
+ }
404
+
405
+ @keyframes fd-ai-loader-in {
406
+ from { opacity: 0; transform: translateY(4px); }
407
+ to { opacity: 1; transform: translateY(0); }
408
+ }
409
+
410
+ /* ── shimmer-dots (default): shimmer text + typing dots ──────────── */
411
+
412
+ .fd-ai-loader-shimmer-text {
413
+ font-size: 13px;
414
+ font-weight: 500;
415
+ background: linear-gradient(
416
+ to right,
417
+ var(--color-fd-muted-foreground, #888) 40%,
418
+ var(--color-fd-foreground, #fff) 60%,
419
+ var(--color-fd-muted-foreground, #888) 80%
420
+ );
421
+ background-size: 200% auto;
422
+ background-clip: text;
423
+ -webkit-background-clip: text;
424
+ color: transparent;
425
+ animation: fd-ai-shimmer-text 3s linear infinite;
426
+ }
427
+
428
+ @keyframes fd-ai-shimmer-text {
429
+ 0% { background-position: 150% center; }
430
+ 100% { background-position: -150% center; }
431
+ }
432
+
433
+ .fd-ai-loader-typing-dots {
434
+ display: inline-flex;
405
435
  align-items: center;
436
+ gap: 2px;
406
437
  }
407
438
 
408
- .fd-ai-loading-text {
409
- font-size: 12px;
410
- color: var(--color-fd-muted-foreground, #888);
439
+ .fd-ai-loader-typing-dot {
440
+ width: 4px;
441
+ height: 4px;
442
+ border-radius: 50%;
443
+ background: var(--color-fd-primary, #6366f1);
444
+ animation: fd-ai-typing 1s infinite;
411
445
  }
412
446
 
413
- .fd-ai-loading-dots {
447
+ .fd-ai-loader-typing-dot:nth-child(2) { animation-delay: 250ms; }
448
+ .fd-ai-loader-typing-dot:nth-child(3) { animation-delay: 500ms; }
449
+
450
+ @keyframes fd-ai-typing {
451
+ 0%, 100% { transform: translateY(0); opacity: 0.5; }
452
+ 50% { transform: translateY(-2px); opacity: 1; }
453
+ }
454
+
455
+ /* ── circular: spinning ring ─────────────────────────────────────── */
456
+
457
+ .fd-ai-loader-circular {
458
+ width: 16px;
459
+ height: 16px;
460
+ border: 2px solid var(--color-fd-primary, #6366f1);
461
+ border-top-color: transparent;
462
+ border-radius: 50%;
463
+ animation: fd-ai-spin 0.8s linear infinite;
464
+ }
465
+
466
+ @keyframes fd-ai-spin {
467
+ to { transform: rotate(360deg); }
468
+ }
469
+
470
+ /* ── dots: bouncing dots ─────────────────────────────────────────── */
471
+
472
+ .fd-ai-loader-dots {
414
473
  display: inline-flex;
474
+ align-items: center;
415
475
  gap: 3px;
476
+ }
477
+
478
+ .fd-ai-loader-bounce-dot {
479
+ width: 6px;
480
+ height: 6px;
481
+ border-radius: 50%;
482
+ background: var(--color-fd-primary, #6366f1);
483
+ animation: fd-ai-bounce-dots 1.4s ease-in-out infinite;
484
+ }
485
+
486
+ .fd-ai-loader-bounce-dot:nth-child(2) { animation-delay: 160ms; }
487
+ .fd-ai-loader-bounce-dot:nth-child(3) { animation-delay: 320ms; }
488
+
489
+ @keyframes fd-ai-bounce-dots {
490
+ 0%, 100% { transform: scale(0.8); opacity: 0.5; }
491
+ 50% { transform: scale(1.2); opacity: 1; }
492
+ }
493
+
494
+ /* ── wave: wave bars ─────────────────────────────────────────────── */
495
+
496
+ .fd-ai-loader-wave {
497
+ display: inline-flex;
416
498
  align-items: center;
499
+ gap: 2px;
500
+ height: 16px;
417
501
  }
418
502
 
419
- .fd-ai-loading-dot {
420
- width: 5px;
421
- height: 5px;
503
+ .fd-ai-loader-wave-bar {
504
+ width: 2px;
505
+ border-radius: 2px;
506
+ background: var(--color-fd-primary, #6366f1);
507
+ animation: fd-ai-wave 1s ease-in-out infinite;
508
+ }
509
+
510
+ .fd-ai-loader-wave-bar:nth-child(1) { height: 6px; animation-delay: 0ms; }
511
+ .fd-ai-loader-wave-bar:nth-child(2) { height: 10px; animation-delay: 100ms; }
512
+ .fd-ai-loader-wave-bar:nth-child(3) { height: 14px; animation-delay: 200ms; }
513
+ .fd-ai-loader-wave-bar:nth-child(4) { height: 10px; animation-delay: 300ms; }
514
+ .fd-ai-loader-wave-bar:nth-child(5) { height: 6px; animation-delay: 400ms; }
515
+
516
+ @keyframes fd-ai-wave {
517
+ 0%, 100% { transform: scaleY(1); }
518
+ 50% { transform: scaleY(0.6); }
519
+ }
520
+
521
+ /* ── pulse: pulsing ring ─────────────────────────────────────────── */
522
+
523
+ .fd-ai-loader-pulse {
524
+ width: 16px;
525
+ height: 16px;
526
+ border: 2px solid var(--color-fd-primary, #6366f1);
422
527
  border-radius: 50%;
423
- background: var(--color-fd-muted-foreground, #888);
424
- animation: fd-ai-dot 1.4s infinite ease-in-out both;
528
+ animation: fd-ai-pulse 1.5s ease-in-out infinite;
529
+ }
530
+
531
+ @keyframes fd-ai-pulse {
532
+ 0%, 100% { transform: scale(0.95); opacity: 0.8; }
533
+ 50% { transform: scale(1.05); opacity: 0.4; }
534
+ }
535
+
536
+ /* ── pulse-dot: pulsing dot ──────────────────────────────────────── */
537
+
538
+ .fd-ai-loader-pulse-dot {
539
+ width: 8px;
540
+ height: 8px;
541
+ border-radius: 50%;
542
+ background: var(--color-fd-primary, #6366f1);
543
+ animation: fd-ai-pulse-dot 1.2s ease-in-out infinite;
544
+ }
545
+
546
+ @keyframes fd-ai-pulse-dot {
547
+ 0%, 100% { transform: scale(1); opacity: 0.8; }
548
+ 50% { transform: scale(1.5); opacity: 1; }
549
+ }
550
+
551
+ /* ── terminal: blinking cursor ───────────────────────────────────── */
552
+
553
+ .fd-ai-loader-terminal {
554
+ display: inline-flex;
555
+ align-items: center;
556
+ gap: 3px;
557
+ }
558
+
559
+ .fd-ai-loader-terminal-prompt {
560
+ font-family: var(--fd-font-mono, ui-monospace, monospace);
561
+ font-size: 13px;
562
+ color: var(--color-fd-primary, #6366f1);
563
+ }
564
+
565
+ .fd-ai-loader-terminal-cursor {
566
+ width: 7px;
567
+ height: 14px;
568
+ background: var(--color-fd-primary, #6366f1);
569
+ animation: fd-ai-blink 1s step-end infinite;
570
+ }
571
+
572
+ @keyframes fd-ai-blink {
573
+ 0%, 100% { opacity: 1; }
574
+ 50% { opacity: 0; }
575
+ }
576
+
577
+ /* ── text-shimmer: shimmer text only ─────────────────────────────── */
578
+ /* (reuses .fd-ai-loader-shimmer-text above) */
579
+
580
+ /* ── text-blink: blinking text ───────────────────────────────────── */
581
+
582
+ .fd-ai-loader-text-blink {
583
+ font-size: 13px;
584
+ font-weight: 500;
585
+ animation: fd-ai-text-blink 2s ease-in-out infinite;
586
+ }
587
+
588
+ @keyframes fd-ai-text-blink {
589
+ 0%, 100% { color: var(--color-fd-primary, #6366f1); }
590
+ 50% { color: var(--color-fd-muted-foreground, #888); }
591
+ }
592
+
593
+ /* ── loading-dots: "Thinking..." with animated dots ──────────────── */
594
+
595
+ .fd-ai-loader-text {
596
+ font-size: 13px;
597
+ font-weight: 500;
598
+ color: var(--color-fd-primary, #6366f1);
425
599
  }
426
600
 
427
- .fd-ai-loading-dot:nth-child(2) {
428
- animation-delay: 0.16s;
601
+ .fd-ai-loader-text-dots {
602
+ display: inline-flex;
429
603
  }
430
- .fd-ai-loading-dot:nth-child(3) {
431
- animation-delay: 0.32s;
604
+
605
+ .fd-ai-loader-text-dot {
606
+ font-size: 13px;
607
+ font-weight: 500;
608
+ color: var(--color-fd-primary, #6366f1);
609
+ animation: fd-ai-loading-dots 1.4s infinite;
610
+ }
611
+
612
+ .fd-ai-loader-text-dot:nth-child(1) { animation-delay: 200ms; }
613
+ .fd-ai-loader-text-dot:nth-child(2) { animation-delay: 400ms; }
614
+ .fd-ai-loader-text-dot:nth-child(3) { animation-delay: 600ms; }
615
+
616
+ @keyframes fd-ai-loading-dots {
617
+ 0%, 100% { opacity: 0; }
618
+ 50% { opacity: 1; }
619
+ }
620
+
621
+ /* ── bars: thick wave bars ───────────────────────────────────────── */
622
+
623
+ .fd-ai-loader-bars {
624
+ display: inline-flex;
625
+ align-items: stretch;
626
+ gap: 3px;
627
+ height: 16px;
628
+ }
629
+
630
+ .fd-ai-loader-bar {
631
+ width: 4px;
632
+ background: var(--color-fd-primary, #6366f1);
633
+ animation: fd-ai-wave-bars 1.2s ease-in-out infinite;
634
+ }
635
+
636
+ .fd-ai-loader-bar:nth-child(1) { animation-delay: 0s; }
637
+ .fd-ai-loader-bar:nth-child(2) { animation-delay: 0.2s; }
638
+ .fd-ai-loader-bar:nth-child(3) { animation-delay: 0.4s; }
639
+
640
+ @keyframes fd-ai-wave-bars {
641
+ 0%, 100% { transform: scaleY(1); opacity: 0.5; }
642
+ 50% { transform: scaleY(0.6); opacity: 1; }
643
+ }
644
+
645
+ /* ─── Streaming cursor ───────────────────────────────────────────── */
646
+
647
+ .fd-ai-streaming::after {
648
+ content: "";
649
+ display: inline-block;
650
+ width: 2px;
651
+ height: 1em;
652
+ background: var(--color-fd-primary, #6366f1);
653
+ margin-left: 2px;
654
+ vertical-align: text-bottom;
655
+ animation: fd-ai-cursor-blink 0.8s step-end infinite;
656
+ }
657
+
658
+ @keyframes fd-ai-cursor-blink {
659
+ 0%, 100% { opacity: 1; }
660
+ 50% { opacity: 0; }
432
661
  }
433
662
 
434
663
  /* ─── Floating trigger button ────────────────────────────────────── */
@@ -747,39 +976,7 @@
747
976
 
748
977
  /* ─── Thinking dots ──────────────────────────────────────────────── */
749
978
 
750
- .fd-ai-fm-thinking {
751
- display: flex;
752
- gap: 4px;
753
- align-items: center;
754
- }
755
-
756
- .fd-ai-fm-thinking-dot {
757
- width: 6px;
758
- height: 6px;
759
- border-radius: 9999px;
760
- background: var(--color-fd-primary, #6366f1);
761
- animation: fd-ai-fm-bounce 1s infinite ease-in-out;
762
- }
763
-
764
- .fd-ai-fm-thinking-dot:nth-child(2) {
765
- animation-delay: 150ms;
766
- }
767
- .fd-ai-fm-thinking-dot:nth-child(3) {
768
- animation-delay: 300ms;
769
- }
770
-
771
- @keyframes fd-ai-fm-bounce {
772
- 0%,
773
- 80%,
774
- 100% {
775
- transform: scale(0.6);
776
- opacity: 0.4;
777
- }
778
- 40% {
779
- transform: scale(1);
780
- opacity: 1;
781
- }
782
- }
979
+ /* Full-modal now uses the shared .fd-ai-loader indicator */
783
980
 
784
981
  /* ─── Bottom input bar ───────────────────────────────────────────── */
785
982
 
package/styles/base.css CHANGED
@@ -390,8 +390,9 @@ figure.shiki:has(figcaption) figcaption {
390
390
 
391
391
  .fd-page-footer {
392
392
  display: flex;
393
+ flex-wrap: wrap;
393
394
  align-items: center;
394
- gap: 1rem;
395
+ gap: 0.75rem 1rem;
395
396
  margin-top: auto;
396
397
  padding-top: 1.5rem;
397
398
  border-top: 1px solid var(--color-fd-border, hsl(0 0% 80% / 50%));
@@ -409,6 +410,13 @@ figure.shiki:has(figcaption) figcaption {
409
410
  margin-left: auto;
410
411
  }
411
412
 
413
+ @media (max-width: 640px) {
414
+ .fd-last-updated-footer {
415
+ margin-left: 0;
416
+ width: 100%;
417
+ }
418
+ }
419
+
412
420
  .fd-llms-txt-links {
413
421
  display: inline-flex;
414
422
  align-items: center;
@@ -427,8 +435,8 @@ figure.shiki:has(figcaption) figcaption {
427
435
  }
428
436
 
429
437
  .fd-llms-txt-link:hover {
430
- /* color: var(--color-fd-foreground, hsl(0 0% 10%));
431
- border-color: var(--color-fd-foreground, hsl(0 0% 10%)); */
438
+ color: var(--color-fd-foreground, hsl(0 0% 10%));
439
+ border-color: var(--color-fd-foreground, hsl(0 0% 10%));
432
440
  text-decoration: none;
433
441
  }
434
442
 
@@ -408,7 +408,7 @@ article a[class*="text-fd-muted-foreground"] {
408
408
  border-radius: 2px;
409
409
  }
410
410
 
411
- .fd-ai-fm-thinking-dot {
411
+ .fd-ai-loader-typing-dot {
412
412
  border-radius: 2px;
413
413
  }
414
414
 
@@ -460,11 +460,17 @@ figure.shiki > div:first-child {
460
460
  .fd-page-action-menu {
461
461
  border-radius: 0 !important;
462
462
  box-shadow: 0 4px 24px hsl(0 0% 0% / 0.5);
463
+ padding: 0px !important;
464
+ box-shadow: 2px 2px 0 0 var(--color-fd-border, #262626);
463
465
  }
464
466
 
465
467
  .fd-page-action-menu-item {
466
468
  border-radius: 0 !important;
467
- font-size: 0.8rem;
469
+ font-size: 0.68rem;
470
+ border-top: 1px solid var(--color-fd-border, #262626);
471
+ text-transform: uppercase;
472
+ color: var(--color-fd-muted-foreground, hsl(0 0% 45%));
473
+ font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
468
474
  }
469
475
 
470
476
  /* ─── AI Chat (pixel-border — zero radius, pixel-art, monospace) ── */
@@ -556,13 +562,13 @@ figure.shiki > div:first-child {
556
562
  letter-spacing: 0.06em;
557
563
  }
558
564
 
559
- .fd-ai-loading-text {
565
+ .fd-ai-loader-shimmer-text {
560
566
  text-transform: uppercase;
561
567
  letter-spacing: 0.04em;
562
568
  font-size: 11px;
563
569
  }
564
570
 
565
- .fd-ai-loading-dot {
571
+ .fd-ai-loader-typing-dot {
566
572
  border-radius: 0;
567
573
  width: 4px;
568
574
  height: 4px;
@@ -640,11 +646,7 @@ figure.shiki > div:first-child {
640
646
  font-size: 11px;
641
647
  }
642
648
 
643
- .fd-ai-fm-thinking-dot {
644
- border-radius: 0;
645
- width: 5px;
646
- height: 5px;
647
- }
649
+ /* Full-modal now uses .fd-ai-loader-typing-dot (see above) */
648
650
 
649
651
  /* ─── Code blocks (pixel-border) ─────────────────────────────────── */
650
652