docs: fix block preview

This commit is contained in:
zernonia 2024-10-22 12:46:09 +02:00
parent 96800c4c44
commit 64c0371280
9 changed files with 379 additions and 304 deletions

View File

@ -0,0 +1,237 @@
<script setup lang="ts">
import { useConfigStore } from '@/stores/config'
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
import MagicString from 'magic-string'
import { codeToHtml } from 'shiki'
import { reactive, ref, watch } from 'vue'
import { compileScript, parse, walk } from 'vue/compiler-sfc'
import { cssVariables } from '../config/shiki'
import BlockCopyButton from './BlockCopyButton.vue'
import StyleSwitcher from './StyleSwitcher.vue'
// import { V0Button } from '@/components/v0-button'
import { Badge } from '@/lib/registry/new-york/ui/badge'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/lib/registry/new-york/ui/resizable'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/new-york/ui/tabs'
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/new-york/ui/toggle-group'
import BlockPreview from './BlockPreview.vue'
const props = defineProps<{
name: string
}>()
const { style, codeConfig } = useConfigStore()
const isLoading = ref(true)
const tabValue = ref('preview')
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
const rawString = ref('')
const codeHtml = ref('')
const metadata = reactive({
description: null as string | null,
iframeHeight: null as string | null,
containerClass: null as string | null,
})
function removeScript(code: string) {
const s = new MagicString(code)
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
let match
// eslint-disable-next-line no-cond-assign
while ((match = scriptTagRegex.exec(code)) !== null) {
const start = match.index
const end = match.index + match[0].length
s.overwrite(start, end, '') // Replace the script tag with an empty string
}
return s.trimStart().toString()
}
function transformImportPath(code: string) {
const s = new MagicString(code)
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return s.toString()
}
watch([style, codeConfig], async () => {
try {
const baseRawString = await import(`../../../src/lib/registry/${style.value}/block/${props.name}.vue?raw`).then(res => res.default.trim())
rawString.value = transformImportPath(removeScript(baseRawString))
if (!metadata.description) {
const { descriptor } = parse(baseRawString)
const ast = compileScript(descriptor, { id: '' })
walk(ast.scriptAst, {
enter(node: any) {
const declaration = node.declaration
// Check if the declaration is a variable declaration
if (declaration?.type === 'VariableDeclaration') {
// Extract variable names and their values
declaration.declarations.forEach((decl: any) => {
// @ts-expect-error ignore missing type
metadata[decl.id.name] = decl.init ? decl.init.value : null
})
}
},
})
}
codeHtml.value = await codeToHtml(rawString.value, {
lang: 'vue',
theme: cssVariables,
})
}
catch (err) {
console.error(err)
}
}, { immediate: true, deep: true })
</script>
<template>
<Tabs
:id="name"
v-model="tabValue"
class="relative grid w-full scroll-m-20 gap-4"
:style=" {
'--container-height': metadata.iframeHeight ?? '600px',
}"
>
<div class="flex flex-col items-center gap-4 sm:flex-row">
<div class="flex items-center gap-2">
<TabsList class="hidden sm:flex">
<TabsTrigger value="preview">
Preview
</TabsTrigger>
<TabsTrigger value="code">
Code
</TabsTrigger>
</TabsList>
<div class="hidden items-center gap-2 sm:flex">
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<div class="flex items-center gap-2">
<a :href="`#${name}`">
<Badge variant="outline">{{ name }}</Badge>
</a>
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<Info class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="right"
:side-offset="10"
class="text-sm"
>
{{ metadata.description }}
</PopoverContent>
</Popover>
</div>
</div>
</div>
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
<ToggleGroup
type="single"
default-value="100"
@update:model-value="(value) => {
resizableRef?.resize(parseInt(value as string))
}"
>
<ToggleGroupItem
value="100"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Monitor class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="60"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Tablet class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="30"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Smartphone class="h-3.5 w-3.5" />
</ToggleGroupItem>
</ToggleGroup>
</div>
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<StyleSwitcher class="h-7" />
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<CircleHelp class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="top"
:side-offset="20"
class="space-y-3 rounded-[0.5rem] text-sm"
>
<p class="font-medium">
What is the difference between the New York and Default style?
</p>
<p>
A style comes with its own set of components, animations,
icons and more.
</p>
<p>
The <span class="font-medium">Default</span> style has
larger inputs, uses lucide-vue-next for icons and
tailwindcss-animate for animations.
</p>
<p>
The <span class="font-medium">New York</span> style ships
with smaller buttons and inputs. It also uses shadows on cards
and buttons.
</p>
</PopoverContent>
</Popover>
<Separator orientation="vertical" class="mx-2 h-4" />
<BlockCopyButton :code="rawString" />
<!-- <V0Button
name="{block.name}"
description="{block.description" || "Edit in v0"}
code="{block.code}"
style="{block.style}"
/> -->
</div>
</div>
<TabsContent
v-show="tabValue === 'preview'"
force-mount
value="preview"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
>
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
<ResizablePanel
id="block-resizable-panel-1"
ref="resizableRef"
:default-size="100"
:min-size="30"
:as-child="true"
>
<BlockPreview :name="name" styles="default" :container-class="metadata.containerClass ?? ''" container />
</ResizablePanel>
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
</ResizablePanelGroup>
</TabsContent>
<TabsContent value="code" class="h-[--container-height]">
<div
class="language-vue !h-full !max-h-[none] !mt-0"
v-html="codeHtml"
/>
</TabsContent>
</Tabs>
</template>

View File

@ -2,11 +2,11 @@
import { useUrlSearchParams } from '@vueuse/core' import { useUrlSearchParams } from '@vueuse/core'
import ComponentLoader from './ComponentLoader.vue' import ComponentLoader from './ComponentLoader.vue'
const params = useUrlSearchParams('hash-params') const params = useUrlSearchParams('history')
</script> </script>
<template> <template>
<div v-if="params.name && params.style" :class="params.containerClass"> <div v-if="params.name" :class="params.containerClass">
<ComponentLoader :key="params.style?.toString()" :name="params.name?.toString()" :type-name="'block'" /> <ComponentLoader :key="params.style?.toString()" :name="params.name?.toString()" :type-name="'block'" />
</div> </div>
</template> </template>

View File

@ -1,245 +1,40 @@
<script setup lang="ts"> <script setup lang="ts">
import { useConfigStore } from '@/stores/config' import { computed, ref } from 'vue'
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
import MagicString from 'magic-string'
import { codeToHtml } from 'shiki'
import { reactive, ref, watch } from 'vue'
import { compileScript, parse, walk } from 'vue/compiler-sfc'
import { cssVariables } from '../config/shiki'
import BlockCopyButton from './BlockCopyButton.vue'
import Spinner from './Spinner.vue' import Spinner from './Spinner.vue'
import StyleSwitcher from './StyleSwitcher.vue'
// import { V0Button } from '@/components/v0-button'
import { Badge } from '@/lib/registry/new-york/ui/badge'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/lib/registry/new-york/ui/resizable'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/new-york/ui/tabs'
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/new-york/ui/toggle-group'
const props = defineProps<{ const props = defineProps<{
name: string name: string
styles?: string
containerClass?: string
container?: boolean
}>() }>()
const { style, codeConfig } = useConfigStore()
const isLoading = ref(true) const isLoading = ref(true)
const tabValue = ref('preview')
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
const rawString = ref('') const iframeURL = computed(() => {
const codeHtml = ref('') const url = new URL(`${window.location.origin}/blocks/renderer`)
const metadata = reactive({ Object.entries(props).forEach(([key, value]) => {
description: null as string | null, if (value)
iframeHeight: null as string | null, url.searchParams.append(key, value as string)
containerClass: null as string | null, })
return url.href
}) })
function removeScript(code: string) {
const s = new MagicString(code)
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
let match
// eslint-disable-next-line no-cond-assign
while ((match = scriptTagRegex.exec(code)) !== null) {
const start = match.index
const end = match.index + match[0].length
s.overwrite(start, end, '') // Replace the script tag with an empty string
}
return s.trimStart().toString()
}
function transformImportPath(code: string) {
const s = new MagicString(code)
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return s.toString()
}
watch([style, codeConfig], async () => {
try {
const baseRawString = await import(`../../../src/lib/registry/${style.value}/block/${props.name}.vue?raw`).then(res => res.default.trim())
rawString.value = transformImportPath(removeScript(baseRawString))
if (!metadata.description) {
const { descriptor } = parse(baseRawString)
const ast = compileScript(descriptor, { id: '' })
walk(ast.scriptAst, {
enter(node: any) {
const declaration = node.declaration
// Check if the declaration is a variable declaration
if (declaration?.type === 'VariableDeclaration') {
// Extract variable names and their values
declaration.declarations.forEach((decl: any) => {
// @ts-expect-error ignore missing type
metadata[decl.id.name] = decl.init ? decl.init.value : null
})
}
},
})
}
codeHtml.value = await codeToHtml(rawString.value, {
lang: 'vue',
theme: cssVariables,
})
}
catch (err) {
console.error(err)
}
}, { immediate: true, deep: true })
</script> </script>
<template> <template>
<Tabs <div class="relative rounded-lg border overflow-hidden bg-background" :class="[container ? '' : 'aspect-[4/2.5]']">
:id="name"
v-model="tabValue"
class="relative grid w-full scroll-m-20 gap-4"
:style=" {
'--container-height': metadata.iframeHeight ?? '600px',
}"
>
<div class="flex flex-col items-center gap-4 sm:flex-row">
<div class="flex items-center gap-2">
<TabsList class="hidden sm:flex">
<TabsTrigger value="preview">
Preview
</TabsTrigger>
<TabsTrigger value="code">
Code
</TabsTrigger>
</TabsList>
<div class="hidden items-center gap-2 sm:flex">
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<div class="flex items-center gap-2">
<a :href="`#${name}`">
<Badge variant="outline">{{ name }}</Badge>
</a>
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<Info class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="right"
:side-offset="10"
class="text-sm"
>
{{ metadata.description }}
</PopoverContent>
</Popover>
</div>
</div>
</div>
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
<ToggleGroup
type="single"
default-value="100"
@update:model-value="(value) => {
resizableRef?.resize(parseInt(value))
}"
>
<ToggleGroupItem
value="100"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Monitor class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="60"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Tablet class="h-3.5 w-3.5" />
</ToggleGroupItem>
<ToggleGroupItem
value="30"
class="h-[22px] w-[22px] rounded-sm p-0"
>
<Smartphone class="h-3.5 w-3.5" />
</ToggleGroupItem>
</ToggleGroup>
</div>
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<StyleSwitcher class="h-7" />
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<CircleHelp class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="top"
:side-offset="20"
class="space-y-3 rounded-[0.5rem] text-sm"
>
<p class="font-medium">
What is the difference between the New York and Default style?
</p>
<p>
A style comes with its own set of components, animations,
icons and more.
</p>
<p>
The <span class="font-medium">Default</span> style has
larger inputs, uses lucide-vue-next for icons and
tailwindcss-animate for animations.
</p>
<p>
The <span class="font-medium">New York</span> style ships
with smaller buttons and inputs. It also uses shadows on cards
and buttons.
</p>
</PopoverContent>
</Popover>
<Separator orientation="vertical" class="mx-2 h-4" />
<BlockCopyButton :code="rawString" />
<!-- <V0Button
name="{block.name}"
description="{block.description" || "Edit in v0"}
code="{block.code}"
style="{block.style}"
/> -->
</div>
</div>
<TabsContent
v-show="tabValue === 'preview'"
force-mount
value="preview"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
>
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
<ResizablePanel
id="block-resizable-panel-1"
ref="resizableRef"
class="relative rounded-lg border bg-background transition-all "
:default-size="100"
:min-size="30"
>
<div v-if="isLoading" class="flex items-center justify-center h-full"> <div v-if="isLoading" class="flex items-center justify-center h-full">
<Spinner /> <Spinner />
</div> </div>
<div
:class="[container ? 'w-full' : 'absolute inset-0 hidden w-[1600px] bg-background md:block']"
>
<iframe <iframe
v-show="!isLoading" v-show="!isLoading"
:src="`/blocks/renderer#name=${name}&style=${style}&containerClass=${encodeURIComponent(metadata.containerClass ?? '')}`" :src="iframeURL"
class="relative z-20 w-full bg-background h-[--container-height]" class="relative z-20 w-full bg-background" :class="[container ? 'h-[--container-height]' : 'size-full']"
@load="isLoading = false" @load="isLoading = false"
/> />
</ResizablePanel> </div>
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" /> </div>
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
</ResizablePanelGroup>
</TabsContent>
<TabsContent value="code" class="h-[--container-height]">
<div
class="language-vue !h-full !max-h-[none] !mt-0"
v-html="codeHtml"
/>
</TabsContent>
</Tabs>
</template> </template>

View File

@ -9,7 +9,7 @@ import PageHeader from '../components/PageHeader.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue' import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue' import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import BlockPreview from './BlockPreview.vue' import BlockContainer from './BlockContainer.vue'
const blocks = ref<string[]>([]) const blocks = ref<string[]>([])
@ -48,6 +48,6 @@ import('../../../__registry__/index').then((res) => {
</PageHeader> </PageHeader>
<section id="blocks" class="grid scroll-mt-24 gap-24 lg:gap-48"> <section id="blocks" class="grid scroll-mt-24 gap-24 lg:gap-48">
<BlockPreview v-for="block in blocks" :key="block" :name="block" /> <BlockContainer v-for="block in blocks" :key="block" :name="block" />
</section> </section>
</template> </template>

View File

@ -1,4 +1,5 @@
export { default as APITable } from './APITable.vue' export { default as APITable } from './APITable.vue'
export { default as BlockPreview } from './BlockPreview.vue'
export { default as Callout } from './Callout.vue' export { default as Callout } from './Callout.vue'
export { default as CodeWrapper } from './CodeWrapper' export { default as CodeWrapper } from './CodeWrapper'
export { default as ComponentPreview } from './ComponentPreview.vue' export { default as ComponentPreview } from './ComponentPreview.vue'

View File

@ -28,6 +28,16 @@
--ring: 240 5% 64.9%; --ring: 240 5% 64.9%;
--radius: 0.5rem; --radius: 0.5rem;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
--vis-primary-color: var(--primary); --vis-primary-color: var(--primary);
--vis-secondary-color: 160 81% 40%; --vis-secondary-color: 160 81% 40%;
--vis-text-color: var(--muted-foreground); --vis-text-color: var(--muted-foreground);
@ -64,6 +74,15 @@
--border: 240 3.7% 15.9%; --border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%; --input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%; --ring: 240 4.9% 83.9%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
} }
@ -97,19 +116,6 @@
src: url("/fonts/Geist/GeistVariableVF.woff2") format("woff2"); src: url("/fonts/Geist/GeistVariableVF.woff2") format("woff2");
} }
/* === Scrollbars === */
::-webkit-scrollbar {
@apply w-2;
@apply h-2;
}
::-webkit-scrollbar-track {
@apply !bg-muted;
}
::-webkit-scrollbar-thumb {
@apply rounded-sm !bg-muted-foreground/30;
}
/* Firefox */ /* Firefox */
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */ /* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
@ -121,15 +127,6 @@
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3); scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
} }
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.antialised { .antialised {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;

View File

@ -1501,6 +1501,20 @@ export const Index = {
component: () => import("../src/lib/registry/default/block/Dashboard07.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/block/Dashboard07.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Dashboard07.vue"], files: ["../src/lib/registry/default/block/Dashboard07.vue"],
}, },
"Sidebar01": {
name: "Sidebar01",
type: "components:block",
registryDependencies: ["breadcrumb","dropdown-menu","label","separator","sidebar"],
component: () => import("../src/lib/registry/default/block/Sidebar01.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Sidebar01.vue"],
},
"Sidebar07": {
name: "Sidebar07",
type: "components:block",
registryDependencies: ["avatar","breadcrumb","collapsible","dropdown-menu","separator","sidebar"],
component: () => import("../src/lib/registry/default/block/Sidebar07.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Sidebar07.vue"],
},
}, "new-york": { }, "new-york": {
"AccordionDemo": { "AccordionDemo": {
name: "AccordionDemo", name: "AccordionDemo",
@ -3000,5 +3014,19 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/block/Dashboard07.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/block/Dashboard07.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Dashboard07.vue"], files: ["../src/lib/registry/new-york/block/Dashboard07.vue"],
}, },
"Sidebar01": {
name: "Sidebar01",
type: "components:block",
registryDependencies: ["breadcrumb","dropdown-menu","label","separator","sidebar"],
component: () => import("../src/lib/registry/new-york/block/Sidebar01.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Sidebar01.vue"],
},
"Sidebar07": {
name: "Sidebar07",
type: "components:block",
registryDependencies: ["avatar","breadcrumb","collapsible","dropdown-menu","separator","sidebar"],
component: () => import("../src/lib/registry/new-york/block/Sidebar07.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Sidebar07.vue"],
},
}, },
} }

View File

@ -80,10 +80,17 @@ for (const style of styles) {
continue continue
const files = item.files?.map((file) => { const files = item.files?.map((file) => {
let content = fs.readFileSync( let content: string = ''
try {
content = fs.readFileSync(
path.join(process.cwd(), 'src/lib/registry', style.name, file), path.join(process.cwd(), 'src/lib/registry', style.name, file),
'utf8', 'utf8',
) )
}
catch (err) {
console.log(`'${file}' is missing`)
}
// Replace Windows-style newlines with Unix-style newlines // Replace Windows-style newlines with Unix-style newlines
content = content.replace(/\r\n/g, newLine) content = content.replace(/\r\n/g, newLine)
@ -99,7 +106,7 @@ for (const style of styles) {
files, files,
} }
const payloadStr = JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine) const payloadStr = `${JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine)}\n`
fs.writeFileSync( fs.writeFileSync(
path.join(targetPath, `${item.name}.json`), path.join(targetPath, `${item.name}.json`),

View File

@ -51,6 +51,16 @@ export default {
DEFAULT: 'hsl(var(--card))', DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))', foreground: 'hsl(var(--card-foreground))',
}, },
sidebar: {
'DEFAULT': 'hsl(var(--sidebar-background))',
'foreground': 'hsl(var(--sidebar-foreground))',
'primary': 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
'accent': 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
'border': 'hsl(var(--sidebar-border))',
'ring': 'hsl(var(--sidebar-ring))',
},
}, },
borderRadius: { borderRadius: {
xl: 'calc(var(--radius) + 4px)', xl: 'calc(var(--radius) + 4px)',