Add shadcn/ui + Storybook

This commit is contained in:
cte
2025-01-30 12:50:09 -08:00
parent 886070de2d
commit 4cf455ed39
17 changed files with 2024 additions and 120 deletions

View File

@@ -4,6 +4,8 @@
"recommendations": [ "recommendations": [
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"connor4312.esbuild-problem-matchers", "connor4312.esbuild-problem-matchers",
"ms-vscode.extension-test-runner" "ms-vscode.extension-test-runner",
"csstools.postcss",
"bradlc.vscode-tailwindcss"
] ]
} }

View File

@@ -9,5 +9,6 @@
"dist": true // set this to false to include "dist" folder in search results "dist": true // set this to false to include "dist" folder in search results
}, },
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off" "typescript.tsc.autoDetect": "off",
"editor.formatOnSave": true
} }

View File

@@ -21,3 +21,5 @@
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
*storybook.log

View File

@@ -0,0 +1,16 @@
import type { StorybookConfig } from "@storybook/react-vite"
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-onboarding",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
}
export default config

View File

@@ -0,0 +1,17 @@
import type { Preview } from "@storybook/react"
import "../src/index.css"
import "./vscode.css"
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
}
export default preview

View File

@@ -0,0 +1,32 @@
/**
* Use `Developer: Generate Color Theme From Current Settings` to generate themes
* using your current VSCode settings.
*
* See: https://code.visualstudio.com/docs/getstarted/themes
*/
:root {
--vscode-editor-background: #1f1f1f; /* "editor.background" */
--vscode-editor-foreground: #cccccc; /* "editor.foreground" */
--vscode-menu-background: #1f1f1f; /* "menu.background" */
--vscode-menu-foreground: #cccccc; /* "menu.foreground" */
--vscode-button-background: #0078d4; /* "button.background" */
--vscode-button-foreground: #ffffff; /* "button.foreground" */
--vscode-button-secondaryBackground: #313131; /* "button.secondaryBackground" */
--vscode-button-secondaryForeground: #cccccc; /* "button.secondaryForeground" */
--vscode-disabledForeground: red; /* "disabledForeground" */
--vscode-descriptionForeground: #9d9d9d; /* "descriptionForeground" */
--vscode-focusBorder: #0078d4; /* "focusBorder" */
--vscode-errorForeground: #f85149; /* "errorForeground" */
--vscode-widget-border: #313131; /* "widget.border" */
--vscode-input-background: #313131; /* "input.background" */
--vscode-input-foreground: #cccccc; /* "input.foreground" */
--vscode-input-border: #3c3c3c; /* "input.border" */
/* I can't find these in the output of `Developer: Generate Color Theme From Current Settings` */
--vscode-charts-red: red;
--vscode-charts-blue: blue;
--vscode-charts-yellow: yellow;
--vscode-charts-orange: orange;
--vscode-charts-green: green;
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,17 +4,24 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "jest", "test": "jest",
"lint": "eslint src --ext ts,tsx --quiet" "lint": "eslint src --ext ts,tsx --quiet",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-slot": "^1.1.1",
"@tailwindcss/vite": "^4.0.0",
"@vscode/webview-ui-toolkit": "^1.4.0", "@vscode/webview-ui-toolkit": "^1.4.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"debounce": "^2.1.1", "debounce": "^2.1.1",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fzf": "^0.5.2", "fzf": "^0.5.2",
"lucide-react": "^0.474.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-remark": "^2.1.0", "react-remark": "^2.1.0",
@@ -24,11 +31,20 @@
"rehype-highlight": "^7.0.0", "rehype-highlight": "^7.0.0",
"shell-quote": "^1.8.2", "shell-quote": "^1.8.2",
"styled-components": "^6.1.13", "styled-components": "^6.1.13",
"tailwind-merge": "^2.6.0",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
"vscrui": "^0.2.0", "tailwindcss-animate": "^1.0.7",
"@tailwindcss/vite": "^4.0.0" "vscrui": "^0.2.0"
}, },
"devDependencies": { "devDependencies": {
"@chromatic-com/storybook": "^3.2.4",
"@storybook/addon-essentials": "^8.5.2",
"@storybook/addon-interactions": "^8.5.2",
"@storybook/addon-onboarding": "^8.5.2",
"@storybook/blocks": "^8.5.2",
"@storybook/react": "^8.5.2",
"@storybook/react-vite": "^8.5.2",
"@storybook/test": "^8.5.2",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@@ -42,15 +58,15 @@
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^0.11.2",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"jest-environment-jsdom": "^27.5.1", "jest-environment-jsdom": "^27.5.1",
"jest-simple-dot-reporter": "^1.0.5", "jest-simple-dot-reporter": "^1.0.5",
"postcss": "^8.5.1", "storybook": "^8.5.2",
"ts-jest": "^27.1.5", "ts-jest": "^27.1.5",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"vite": "^5.4.14" "vite": "^5.4.14"

View File

@@ -0,0 +1,47 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-foreground shadow-sm hover:bg-foreground/80",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
},
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@@ -1,44 +1,64 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin "tailwindcss-animate";
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
}
@layer base { @layer base {
/* Theme Variables - VSCode Integration */
:root { :root {
/* Base Colors */
--background: var(--vscode-editor-background); --background: var(--vscode-editor-background);
--foreground: var(--vscode-editor-foreground); --foreground: var(--vscode-editor-foreground);
/* Component Colors */
--card: var(--vscode-editor-background); --card: var(--vscode-editor-background);
--card-foreground: var(--vscode-editor-foreground); --card-foreground: var(--vscode-editor-foreground);
--popover: var(--vscode-menu-background, var(--vscode-editor-background)); --popover: var(--vscode-menu-background, var(--vscode-editor-background));
--popover-foreground: var(--vscode-menu-foreground, var(--vscode-editor-foreground)); --popover-foreground: var(--vscode-menu-foreground, var(--vscode-editor-foreground));
/* Button Colors */
--primary: var(--vscode-button-background); --primary: var(--vscode-button-background);
--primary-foreground: var(--vscode-button-foreground); --primary-foreground: var(--vscode-button-foreground);
--secondary: var(--vscode-button-secondaryBackground); --secondary: var(--vscode-button-secondaryBackground);
--secondary-foreground: var(--vscode-button-secondaryForeground); --secondary-foreground: var(--vscode-button-secondaryForeground);
--accent: var(--vscode-focusBorder);
--accent-foreground: var(--vscode-button-foreground);
/* State Colors */
--muted: var(--vscode-disabledForeground); --muted: var(--vscode-disabledForeground);
--muted-foreground: var(--vscode-descriptionForeground); --muted-foreground: var(--vscode-descriptionForeground);
--accent: var(--vscode-input-border);
--accent-foreground: var(--vscode-button-foreground);
--destructive: var(--vscode-errorForeground); --destructive: var(--vscode-errorForeground);
--destructive-foreground: var(--vscode-editor-background); --destructive-foreground: var(--vscode-button-foreground);
/* UI Elements */
--border: var(--vscode-widget-border); --border: var(--vscode-widget-border);
--input: var(--vscode-input-background); --input: var(--vscode-input-background);
--ring: var(--vscode-focusBorder); --ring: var(--vscode-input-border);
--radius: 0.5rem;
/* Chart Colors - Using VSCode's chart colors */
--chart-1: var(--vscode-charts-red); --chart-1: var(--vscode-charts-red);
--chart-2: var(--vscode-charts-blue); --chart-2: var(--vscode-charts-blue);
--chart-3: var(--vscode-charts-yellow); --chart-3: var(--vscode-charts-yellow);
--chart-4: var(--vscode-charts-orange); --chart-4: var(--vscode-charts-orange);
--chart-5: var(--vscode-charts-green); --chart-5: var(--vscode-charts-green);
--radius: 0.5rem;
} }
} }
@@ -70,10 +90,10 @@ vscode-button::part(control):focus {
outline: none; outline: none;
} }
/* /**
Use vscode native scrollbar styles * Use vscode native scrollbar styles
https://github.com/gitkraken/vscode-gitlens/blob/b1d71d4844523e8b2ef16f9e007068e91f46fd88/src/webviews/apps/home/home.scss * https://github.com/gitkraken/vscode-gitlens/blob/b1d71d4844523e8b2ef16f9e007068e91f46fd88/src/webviews/apps/home/home.scss
*/ */
html { html {
height: 100%; height: 100%;
@@ -163,10 +183,11 @@ The above scrollbar styling uses some transparent background color magic to acco
background-color: transparent; background-color: transparent;
} }
/* /**
Dropdown label * Dropdown label
https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#with-label * https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#with-label
*/ */
.dropdown-container { .dropdown-container {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
@@ -174,6 +195,7 @@ https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#wi
align-items: flex-start; align-items: flex-start;
justify-content: flex-start; justify-content: flex-start;
} }
.dropdown-container label { .dropdown-container label {
display: block; display: block;
color: var(--vscode-foreground); color: var(--vscode-foreground);
@@ -184,6 +206,7 @@ https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#wi
} }
/* Fix dropdown double scrollbar overflow */ /* Fix dropdown double scrollbar overflow */
#api-provider > div > ul { #api-provider > div > ul {
overflow: unset; overflow: unset;
} }
@@ -197,18 +220,20 @@ vscode-dropdown::part(listbox) {
} }
/* Faded icon buttons in textfields */ /* Faded icon buttons in textfields */
.input-icon-button { .input-icon-button {
cursor: pointer; cursor: pointer;
opacity: 0.65; opacity: 0.65;
} }
.input-icon-button:hover { .input-icon-button:hover {
opacity: 1; opacity: 1;
} }
.input-icon-button.disabled { .input-icon-button.disabled {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.4; opacity: 0.4;
} }
.input-icon-button.disabled:hover { .input-icon-button.disabled:hover {
opacity: 0.4; opacity: 0.4;
} }
@@ -220,10 +245,6 @@ vscode-dropdown::part(listbox) {
border-radius: 3px; border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent); box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
color: transparent; color: transparent;
/* padding: 0.5px;
margin: -0.5px;
position: relative;
bottom: -0.5px; */
} }
.mention-context-highlight { .mention-context-highlight {

View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from "@storybook/react"
import { fn } from "@storybook/test"
import { Button } from "@/components/ui/button"
const meta = {
title: "Example/Button",
component: Button,
parameters: { layout: "centered" },
tags: ["autodocs"],
argTypes: {},
args: { onClick: fn(), children: "Button" },
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
variant: "default",
},
}
export const Secondary: Story = {
args: {
variant: "secondary",
},
}
export const Outline: Story = {
args: {
variant: "outline",
},
}
export const Ghost: Story = {
args: {
variant: "ghost",
},
}
export const Link: Story = {
args: {
variant: "link",
},
}
export const Destructive: Story = {
args: {
variant: "destructive",
},
}

View File

@@ -0,0 +1,7 @@
import { Meta } from "@storybook/blocks";
<Meta title="Welcome" />
# Welcome
This Roo Code storybook is used to independently develop components for the Roo Code webview UI.

View File

View File

@@ -14,7 +14,11 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}, },
"include": ["src", "../src/shared"] "include": ["src", "../src/shared"]
} }

View File

@@ -1,23 +1,30 @@
import path from "path"
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
react(), react(),
tailwindcss(), tailwindcss(),
], ],
build: { resolve: {
outDir: "build", alias: {
rollupOptions: { "@": path.resolve(__dirname, "./src"),
output: { },
entryFileNames: `assets/[name].js`, },
chunkFileNames: `assets/[name].js`, build: {
assetFileNames: `assets/[name].[ext]`, outDir: "build",
}, rollupOptions: {
output: {
entryFileNames: `assets/[name].js`,
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`,
},
},
},
server: {
port: 3000,
}, },
},
server: {
port: 3000,
},
}); });