Explorar o código

Add shadcn/ui + Storybook

cte hai 11 meses
pai
achega
4cf455ed39

+ 3 - 1
.vscode/extensions.json

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

+ 2 - 1
.vscode/settings.json

@@ -9,5 +9,6 @@
 		"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
-	"typescript.tsc.autoDetect": "off"
+	"typescript.tsc.autoDetect": "off",
+	"editor.formatOnSave": true
 }

+ 2 - 0
webview-ui/.gitignore

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

+ 16 - 0
webview-ui/.storybook/main.ts

@@ -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

+ 17 - 0
webview-ui/.storybook/preview.ts

@@ -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

+ 32 - 0
webview-ui/.storybook/vscode.css

@@ -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;
+}

+ 21 - 0
webview-ui/components.json

@@ -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"
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 934 - 102
webview-ui/package-lock.json


+ 22 - 6
webview-ui/package.json

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

+ 47 - 0
webview-ui/src/components/ui/button.tsx

@@ -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 }

+ 51 - 30
webview-ui/src/index.css

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

+ 6 - 0
webview-ui/src/lib/utils.ts

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

+ 53 - 0
webview-ui/src/stories/Button.stories.ts

@@ -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",
+	},
+}

+ 7 - 0
webview-ui/src/stories/Welcome.mdx

@@ -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.

+ 0 - 0
webview-ui/src/stories/assets/.gitkeep


+ 5 - 1
webview-ui/tsconfig.json

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

+ 23 - 16
webview-ui/vite.config.mts

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

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio