@farming-labs/nuxt-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 +2 -2
- package/src/components/DocsContent.vue +289 -10
- package/src/components/DocsLayout.vue +1 -0
- package/src/components/DocsPage.vue +4 -1
- package/src/components/FloatingAIChat.vue +2 -2
- package/src/components/SearchDialog.vue +341 -62
- package/src/components/TableOfContents.vue +98 -54
- package/src/themes/colorful.js +1 -1
- package/src/themes/default.js +1 -1
- package/styles/colorful.css +49 -3
- package/styles/darksharp.css +18 -0
- package/styles/docs.css +202 -12
- package/styles/greentree.css +237 -27
- package/styles/omni.css +292 -0
- package/styles/pixel-border-bundle.css +2 -2
- package/styles/pixel-border.css +108 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/nuxt-theme",
|
|
3
|
-
"version": "0.0.3-beta.
|
|
3
|
+
"version": "0.0.3-beta.3",
|
|
4
4
|
"description": "Nuxt/Vue UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"sugar-high": "^0.9.5",
|
|
63
|
-
"@farming-labs/docs": "0.0.3-beta.
|
|
63
|
+
"@farming-labs/docs": "0.0.3-beta.3"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"nuxt": ">=3.0.0",
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
3
3
|
import DocsPage from "./DocsPage.vue";
|
|
4
4
|
|
|
5
|
+
const DEFAULT_OPEN_PROVIDERS = [
|
|
6
|
+
{ name: "ChatGPT", urlTemplate: "https://chatgpt.com/?hints=search&q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
7
|
+
{ name: "Claude", urlTemplate: "https://claude.ai/new?q=Read+{mdxUrl},+I+want+to+ask+questions+about+it." },
|
|
8
|
+
];
|
|
9
|
+
|
|
5
10
|
const props = defineProps<{
|
|
6
11
|
data: {
|
|
7
12
|
title: string;
|
|
8
13
|
description?: string;
|
|
9
14
|
html: string;
|
|
15
|
+
rawMarkdown?: string;
|
|
10
16
|
previousPage?: { name: string; url: string } | null;
|
|
11
17
|
nextPage?: { name: string; url: string } | null;
|
|
12
18
|
editOnGithub?: string;
|
|
@@ -15,15 +21,25 @@ const props = defineProps<{
|
|
|
15
21
|
config?: Record<string, unknown> | null;
|
|
16
22
|
}>();
|
|
17
23
|
|
|
24
|
+
const route = useRoute();
|
|
25
|
+
const openDropdownMenu = ref(false);
|
|
26
|
+
const copyLabel = ref("Copy page");
|
|
27
|
+
const copied = ref(false);
|
|
28
|
+
|
|
18
29
|
const titleSuffix = computed(() =>
|
|
19
30
|
props.config?.metadata?.titleTemplate
|
|
20
|
-
? String(props.config.metadata.titleTemplate).replace("%s", "")
|
|
31
|
+
? String((props.config.metadata as Record<string, string>).titleTemplate).replace("%s", "")
|
|
21
32
|
: " – Docs",
|
|
22
33
|
);
|
|
23
34
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
35
|
+
const themeUi = computed(() => (props.config?.theme as Record<string, unknown>)?.ui as Record<string, unknown> | undefined);
|
|
36
|
+
const layout = computed(() => themeUi.value?.layout as Record<string, unknown> | undefined);
|
|
37
|
+
const tocConfig = computed(() => layout.value?.toc as Record<string, unknown> | undefined);
|
|
38
|
+
const tocEnabledVal = computed(() => tocConfig.value?.enabled ?? true);
|
|
39
|
+
const tocStyleVal = computed(() => {
|
|
40
|
+
const style = tocConfig.value?.style as string | undefined;
|
|
41
|
+
return style === "directional" ? "directional" : "default";
|
|
42
|
+
});
|
|
27
43
|
|
|
28
44
|
const breadcrumbEnabled = computed(() => {
|
|
29
45
|
const bc = props.config?.breadcrumb;
|
|
@@ -45,30 +61,293 @@ const llmsTxtEnabled = computed(() => {
|
|
|
45
61
|
|
|
46
62
|
const entry = computed(() => (props.config?.entry as string) ?? "docs");
|
|
47
63
|
|
|
64
|
+
const copyMarkdownEnabled = computed(() => {
|
|
65
|
+
const pa = props.config?.pageActions as Record<string, unknown> | undefined;
|
|
66
|
+
if (!pa) return false;
|
|
67
|
+
const cm = pa.copyMarkdown;
|
|
68
|
+
if (cm === true) return true;
|
|
69
|
+
if (typeof cm === "object" && cm !== null) return (cm as { enabled?: boolean }).enabled !== false;
|
|
70
|
+
return false;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const openDocsEnabled = computed(() => {
|
|
74
|
+
const pa = props.config?.pageActions as Record<string, unknown> | undefined;
|
|
75
|
+
if (!pa) return false;
|
|
76
|
+
const od = pa.openDocs;
|
|
77
|
+
if (od === true) return true;
|
|
78
|
+
if (typeof od === "object" && od !== null) return (od as { enabled?: boolean }).enabled !== false;
|
|
79
|
+
return false;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const openDocsProviders = computed(() => {
|
|
83
|
+
const pa = props.config?.pageActions as Record<string, unknown> | undefined;
|
|
84
|
+
const od = pa && typeof pa === "object" && pa.openDocs != null ? pa.openDocs : null;
|
|
85
|
+
const list = od && typeof od === "object" && "providers" in od
|
|
86
|
+
? (od as { providers?: Array<{ name?: string; urlTemplate?: string }> }).providers
|
|
87
|
+
: undefined;
|
|
88
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
89
|
+
const mapped = list
|
|
90
|
+
.map((p) => ({
|
|
91
|
+
name: typeof p?.name === "string" ? p.name : "Open",
|
|
92
|
+
urlTemplate: typeof p?.urlTemplate === "string" ? p.urlTemplate : "",
|
|
93
|
+
}))
|
|
94
|
+
.filter((p) => p.urlTemplate.length > 0);
|
|
95
|
+
if (mapped.length > 0) return mapped;
|
|
96
|
+
}
|
|
97
|
+
return DEFAULT_OPEN_PROVIDERS;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const pageActionsPosition = computed(() => {
|
|
101
|
+
const pa = props.config?.pageActions as Record<string, unknown> | undefined;
|
|
102
|
+
if (typeof pa === "object" && pa !== null && pa.position) return pa.position as string;
|
|
103
|
+
return "below-title";
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const pageActionsAlignment = computed(() => {
|
|
107
|
+
const pa = props.config?.pageActions as Record<string, unknown> | undefined;
|
|
108
|
+
if (typeof pa === "object" && pa !== null && pa.alignment) return pa.alignment as string;
|
|
109
|
+
return "left";
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const lastUpdatedConfig = computed(() => {
|
|
113
|
+
const lu = props.config?.lastUpdated;
|
|
114
|
+
if (lu === false) return { enabled: false, position: "footer" as const };
|
|
115
|
+
if (lu === true || lu === undefined) return { enabled: true, position: "footer" as const };
|
|
116
|
+
const o = lu as { enabled?: boolean; position?: "footer" | "below-title" };
|
|
117
|
+
return {
|
|
118
|
+
enabled: o.enabled !== false,
|
|
119
|
+
position: (o.position ?? "footer") as "footer" | "below-title",
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const showLastUpdatedInFooter = computed(
|
|
124
|
+
() => !!props.data.lastModified && lastUpdatedConfig.value.enabled && lastUpdatedConfig.value.position === "footer",
|
|
125
|
+
);
|
|
126
|
+
const showLastUpdatedBelowTitle = computed(
|
|
127
|
+
() => !!props.data.lastModified && lastUpdatedConfig.value.enabled && lastUpdatedConfig.value.position === "below-title",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const htmlWithoutFirstH1 = computed(() => {
|
|
131
|
+
const html = props.data.html || "";
|
|
132
|
+
return html.replace(/<h1[^>]*>[\s\S]*?<\/h1>\s*/i, "");
|
|
133
|
+
});
|
|
134
|
+
|
|
48
135
|
const metaDescription = computed(
|
|
49
|
-
() => props.data.description ?? (props.config?.metadata as
|
|
136
|
+
() => props.data.description ?? (props.config?.metadata as Record<string, string>)?.description ?? undefined,
|
|
50
137
|
);
|
|
51
138
|
|
|
139
|
+
const showPageActions = computed(
|
|
140
|
+
() => (copyMarkdownEnabled.value || openDocsEnabled.value) && openDocsProviders.value.length >= 0,
|
|
141
|
+
);
|
|
142
|
+
const showActionsAbove = computed(() => pageActionsPosition.value === "above-title" && showPageActions.value);
|
|
143
|
+
const showActionsBelow = computed(() => pageActionsPosition.value === "below-title" && showPageActions.value);
|
|
144
|
+
|
|
52
145
|
useHead({
|
|
53
146
|
title: () => `${props.data.title}${titleSuffix.value}`,
|
|
54
147
|
meta: () =>
|
|
55
148
|
metaDescription.value ? [{ name: "description", content: metaDescription.value }] : [],
|
|
56
149
|
});
|
|
150
|
+
|
|
151
|
+
function handleCopyPage() {
|
|
152
|
+
let text = "";
|
|
153
|
+
const raw = props.data.rawMarkdown;
|
|
154
|
+
if (raw && typeof raw === "string" && raw.length > 0) {
|
|
155
|
+
text = raw;
|
|
156
|
+
} else {
|
|
157
|
+
const article = document.querySelector("#nd-page");
|
|
158
|
+
if (article) text = (article as HTMLElement).innerText || "";
|
|
159
|
+
}
|
|
160
|
+
if (!text) return;
|
|
161
|
+
navigator.clipboard.writeText(text).then(
|
|
162
|
+
() => {
|
|
163
|
+
copyLabel.value = "Copied!";
|
|
164
|
+
copied.value = true;
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
copyLabel.value = "Copy page";
|
|
167
|
+
copied.value = false;
|
|
168
|
+
}, 2000);
|
|
169
|
+
},
|
|
170
|
+
() => {
|
|
171
|
+
copyLabel.value = "Copy failed";
|
|
172
|
+
setTimeout(() => { copyLabel.value = "Copy page"; }, 2000);
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function toggleDropdown() {
|
|
178
|
+
openDropdownMenu.value = !openDropdownMenu.value;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function closeDropdown() {
|
|
182
|
+
openDropdownMenu.value = false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function openInProvider(provider: { name: string; urlTemplate: string }) {
|
|
186
|
+
const pathname = route.path;
|
|
187
|
+
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
188
|
+
const mdxUrl = typeof window !== "undefined"
|
|
189
|
+
? window.location.origin + pathname + (pathname.endsWith("/") ? "page.mdx" : ".mdx")
|
|
190
|
+
: "";
|
|
191
|
+
const githubUrl = props.data.editOnGithub || "";
|
|
192
|
+
const url = provider.urlTemplate
|
|
193
|
+
.replace(/\{url\}/g, encodeURIComponent(pageUrl))
|
|
194
|
+
.replace(/\{mdxUrl\}/g, encodeURIComponent(mdxUrl))
|
|
195
|
+
.replace(/\{githubUrl\}/g, githubUrl);
|
|
196
|
+
if (typeof window !== "undefined") window.open(url, "_blank", "noopener,noreferrer");
|
|
197
|
+
closeDropdown();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function handleClickOutside(e: MouseEvent) {
|
|
201
|
+
const target = e.target as Node;
|
|
202
|
+
if (openDropdownMenu.value && !(target as Element).closest?.(".fd-page-action-dropdown")) {
|
|
203
|
+
closeDropdown();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
onMounted(() => {
|
|
208
|
+
document.addEventListener("click", handleClickOutside);
|
|
209
|
+
});
|
|
210
|
+
onUnmounted(() => {
|
|
211
|
+
document.removeEventListener("click", handleClickOutside);
|
|
212
|
+
});
|
|
57
213
|
</script>
|
|
58
214
|
|
|
59
215
|
<template>
|
|
60
216
|
<DocsPage
|
|
61
217
|
:entry="entry"
|
|
62
|
-
:toc-enabled="
|
|
63
|
-
:toc-style="
|
|
218
|
+
:toc-enabled="tocEnabledVal"
|
|
219
|
+
:toc-style="tocStyleVal"
|
|
64
220
|
:breadcrumb-enabled="breadcrumbEnabled"
|
|
65
221
|
:previous-page="data.previousPage ?? null"
|
|
66
222
|
:next-page="data.nextPage ?? null"
|
|
67
223
|
:edit-on-github="showEditOnGithub ? data.editOnGithub : null"
|
|
68
|
-
:last-modified="
|
|
224
|
+
:last-modified="showLastUpdatedInFooter ? data.lastModified : null"
|
|
69
225
|
:llms-txt-enabled="llmsTxtEnabled"
|
|
70
226
|
>
|
|
227
|
+
<!-- Above-title actions -->
|
|
228
|
+
<div
|
|
229
|
+
v-if="showActionsAbove"
|
|
230
|
+
class="fd-page-actions"
|
|
231
|
+
data-page-actions
|
|
232
|
+
:data-actions-alignment="pageActionsAlignment"
|
|
233
|
+
>
|
|
234
|
+
<button
|
|
235
|
+
v-if="copyMarkdownEnabled"
|
|
236
|
+
type="button"
|
|
237
|
+
class="fd-page-action-btn"
|
|
238
|
+
aria-label="Copy page content"
|
|
239
|
+
:data-copied="copied"
|
|
240
|
+
@click="handleCopyPage"
|
|
241
|
+
>
|
|
242
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
243
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
244
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
245
|
+
</svg>
|
|
246
|
+
<span>{{ copyLabel }}</span>
|
|
247
|
+
</button>
|
|
248
|
+
<div v-if="openDocsEnabled && openDocsProviders.length > 0" class="fd-page-action-dropdown">
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
class="fd-page-action-btn"
|
|
252
|
+
:aria-expanded="openDropdownMenu"
|
|
253
|
+
aria-haspopup="true"
|
|
254
|
+
@click="toggleDropdown"
|
|
255
|
+
>
|
|
256
|
+
<span>Open in</span>
|
|
257
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
258
|
+
<polyline points="6 9 12 15 18 9" />
|
|
259
|
+
</svg>
|
|
260
|
+
</button>
|
|
261
|
+
<div
|
|
262
|
+
class="fd-page-action-menu"
|
|
263
|
+
role="menu"
|
|
264
|
+
:hidden="!openDropdownMenu"
|
|
265
|
+
>
|
|
266
|
+
<a
|
|
267
|
+
v-for="provider in openDocsProviders"
|
|
268
|
+
:key="provider.name"
|
|
269
|
+
role="menuitem"
|
|
270
|
+
href="#"
|
|
271
|
+
class="fd-page-action-menu-item"
|
|
272
|
+
@click.prevent="openInProvider(provider)"
|
|
273
|
+
>
|
|
274
|
+
<span class="fd-page-action-menu-label">Open in {{ provider.name }}</span>
|
|
275
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
276
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
277
|
+
<polyline points="15 3 21 3 21 9" />
|
|
278
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
279
|
+
</svg>
|
|
280
|
+
</a>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<h1 class="fd-page-title">{{ data.title }}</h1>
|
|
71
286
|
<p v-if="data.description" class="fd-page-description">{{ data.description }}</p>
|
|
72
|
-
<
|
|
287
|
+
<p v-if="showLastUpdatedBelowTitle && data.lastModified" class="fd-last-modified fd-last-modified-below-title">
|
|
288
|
+
Last updated: {{ data.lastModified }}
|
|
289
|
+
</p>
|
|
290
|
+
|
|
291
|
+
<!-- Below-title actions -->
|
|
292
|
+
<template v-if="showActionsBelow">
|
|
293
|
+
<hr class="fd-page-actions-divider" aria-hidden="true" />
|
|
294
|
+
<div
|
|
295
|
+
class="fd-page-actions"
|
|
296
|
+
data-page-actions
|
|
297
|
+
:data-actions-alignment="pageActionsAlignment"
|
|
298
|
+
>
|
|
299
|
+
<button
|
|
300
|
+
v-if="copyMarkdownEnabled"
|
|
301
|
+
type="button"
|
|
302
|
+
class="fd-page-action-btn"
|
|
303
|
+
aria-label="Copy page content"
|
|
304
|
+
:data-copied="copied"
|
|
305
|
+
@click="handleCopyPage"
|
|
306
|
+
>
|
|
307
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
308
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
309
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
310
|
+
</svg>
|
|
311
|
+
<span>{{ copyLabel }}</span>
|
|
312
|
+
</button>
|
|
313
|
+
<div v-if="openDocsEnabled && openDocsProviders.length > 0" class="fd-page-action-dropdown">
|
|
314
|
+
<button
|
|
315
|
+
type="button"
|
|
316
|
+
class="fd-page-action-btn"
|
|
317
|
+
:aria-expanded="openDropdownMenu"
|
|
318
|
+
aria-haspopup="true"
|
|
319
|
+
@click="toggleDropdown"
|
|
320
|
+
>
|
|
321
|
+
<span>Open in</span>
|
|
322
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
323
|
+
<polyline points="6 9 12 15 18 9" />
|
|
324
|
+
</svg>
|
|
325
|
+
</button>
|
|
326
|
+
<div
|
|
327
|
+
class="fd-page-action-menu"
|
|
328
|
+
role="menu"
|
|
329
|
+
:hidden="!openDropdownMenu"
|
|
330
|
+
>
|
|
331
|
+
<a
|
|
332
|
+
v-for="provider in openDocsProviders"
|
|
333
|
+
:key="provider.name"
|
|
334
|
+
role="menuitem"
|
|
335
|
+
href="#"
|
|
336
|
+
class="fd-page-action-menu-item"
|
|
337
|
+
@click.prevent="openInProvider(provider)"
|
|
338
|
+
>
|
|
339
|
+
<span class="fd-page-action-menu-label">Open in {{ provider.name }}</span>
|
|
340
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
341
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
342
|
+
<polyline points="15 3 21 3 21 9" />
|
|
343
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
344
|
+
</svg>
|
|
345
|
+
</a>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
</template>
|
|
350
|
+
|
|
351
|
+
<div v-html="htmlWithoutFirstH1" />
|
|
73
352
|
</DocsPage>
|
|
74
353
|
</template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, onMounted, watch } from "vue";
|
|
3
|
+
import { useRoute } from "vue-router";
|
|
3
4
|
import Breadcrumb from "./Breadcrumb.vue";
|
|
4
5
|
import TableOfContents from "./TableOfContents.vue";
|
|
5
6
|
|
|
@@ -104,7 +105,9 @@ watch(
|
|
|
104
105
|
<Breadcrumb v-if="breadcrumbEnabled" :pathname="route.path" :entry="entry" />
|
|
105
106
|
|
|
106
107
|
<div class="fd-page-body">
|
|
107
|
-
<
|
|
108
|
+
<div class="fd-docs-content">
|
|
109
|
+
<slot />
|
|
110
|
+
</div>
|
|
108
111
|
</div>
|
|
109
112
|
|
|
110
113
|
<footer class="fd-page-footer">
|
|
@@ -243,7 +243,7 @@ function handleFmKeyDown(e: KeyboardEvent) {
|
|
|
243
243
|
:style="btnStyle"
|
|
244
244
|
@click="isOpen = true"
|
|
245
245
|
>
|
|
246
|
-
<component :is="triggerComponent" />
|
|
246
|
+
<component :is="triggerComponent" :ai-label="label" />
|
|
247
247
|
</div>
|
|
248
248
|
<button
|
|
249
249
|
v-else-if="!isOpen"
|
|
@@ -499,7 +499,7 @@ function handleFmKeyDown(e: KeyboardEvent) {
|
|
|
499
499
|
:style="btnStyle"
|
|
500
500
|
@click="isOpen = true"
|
|
501
501
|
>
|
|
502
|
-
<component :is="triggerComponent" />
|
|
502
|
+
<component :is="triggerComponent" :ai-label="label" />
|
|
503
503
|
</div>
|
|
504
504
|
<button
|
|
505
505
|
v-else
|