feat(storybook): auto-regenerate themes.css on color-registry changes (#16919)

* feat(storybook): auto-regenerate themes.css on color-registry changes

Add a Vite plugin that watches `color-registry.ts` and
`tailwind-color-palette.json`, then re-runs `storybook:css` and
triggers a full reload.

Also pass explicit svelte config path and watch UI package dist for
changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Vaclav Vancura <commit@vancura.dev>

* fix(storybook): queue file changes during in-flight theme regeneration

Previously, saves that arrived while storybook:css was already
running were silently dropped. Now a queued flag is set and a
second regeneration runs after the first completes, so
back-to-back edits are never lost.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Vaclav Vancura <commit@vancura.dev>

* fix(storybook): use node: prefix for Node.js built-in imports

Signed-off-by: Vaclav Vancura <commit@vancura.dev>

---------

Signed-off-by: Vaclav Vancura <commit@vancura.dev>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Václav Vančura 2026-04-17 14:27:59 +02:00 committed by GitHub
parent f7f993085f
commit 7259f66339
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,17 +17,81 @@
***********************************************************************/
/* eslint-env node */
import { join } from 'path';
import * as path from 'path';
import { join } from 'node:path';
import * as path from 'node:path';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { svelteTesting } from '@testing-library/svelte/vite';
import tailwindcss from '@tailwindcss/vite';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
import chokidar from 'chokidar';
import { defineConfig } from 'vite';
import { fileURLToPath } from 'url';
import { fileURLToPath } from 'node:url';
const execAsync = promisify(exec);
let filename = fileURLToPath(import.meta.url);
const PACKAGE_ROOT = path.dirname(filename);
const ROOT_DIR = path.join(PACKAGE_ROOT, '..');
// Vite plugin to watch color-registry and regenerate themes.css
function colorRegistryWatcher() {
let isRegenerating = false;
let queuedRegeneration = false;
async function regenerate(server) {
try {
await execAsync('pnpm run storybook:css', { cwd: ROOT_DIR });
console.log('[color-registry-watcher] themes.css regenerated successfully\n');
server.ws.send({
type: 'full-reload',
path: '*',
});
} catch (error) {
console.error('[color-registry-watcher] Failed to regenerate themes.css:', error.message);
}
}
return {
name: 'color-registry-watcher',
configureServer(server) {
const filesToWatch = [
path.join(ROOT_DIR, 'packages/main/src/plugin/color-registry.ts'),
path.join(ROOT_DIR, 'tailwind-color-palette.json'),
];
const watcher = chokidar.watch(filesToWatch, {
persistent: true,
ignoreInitial: true,
});
watcher.on('change', async changedFile => {
if (isRegenerating) {
queuedRegeneration = true;
return;
}
isRegenerating = true;
console.log(`\n[color-registry-watcher] ${path.basename(changedFile)} changed, regenerating themes.css...`);
await regenerate(server);
while (queuedRegeneration) {
queuedRegeneration = false;
console.log('[color-registry-watcher] Processing queued change...');
await regenerate(server);
}
isRegenerating = false;
});
server.httpServer?.on('close', () => {
watcher.close();
});
},
};
}
// https://vitejs.dev/config/
export default defineConfig({
@ -38,7 +102,12 @@ export default defineConfig({
'/@/': join(PACKAGE_ROOT, 'src') + '/',
},
},
plugins: [tailwindcss(), svelte(), svelteTesting()],
plugins: [
tailwindcss(),
svelte({ configFile: '../svelte.config.js', hot: true }),
svelteTesting(),
colorRegistryWatcher(),
],
test: {
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
globals: true,
@ -53,6 +122,10 @@ export default defineConfig({
fs: {
strict: true,
},
watch: {
// Watch the UI package dist folder for changes
ignored: ['!**/node_modules/@podman-desktop/ui-svelte/**'],
},
},
build: {
sourcemap: true,