feat(ui): scaffold the blueprint SPA — vite 7 + tailwind 4 + kit theme
New workspace member built on @unom/ui 0.9.1 + @unom/app-ui 0.2.0 + @punktfunk/plugin-kit/react, effect 4.0.0-beta.99 exact. base:'./' with build straight into ../plugin/dist/ui; :5599 dev server proxies /api to the plugin dev API only in live mode. styles.css = tailwind + tw-animate + the kit's violet theme (NOT @unom/ui's own neutral palette) + bare-dir @source globs over the design-system dist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<!-- The console pins dark; the standalone dev tab matches it. Removing `dark` yields light. -->
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ROM Manager — Punktfunk</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "punktfunk-plugin-rom-manager-ui",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "The ROM Manager plugin SPA — the blueprint UI every Punktfunk plugin copies: @unom/ui + @unom/app-ui console polish, an Effect-native data layer derived from the shared HttpApi contract, and a zero-host mock dev loop.",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:live": "VITE_API_MODE=live vite",
|
||||
"build": "vite build",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"storybook": "storybook dev -p 6013",
|
||||
"build-storybook": "storybook build",
|
||||
"screenshots": "node tools/screenshots.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@effect/atom-react": "4.0.0-beta.99",
|
||||
"@fontsource-variable/geist": "^5.2.9",
|
||||
"@punktfunk/plugin-kit": "^0.1.1",
|
||||
"@unom/app-ui": "^0.2.0",
|
||||
"@unom/style": "^0.4.4",
|
||||
"@unom/ui": "^0.9.1",
|
||||
"effect": "4.0.0-beta.99",
|
||||
"lucide-react": "^0.469.0",
|
||||
"motion": "^12.42.2",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rom-manager/contract": "workspace:*",
|
||||
"@storybook/react-vite": "^10.4.6",
|
||||
"@tailwindcss/vite": "^4.3.2",
|
||||
"@types/node": "^22.20.0",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"playwright": "^1.61.1",
|
||||
"storybook": "^10.4.6",
|
||||
"tailwindcss": "^4.3.2",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.6"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// The flat route list, shared by App.tsx and the pages (kept out of App.tsx so pages
|
||||
// can type their `navigate` prop without importing the shell).
|
||||
export const ROUTES = [
|
||||
"overview",
|
||||
"library",
|
||||
"sources",
|
||||
"emulators",
|
||||
"settings",
|
||||
] as const;
|
||||
|
||||
export type Route = (typeof ROUTES)[number];
|
||||
|
||||
export type PageProps = {
|
||||
readonly navigate: (route: Route) => void;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/* The plugin UI's single stylesheet entry. The kit theme.css carries the console's
|
||||
* violet identity (shadcn + @unom token vocabularies, Penner easings, `.dark` palette)
|
||||
* so the embedded iframe is indistinguishable from the console around it. Do NOT import
|
||||
* @unom/ui/styles/theme.css — that is the library's own neutral palette. */
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@import "@punktfunk/plugin-kit/theme.css";
|
||||
@import "@fontsource-variable/geist";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
/* Scan the design system's built output so its class names survive Tailwind's purge.
|
||||
* Bare directory globs on purpose — rebased file globs proved flaky. */
|
||||
@source "../node_modules/@unom/ui/dist";
|
||||
@source "../node_modules/@unom/app-ui/dist";
|
||||
|
||||
body {
|
||||
@apply bg-background font-sans text-foreground antialiased;
|
||||
}
|
||||
|
||||
/* @unom/ui uses `text-secondary` as muted TEXT (EmptyState captions, badge text);
|
||||
* the console palette maps --secondary to a dark SURFACE, which renders that text
|
||||
* unreadable on dark. Re-point the text utility at the muted foreground until the
|
||||
* console/theme unification resolves the vocabulary collision upstream. */
|
||||
.text-secondary {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"types": ["vite/client"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src", ".storybook", "vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Vite config for the plugin SPA. `base: "./"` because the console serves the built
|
||||
// bundle from /plugin-ui/<id>/ — asset URLs must stay relative to survive the proxy.
|
||||
// Dev has two modes (see src/mocks/): plain `bun run dev` runs entirely on fixtures,
|
||||
// `bun run dev:live` proxies /api to the plugin's dev server (plugin/src/dev.ts, :5885).
|
||||
|
||||
import { fileURLToPath } from "node:url";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
base: "./",
|
||||
plugins: [react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) },
|
||||
},
|
||||
// Scan only the app entry — without this the dev dep-scanner also crawls any
|
||||
// storybook-static/ build output sitting in the workspace and errors out.
|
||||
optimizeDeps: { entries: ["index.html"] },
|
||||
build: {
|
||||
outDir: "../plugin/dist/ui",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
port: 5599,
|
||||
...(process.env.VITE_API_MODE === "live"
|
||||
? {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: process.env.ROM_MANAGER_URL ?? "http://127.0.0.1:5885",
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user