@farming-labs/astro-theme 0.0.3-beta.2 → 0.0.3-beta.3
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/DocsContent.astro +262 -2
- package/src/components/DocsLayout.astro +35 -14
- package/src/components/DocsPage.astro +50 -23
- package/src/components/FloatingAIChat.astro +25 -3
- package/src/components/SearchDialog.astro +345 -265
- package/styles/colorful.css +49 -3
- package/styles/darksharp.css +18 -0
- package/styles/docs.css +229 -17
- package/styles/greentree.css +237 -27
- package/styles/omni.css +359 -0
- package/styles/pixel-border.css +108 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/astro-theme",
|
|
3
|
-
"version": "0.0.3-beta.
|
|
3
|
+
"version": "0.0.3-beta.3",
|
|
4
4
|
"description": "Astro UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"sugar-high": "^0.9.5",
|
|
82
|
-
"@farming-labs/docs": "0.0.3-beta.
|
|
83
|
-
"@farming-labs/astro": "0.0.3-beta.
|
|
82
|
+
"@farming-labs/docs": "0.0.3-beta.3",
|
|
83
|
+
"@farming-labs/astro": "0.0.3-beta.3"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"astro": ">=4.0.0"
|
|
@@ -27,6 +27,78 @@ const llmsTxtEnabled = (() => {
|
|
|
27
27
|
if (typeof cfg === "object" && cfg !== null) return cfg.enabled !== false;
|
|
28
28
|
return false;
|
|
29
29
|
})();
|
|
30
|
+
|
|
31
|
+
const copyMarkdownEnabled = (() => {
|
|
32
|
+
const pa = config?.pageActions;
|
|
33
|
+
if (!pa) return false;
|
|
34
|
+
const cm = pa.copyMarkdown;
|
|
35
|
+
if (cm === true) return true;
|
|
36
|
+
if (typeof cm === "object" && cm !== null) return cm.enabled !== false;
|
|
37
|
+
return false;
|
|
38
|
+
})();
|
|
39
|
+
|
|
40
|
+
const openDocsEnabled = (() => {
|
|
41
|
+
const pa = config?.pageActions;
|
|
42
|
+
if (!pa) return false;
|
|
43
|
+
const od = pa.openDocs;
|
|
44
|
+
if (od === true) return true;
|
|
45
|
+
if (typeof od === "object" && od !== null) return od.enabled !== false;
|
|
46
|
+
return false;
|
|
47
|
+
})();
|
|
48
|
+
|
|
49
|
+
const DEFAULT_OPEN_PROVIDERS = [
|
|
50
|
+
{ name: "ChatGPT", urlTemplate: "https://chatgpt.com/?hints=search&q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
51
|
+
{ name: "Claude", urlTemplate: "https://claude.ai/new?q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const openDocsProviders = (() => {
|
|
55
|
+
const pa = config?.pageActions;
|
|
56
|
+
const od = pa && typeof pa === "object" && pa.openDocs != null ? pa.openDocs : null;
|
|
57
|
+
const list =
|
|
58
|
+
od && typeof od === "object" && "providers" in od
|
|
59
|
+
? (od as { providers?: Array<{ name?: string; urlTemplate?: string }> }).providers
|
|
60
|
+
: undefined;
|
|
61
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
62
|
+
const mapped = list
|
|
63
|
+
.map((p) => ({
|
|
64
|
+
name: typeof p?.name === "string" ? p.name : "Open",
|
|
65
|
+
urlTemplate: typeof (p as { urlTemplate?: string })?.urlTemplate === "string" ? (p as { urlTemplate: string }).urlTemplate : "",
|
|
66
|
+
}))
|
|
67
|
+
.filter((p) => p.urlTemplate.length > 0);
|
|
68
|
+
if (mapped.length > 0) return mapped;
|
|
69
|
+
}
|
|
70
|
+
return DEFAULT_OPEN_PROVIDERS;
|
|
71
|
+
})();
|
|
72
|
+
|
|
73
|
+
const pathname = Astro.url.pathname;
|
|
74
|
+
const githubFileUrl = config?.github && data.editOnGithub ? data.editOnGithub : null;
|
|
75
|
+
|
|
76
|
+
const pageActionsPosition = (() => {
|
|
77
|
+
const pa = config?.pageActions;
|
|
78
|
+
if (typeof pa === "object" && pa !== null && pa.position) return pa.position;
|
|
79
|
+
return "below-title";
|
|
80
|
+
})();
|
|
81
|
+
|
|
82
|
+
const pageActionsAlignment = (() => {
|
|
83
|
+
const pa = config?.pageActions;
|
|
84
|
+
if (typeof pa === "object" && pa !== null && pa.alignment) return pa.alignment;
|
|
85
|
+
return "left";
|
|
86
|
+
})();
|
|
87
|
+
|
|
88
|
+
const lastUpdatedConfig = (() => {
|
|
89
|
+
const lu = config?.lastUpdated;
|
|
90
|
+
if (lu === false) return { enabled: false, position: "footer" as const };
|
|
91
|
+
if (lu === true || lu === undefined) return { enabled: true, position: "footer" as const };
|
|
92
|
+
return {
|
|
93
|
+
enabled: (lu as { enabled?: boolean }).enabled !== false,
|
|
94
|
+
position: ((lu as { position?: "footer" | "below-title" }).position ?? "footer") as "footer" | "below-title",
|
|
95
|
+
};
|
|
96
|
+
})();
|
|
97
|
+
|
|
98
|
+
const showLastUpdatedInFooter = !!data.lastModified && lastUpdatedConfig.enabled && lastUpdatedConfig.position === "footer";
|
|
99
|
+
const showLastUpdatedBelowTitle = !!data.lastModified && lastUpdatedConfig.enabled && lastUpdatedConfig.position === "below-title";
|
|
100
|
+
|
|
101
|
+
const htmlWithoutFirstH1 = (data.html || "").replace(/<h1[^>]*>[\s\S]*?<\/h1>\s*/i, "");
|
|
30
102
|
---
|
|
31
103
|
|
|
32
104
|
<head>
|
|
@@ -42,9 +114,197 @@ const llmsTxtEnabled = (() => {
|
|
|
42
114
|
previousPage={data.previousPage}
|
|
43
115
|
nextPage={data.nextPage}
|
|
44
116
|
editOnGithub={showEditOnGithub ? data.editOnGithub : null}
|
|
45
|
-
lastModified={
|
|
117
|
+
lastModified={showLastUpdatedInFooter ? data.lastModified : null}
|
|
46
118
|
llmsTxtEnabled={llmsTxtEnabled}
|
|
47
119
|
>
|
|
120
|
+
{pageActionsPosition === "above-title" && (copyMarkdownEnabled || openDocsEnabled) && (
|
|
121
|
+
<div class="fd-page-actions" data-page-actions data-actions-alignment={pageActionsAlignment}>
|
|
122
|
+
{copyMarkdownEnabled && (
|
|
123
|
+
<>
|
|
124
|
+
{"rawMarkdown" in data && data.rawMarkdown != null && typeof data.rawMarkdown === "string" && (
|
|
125
|
+
<textarea id="fd-page-raw-markdown" hidden aria-hidden="true" readonly>{data.rawMarkdown}</textarea>
|
|
126
|
+
)}
|
|
127
|
+
<button type="button" class="fd-page-action-btn" data-copy-page aria-label="Copy page content">
|
|
128
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
129
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
130
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
131
|
+
</svg>
|
|
132
|
+
<span data-copy-label>Copy page</span>
|
|
133
|
+
</button>
|
|
134
|
+
</>
|
|
135
|
+
)}
|
|
136
|
+
{openDocsEnabled && openDocsProviders.length > 0 && (
|
|
137
|
+
<div class="fd-page-action-dropdown" data-open-dropdown>
|
|
138
|
+
<button type="button" class="fd-page-action-btn" aria-expanded="false" aria-haspopup="true" data-dropdown-trigger>
|
|
139
|
+
<span>Open in</span>
|
|
140
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
141
|
+
<polyline points="6 9 12 15 18 9" />
|
|
142
|
+
</svg>
|
|
143
|
+
</button>
|
|
144
|
+
<div class="fd-page-action-menu" role="menu" hidden data-dropdown-menu>
|
|
145
|
+
{openDocsProviders.map((provider) => (
|
|
146
|
+
<a
|
|
147
|
+
role="menuitem"
|
|
148
|
+
class="fd-page-action-menu-item"
|
|
149
|
+
href="#"
|
|
150
|
+
data-open-provider
|
|
151
|
+
data-name={provider.name}
|
|
152
|
+
data-url-template={provider.urlTemplate}
|
|
153
|
+
>
|
|
154
|
+
<span class="fd-page-action-menu-label">Open in {provider.name}</span>
|
|
155
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
156
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
157
|
+
<polyline points="15 3 21 3 21 9" />
|
|
158
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
159
|
+
</svg>
|
|
160
|
+
</a>
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
<h1 class="fd-page-title">{data.title}</h1>
|
|
48
168
|
{data.description && <p class="fd-page-description">{data.description}</p>}
|
|
49
|
-
|
|
169
|
+
{showLastUpdatedBelowTitle && data.lastModified && (
|
|
170
|
+
<p class="fd-last-modified fd-last-modified-below-title">Last updated: {data.lastModified}</p>
|
|
171
|
+
)}
|
|
172
|
+
{pageActionsPosition === "below-title" && (copyMarkdownEnabled || openDocsEnabled) && (
|
|
173
|
+
<>
|
|
174
|
+
<hr class="fd-page-actions-divider" aria-hidden="true" />
|
|
175
|
+
<div class="fd-page-actions" data-page-actions data-actions-alignment={pageActionsAlignment}>
|
|
176
|
+
{copyMarkdownEnabled && (
|
|
177
|
+
<>
|
|
178
|
+
{"rawMarkdown" in data && data.rawMarkdown != null && typeof data.rawMarkdown === "string" && (
|
|
179
|
+
<textarea id="fd-page-raw-markdown" hidden aria-hidden="true" readonly>{data.rawMarkdown}</textarea>
|
|
180
|
+
)}
|
|
181
|
+
<button type="button" class="fd-page-action-btn" data-copy-page aria-label="Copy page content">
|
|
182
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
183
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
184
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
185
|
+
</svg>
|
|
186
|
+
<span data-copy-label>Copy page</span>
|
|
187
|
+
</button>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
{openDocsEnabled && openDocsProviders.length > 0 && (
|
|
191
|
+
<div class="fd-page-action-dropdown" data-open-dropdown>
|
|
192
|
+
<button type="button" class="fd-page-action-btn" aria-expanded="false" aria-haspopup="true" data-dropdown-trigger>
|
|
193
|
+
<span>Open in</span>
|
|
194
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
195
|
+
<polyline points="6 9 12 15 18 9" />
|
|
196
|
+
</svg>
|
|
197
|
+
</button>
|
|
198
|
+
<div class="fd-page-action-menu" role="menu" hidden data-dropdown-menu>
|
|
199
|
+
{openDocsProviders.map((provider) => (
|
|
200
|
+
<a
|
|
201
|
+
role="menuitem"
|
|
202
|
+
class="fd-page-action-menu-item"
|
|
203
|
+
href="#"
|
|
204
|
+
data-open-provider
|
|
205
|
+
data-name={provider.name}
|
|
206
|
+
data-url-template={provider.urlTemplate}
|
|
207
|
+
>
|
|
208
|
+
<span class="fd-page-action-menu-label">Open in {provider.name}</span>
|
|
209
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
210
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
211
|
+
<polyline points="15 3 21 3 21 9" />
|
|
212
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
213
|
+
</svg>
|
|
214
|
+
</a>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
</>
|
|
221
|
+
)}
|
|
222
|
+
<Fragment set:html={htmlWithoutFirstH1} />
|
|
50
223
|
</DocsPage>
|
|
224
|
+
|
|
225
|
+
{(copyMarkdownEnabled || openDocsEnabled) && (
|
|
226
|
+
<script define:vars={{ pathname, openDocsProviders, githubFileUrl }}>
|
|
227
|
+
(function () {
|
|
228
|
+
var pathnameVal = typeof pathname === "string" ? pathname : "";
|
|
229
|
+
var providers = Array.isArray(openDocsProviders) ? openDocsProviders : [];
|
|
230
|
+
var githubFileUrlVal = githubFileUrl || "";
|
|
231
|
+
|
|
232
|
+
function init() {
|
|
233
|
+
document.querySelectorAll("[data-copy-page]").forEach(function (btn) {
|
|
234
|
+
btn.addEventListener("click", function () {
|
|
235
|
+
var text = "";
|
|
236
|
+
var rawEl = document.getElementById("fd-page-raw-markdown");
|
|
237
|
+
if (rawEl && rawEl.value && rawEl.value.length > 0) {
|
|
238
|
+
text = rawEl.value;
|
|
239
|
+
} else {
|
|
240
|
+
var article = document.querySelector("#nd-page");
|
|
241
|
+
if (article) text = article.innerText || "";
|
|
242
|
+
}
|
|
243
|
+
if (!text) return;
|
|
244
|
+
var label = btn.querySelector("[data-copy-label]");
|
|
245
|
+
navigator.clipboard.writeText(text).then(
|
|
246
|
+
function () {
|
|
247
|
+
if (label) label.textContent = "Copied!";
|
|
248
|
+
btn.setAttribute("data-copied", "true");
|
|
249
|
+
setTimeout(function () {
|
|
250
|
+
if (label) label.textContent = "Copy page";
|
|
251
|
+
btn.removeAttribute("data-copied");
|
|
252
|
+
}, 2000);
|
|
253
|
+
},
|
|
254
|
+
function () {
|
|
255
|
+
if (label) label.textContent = "Copy failed";
|
|
256
|
+
setTimeout(function () {
|
|
257
|
+
if (label) label.textContent = "Copy page";
|
|
258
|
+
}, 2000);
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
document.querySelectorAll("[data-dropdown-trigger]").forEach(function (trigger) {
|
|
265
|
+
var dropdown = trigger.closest("[data-open-dropdown]");
|
|
266
|
+
var menu = dropdown ? dropdown.querySelector("[data-dropdown-menu]") : null;
|
|
267
|
+
if (!menu) return;
|
|
268
|
+
|
|
269
|
+
trigger.addEventListener("click", function (e) {
|
|
270
|
+
e.preventDefault();
|
|
271
|
+
var open = menu.hidden;
|
|
272
|
+
menu.hidden = !open;
|
|
273
|
+
trigger.setAttribute("aria-expanded", String(!open));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
document.addEventListener("click", function (e) {
|
|
277
|
+
if (!menu.hidden && dropdown && !dropdown.contains(e.target)) {
|
|
278
|
+
menu.hidden = true;
|
|
279
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
document.querySelectorAll("[data-open-provider]").forEach(function (el) {
|
|
285
|
+
el.addEventListener("click", function (e) {
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
var urlTemplate = el.getAttribute("data-url-template") || "";
|
|
288
|
+
var pageUrl = window.location.href;
|
|
289
|
+
var mdxUrl = window.location.origin + pathnameVal + (pathnameVal.endsWith("/") ? "page.mdx" : ".mdx");
|
|
290
|
+
var url = urlTemplate
|
|
291
|
+
.replace(/\{url\}/g, encodeURIComponent(pageUrl))
|
|
292
|
+
.replace(/\{mdxUrl\}/g, encodeURIComponent(mdxUrl))
|
|
293
|
+
.replace(/\{githubUrl\}/g, githubFileUrlVal);
|
|
294
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
295
|
+
var menu = el.closest("[data-dropdown-menu]");
|
|
296
|
+
var triggerEl = document.querySelector("[data-dropdown-trigger]");
|
|
297
|
+
if (menu) menu.hidden = true;
|
|
298
|
+
if (triggerEl) triggerEl.setAttribute("aria-expanded", "false");
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (document.readyState === "loading") {
|
|
304
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
305
|
+
} else {
|
|
306
|
+
init();
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
</script>
|
|
310
|
+
)}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import ThemeToggle from "./ThemeToggle.astro";
|
|
3
3
|
import FloatingAIChat from "./FloatingAIChat.astro";
|
|
4
4
|
|
|
5
|
-
const { tree, config = null, title, titleUrl } = Astro.props;
|
|
5
|
+
const { tree, config = null, title, titleUrl, flatPages = null } = Astro.props;
|
|
6
|
+
|
|
7
|
+
const sidebarFlat = !!(config?.sidebar && typeof config.sidebar === "object" && (config.sidebar as { flat?: boolean }).flat);
|
|
8
|
+
const useFlatSidebar = sidebarFlat && Array.isArray(flatPages) && flatPages.length > 0;
|
|
6
9
|
|
|
7
10
|
const resolvedTitle = title ?? config?.nav?.title ?? "Docs";
|
|
8
11
|
const resolvedTitleUrl = titleUrl ?? config?.nav?.url ?? "/docs";
|
|
@@ -192,6 +195,22 @@ const showFloatingAI = aiConfig?.mode === "floating" && aiConfig?.enabled;
|
|
|
192
195
|
<nav class="fd-sidebar-nav">
|
|
193
196
|
<slot name="sidebar" />
|
|
194
197
|
</nav>
|
|
198
|
+
) : useFlatSidebar ? (
|
|
199
|
+
<nav class="fd-sidebar-nav">
|
|
200
|
+
{flatPages.map((page, i) => {
|
|
201
|
+
const icon = getIcon(page.icon);
|
|
202
|
+
return (
|
|
203
|
+
<a
|
|
204
|
+
href={page.url}
|
|
205
|
+
class={`fd-sidebar-link fd-sidebar-top-link ${isActive(page.url) ? 'fd-sidebar-link-active' : ''} ${i === 0 ? 'fd-sidebar-first-item' : ''}`}
|
|
206
|
+
data-active={isActive(page.url) || undefined}
|
|
207
|
+
>
|
|
208
|
+
{icon && <span class="fd-sidebar-icon" set:html={icon} />}
|
|
209
|
+
{page.name}
|
|
210
|
+
</a>
|
|
211
|
+
);
|
|
212
|
+
})}
|
|
213
|
+
</nav>
|
|
195
214
|
) : (
|
|
196
215
|
<nav class="fd-sidebar-nav">
|
|
197
216
|
{tree?.children?.map((node, i) => {
|
|
@@ -334,29 +353,31 @@ const showFloatingAI = aiConfig?.mode === "floating" && aiConfig?.enabled;
|
|
|
334
353
|
overlay!.style.display = 'none';
|
|
335
354
|
});
|
|
336
355
|
|
|
337
|
-
// Search Cmd+K
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
// Search Cmd+K — lock body scroll when open, always restore on close
|
|
357
|
+
function setSearchOpen(open: boolean) {
|
|
358
|
+
const dialog = document.getElementById('fd-search-dialog');
|
|
359
|
+
if (dialog) {
|
|
360
|
+
dialog.style.display = open ? 'flex' : 'none';
|
|
361
|
+
document.body.style.overflow = open ? 'hidden' : '';
|
|
343
362
|
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
document.addEventListener('keydown', (e) => {
|
|
344
366
|
if (e.key === 'Escape') {
|
|
345
367
|
sidebar?.classList.remove('fd-sidebar-open');
|
|
346
368
|
overlay!.style.display = 'none';
|
|
347
|
-
|
|
348
|
-
|
|
369
|
+
setSearchOpen(false);
|
|
370
|
+
document.body.style.overflow = '';
|
|
349
371
|
}
|
|
350
372
|
});
|
|
351
373
|
|
|
352
374
|
document.getElementById('fd-search-open')?.addEventListener('click', () => {
|
|
353
|
-
|
|
354
|
-
|
|
375
|
+
if (typeof (window as any).__fdOmniSearchSetOpen === 'function') (window as any).__fdOmniSearchSetOpen(true);
|
|
376
|
+
else setSearchOpen(true);
|
|
355
377
|
});
|
|
356
|
-
|
|
357
378
|
document.getElementById('fd-search-open-mobile')?.addEventListener('click', () => {
|
|
358
|
-
|
|
359
|
-
|
|
379
|
+
if (typeof (window as any).__fdOmniSearchSetOpen === 'function') (window as any).__fdOmniSearchSetOpen(true);
|
|
380
|
+
else setSearchOpen(true);
|
|
360
381
|
});
|
|
361
382
|
|
|
362
383
|
// Close sidebar on link click (mobile)
|
|
@@ -35,7 +35,9 @@ const parentUrl = segments.length >= 2
|
|
|
35
35
|
)}
|
|
36
36
|
|
|
37
37
|
<div class="fd-page-body">
|
|
38
|
-
<
|
|
38
|
+
<div class="fd-docs-content">
|
|
39
|
+
<slot />
|
|
40
|
+
</div>
|
|
39
41
|
</div>
|
|
40
42
|
|
|
41
43
|
<footer class="fd-page-footer">
|
|
@@ -92,7 +94,7 @@ const parentUrl = segments.length >= 2
|
|
|
92
94
|
</article>
|
|
93
95
|
|
|
94
96
|
{tocEnabled && (
|
|
95
|
-
<aside class="fd-toc">
|
|
97
|
+
<aside class="fd-toc" id="fd-toc" data-toc-ready="false">
|
|
96
98
|
<div class={`fd-toc-inner ${tocStyle === "directional" ? "fd-toc-directional" : ""}`} data-toc-style={tocStyle}>
|
|
97
99
|
<h3 class="fd-toc-title">
|
|
98
100
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
@@ -122,9 +124,9 @@ const parentUrl = segments.length >= 2
|
|
|
122
124
|
function initDocsPage() {
|
|
123
125
|
const container = document.querySelector('.fd-page-body');
|
|
124
126
|
const tocList = document.getElementById('fd-toc-list');
|
|
127
|
+
const tocAside = document.getElementById('fd-toc');
|
|
125
128
|
if (container && tocList) {
|
|
126
129
|
const headings = container.querySelectorAll('h2[id], h3[id], h4[id]');
|
|
127
|
-
tocList.innerHTML = '';
|
|
128
130
|
const isClerk = tocList.closest('[data-toc-style]')?.getAttribute('data-toc-style') === 'directional';
|
|
129
131
|
|
|
130
132
|
const tocItems = [];
|
|
@@ -136,6 +138,7 @@ const parentUrl = segments.length >= 2
|
|
|
136
138
|
});
|
|
137
139
|
});
|
|
138
140
|
|
|
141
|
+
const fragment = document.createDocumentFragment();
|
|
139
142
|
tocItems.forEach((item, index) => {
|
|
140
143
|
const li = document.createElement('li');
|
|
141
144
|
li.className = 'fd-toc-item';
|
|
@@ -189,9 +192,11 @@ const parentUrl = segments.length >= 2
|
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
li.appendChild(a);
|
|
192
|
-
|
|
195
|
+
fragment.appendChild(li);
|
|
193
196
|
});
|
|
194
197
|
|
|
198
|
+
tocList.replaceChildren(fragment);
|
|
199
|
+
|
|
195
200
|
let maskEl = null;
|
|
196
201
|
let thumbEl = null;
|
|
197
202
|
|
|
@@ -275,6 +280,31 @@ const parentUrl = segments.length >= 2
|
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
const activeSet = new Set();
|
|
283
|
+
let tocUpdateScheduled = false;
|
|
284
|
+
|
|
285
|
+
function scheduleTocActiveUpdate() {
|
|
286
|
+
if (tocUpdateScheduled) return;
|
|
287
|
+
tocUpdateScheduled = true;
|
|
288
|
+
requestAnimationFrame(() => {
|
|
289
|
+
tocUpdateScheduled = false;
|
|
290
|
+
if (isClerk) {
|
|
291
|
+
tocList.querySelectorAll('.fd-toc-clerk-link').forEach(link => {
|
|
292
|
+
const id = link.getAttribute('href')?.slice(1);
|
|
293
|
+
if (activeSet.has(id)) {
|
|
294
|
+
link.setAttribute('data-active', 'true');
|
|
295
|
+
} else {
|
|
296
|
+
link.removeAttribute('data-active');
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
if (typeof updateThumb === 'function') updateThumb(activeSet);
|
|
300
|
+
} else {
|
|
301
|
+
tocList.querySelectorAll('.fd-toc-link').forEach(link => {
|
|
302
|
+
const id = link.getAttribute('href')?.slice(1);
|
|
303
|
+
link.classList.toggle('fd-toc-link-active', activeSet.has(id));
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
278
308
|
|
|
279
309
|
const observer = new IntersectionObserver((entries) => {
|
|
280
310
|
for (const entry of entries) {
|
|
@@ -284,26 +314,12 @@ const parentUrl = segments.length >= 2
|
|
|
284
314
|
activeSet.delete(entry.target.id);
|
|
285
315
|
}
|
|
286
316
|
}
|
|
287
|
-
|
|
288
|
-
if (isClerk) {
|
|
289
|
-
tocList.querySelectorAll('.fd-toc-clerk-link').forEach(link => {
|
|
290
|
-
const id = link.getAttribute('href')?.slice(1);
|
|
291
|
-
if (activeSet.has(id)) {
|
|
292
|
-
link.setAttribute('data-active', 'true');
|
|
293
|
-
} else {
|
|
294
|
-
link.removeAttribute('data-active');
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
if (typeof updateThumb === 'function') updateThumb(activeSet);
|
|
298
|
-
} else {
|
|
299
|
-
tocList.querySelectorAll('.fd-toc-link').forEach(link => {
|
|
300
|
-
const id = link.getAttribute('href')?.slice(1);
|
|
301
|
-
link.classList.toggle('fd-toc-link-active', activeSet.has(id));
|
|
302
|
-
});
|
|
303
|
-
}
|
|
317
|
+
scheduleTocActiveUpdate();
|
|
304
318
|
}, { rootMargin: '-80px 0px -80% 0px' });
|
|
305
319
|
|
|
306
320
|
headings.forEach(el => observer.observe(el));
|
|
321
|
+
|
|
322
|
+
if (tocAside) tocAside.setAttribute('data-toc-ready', 'true');
|
|
307
323
|
}
|
|
308
324
|
|
|
309
325
|
// Copy buttons
|
|
@@ -340,6 +356,17 @@ const parentUrl = segments.length >= 2
|
|
|
340
356
|
});
|
|
341
357
|
}
|
|
342
358
|
|
|
343
|
-
|
|
344
|
-
|
|
359
|
+
function runTocWhenIdle() {
|
|
360
|
+
if (typeof requestIdleCallback !== 'undefined') {
|
|
361
|
+
requestIdleCallback(() => initDocsPage(), { timeout: 300 });
|
|
362
|
+
} else {
|
|
363
|
+
setTimeout(initDocsPage, 0);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (document.readyState === 'loading') {
|
|
367
|
+
document.addEventListener('DOMContentLoaded', runTocWhenIdle);
|
|
368
|
+
} else {
|
|
369
|
+
runTocWhenIdle();
|
|
370
|
+
}
|
|
371
|
+
document.addEventListener('astro:after-swap', () => setTimeout(initDocsPage, 0));
|
|
345
372
|
</script>
|
|
@@ -37,6 +37,7 @@ function getContainerStyle(style: string, pos: string) {
|
|
|
37
37
|
|
|
38
38
|
const btnStyle = BTN_POSITIONS[position] || BTN_POSITIONS["bottom-right"];
|
|
39
39
|
const containerStyle = getContainerStyle(floatingStyle, position);
|
|
40
|
+
const dialogAnimation = floatingStyle === "modal" ? "fd-ai-modal-in" : "fd-ai-float-in";
|
|
40
41
|
---
|
|
41
42
|
|
|
42
43
|
<div id="fd-float-config" data-api={api} data-ai-label={aiLabel} data-full-modal={isFullModal ? "true" : "false"} style="display:none"></div>
|
|
@@ -114,7 +115,7 @@ const containerStyle = getContainerStyle(floatingStyle, position);
|
|
|
114
115
|
)}
|
|
115
116
|
|
|
116
117
|
<!-- Panel/modal/popover container -->
|
|
117
|
-
<div class="fd-ai-dialog" id="fd-float-dialog" style={`display:none;${containerStyle};animation
|
|
118
|
+
<div class="fd-ai-dialog" id="fd-float-dialog" style={`display:none;${containerStyle};animation:${dialogAnimation} 200ms ease-out`}>
|
|
118
119
|
<div class="fd-ai-header">
|
|
119
120
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
120
121
|
<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"/>
|
|
@@ -278,6 +279,7 @@ const containerStyle = getContainerStyle(floatingStyle, position);
|
|
|
278
279
|
let isStreaming = false;
|
|
279
280
|
let isOpen = false;
|
|
280
281
|
let renderScheduled = false;
|
|
282
|
+
let streamRenderThrottle: ReturnType<typeof setTimeout> | null = null;
|
|
281
283
|
|
|
282
284
|
const overlay = document.getElementById('fd-float-overlay');
|
|
283
285
|
const bar = document.getElementById('fd-float-bar');
|
|
@@ -309,18 +311,19 @@ const containerStyle = getContainerStyle(floatingStyle, position);
|
|
|
309
311
|
if (floatDialog) floatDialog.style.display = '';
|
|
310
312
|
if (floatBtn) floatBtn.style.display = 'none';
|
|
311
313
|
if (modalBg) modalBg.style.display = '';
|
|
314
|
+
document.body.style.overflow = 'hidden';
|
|
312
315
|
setTimeout(() => input?.focus(), 100);
|
|
313
316
|
}
|
|
314
317
|
}
|
|
315
318
|
|
|
316
319
|
function closeChat() {
|
|
317
320
|
isOpen = false;
|
|
321
|
+
document.body.style.overflow = '';
|
|
318
322
|
if (isFullModal) {
|
|
319
323
|
if (overlay) overlay.style.display = 'none';
|
|
320
324
|
if (bar) { bar.classList.remove('fd-ai-fm-input-bar--open'); bar.classList.add('fd-ai-fm-input-bar--closed'); bar.style.cssText = bar.dataset.btnStyle || ''; }
|
|
321
325
|
if (trigger) trigger.style.display = '';
|
|
322
326
|
if (inputContainer) inputContainer.style.display = 'none';
|
|
323
|
-
document.body.style.overflow = '';
|
|
324
327
|
} else {
|
|
325
328
|
if (floatDialog) floatDialog.style.display = 'none';
|
|
326
329
|
if (floatBtn) floatBtn.style.display = '';
|
|
@@ -356,6 +359,20 @@ const containerStyle = getContainerStyle(floatingStyle, position);
|
|
|
356
359
|
if (footerHint) footerHint.style.display = 'none';
|
|
357
360
|
if (clearWrap) clearWrap.style.display = 'flex';
|
|
358
361
|
|
|
362
|
+
const lastMsg = messages[messages.length - 1];
|
|
363
|
+
const isStreamingLast = isStreaming && lastMsg.role === 'assistant' && lastMsg.content;
|
|
364
|
+
const wrappers = messagesEl.querySelectorAll(isFullModal ? '.fd-ai-fm-msg' : '.fd-ai-msg');
|
|
365
|
+
if (isStreamingLast && wrappers.length === messages.length) {
|
|
366
|
+
const lastWrapper = wrappers[wrappers.length - 1];
|
|
367
|
+
const contentDiv = lastWrapper.querySelector(isFullModal ? '.fd-ai-fm-msg-content' : '.fd-ai-bubble-ai');
|
|
368
|
+
if (contentDiv) {
|
|
369
|
+
contentDiv.innerHTML = `<div class="fd-ai-streaming">${renderMarkdown(lastMsg.content)}</div>`;
|
|
370
|
+
if (isFullModal) messagesEl.scrollTo({ top: messagesEl.scrollHeight, behavior: 'auto' });
|
|
371
|
+
else messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
359
376
|
const thinking = thinkingHtml(aiLabel);
|
|
360
377
|
messagesEl.innerHTML = messages.map((m, i) => {
|
|
361
378
|
const label = m.role === 'user' ? 'You' : aiLabel;
|
|
@@ -420,7 +437,12 @@ const containerStyle = getContainerStyle(floatingStyle, position);
|
|
|
420
437
|
if (content) {
|
|
421
438
|
assistantContent += content;
|
|
422
439
|
messages[messages.length - 1].content = assistantContent;
|
|
423
|
-
|
|
440
|
+
if (!streamRenderThrottle) {
|
|
441
|
+
streamRenderThrottle = setTimeout(() => {
|
|
442
|
+
streamRenderThrottle = null;
|
|
443
|
+
doRender();
|
|
444
|
+
}, 80);
|
|
445
|
+
}
|
|
424
446
|
}
|
|
425
447
|
} catch {}
|
|
426
448
|
}
|