@rimelight/ui 0.0.34 → 0.0.35
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/README.md +1 -0
- package/package.json +19 -24
- package/src/components/astro/RLAAccordion.astro +94 -18
- package/src/components/astro/RLAAvatar.astro +135 -68
- package/src/components/astro/RLABadge.astro +4 -2
- package/src/components/astro/RLABanner.astro +138 -23
- package/src/components/astro/RLABreadcrumb.astro +13 -0
- package/src/components/astro/RLAButton.astro +8 -4
- package/src/components/astro/RLACallout.astro +112 -0
- package/src/components/astro/RLACard.astro +6 -5
- package/src/components/astro/RLACarousel.astro +104 -75
- package/src/components/astro/RLAChart.astro +4 -1
- package/src/components/astro/RLAChip.astro +11 -5
- package/src/components/astro/RLACollapsible.astro +4 -1
- package/src/components/astro/RLAColorArea.astro +432 -0
- package/src/components/astro/RLAColorField.astro +300 -0
- package/src/components/astro/RLAColorSlider.astro +413 -0
- package/src/components/astro/RLAConfirm.astro +13 -11
- package/src/components/astro/RLACopyMarkdown.astro +78 -0
- package/src/components/astro/RLADate.astro +30 -0
- package/src/components/astro/RLADrawer.astro +50 -3
- package/src/components/astro/RLADropdownMenu.astro +78 -51
- package/src/components/astro/RLAFieldGroup.astro +4 -1
- package/src/components/astro/RLAFileUpload.astro +114 -100
- package/src/components/astro/RLAFooter.astro +2 -1
- package/src/components/astro/RLAFormField.astro +10 -6
- package/src/components/astro/RLAGrid.astro +31 -39
- package/src/components/astro/RLAHead.astro +139 -39
- package/src/components/astro/RLAHeader.astro +2 -1
- package/src/components/astro/RLAImage.astro +274 -251
- package/src/components/astro/RLAInput.astro +14 -9
- package/src/components/astro/RLAInputMenu.astro +119 -83
- package/src/components/astro/RLAKbd.astro +5 -5
- package/src/components/astro/RLALayoutGrid.astro +395 -0
- package/src/components/astro/RLALink.astro +23 -1
- package/src/components/astro/RLALinkGroup.astro +99 -99
- package/src/components/astro/RLALocaleSelector.astro +29 -35
- package/src/components/astro/RLALogo.astro +117 -90
- package/src/components/astro/RLAModal.astro +48 -18
- package/src/components/astro/RLANavigationMenu.astro +381 -326
- package/src/components/astro/{RLADocsPackageManagers.astro → RLAPackageManagers.astro} +81 -87
- package/src/components/astro/RLAPageSection.astro +4 -2
- package/src/components/astro/RLAPopover.astro +4 -3
- package/src/components/astro/RLAPost.astro +11 -2
- package/src/components/astro/RLAScrollArea.astro +4 -1
- package/src/components/astro/RLAScrollToTop.astro +200 -172
- package/src/components/astro/RLASearch.astro +282 -0
- package/src/components/astro/RLASelect.astro +78 -55
- package/src/components/astro/RLASeparator.astro +11 -6
- package/src/components/astro/RLAShare.astro +60 -0
- package/src/components/astro/RLAShortcuts.astro +429 -0
- package/src/components/astro/RLASidebar.astro +158 -212
- package/src/components/astro/RLASlideover.astro +47 -17
- package/src/components/astro/RLASplitter.astro +194 -141
- package/src/components/astro/RLASteps.astro +118 -71
- package/src/components/astro/RLASurround.astro +39 -0
- package/src/components/astro/RLATable.astro +4 -1
- package/src/components/astro/RLATableOfContents.astro +109 -23
- package/src/components/astro/RLATabs.astro +147 -133
- package/src/components/astro/RLATextarea.astro +4 -1
- package/src/components/astro/RLAThemeSelector.astro +105 -76
- package/src/components/astro/RLAToast.astro +35 -21
- package/src/components/vue/RLVBanner.vue +230 -0
- package/src/components/vue/RLVButton.vue +3 -2
- package/src/components/vue/RLVCheckbox.vue +4 -2
- package/src/components/vue/RLVFooter.vue +3 -2
- package/src/components/vue/RLVHeader.vue +3 -2
- package/src/components/vue/RLVIcon.vue +4 -2
- package/src/components/vue/RLVInput.vue +5 -4
- package/src/components/vue/RLVLogo.vue +25 -3
- package/src/components/vue/RLVPlaceholder.vue +3 -2
- package/src/components/vue/RLVPost.vue +8 -2
- package/src/components/vue/RLVProgress.vue +60 -0
- package/src/components/vue/RLVScrollToTop.vue +65 -52
- package/src/components/vue/RLVSection.vue +14 -13
- package/src/components/vue/RLVToast.vue +17 -4
- package/src/composables/index.ts +1 -0
- package/src/composables/useHeaderStore.ts +1 -0
- package/src/composables/useShortcuts.ts +11 -0
- package/src/config/security.ts +43 -36
- package/src/config/site.config.ts +1 -0
- package/src/docs/components/ApiChangelog.astro +147 -0
- package/src/{plugins/starlightDocsApi → docs}/components/ApiEmits.astro +4 -4
- package/src/{plugins/starlightDocsApi → docs}/components/ApiProps.astro +4 -4
- package/src/{plugins/starlightDocsApi → docs}/components/ApiSlots.astro +4 -4
- package/src/docs/components/ApiTheme.astro +100 -0
- package/src/docs/extractAll.ts +94 -0
- package/src/docs/extractors/extractAstro.ts +69 -0
- package/src/docs/extractors/extractTheme.ts +56 -0
- package/src/{plugins/starlightDocsApi/extract-vue.ts → docs/extractors/extractVue.ts} +164 -15
- package/src/{plugins/starlightDocsApi → docs}/utils.ts +1 -2
- package/src/env.d.ts +0 -12
- package/src/integrations/ui.ts +35 -19
- package/src/middleware/security.ts +75 -19
- package/src/presets/index.ts +118 -293
- package/src/styles/index.css +0 -2
- package/src/themes/accordion.theme.ts +29 -3
- package/src/themes/aspect-ratio.theme.ts +1 -1
- package/src/themes/avatar-group.theme.ts +1 -1
- package/src/themes/avatar.theme.ts +2 -2
- package/src/themes/badge.theme.ts +2 -3
- package/src/themes/banner.theme.ts +40 -4
- package/src/themes/breadcrumb.theme.ts +1 -1
- package/src/themes/button.theme.ts +21 -22
- package/src/themes/{alert.theme.ts → callout.theme.ts} +12 -39
- package/src/themes/card.theme.ts +17 -9
- package/src/themes/carousel.theme.ts +1 -1
- package/src/themes/checkbox.theme.ts +1 -2
- package/src/themes/chip.theme.ts +2 -3
- package/src/themes/color-area.theme.ts +33 -0
- package/src/themes/color-field.theme.ts +32 -0
- package/src/themes/color-slider.theme.ts +34 -0
- package/src/themes/confirm.theme.ts +1 -1
- package/src/themes/container.theme.ts +1 -1
- package/src/themes/drawer.theme.ts +1 -1
- package/src/themes/footer.theme.ts +2 -2
- package/src/themes/grid.theme.ts +38 -0
- package/src/themes/header.theme.ts +4 -3
- package/src/themes/hover-card.theme.ts +1 -1
- package/src/themes/image.theme.ts +11 -1
- package/src/themes/input.theme.ts +7 -1
- package/src/themes/kbd.theme.ts +2 -3
- package/src/themes/label.theme.ts +1 -1
- package/src/themes/layout-grid.theme.ts +16 -0
- package/src/themes/link-group.theme.ts +0 -4
- package/src/themes/marquee.theme.ts +1 -1
- package/src/themes/modal.theme.ts +1 -1
- package/src/themes/navigation-menu.theme.ts +2 -2
- package/src/themes/page-section.theme.ts +4 -2
- package/src/themes/pagination.theme.ts +1 -1
- package/src/themes/pin-input.theme.ts +1 -1
- package/src/themes/popover.theme.ts +1 -1
- package/src/themes/post.theme.ts +23 -3
- package/src/themes/progress.theme.ts +1 -2
- package/src/themes/scroll-to-top.theme.ts +2 -3
- package/src/themes/search.theme.ts +64 -0
- package/src/themes/separator.theme.ts +1 -1
- package/src/themes/slideover.theme.ts +1 -1
- package/src/themes/slider.theme.ts +1 -2
- package/src/themes/spinner.theme.ts +1 -2
- package/src/themes/{megamenu.theme.ts → steps.theme.ts} +3 -3
- package/src/themes/sticky-surface.theme.ts +1 -1
- package/src/themes/switch.theme.ts +1 -2
- package/src/themes/table-of-contents.theme.ts +21 -2
- package/src/themes/timeline.theme.ts +7 -2
- package/src/themes/toast.theme.ts +1 -1
- package/src/types/components/accordion.ts +35 -2
- package/src/types/components/avatar.ts +16 -0
- package/src/types/components/banner.ts +42 -0
- package/src/types/components/button.ts +3 -4
- package/src/types/components/{alert.ts → callout.ts} +6 -6
- package/src/types/components/color-area.ts +19 -0
- package/src/types/components/color-field.ts +21 -0
- package/src/types/components/color-slider.ts +20 -0
- package/src/types/components/grid.ts +23 -0
- package/src/types/components/input.ts +4 -0
- package/src/types/components/layout-grid.ts +145 -0
- package/src/types/components/link-group.ts +4 -2
- package/src/types/components/link.ts +15 -0
- package/src/types/components/logo.ts +3 -3
- package/src/types/components/post.ts +12 -0
- package/src/types/components/search.ts +29 -0
- package/src/types/components/sidebar.ts +2 -6
- package/src/types/components/{megamenu.ts → steps.ts} +7 -1
- package/src/types/components/table-of-contents.ts +22 -0
- package/src/types/components/toast.ts +4 -0
- package/src/types/docs.ts +40 -0
- package/src/types/index.ts +151 -1
- package/src/types/theme.ts +6 -6
- package/src/utils/color.ts +165 -0
- package/src/utils/docs.ts +260 -110
- package/src/utils/headerStack.ts +24 -4
- package/src/utils/index.ts +2 -0
- package/src/utils/shortcuts.ts +322 -0
- package/src/components/TableOfContents.astro +0 -107
- package/src/components/astro/RLAAlert.astro +0 -93
- package/src/components/astro/RLAChangelog.astro +0 -23
- package/src/components/astro/RLAMegamenu.astro +0 -23
- package/src/plugins/index.ts +0 -2
- package/src/plugins/starlightDocsApi/components/ApiTheme.astro +0 -27
- package/src/plugins/starlightDocsApi/index.ts +0 -272
- package/src/styles/prism.css +0 -42
- package/src/themes/changelog.theme.ts +0 -13
- package/src/types/components/index.ts +0 -134
- /package/src/{plugins/starlightDocsApi → docs}/types.ts +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ComponentMetaMap } from "../types"
|
|
3
|
+
import { generateThemeCode } from "../utils"
|
|
4
|
+
import meta from "virtual:rimelight-ui-docs-meta"
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
component: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { component } = Astro.props
|
|
11
|
+
|
|
12
|
+
const allMeta = meta as unknown as ComponentMetaMap
|
|
13
|
+
const compMeta = allMeta[component]
|
|
14
|
+
|
|
15
|
+
const theme = compMeta?.theme ?? null
|
|
16
|
+
const themeCode = theme ? generateThemeCode(component, theme) : null
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
{themeCode ? (
|
|
20
|
+
<div class="api-theme-container relative border border-gray-800 rounded-lg overflow-hidden group mb-6" data-component={component}>
|
|
21
|
+
<div class="api-theme-collapse overflow-hidden max-h-[240px] relative transition-[max-height] duration-300 ease-in-out">
|
|
22
|
+
<div class="expressive-code m-0 border-0 rounded-none">
|
|
23
|
+
<figure class="frame m-0 border-0">
|
|
24
|
+
<pre class="language-ts overflow-x-auto m-0 border-0 rounded-none bg-gray-900"><code class="language-ts m-0">{themeCode}</code></pre>
|
|
25
|
+
</figure>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
{/* Gradient Fade Overlay */}
|
|
29
|
+
<div class="api-theme-fade absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-gray-950 to-transparent pointer-events-none transition-opacity duration-300"></div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
{/* Toggle Button Container */}
|
|
33
|
+
<div class="api-theme-trigger-bar absolute bottom-0 left-0 right-0 flex justify-center pb-3 bg-gradient-to-t from-gray-950/90 via-gray-950/70 to-transparent pt-12">
|
|
34
|
+
<button class="api-theme-btn px-4 py-1.5 text-xs font-semibold text-white bg-gray-800 hover:bg-gray-700 border border-gray-700 hover:border-gray-650 rounded-md shadow-md transition-all duration-200 cursor-pointer">
|
|
35
|
+
Show theme config
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
) : (
|
|
40
|
+
<p class="text-sm italic text-gray-500">No theme data available.</p>
|
|
41
|
+
)}
|
|
42
|
+
|
|
43
|
+
<script>
|
|
44
|
+
function setupThemeCollapser() {
|
|
45
|
+
if (typeof document === 'undefined') return;
|
|
46
|
+
const containers = document.querySelectorAll('.api-theme-container');
|
|
47
|
+
containers.forEach(container => {
|
|
48
|
+
if (container.getAttribute('data-collapser-ready') === 'true') return;
|
|
49
|
+
container.setAttribute('data-collapser-ready', 'true');
|
|
50
|
+
|
|
51
|
+
const collapseWrapper = container.querySelector('.api-theme-collapse') as HTMLElement;
|
|
52
|
+
const fadeOverlay = container.querySelector('.api-theme-fade') as HTMLElement;
|
|
53
|
+
const btn = container.querySelector('.api-theme-btn') as HTMLButtonElement;
|
|
54
|
+
const triggerBar = container.querySelector('.api-theme-trigger-bar') as HTMLElement;
|
|
55
|
+
|
|
56
|
+
if (!collapseWrapper || !btn || !triggerBar || !fadeOverlay) return;
|
|
57
|
+
|
|
58
|
+
// Let code blocks load and measure scrollHeight
|
|
59
|
+
const checkAndSetup = () => {
|
|
60
|
+
const scrollHeight = collapseWrapper.scrollHeight;
|
|
61
|
+
if (scrollHeight <= 240) {
|
|
62
|
+
collapseWrapper.style.maxHeight = 'none';
|
|
63
|
+
fadeOverlay.style.display = 'none';
|
|
64
|
+
triggerBar.style.display = 'none';
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let isExpanded = false;
|
|
69
|
+
|
|
70
|
+
btn.addEventListener('click', () => {
|
|
71
|
+
isExpanded = !isExpanded;
|
|
72
|
+
if (isExpanded) {
|
|
73
|
+
collapseWrapper.style.maxHeight = `${collapseWrapper.scrollHeight}px`;
|
|
74
|
+
btn.textContent = 'Hide theme config';
|
|
75
|
+
fadeOverlay.style.opacity = '0';
|
|
76
|
+
triggerBar.classList.remove('absolute', 'bottom-0');
|
|
77
|
+
triggerBar.classList.add('relative', 'bg-gray-950', 'py-3', 'border-t', 'border-gray-800');
|
|
78
|
+
triggerBar.style.paddingTop = '12px';
|
|
79
|
+
} else {
|
|
80
|
+
collapseWrapper.style.maxHeight = '240px';
|
|
81
|
+
btn.textContent = 'Show theme config';
|
|
82
|
+
fadeOverlay.style.opacity = '1';
|
|
83
|
+
triggerBar.classList.add('absolute', 'bottom-0');
|
|
84
|
+
triggerBar.classList.remove('relative', 'bg-gray-950', 'py-3', 'border-t', 'border-gray-800');
|
|
85
|
+
triggerBar.style.paddingTop = '48px';
|
|
86
|
+
container.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Delay slightly to ensure fonts/expressive code are rendered
|
|
92
|
+
setTimeout(checkAndSetup, 150);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof document !== 'undefined') {
|
|
97
|
+
setupThemeCollapser();
|
|
98
|
+
document.addEventListener('astro:after-swap', setupThemeCollapser);
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import type { ComponentMeta, PropMeta, SlotMeta, EmitMeta } from "./types"
|
|
5
|
+
import { extractAstroMeta } from "./extractors/extractAstro"
|
|
6
|
+
import { extractVueMeta } from "./extractors/extractVue"
|
|
7
|
+
import { extractThemeMeta } from "./extractors/extractTheme"
|
|
8
|
+
|
|
9
|
+
const pathDirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const uiRoot = path.resolve(pathDirname, "..", "..")
|
|
11
|
+
|
|
12
|
+
function pascalToKebab(name: string): string {
|
|
13
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getComponentNameFromFilename(filename: string): string {
|
|
17
|
+
return filename.replace(/^(RLA|RLV)/, "").replace(/\.(astro|vue)$/, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function extractAllMeta(): Promise<Record<string, ComponentMeta>> {
|
|
21
|
+
const componentsDir = path.join(uiRoot, "src", "components")
|
|
22
|
+
const themesDir = path.join(uiRoot, "src", "themes")
|
|
23
|
+
|
|
24
|
+
const vueDir = path.join(componentsDir, "vue")
|
|
25
|
+
const astroDir = path.join(componentsDir, "astro")
|
|
26
|
+
|
|
27
|
+
const vueFiles = existsSync(vueDir)
|
|
28
|
+
? readdirSync(vueDir).filter((f: string) => f.endsWith(".vue"))
|
|
29
|
+
: []
|
|
30
|
+
const astroFiles = existsSync(astroDir)
|
|
31
|
+
? readdirSync(astroDir).filter((f: string) => f.endsWith(".astro") && f !== "Head.astro")
|
|
32
|
+
: []
|
|
33
|
+
|
|
34
|
+
const vueComponents = new Map<string, string>()
|
|
35
|
+
const astroComponents = new Map<string, string>()
|
|
36
|
+
|
|
37
|
+
for (const f of vueFiles) vueComponents.set(getComponentNameFromFilename(f), f)
|
|
38
|
+
for (const f of astroFiles) astroComponents.set(getComponentNameFromFilename(f), f)
|
|
39
|
+
|
|
40
|
+
const allNames = new Set([...vueComponents.keys(), ...astroComponents.keys()])
|
|
41
|
+
const result: Record<string, ComponentMeta> = {}
|
|
42
|
+
|
|
43
|
+
await Promise.all(
|
|
44
|
+
[...allNames].toSorted().map(async (name) => {
|
|
45
|
+
const vueFile = vueComponents.get(name)
|
|
46
|
+
const astroFile = astroComponents.get(name)
|
|
47
|
+
|
|
48
|
+
let props: PropMeta[] = []
|
|
49
|
+
let slots: SlotMeta[] = []
|
|
50
|
+
let emits: EmitMeta[] = []
|
|
51
|
+
const frameworks: ("astro" | "vue")[] = []
|
|
52
|
+
|
|
53
|
+
if (vueFile) {
|
|
54
|
+
frameworks.push("vue")
|
|
55
|
+
try {
|
|
56
|
+
const vueMeta = extractVueMeta(path.join(vueDir, vueFile))
|
|
57
|
+
props = vueMeta.props
|
|
58
|
+
slots = vueMeta.slots
|
|
59
|
+
emits = vueMeta.emits
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.warn(`[docs] Failed to extract Vue meta for ${vueFile}:`, err)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (astroFile) {
|
|
66
|
+
frameworks.push("astro")
|
|
67
|
+
if (!vueFile) {
|
|
68
|
+
try {
|
|
69
|
+
const astroMeta = extractAstroMeta(path.join(astroDir, astroFile))
|
|
70
|
+
props = astroMeta.props
|
|
71
|
+
slots = astroMeta.slots
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.warn(`[docs] Failed to extract Astro meta for ${astroFile}:`, err)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const theme = await extractThemeMeta(name, themesDir)
|
|
79
|
+
|
|
80
|
+
result[pascalToKebab(name)] = {
|
|
81
|
+
name,
|
|
82
|
+
astroComponent: astroFile ?? null,
|
|
83
|
+
vueComponent: vueFile ?? null,
|
|
84
|
+
frameworks,
|
|
85
|
+
props,
|
|
86
|
+
slots,
|
|
87
|
+
emits,
|
|
88
|
+
theme
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import ts from "typescript"
|
|
3
|
+
import type { PropMeta, SlotMeta } from "../types"
|
|
4
|
+
|
|
5
|
+
function extractAstroPropsFromInterface(sourceFile: ts.SourceFile): PropMeta[] {
|
|
6
|
+
const props: PropMeta[] = []
|
|
7
|
+
|
|
8
|
+
function visit(node: ts.Node) {
|
|
9
|
+
if (
|
|
10
|
+
(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) &&
|
|
11
|
+
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
|
|
12
|
+
) {
|
|
13
|
+
const name = node.name.text
|
|
14
|
+
if (name.endsWith("Props") || name === "Props") {
|
|
15
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
16
|
+
for (const member of node.members) {
|
|
17
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
18
|
+
const propName = member.name.text
|
|
19
|
+
if (propName === "class" || propName.startsWith("data-")) continue
|
|
20
|
+
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
21
|
+
const isOptional = member.questionToken !== undefined
|
|
22
|
+
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} else if (ts.isTypeAliasDeclaration(node) && ts.isTypeLiteralNode(node.type)) {
|
|
26
|
+
for (const member of node.type.members) {
|
|
27
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
28
|
+
const propName = member.name.text
|
|
29
|
+
if (propName === "class" || propName.startsWith("data-")) continue
|
|
30
|
+
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
31
|
+
const isOptional = member.questionToken !== undefined
|
|
32
|
+
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
ts.forEachChild(node, visit)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
visit(sourceFile)
|
|
42
|
+
return props
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function extractAstroMeta(filePath: string): { props: PropMeta[]; slots: SlotMeta[] } {
|
|
46
|
+
const content = readFileSync(filePath, "utf-8")
|
|
47
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
48
|
+
if (!frontmatterMatch) return { props: [], slots: [] }
|
|
49
|
+
|
|
50
|
+
const frontmatter = frontmatterMatch[1]
|
|
51
|
+
const sourceFile = ts.createSourceFile(
|
|
52
|
+
"component.ts",
|
|
53
|
+
frontmatter ?? "",
|
|
54
|
+
ts.ScriptTarget.Latest,
|
|
55
|
+
true,
|
|
56
|
+
ts.ScriptKind.TS
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const props = extractAstroPropsFromInterface(sourceFile)
|
|
60
|
+
|
|
61
|
+
const slots: SlotMeta[] = []
|
|
62
|
+
const slotMatches = content.matchAll(/<slot(?:\s+name=["']([^"']+)["'])?/g)
|
|
63
|
+
for (const match of slotMatches) {
|
|
64
|
+
slots.push({ name: match[1] ?? "default", bindings: {}, description: "" })
|
|
65
|
+
}
|
|
66
|
+
if (!slots.length) slots.push({ name: "default", bindings: {}, description: "" })
|
|
67
|
+
|
|
68
|
+
return { props, slots }
|
|
69
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { execSync } from "node:child_process"
|
|
4
|
+
import type { ThemeMeta } from "../types"
|
|
5
|
+
|
|
6
|
+
export async function extractThemeMeta(
|
|
7
|
+
componentName: string,
|
|
8
|
+
themesDir: string
|
|
9
|
+
): Promise<ThemeMeta | null> {
|
|
10
|
+
if (!existsSync(themesDir)) return null
|
|
11
|
+
|
|
12
|
+
const themeFiles = readdirSync(themesDir).filter((f) => f.endsWith(".theme.ts"))
|
|
13
|
+
let themePath: string | null = null
|
|
14
|
+
for (const file of themeFiles) {
|
|
15
|
+
const baseName = file.replace(".theme.ts", "")
|
|
16
|
+
if (baseName === componentName.toLowerCase()) {
|
|
17
|
+
themePath = path.join(themesDir, file)
|
|
18
|
+
break
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!themePath || !existsSync(themePath)) return null
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const fileUrl = "file:///" + themePath.replace(/\\/g, "/")
|
|
26
|
+
// Run evaluation in a separate Node process with the tsx loader.
|
|
27
|
+
// This bypasses Vite's ModuleRunner restrictions inside Astro config setups.
|
|
28
|
+
// We pass the loader script via stdin to avoid any command line escaping issues.
|
|
29
|
+
const input = `import('${fileUrl}').then(m => console.log(JSON.stringify(m.default))).catch(err => { console.error(err); process.exit(1); })`
|
|
30
|
+
const output = execSync(`"${process.execPath}" --import tsx -`, {
|
|
31
|
+
input,
|
|
32
|
+
encoding: "utf-8",
|
|
33
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const themeObj = JSON.parse(output.trim())
|
|
37
|
+
if (!themeObj) return null
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
slots: Array.isArray(themeObj.slots) ? themeObj.slots : [],
|
|
41
|
+
base: typeof themeObj.base === "object" && themeObj.base !== null ? themeObj.base : {},
|
|
42
|
+
variants:
|
|
43
|
+
typeof themeObj.variants === "object" && themeObj.variants !== null
|
|
44
|
+
? themeObj.variants
|
|
45
|
+
: {},
|
|
46
|
+
compoundVariants: Array.isArray(themeObj.compoundVariants) ? themeObj.compoundVariants : [],
|
|
47
|
+
defaultVariants:
|
|
48
|
+
typeof themeObj.defaultVariants === "object" && themeObj.defaultVariants !== null
|
|
49
|
+
? themeObj.defaultVariants
|
|
50
|
+
: {}
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.warn(`[docs] Failed to extract theme via child process for ${componentName}:`, err)
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs"
|
|
1
|
+
import { readFileSync, existsSync, statSync } from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
2
3
|
import ts from "typescript"
|
|
3
|
-
import type { PropMeta, SlotMeta, EmitMeta } from "
|
|
4
|
+
import type { PropMeta, SlotMeta, EmitMeta } from "../types"
|
|
4
5
|
|
|
5
6
|
function extractScriptBlock(content: string): string | null {
|
|
6
7
|
const match =
|
|
@@ -73,7 +74,7 @@ function getTypeRefName(typeArg: ts.TypeNode): string | null {
|
|
|
73
74
|
return null
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
export function
|
|
77
|
+
export function extractVueMeta(filePath: string): {
|
|
77
78
|
props: PropMeta[]
|
|
78
79
|
slots: SlotMeta[]
|
|
79
80
|
emits: EmitMeta[]
|
|
@@ -94,13 +95,31 @@ export function extractVueMetaDirect(filePath: string): {
|
|
|
94
95
|
ts.ScriptKind.TS
|
|
95
96
|
)
|
|
96
97
|
|
|
97
|
-
const interfaces = new Map<string, ts.InterfaceDeclaration>()
|
|
98
|
+
const interfaces = new Map<string, ts.InterfaceDeclaration | ts.TypeAliasDeclaration>()
|
|
98
99
|
const destructuredDefaults: Record<string, string> = {}
|
|
100
|
+
const imports = new Map<string, string>()
|
|
99
101
|
|
|
100
102
|
function collectDeclarations(node: ts.Node) {
|
|
101
|
-
if (ts.isInterfaceDeclaration(node)) {
|
|
103
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
102
104
|
interfaces.set(node.name.text, node)
|
|
103
105
|
}
|
|
106
|
+
if (ts.isImportDeclaration(node)) {
|
|
107
|
+
const moduleSpecifier = node.moduleSpecifier
|
|
108
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
109
|
+
const importPath = moduleSpecifier.text
|
|
110
|
+
const importClause = node.importClause
|
|
111
|
+
if (importClause) {
|
|
112
|
+
if (importClause.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
|
|
113
|
+
for (const element of importClause.namedBindings.elements) {
|
|
114
|
+
imports.set(element.name.text, importPath)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (importClause.name) {
|
|
118
|
+
imports.set(importClause.name.text, importPath)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
104
123
|
if (ts.isVariableStatement(node)) {
|
|
105
124
|
for (const decl of node.declarationList.declarations) {
|
|
106
125
|
if (
|
|
@@ -138,9 +157,59 @@ export function extractVueMetaDirect(filePath: string): {
|
|
|
138
157
|
const typeArg = node.typeArguments[0]!
|
|
139
158
|
const refName = getTypeRefName(typeArg)
|
|
140
159
|
if (refName) {
|
|
141
|
-
|
|
160
|
+
let iface = interfaces.get(refName)
|
|
161
|
+
if (!iface && imports.has(refName)) {
|
|
162
|
+
const relPath = imports.get(refName)!
|
|
163
|
+
const baseDir = path.dirname(filePath)
|
|
164
|
+
const absolutePath = path.resolve(baseDir, relPath)
|
|
165
|
+
const extensions = [".ts", ".d.ts", "/index.ts", "/index.d.ts"]
|
|
166
|
+
let foundPath = ""
|
|
167
|
+
if (existsSync(absolutePath) && statSync(absolutePath).isFile()) {
|
|
168
|
+
foundPath = absolutePath
|
|
169
|
+
} else {
|
|
170
|
+
for (const ext of extensions) {
|
|
171
|
+
const testPath = absolutePath + ext
|
|
172
|
+
if (existsSync(testPath)) {
|
|
173
|
+
foundPath = testPath
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (foundPath) {
|
|
179
|
+
try {
|
|
180
|
+
const importContent = readFileSync(foundPath, "utf-8")
|
|
181
|
+
const importSourceFile = ts.createSourceFile(
|
|
182
|
+
"import.ts",
|
|
183
|
+
importContent,
|
|
184
|
+
ts.ScriptTarget.Latest,
|
|
185
|
+
true,
|
|
186
|
+
ts.ScriptKind.TS
|
|
187
|
+
)
|
|
188
|
+
let foundNode: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | null = null
|
|
189
|
+
function findDecl(n: ts.Node) {
|
|
190
|
+
if (
|
|
191
|
+
(ts.isInterfaceDeclaration(n) || ts.isTypeAliasDeclaration(n)) &&
|
|
192
|
+
n.name.text === refName
|
|
193
|
+
) {
|
|
194
|
+
foundNode = n
|
|
195
|
+
}
|
|
196
|
+
ts.forEachChild(n, findDecl)
|
|
197
|
+
}
|
|
198
|
+
findDecl(importSourceFile)
|
|
199
|
+
if (foundNode) {
|
|
200
|
+
iface = foundNode
|
|
201
|
+
}
|
|
202
|
+
} catch {}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
142
206
|
if (iface) {
|
|
143
|
-
|
|
207
|
+
let extracted: PropMeta[] = []
|
|
208
|
+
if (ts.isInterfaceDeclaration(iface)) {
|
|
209
|
+
extracted = extractPropsFromInterface(iface)
|
|
210
|
+
} else if (ts.isTypeAliasDeclaration(iface) && ts.isTypeLiteralNode(iface.type)) {
|
|
211
|
+
extracted = extractPropsFromTypeLiteral(iface.type)
|
|
212
|
+
}
|
|
144
213
|
for (const p of extracted) {
|
|
145
214
|
const d = destructuredDefaults[p.name]
|
|
146
215
|
if (d !== undefined) p.default = d
|
|
@@ -177,9 +246,63 @@ export function extractVueMetaDirect(filePath: string): {
|
|
|
177
246
|
const typeArg = callArg.typeArguments[0]!
|
|
178
247
|
const refName = getTypeRefName(typeArg)
|
|
179
248
|
if (refName) {
|
|
180
|
-
|
|
249
|
+
let iface = interfaces.get(refName)
|
|
250
|
+
if (!iface && imports.has(refName)) {
|
|
251
|
+
const relPath = imports.get(refName)!
|
|
252
|
+
const baseDir = path.dirname(filePath)
|
|
253
|
+
const absolutePath = path.resolve(baseDir, relPath)
|
|
254
|
+
const extensions = [".ts", ".d.ts", "/index.ts", "/index.d.ts"]
|
|
255
|
+
let foundPath = ""
|
|
256
|
+
if (existsSync(absolutePath) && statSync(absolutePath).isFile()) {
|
|
257
|
+
foundPath = absolutePath
|
|
258
|
+
} else {
|
|
259
|
+
for (const ext of extensions) {
|
|
260
|
+
const testPath = absolutePath + ext
|
|
261
|
+
if (existsSync(testPath)) {
|
|
262
|
+
foundPath = testPath
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (foundPath) {
|
|
268
|
+
try {
|
|
269
|
+
const importContent = readFileSync(foundPath, "utf-8")
|
|
270
|
+
const importSourceFile = ts.createSourceFile(
|
|
271
|
+
"import.ts",
|
|
272
|
+
importContent,
|
|
273
|
+
ts.ScriptTarget.Latest,
|
|
274
|
+
true,
|
|
275
|
+
ts.ScriptKind.TS
|
|
276
|
+
)
|
|
277
|
+
let foundNode: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | null =
|
|
278
|
+
null
|
|
279
|
+
function findDecl(n: ts.Node) {
|
|
280
|
+
if (
|
|
281
|
+
(ts.isInterfaceDeclaration(n) || ts.isTypeAliasDeclaration(n)) &&
|
|
282
|
+
n.name.text === refName
|
|
283
|
+
) {
|
|
284
|
+
foundNode = n
|
|
285
|
+
}
|
|
286
|
+
ts.forEachChild(n, findDecl)
|
|
287
|
+
}
|
|
288
|
+
findDecl(importSourceFile)
|
|
289
|
+
if (foundNode) {
|
|
290
|
+
iface = foundNode
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
181
296
|
if (iface) {
|
|
182
|
-
|
|
297
|
+
let baseProps: PropMeta[] = []
|
|
298
|
+
if (ts.isInterfaceDeclaration(iface)) {
|
|
299
|
+
baseProps = extractPropsFromInterface(iface)
|
|
300
|
+
} else if (
|
|
301
|
+
ts.isTypeAliasDeclaration(iface) &&
|
|
302
|
+
ts.isTypeLiteralNode(iface.type)
|
|
303
|
+
) {
|
|
304
|
+
baseProps = extractPropsFromTypeLiteral(iface.type)
|
|
305
|
+
}
|
|
183
306
|
for (const p of baseProps) {
|
|
184
307
|
const d = defaults[p.name]
|
|
185
308
|
if (d !== undefined) p.default = d
|
|
@@ -203,14 +326,40 @@ export function extractVueMetaDirect(filePath: string): {
|
|
|
203
326
|
const typeArg = node.typeArguments?.[0]
|
|
204
327
|
if (typeArg && ts.isTypeLiteralNode(typeArg)) {
|
|
205
328
|
for (const member of typeArg.members) {
|
|
206
|
-
if (
|
|
329
|
+
if (
|
|
330
|
+
(ts.isMethodSignature(member) || ts.isPropertySignature(member)) &&
|
|
331
|
+
member.name &&
|
|
332
|
+
ts.isIdentifier(member.name)
|
|
333
|
+
) {
|
|
207
334
|
const slotName = member.name.text
|
|
208
335
|
const bindings: Record<string, string> = {}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
336
|
+
|
|
337
|
+
let params: readonly ts.ParameterDeclaration[] = []
|
|
338
|
+
if (ts.isMethodSignature(member)) {
|
|
339
|
+
params = member.parameters
|
|
340
|
+
} else if (
|
|
341
|
+
ts.isPropertySignature(member) &&
|
|
342
|
+
member.type &&
|
|
343
|
+
ts.isFunctionTypeNode(member.type)
|
|
344
|
+
) {
|
|
345
|
+
params = member.type.parameters
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (params.length > 0) {
|
|
349
|
+
const param = params[0]
|
|
350
|
+
if (param?.type && ts.isTypeLiteralNode(param.type)) {
|
|
351
|
+
for (const pm of param.type.members) {
|
|
352
|
+
if (
|
|
353
|
+
ts.isPropertySignature(pm) &&
|
|
354
|
+
pm.name &&
|
|
355
|
+
ts.isIdentifier(pm.name) &&
|
|
356
|
+
pm.type
|
|
357
|
+
) {
|
|
358
|
+
bindings[pm.name.text] = pm.type.getText()
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
} else if (param?.type) {
|
|
362
|
+
bindings["props"] = param.type.getText()
|
|
214
363
|
}
|
|
215
364
|
}
|
|
216
365
|
slots.push({ name: slotName, bindings, description: "" })
|
|
@@ -22,7 +22,6 @@ export function filterProps(
|
|
|
22
22
|
|
|
23
23
|
export function generateThemeCode(componentName: string, theme: ThemeMeta): string {
|
|
24
24
|
const componentConfig: Record<string, unknown> = {
|
|
25
|
-
...(theme.slots.length ? { slots: theme.slots } : {}),
|
|
26
25
|
...(Object.keys(theme.base).length ? { base: theme.base } : {}),
|
|
27
26
|
...(Object.keys(theme.variants).length ? { variants: theme.variants } : {}),
|
|
28
27
|
...(theme.compoundVariants.length ? { compoundVariants: theme.compoundVariants } : {}),
|
|
@@ -36,7 +35,7 @@ import { defineConfig } from "astro/config"
|
|
|
36
35
|
export default defineConfig({
|
|
37
36
|
integrations: [
|
|
38
37
|
ui({
|
|
39
|
-
|
|
38
|
+
ui: {
|
|
40
39
|
${componentName}: ${JSON.stringify(componentConfig, null, 6).replace(/\n/g, "\n ")}
|
|
41
40
|
}
|
|
42
41
|
})
|
package/src/env.d.ts
CHANGED
|
@@ -17,18 +17,6 @@ declare module "virtual:rimelight-ui-docs-meta" {
|
|
|
17
17
|
export default meta
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
declare module "turndown" {
|
|
21
|
-
class TurndownService {
|
|
22
|
-
constructor(options?: Record<string, unknown>)
|
|
23
|
-
turndown(html: Node | string): string
|
|
24
|
-
use(plugin: () => void): void
|
|
25
|
-
}
|
|
26
|
-
export default TurndownService
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
declare module "turndown-plugin-gfm" {
|
|
30
|
-
export function gfm(): void
|
|
31
|
-
}
|
|
32
20
|
|
|
33
21
|
declare namespace App {
|
|
34
22
|
interface Locals {
|