@baybreezy/docd 0.0.1
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/app/app.config.ts +239 -0
- package/app/app.vue +19 -0
- package/app/assets/css/tailwind.css +335 -0
- package/app/assets/css/theme.css +1171 -0
- package/app/components/OgImage/Docs.takumi.vue +69 -0
- package/app/components/OgImage/Landing.takumi.vue +64 -0
- package/app/components/OgImage/Site.takumi.vue +32 -0
- package/app/components/Ui/Avatar/Avatar.vue +75 -0
- package/app/components/Ui/Avatar/Badge.vue +34 -0
- package/app/components/Ui/Avatar/Fallback.vue +30 -0
- package/app/components/Ui/Avatar/Group.vue +29 -0
- package/app/components/Ui/Avatar/GroupCount.vue +29 -0
- package/app/components/Ui/Avatar/Image.vue +29 -0
- package/app/components/Ui/Badge.vue +86 -0
- package/app/components/Ui/BadgeGroup.vue +314 -0
- package/app/components/Ui/BgPattern/Grid.vue +137 -0
- package/app/components/Ui/BorderBeam.vue +97 -0
- package/app/components/Ui/Breadcrumbs.vue +103 -0
- package/app/components/Ui/Button.vue +169 -0
- package/app/components/Ui/ButtonGroup/ButtonGroup.vue +54 -0
- package/app/components/Ui/ButtonGroup/Separator.vue +38 -0
- package/app/components/Ui/ButtonGroup/Text.vue +33 -0
- package/app/components/Ui/Card/Action.vue +31 -0
- package/app/components/Ui/Card/Card.vue +58 -0
- package/app/components/Ui/Card/Content.vue +34 -0
- package/app/components/Ui/Card/Description.vue +36 -0
- package/app/components/Ui/Card/Footer.vue +31 -0
- package/app/components/Ui/Card/Header.vue +31 -0
- package/app/components/Ui/Card/Title.vue +37 -0
- package/app/components/Ui/Collapsible/Collapsible.vue +16 -0
- package/app/components/Ui/Collapsible/Content.vue +27 -0
- package/app/components/Ui/Collapsible/Trigger.vue +13 -0
- package/app/components/Ui/Command/Command.vue +115 -0
- package/app/components/Ui/Command/Dialog.vue +65 -0
- package/app/components/Ui/Command/Empty.vue +32 -0
- package/app/components/Ui/Command/Footer.vue +40 -0
- package/app/components/Ui/Command/Group.vue +58 -0
- package/app/components/Ui/Command/Input.vue +57 -0
- package/app/components/Ui/Command/Item.vue +102 -0
- package/app/components/Ui/Command/List.vue +33 -0
- package/app/components/Ui/Command/Separator.vue +30 -0
- package/app/components/Ui/Command/Shortcut.vue +28 -0
- package/app/components/Ui/Container.vue +46 -0
- package/app/components/Ui/ContextMenu/Arrow.vue +43 -0
- package/app/components/Ui/ContextMenu/CheckboxItem.vue +42 -0
- package/app/components/Ui/ContextMenu/Content.vue +53 -0
- package/app/components/Ui/ContextMenu/ContextMenu.vue +14 -0
- package/app/components/Ui/ContextMenu/Group.vue +12 -0
- package/app/components/Ui/ContextMenu/Item.vue +65 -0
- package/app/components/Ui/ContextMenu/ItemIndicator.vue +30 -0
- package/app/components/Ui/ContextMenu/Label.vue +35 -0
- package/app/components/Ui/ContextMenu/Portal.vue +12 -0
- package/app/components/Ui/ContextMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/ContextMenu/RadioItem.vue +40 -0
- package/app/components/Ui/ContextMenu/Separator.vue +25 -0
- package/app/components/Ui/ContextMenu/Shortcut.vue +30 -0
- package/app/components/Ui/ContextMenu/Sub.vue +15 -0
- package/app/components/Ui/ContextMenu/SubContent.vue +55 -0
- package/app/components/Ui/ContextMenu/SubTrigger.vue +42 -0
- package/app/components/Ui/ContextMenu/Trigger.vue +12 -0
- package/app/components/Ui/Dialog/Close.vue +12 -0
- package/app/components/Ui/Dialog/Content.vue +79 -0
- package/app/components/Ui/Dialog/Description.vue +29 -0
- package/app/components/Ui/Dialog/Dialog.vue +14 -0
- package/app/components/Ui/Dialog/Footer.vue +32 -0
- package/app/components/Ui/Dialog/Header.vue +32 -0
- package/app/components/Ui/Dialog/Overlay.vue +25 -0
- package/app/components/Ui/Dialog/Portal.vue +12 -0
- package/app/components/Ui/Dialog/Title.vue +29 -0
- package/app/components/Ui/Dialog/Trigger.vue +12 -0
- package/app/components/Ui/Divider.vue +102 -0
- package/app/components/Ui/Drawer/Close.vue +12 -0
- package/app/components/Ui/Drawer/Content.vue +67 -0
- package/app/components/Ui/Drawer/Description.vue +33 -0
- package/app/components/Ui/Drawer/Drawer.vue +17 -0
- package/app/components/Ui/Drawer/Footer.vue +35 -0
- package/app/components/Ui/Drawer/Header.vue +34 -0
- package/app/components/Ui/Drawer/Overlay.vue +20 -0
- package/app/components/Ui/Drawer/Portal.vue +12 -0
- package/app/components/Ui/Drawer/Title.vue +35 -0
- package/app/components/Ui/Drawer/Trigger.vue +12 -0
- package/app/components/Ui/DropdownMenu/Arrow.vue +43 -0
- package/app/components/Ui/DropdownMenu/CheckboxItem.vue +41 -0
- package/app/components/Ui/DropdownMenu/Content.vue +56 -0
- package/app/components/Ui/DropdownMenu/DropdownMenu.vue +15 -0
- package/app/components/Ui/DropdownMenu/Group.vue +12 -0
- package/app/components/Ui/DropdownMenu/Item.vue +64 -0
- package/app/components/Ui/DropdownMenu/ItemIndicator.vue +32 -0
- package/app/components/Ui/DropdownMenu/Label.vue +34 -0
- package/app/components/Ui/DropdownMenu/Portal.vue +12 -0
- package/app/components/Ui/DropdownMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/DropdownMenu/RadioItem.vue +40 -0
- package/app/components/Ui/DropdownMenu/Separator.vue +25 -0
- package/app/components/Ui/DropdownMenu/Shortcut.vue +44 -0
- package/app/components/Ui/DropdownMenu/Sub.vue +14 -0
- package/app/components/Ui/DropdownMenu/SubContent.vue +56 -0
- package/app/components/Ui/DropdownMenu/SubTrigger.vue +47 -0
- package/app/components/Ui/DropdownMenu/Trigger.vue +12 -0
- package/app/components/Ui/FancyIcon.vue +243 -0
- package/app/components/Ui/GradientDivider.vue +26 -0
- package/app/components/Ui/IframeLazy.vue +97 -0
- package/app/components/Ui/Input.vue +97 -0
- package/app/components/Ui/InputGroup/Addon.vue +66 -0
- package/app/components/Ui/InputGroup/Button.vue +50 -0
- package/app/components/Ui/InputGroup/Input.vue +25 -0
- package/app/components/Ui/InputGroup/InputGroup.vue +44 -0
- package/app/components/Ui/InputGroup/Text.vue +33 -0
- package/app/components/Ui/InputGroup/Textarea.vue +25 -0
- package/app/components/Ui/Kbd/Group.vue +32 -0
- package/app/components/Ui/Kbd/Kbd.vue +62 -0
- package/app/components/Ui/Label.vue +40 -0
- package/app/components/Ui/Popover/Anchor.vue +13 -0
- package/app/components/Ui/Popover/Arrow.vue +42 -0
- package/app/components/Ui/Popover/Close.vue +13 -0
- package/app/components/Ui/Popover/Content.vue +55 -0
- package/app/components/Ui/Popover/Popover.vue +15 -0
- package/app/components/Ui/Popover/Portal.vue +13 -0
- package/app/components/Ui/Popover/Trigger.vue +12 -0
- package/app/components/Ui/Popover/X.vue +38 -0
- package/app/components/Ui/ScrollArea/Corner.vue +12 -0
- package/app/components/Ui/ScrollArea/ScrollArea.vue +38 -0
- package/app/components/Ui/ScrollArea/Scrollbar.vue +39 -0
- package/app/components/Ui/ScrollArea/Thumb.vue +39 -0
- package/app/components/Ui/ScrollArea/Viewport.vue +27 -0
- package/app/components/Ui/Scrollspy.vue +61 -0
- package/app/components/Ui/Separator.vue +40 -0
- package/app/components/Ui/Skeleton.vue +39 -0
- package/app/components/Ui/Sonner.vue +25 -0
- package/app/components/Ui/Tabs/Content.vue +27 -0
- package/app/components/Ui/Tabs/Indicator.vue +37 -0
- package/app/components/Ui/Tabs/List.vue +37 -0
- package/app/components/Ui/Tabs/Tabs.vue +25 -0
- package/app/components/Ui/Tabs/Trigger.vue +41 -0
- package/app/components/Ui/Textarea.vue +81 -0
- package/app/components/Ui/Tooltip/Arrow.vue +30 -0
- package/app/components/Ui/Tooltip/Content.vue +50 -0
- package/app/components/Ui/Tooltip/Portal.vue +12 -0
- package/app/components/Ui/Tooltip/Provider.vue +14 -0
- package/app/components/Ui/Tooltip/Tooltip.vue +25 -0
- package/app/components/Ui/Tooltip/Trigger.vue +12 -0
- package/app/components/Ui/Tree/Item.vue +15 -0
- package/app/components/Ui/Tree/Tree.vue +15 -0
- package/app/components/Ui/Tree/Virtualizer.vue +13 -0
- package/app/components/component-api/ComponentApiSection.vue +135 -0
- package/app/components/content/docs/Callout/DocsCallout.vue +99 -0
- package/app/components/content/prose/ProseA.global.vue +31 -0
- package/app/components/content/prose/ProseBlockquote.global.vue +28 -0
- package/app/components/content/prose/ProseCallout.global.vue +306 -0
- package/app/components/content/prose/ProseCard.global.vue +100 -0
- package/app/components/content/prose/ProseCodeCollapse.global.vue +125 -0
- package/app/components/content/prose/ProseCodeCopy.global.vue +67 -0
- package/app/components/content/prose/ProseCodeGroup.global.vue +104 -0
- package/app/components/content/prose/ProseCodeTree.global.vue +277 -0
- package/app/components/content/prose/ProseCollapsible.global.vue +64 -0
- package/app/components/content/prose/ProseColorModeImage.global.vue +58 -0
- package/app/components/content/prose/ProseComponentApi.global.vue +104 -0
- package/app/components/content/prose/ProseComponentEvents.global.vue +27 -0
- package/app/components/content/prose/ProseComponentExposed.global.vue +27 -0
- package/app/components/content/prose/ProseComponentProps.global.vue +27 -0
- package/app/components/content/prose/ProseComponentSlots.global.vue +27 -0
- package/app/components/content/prose/ProseEm.global.vue +5 -0
- package/app/components/content/prose/ProseField.global.vue +118 -0
- package/app/components/content/prose/ProseFieldGroup.global.vue +62 -0
- package/app/components/content/prose/ProseH1.global.vue +58 -0
- package/app/components/content/prose/ProseH2.global.vue +58 -0
- package/app/components/content/prose/ProseH3.global.vue +58 -0
- package/app/components/content/prose/ProseH4.global.vue +58 -0
- package/app/components/content/prose/ProseH5.global.vue +47 -0
- package/app/components/content/prose/ProseH6.global.vue +47 -0
- package/app/components/content/prose/ProseHr.global.vue +33 -0
- package/app/components/content/prose/ProseIconList.global.vue +113 -0
- package/app/components/content/prose/ProseImg.global.vue +119 -0
- package/app/components/content/prose/ProseLead.global.vue +28 -0
- package/app/components/content/prose/ProseLi.global.vue +25 -0
- package/app/components/content/prose/ProseMermaid.global.vue +127 -0
- package/app/components/content/prose/ProseOl.global.vue +25 -0
- package/app/components/content/prose/ProseP.global.vue +25 -0
- package/app/components/content/prose/ProsePmInstall.global.vue +60 -0
- package/app/components/content/prose/ProsePmRun.global.vue +51 -0
- package/app/components/content/prose/ProsePmX.global.vue +56 -0
- package/app/components/content/prose/ProsePre.global.vue +181 -0
- package/app/components/content/prose/ProseShowCase.global.vue +56 -0
- package/app/components/content/prose/ProseSmartIcon.global.vue +54 -0
- package/app/components/content/prose/ProseStep.global.vue +22 -0
- package/app/components/content/prose/ProseSteps.global.vue +22 -0
- package/app/components/content/prose/ProseStrong.global.vue +34 -0
- package/app/components/content/prose/ProseTable.global.vue +34 -0
- package/app/components/content/prose/ProseTabs.global.vue +104 -0
- package/app/components/content/prose/ProseTabsInner.global.vue +274 -0
- package/app/components/content/prose/ProseTbody.global.vue +5 -0
- package/app/components/content/prose/ProseTd.global.vue +25 -0
- package/app/components/content/prose/ProseTh.global.vue +25 -0
- package/app/components/content/prose/ProseThead.global.vue +9 -0
- package/app/components/content/prose/ProseTr.global.vue +35 -0
- package/app/components/content/prose/ProseUl.global.vue +25 -0
- package/app/components/docs/DocsExtraStuff.vue +39 -0
- package/app/components/docs/DocsGithubLink.vue +11 -0
- package/app/components/docs/DocsHeader.vue +3 -0
- package/app/components/docs/DocsLogo.vue +74 -0
- package/app/components/docs/DocsMobileBar.vue +26 -0
- package/app/components/docs/DocsMobileNav.vue +49 -0
- package/app/components/docs/DocsNav.vue +161 -0
- package/app/components/docs/DocsPageTransition.vue +32 -0
- package/app/components/docs/DocsSearch.vue +220 -0
- package/app/components/docs/DocsSearchButton.vue +20 -0
- package/app/components/docs/DocsThemeCustomizer.vue +229 -0
- package/app/components/docs/DocsThemeToggler.vue +98 -0
- package/app/components/docs/DocsTocRail.vue +162 -0
- package/app/components/docs/DocsToclink.vue +52 -0
- package/app/components/docs/DocsZigZagRail.vue +70 -0
- package/app/components/landing/Hero.vue +222 -0
- package/app/components/page/PageFooter.vue +49 -0
- package/app/components/page/PageHeader.vue +68 -0
- package/app/components/page/PageHeaderLinks.vue +138 -0
- package/app/components/page/PageMobileToc.vue +162 -0
- package/app/components/page/PageToc.vue +33 -0
- package/app/composables/useDefaultSlotItems.ts +90 -0
- package/app/composables/useDocPage.ts +66 -0
- package/app/composables/useDocSearch.ts +23 -0
- package/app/composables/useDocd.ts +107 -0
- package/app/composables/useLogoAssets.ts +319 -0
- package/app/composables/usePm.ts +73 -0
- package/app/composables/useProseComponentMeta.ts +30 -0
- package/app/composables/useScrollspy.ts +327 -0
- package/app/composables/useSeo.ts +240 -0
- package/app/composables/useUIConfig.ts +36 -0
- package/app/layouts/default.vue +5 -0
- package/app/layouts/docs.vue +64 -0
- package/app/middleware/colorMode.global.ts +8 -0
- package/app/middleware/doc-page-layout.ts +11 -0
- package/app/middleware/landing-page-layout.ts +11 -0
- package/app/pages/[...slug].vue +63 -0
- package/app/plugins/mermaid.client.ts +35 -0
- package/app/plugins/navigation-direction.client.ts +15 -0
- package/app/plugins/theme-restore.client.ts +7 -0
- package/app/templates/landing.vue +40 -0
- package/app/transitions/presets.ts +71 -0
- package/app/types/prose-component-meta.d.ts +4 -0
- package/app/utils/og.ts +26 -0
- package/app/utils/prerender.ts +9 -0
- package/app/utils/translucent.ts +56 -0
- package/app/utils/tw-helper.ts +4 -0
- package/content.config.ts +81 -0
- package/index.d.ts +25 -0
- package/modules/config.ts +89 -0
- package/modules/custom-icons.ts +17 -0
- package/modules/markdown-rewrite.ts +96 -0
- package/modules/prose-component-meta.ts +150 -0
- package/modules/routing.ts +22 -0
- package/modules/skills/index.ts +167 -0
- package/modules/skills/runtime/server/routes/skills-files.ts +49 -0
- package/modules/skills/runtime/server/routes/skills-index.ts +8 -0
- package/nuxt.config.ts +96 -0
- package/nuxt.schema.ts +288 -0
- package/package.json +95 -0
- package/server/mcp/tools/get-page.ts +66 -0
- package/server/mcp/tools/list-pages.ts +61 -0
- package/server/routes/sitemap.xml.ts +73 -0
- package/server/utils/content.ts +3 -0
- package/tailwind.config.cjs +55 -0
- package/utils/component-api-content.ts +467 -0
- package/utils/component-api-discovery.ts +165 -0
- package/utils/component-api-schema.ts +28 -0
- package/utils/component-api.ts +243 -0
- package/utils/generate-prose-component-meta.ts +303 -0
- package/utils/git.ts +123 -0
- package/utils/meta.ts +40 -0
- package/utils/pages.ts +21 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import type { DocLogoConfig } from "../app.config";
|
|
2
|
+
|
|
3
|
+
type DocdLogoAction = {
|
|
4
|
+
label: string;
|
|
5
|
+
icon: string;
|
|
6
|
+
action: () => void | Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function isSvgUrl(url: string): boolean {
|
|
10
|
+
return url.toLowerCase().endsWith(".svg");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getExtension(url: string): string {
|
|
14
|
+
const match = url.match(/\.([a-z0-9]+)(?:\?|$)/i);
|
|
15
|
+
return match?.[1] ? `.${match[1].toLowerCase()}` : ".png";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeSvg(svg: string, name: string): string {
|
|
19
|
+
let result = svg.replace(
|
|
20
|
+
/fill="(black|white|#[0-9a-fA-F]{3,8}|rgba?\([^)]+\))"/g,
|
|
21
|
+
'fill="currentColor"'
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (name) {
|
|
25
|
+
result = result.replace(/<svg\b/, `<svg id="${name}"`);
|
|
26
|
+
result = result.replace(/(<svg[^>]*>)/, `$1<title>${name}</title>`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function fetchSvgContent(url: string, name: string): Promise<string | null> {
|
|
33
|
+
try {
|
|
34
|
+
const absoluteUrl = new URL(url, window.location.origin).href;
|
|
35
|
+
const response = await fetch(absoluteUrl);
|
|
36
|
+
if (!response.ok) return null;
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
return normalizeSvg(text, name);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function copyTextToClipboard(text: string): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
await navigator.clipboard.writeText(text);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function triggerDownload(blob: Blob, filename: string) {
|
|
54
|
+
const url = URL.createObjectURL(blob);
|
|
55
|
+
const link = document.createElement("a");
|
|
56
|
+
link.href = url;
|
|
57
|
+
link.download = filename;
|
|
58
|
+
document.body.appendChild(link);
|
|
59
|
+
link.click();
|
|
60
|
+
document.body.removeChild(link);
|
|
61
|
+
URL.revokeObjectURL(url);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function triggerLinkDownload(url: string, filename: string) {
|
|
65
|
+
const link = document.createElement("a");
|
|
66
|
+
link.href = url;
|
|
67
|
+
link.download = filename;
|
|
68
|
+
document.body.appendChild(link);
|
|
69
|
+
link.click();
|
|
70
|
+
document.body.removeChild(link);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function useLogoAssets() {
|
|
74
|
+
const appConfig = useAppConfig();
|
|
75
|
+
const colorMode = useColorMode() as { value: string; forced?: boolean };
|
|
76
|
+
|
|
77
|
+
const header = computed(
|
|
78
|
+
() =>
|
|
79
|
+
(appConfig.docd as { ui?: { header?: { title?: string; logo?: DocLogoConfig } } } | undefined)
|
|
80
|
+
?.ui?.header
|
|
81
|
+
);
|
|
82
|
+
const logo = computed(() => header.value?.logo);
|
|
83
|
+
|
|
84
|
+
const hasLogo = computed(() => !!(logo.value?.light || logo.value?.dark));
|
|
85
|
+
const hasWordmark = computed(() => !!(logo.value?.wordmark?.light || logo.value?.wordmark?.dark));
|
|
86
|
+
const hasDisplayAsset = computed(() => hasLogo.value || hasWordmark.value);
|
|
87
|
+
|
|
88
|
+
const displayMode = computed(() => logo.value?.display ?? "logo");
|
|
89
|
+
const isWordmarkDisplay = computed(() => displayMode.value === "wordmark" && hasWordmark.value);
|
|
90
|
+
|
|
91
|
+
const currentLogoUrl = computed(() => {
|
|
92
|
+
if (!logo.value) return "";
|
|
93
|
+
if (colorMode.value === "dark") return logo.value.dark || logo.value.light || "";
|
|
94
|
+
return logo.value.light || logo.value.dark || "";
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const currentWordmarkUrl = computed(() => {
|
|
98
|
+
if (!logo.value?.wordmark) return "";
|
|
99
|
+
if (colorMode.value === "dark")
|
|
100
|
+
return logo.value.wordmark.dark || logo.value.wordmark.light || "";
|
|
101
|
+
return logo.value.wordmark.light || logo.value.wordmark.dark || "";
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const headerLightUrl = computed(() => {
|
|
105
|
+
if (!logo.value) return "";
|
|
106
|
+
if (isWordmarkDisplay.value) {
|
|
107
|
+
return (
|
|
108
|
+
logo.value.wordmark?.light ||
|
|
109
|
+
logo.value.wordmark?.dark ||
|
|
110
|
+
logo.value.light ||
|
|
111
|
+
logo.value.dark ||
|
|
112
|
+
""
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return logo.value.light || logo.value.dark || "";
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const headerDarkUrl = computed(() => {
|
|
119
|
+
if (!logo.value) return "";
|
|
120
|
+
if (isWordmarkDisplay.value) {
|
|
121
|
+
return (
|
|
122
|
+
logo.value.wordmark?.dark ||
|
|
123
|
+
logo.value.wordmark?.light ||
|
|
124
|
+
logo.value.dark ||
|
|
125
|
+
logo.value.light ||
|
|
126
|
+
""
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return logo.value.dark || logo.value.light || "";
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const activeDisplayUrl = computed(() =>
|
|
133
|
+
colorMode.value === "dark"
|
|
134
|
+
? headerDarkUrl.value || headerLightUrl.value
|
|
135
|
+
: headerLightUrl.value || headerDarkUrl.value
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const faviconUrl = computed(() => logo.value?.favicon || "/favicon.ico");
|
|
139
|
+
const logoAlt = computed(() => logo.value?.alt || header.value?.title || "Logo");
|
|
140
|
+
const logoClasses = computed(() => logo.value?.classes || "");
|
|
141
|
+
|
|
142
|
+
const brandName = computed(() => header.value?.title || logoAlt.value || "logo");
|
|
143
|
+
const prefix = computed(() => brandName.value.toLowerCase().replace(/\s+/g, "-"));
|
|
144
|
+
const logoName = computed(() => `${brandName.value} Logo`);
|
|
145
|
+
const wordmarkName = computed(() => `${brandName.value} Wordmark`);
|
|
146
|
+
|
|
147
|
+
const logoIsSvg = computed(() => isSvgUrl(currentLogoUrl.value));
|
|
148
|
+
const wordmarkIsSvg = computed(() => isSvgUrl(currentWordmarkUrl.value));
|
|
149
|
+
const brandAssetsUrl = computed(() => logo.value?.brandAssetsUrl || "");
|
|
150
|
+
|
|
151
|
+
async function copyLogo() {
|
|
152
|
+
if (!currentLogoUrl.value || !logoIsSvg.value) return;
|
|
153
|
+
const svg = await fetchSvgContent(currentLogoUrl.value, logoName.value);
|
|
154
|
+
if (!svg) {
|
|
155
|
+
useSonner.error("Logo copy failed", {
|
|
156
|
+
description: "The active logo could not be copied.",
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const ok = await copyTextToClipboard(svg);
|
|
162
|
+
if (ok) {
|
|
163
|
+
useSonner.success("Logo copied", {
|
|
164
|
+
description: "The active logo SVG has been copied to your clipboard.",
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
useSonner.error("Logo copy failed", {
|
|
170
|
+
description: "Clipboard access was denied.",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function copyWordmark() {
|
|
175
|
+
if (!currentWordmarkUrl.value || !wordmarkIsSvg.value) return;
|
|
176
|
+
const svg = await fetchSvgContent(currentWordmarkUrl.value, wordmarkName.value);
|
|
177
|
+
if (!svg) {
|
|
178
|
+
useSonner.error("Wordmark copy failed", {
|
|
179
|
+
description: "The active wordmark could not be copied.",
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const ok = await copyTextToClipboard(svg);
|
|
185
|
+
if (ok) {
|
|
186
|
+
useSonner.success("Wordmark copied", {
|
|
187
|
+
description: "The active wordmark SVG has been copied to your clipboard.",
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
useSonner.error("Wordmark copy failed", {
|
|
193
|
+
description: "Clipboard access was denied.",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function downloadLogo() {
|
|
198
|
+
if (!currentLogoUrl.value) return;
|
|
199
|
+
|
|
200
|
+
if (logoIsSvg.value) {
|
|
201
|
+
const svg = await fetchSvgContent(currentLogoUrl.value, logoName.value);
|
|
202
|
+
if (!svg) {
|
|
203
|
+
useSonner.error("Logo download failed", {
|
|
204
|
+
description: "The active logo SVG could not be downloaded.",
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
triggerDownload(new Blob([svg], { type: "image/svg+xml" }), `${prefix.value}-logo.svg`);
|
|
210
|
+
} else {
|
|
211
|
+
triggerLinkDownload(
|
|
212
|
+
currentLogoUrl.value,
|
|
213
|
+
`${prefix.value}-logo${getExtension(currentLogoUrl.value)}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
useSonner.success("Logo downloaded", {
|
|
218
|
+
description: "The active logo asset has been downloaded.",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function downloadWordmark() {
|
|
223
|
+
if (!currentWordmarkUrl.value) return;
|
|
224
|
+
|
|
225
|
+
if (wordmarkIsSvg.value) {
|
|
226
|
+
const svg = await fetchSvgContent(currentWordmarkUrl.value, wordmarkName.value);
|
|
227
|
+
if (!svg) {
|
|
228
|
+
useSonner.error("Wordmark download failed", {
|
|
229
|
+
description: "The active wordmark SVG could not be downloaded.",
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
triggerDownload(new Blob([svg], { type: "image/svg+xml" }), `${prefix.value}-wordmark.svg`);
|
|
235
|
+
} else {
|
|
236
|
+
triggerLinkDownload(
|
|
237
|
+
currentWordmarkUrl.value,
|
|
238
|
+
`${prefix.value}-wordmark${getExtension(currentWordmarkUrl.value)}`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
useSonner.success("Wordmark downloaded", {
|
|
243
|
+
description: "The active wordmark asset has been downloaded.",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const contextMenuItems = computed<DocdLogoAction[][]>(() => {
|
|
248
|
+
const groups: DocdLogoAction[][] = [];
|
|
249
|
+
const copyGroup: DocdLogoAction[] = [];
|
|
250
|
+
const downloadGroup: DocdLogoAction[] = [];
|
|
251
|
+
|
|
252
|
+
if (currentLogoUrl.value && logoIsSvg.value) {
|
|
253
|
+
copyGroup.push({
|
|
254
|
+
label: "Copy logo SVG",
|
|
255
|
+
icon: "lucide:copy",
|
|
256
|
+
action: copyLogo,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (currentWordmarkUrl.value && wordmarkIsSvg.value) {
|
|
261
|
+
copyGroup.push({
|
|
262
|
+
label: "Copy wordmark SVG",
|
|
263
|
+
icon: "lucide:copy",
|
|
264
|
+
action: copyWordmark,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (currentLogoUrl.value) {
|
|
269
|
+
downloadGroup.push({
|
|
270
|
+
label: "Download logo",
|
|
271
|
+
icon: "lucide:download",
|
|
272
|
+
action: downloadLogo,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (currentWordmarkUrl.value) {
|
|
277
|
+
downloadGroup.push({
|
|
278
|
+
label: "Download wordmark",
|
|
279
|
+
icon: "lucide:download",
|
|
280
|
+
action: downloadWordmark,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (copyGroup.length) groups.push(copyGroup);
|
|
285
|
+
if (downloadGroup.length) groups.push(downloadGroup);
|
|
286
|
+
|
|
287
|
+
if (brandAssetsUrl.value) {
|
|
288
|
+
groups.push([
|
|
289
|
+
{
|
|
290
|
+
label: "View brand assets",
|
|
291
|
+
icon: "lucide:palette",
|
|
292
|
+
action: () => {
|
|
293
|
+
window.open(brandAssetsUrl.value, "_blank", "noopener,noreferrer");
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
]);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return groups;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
hasLogo,
|
|
304
|
+
hasWordmark,
|
|
305
|
+
hasDisplayAsset,
|
|
306
|
+
displayMode,
|
|
307
|
+
isWordmarkDisplay,
|
|
308
|
+
currentLogoUrl,
|
|
309
|
+
currentWordmarkUrl,
|
|
310
|
+
activeDisplayUrl,
|
|
311
|
+
headerLightUrl,
|
|
312
|
+
headerDarkUrl,
|
|
313
|
+
faviconUrl,
|
|
314
|
+
logoAlt,
|
|
315
|
+
logoClasses,
|
|
316
|
+
contextMenuItems,
|
|
317
|
+
actionGroups: contextMenuItems,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package manager name type
|
|
3
|
+
*/
|
|
4
|
+
export type PackageManagerName = "npm" | "pnpm" | "bun" | "yarn";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Package manager interface
|
|
8
|
+
*/
|
|
9
|
+
export interface PackageManager {
|
|
10
|
+
name: PackageManagerName;
|
|
11
|
+
command: string;
|
|
12
|
+
install: string;
|
|
13
|
+
installEmpty: string;
|
|
14
|
+
run: string;
|
|
15
|
+
x: string;
|
|
16
|
+
saveDev: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* List of supported package managers
|
|
22
|
+
*/
|
|
23
|
+
const PACKAGE_MANAGERS: readonly PackageManager[] = [
|
|
24
|
+
{
|
|
25
|
+
name: "npm",
|
|
26
|
+
command: "npm ",
|
|
27
|
+
install: "i ",
|
|
28
|
+
installEmpty: "install",
|
|
29
|
+
run: "run ",
|
|
30
|
+
x: "npx ",
|
|
31
|
+
saveDev: "-D ",
|
|
32
|
+
icon: "material-icon-theme:npm",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "pnpm",
|
|
36
|
+
command: "pnpm ",
|
|
37
|
+
install: "i ",
|
|
38
|
+
installEmpty: "install",
|
|
39
|
+
run: "run ",
|
|
40
|
+
x: "pnpm dlx ",
|
|
41
|
+
saveDev: "-D ",
|
|
42
|
+
icon: "material-icon-theme:pnpm",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "bun",
|
|
46
|
+
command: "bun ",
|
|
47
|
+
install: "add ",
|
|
48
|
+
installEmpty: "install",
|
|
49
|
+
run: "run ",
|
|
50
|
+
x: "bun x ",
|
|
51
|
+
saveDev: "-d ",
|
|
52
|
+
icon: "material-icon-theme:bun",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "yarn",
|
|
56
|
+
command: "yarn ",
|
|
57
|
+
install: "add ",
|
|
58
|
+
installEmpty: "install",
|
|
59
|
+
run: "run ",
|
|
60
|
+
x: "yarn dlx ",
|
|
61
|
+
saveDev: "-D ",
|
|
62
|
+
icon: "material-icon-theme:yarn",
|
|
63
|
+
},
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Composable to access package manager data
|
|
68
|
+
*/
|
|
69
|
+
export function usePm() {
|
|
70
|
+
return {
|
|
71
|
+
packageManagers: PACKAGE_MANAGERS,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import manifestSource from "#build/docd/prose-component-meta";
|
|
2
|
+
|
|
3
|
+
import { normalizeComponentMetaPath } from "../../utils/component-api";
|
|
4
|
+
import type {
|
|
5
|
+
ProseComponentMetaEntry,
|
|
6
|
+
ProseComponentMetaManifest,
|
|
7
|
+
} from "../../utils/generate-prose-component-meta";
|
|
8
|
+
|
|
9
|
+
const proseComponentMetaManifest = manifestSource as ProseComponentMetaManifest;
|
|
10
|
+
|
|
11
|
+
export type {
|
|
12
|
+
ProseComponentApiLayout,
|
|
13
|
+
ProseComponentMetaEntry,
|
|
14
|
+
} from "../../utils/generate-prose-component-meta";
|
|
15
|
+
export type { ComponentApiSectionKind } from "../../utils/component-api";
|
|
16
|
+
|
|
17
|
+
export function useProseComponentMetaManifest() {
|
|
18
|
+
return proseComponentMetaManifest;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveProseComponentMeta(path?: string | null): ProseComponentMetaEntry | null {
|
|
22
|
+
if (!path) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const manifest = useProseComponentMetaManifest();
|
|
27
|
+
const normalizedPath = normalizeComponentMetaPath(path);
|
|
28
|
+
|
|
29
|
+
return manifest.components[normalizedPath] || null;
|
|
30
|
+
}
|