Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 282 additions & 53 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

10 changes: 3 additions & 7 deletions sites/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"build": "pnpm build:content && pnpm build:svelte",
"build:content": "velite && tsx scripts/velite/velite-update-json.ts",
"build:svelte": "vite build",
"build:screenshots": "tsx scripts/capture-screenshots.ts",
"build:screenshots": "tsx scripts/capture-registry.ts",
"replace:velite": "tsx scripts/velite/velite-watch-output.ts",
"preview": "vite preview",
"sync": "svelte-kit sync",
Expand All @@ -30,7 +30,6 @@
"@dnd-kit-svelte/sortable": "^0.0.8",
"@dnd-kit-svelte/utilities": "^0.0.8",
"@internationalized/date": "^3.5.6",
"@layerstack/utils": "^1.0.0",
"@lucide/svelte": "^0.482.0",
"@prettier/sync": "0.3.0",
"@shadcn-svelte/registry": "workspace:*",
Expand All @@ -47,8 +46,6 @@
"@types/lodash.template": "^4.5.3",
"@types/mdast": "^4.0.4",
"@types/node": "^20.14.10",
"@unovis/svelte": "1.4.3",
"@unovis/ts": "1.4.3",
"acorn": "^8.13.0",
"bits-ui": "^1.4.7",
"clsx": "^2.1.1",
Expand All @@ -58,6 +55,7 @@
"embla-carousel-svelte": "8.1.6",
"estree-walker": "^3.0.3",
"formsnap": "2.0.0",
"globby": "^14.1.0",
"hast-util-to-html": "^9.0.1",
"layerchart": "2.0.0-next.6",
"lodash.template": "^4.5.0",
Expand All @@ -74,17 +72,15 @@
"rimraf": "^4.4.1",
"runed": "^0.27.0",
"shadcn-svelte": "workspace:*",
"sharp": "^0.34.1",
"shiki": "^1.2.1",
"svelte": "^5.16.1",
"svelte-check": "^4.2.1",
"svelte-local-storage-store": "^0.6.4",
"svelte-persisted-store": "^0.11.0",
"svelte-sonner": "^1.0.1",
"sveltekit-superforms": "^2.19.1",
"tailwind-merge": "^3.0.2",
"tailwind-variants": "^0.2.1",
"tailwindcss": "^4.1.7",
"ts-blank-space": "^0.4.4",
"tslib": "^2.6.3",
"tsx": "^4.16.2",
"tw-animate-css": "^1.2.4",
Expand Down
8 changes: 7 additions & 1 deletion sites/docs/scripts/build-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,13 @@ export const Index = {`;
const themeCSS = [];
for (const baseColor of baseColors) {
const base = generateBaseColorTemplate(baseColor);
const zincCssVars = generateBaseColorTemplate("zinc");

themeCSS.push(
template(THEME_STYLES_WITH_VARIABLES)({
colors: {
...zincCssVars.cssVars,
...baseColorsOKLCH[baseColor as keyof typeof baseColorsOKLCH],
...base.cssVars,
},
theme: baseColor,
})
Expand All @@ -206,4 +207,9 @@ export const Index = {`;
// ----------------------------------------------------------------------------

writeFileWithDirs(path.join(THEMES_CSS_PATH, `themes.css`), themeCSS.join("\n\n"), "utf-8");
writeFileWithDirs(
path.resolve("src", "styles", "old-themes.css"),
themeCSS.join("\n\n"),
"utf-8"
);
}
101 changes: 101 additions & 0 deletions sites/docs/scripts/capture-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import fs from "node:fs";
import promises from "node:fs/promises";
import path from "node:path";
import puppeteer, { Browser } from "puppeteer";
import { globby } from "globby";
import sharp from "sharp";

import { getAllBlockIds } from "../src/lib/blocks.js";

const SCREENSHOT_PATH = path.join(process.cwd(), "static/img/registry");

async function captureBlockScreenshot(browser: Browser, block: string) {
const pageUrl = `http://localhost:5173/view/${block}`;
const page = await browser.newPage();
await page.goto(pageUrl);

for (const theme of ["light", "dark"]) {
const screenshotPath = path.join(SCREENSHOT_PATH, `${block}-${theme}-uncompressed.png`);
if (fs.existsSync(screenshotPath)) {
fs.unlinkSync(screenshotPath);
}

await page.evaluate((currentTheme) => {
localStorage.setItem("mode-watcher-mode", currentTheme);
}, theme);

await page.reload({ waitUntil: "networkidle2" });

if (block.startsWith("chart") || block.startsWith("dashboard")) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}

await page.evaluate(() => {
const indicator = document.querySelector("[data-tailwind-indicator]");
if (indicator) indicator.remove();
});

await page.screenshot({ path: screenshotPath });
}

await page.close();
}

async function captureScreenshots() {
if (!fs.existsSync(SCREENSHOT_PATH)) {
fs.mkdirSync(SCREENSHOT_PATH, { recursive: true });
}

const blocks = getAllBlockIds();

const browser = await puppeteer.launch({
defaultViewport: {
width: 1440,
height: 900,
deviceScaleFactor: 2,
},
});

for (const block of blocks) {
await captureBlockScreenshot(browser, block);
console.log(`✅ Captured ${block}`);
}

await browser.close();
}

async function compressImages(): Promise<void> {
const files = await globby([`${SCREENSHOT_PATH}/**/*-uncompressed.png`], { absolute: true });

await Promise.all(
files.map(async (file) => {
const beforeStat = await promises.stat(file);
const beforeKB = (beforeStat.size / 1024).toFixed(1);

console.log(`🔄 Compressing ${path.basename(file)} (before: ${beforeKB} KB)…`);
const out = file.replace("-uncompressed", "");
await sharp(file).png({ compressionLevel: 9, quality: 75 }).toFile(out);

const afterStat = await promises.stat(out);
const afterKB = (afterStat.size / 1024).toFixed(1);
const delta = (((afterStat.size - beforeStat.size) / beforeStat.size) * 100).toFixed(1);

fs.unlinkSync(file);

console.log(
`✅ ${path.basename(file)}: ${beforeKB} KB → ${afterKB} KB (${delta}% change)`
);
})
);
}

try {
console.log("🎬 Capturing registry screenshots...");
await captureScreenshots();
console.log("⚙️ Converting PNGs to WebP...");
await compressImages();
console.log("✨ Done!");
} catch (error) {
console.error("❌ Error:", error);
process.exit(1);
}
2 changes: 1 addition & 1 deletion sites/docs/src/__registry__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ export const Index = {
"drawer-demo": {
name: "drawer-demo",
type: "registry:example",
registryDependencies: ["drawer","button"],
registryDependencies: ["drawer","button","chart"],
component: () => import("../lib/registry/examples/drawer-demo.svelte").then((m) => m.default),
files: ["../lib/registry/examples/drawer-demo.svelte"],
raw: () => import("../lib/registry/examples/drawer-demo.svelte?raw").then((m) => m.default),
Expand Down
4 changes: 0 additions & 4 deletions sites/docs/src/lib/active-theme.ts

This file was deleted.

77 changes: 3 additions & 74 deletions sites/docs/src/lib/blocks.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
import { z } from "zod";
import type { Component } from "svelte";
import { type Highlighter, getHighlighter } from "shiki";
import { Blocks } from "../__registry__/blocks.js";
import { lambdaStudioBlackout } from "../styles/dark.js";
import { blockMeta } from "$lib/registry/registry-block-meta.js";

export type RawBlock = {
raw: () => Promise<string>;
component: () => Promise<Component>;
};

// This also defines the order they appear on the blocks page.
export const BLOCK_WHITELIST: BlockName[] = [
"sidebar-01",
"sidebar-02",
"sidebar-03",
"sidebar-04",
"sidebar-05",
"sidebar-06",
"sidebar-07",
"sidebar-08",
"sidebar-09",
"sidebar-10",
"sidebar-11",
"sidebar-12",
"sidebar-13",
"sidebar-14",
"sidebar-15",
"login-01",
];

export type BlockName = keyof typeof Blocks;

export const blockSchema = z.object({
Expand All @@ -46,57 +23,9 @@ export const blockSchema = z.object({

export type Block = z.infer<typeof blockSchema>;

export function getAllBlockIds(): readonly BlockName[] {
const blocks = Object.keys(Blocks) as BlockName[];
return blocks.map((name) => name).filter((b) => BLOCK_WHITELIST.includes(b));
}

export async function getBlock(name: BlockName) {
const block = Blocks[name];
const content = await getBlockContent(name);

return blockSchema.parse({ name, ...block, ...content });
}

async function getBlockCode(name: BlockName) {
const block = Blocks[name];
const code = await block.raw();
// use 2 spaces rather than tabs, making it the same as the rest of the codeblocks in /docs
const detabbed = code.replaceAll("\t", " ");
return detabbed;
}

async function getBlockContent(name: BlockName) {
const raw = await getBlockCode(name);
const { description, iframeHeight, className } = blockMeta[name];
const code = raw.replaceAll(`$lib/registry/`, "$lib/components/");

return {
description,
code,
container: {
height: iframeHeight,
className,
},
};
}

let highlighter: Highlighter;

export async function highlightCode(code: string) {
if (!highlighter) {
highlighter = await getHighlighter({
langs: ["svelte"],
themes: [lambdaStudioBlackout],
});
}

const html = highlighter.codeToHtml(code, {
lang: "svelte",
theme: "Lambda Studio - Blackout",
});

return html;
export function getAllBlockIds(): string[] {
const blocks = Object.keys(Blocks) as string[];
return blocks.filter((b) => !b.startsWith("chart-"));
}

export function isBlock(name: string): name is BlockName {
Expand Down
70 changes: 0 additions & 70 deletions sites/docs/src/lib/components/docs/block-copy-code-button.svelte

This file was deleted.

Loading