mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Merge pull request #664 from RooVetGit/cte/shadcn-ui-storybook
Add shadcn/ui + Storybook
This commit is contained in:
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -4,6 +4,9 @@
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"connor4312.esbuild-problem-matchers",
|
||||
"ms-vscode.extension-test-runner"
|
||||
"ms-vscode.extension-test-runner",
|
||||
"csstools.postcss",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"tobermory.es6-string-html"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -255,9 +255,15 @@ Roo Code is available on:
|
||||
```bash
|
||||
code --install-extension bin/roo-code-4.0.0.vsix
|
||||
```
|
||||
5. **Debug**:
|
||||
5. **Start the webview (Vite/React app with HMR)**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
6. **Debug**:
|
||||
- Press `F5` (or **Run** → **Start Debugging**) in VSCode to open a new session with Roo Code loaded.
|
||||
|
||||
Changes to the webview will appear immediately. Changes to the core extension will require a restart of the extension host.
|
||||
|
||||
We use [changesets](https://github.com/changesets/changesets) for versioning and publishing. Check our `CHANGELOG.md` for release notes.
|
||||
|
||||
---
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
"lint": "eslint src --ext ts && npm run lint --prefix webview-ui",
|
||||
"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
|
||||
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
||||
"start:webview": "cd webview-ui && npm run start",
|
||||
"dev": "cd webview-ui && npm run dev",
|
||||
"test": "jest && npm run test:webview",
|
||||
"test:webview": "cd webview-ui && npm run test",
|
||||
"test:extension": "vscode-test",
|
||||
|
||||
@@ -52,6 +52,11 @@ const vscode = {
|
||||
this.id = id
|
||||
}
|
||||
},
|
||||
ExtensionMode: {
|
||||
Production: 1,
|
||||
Development: 2,
|
||||
Test: 3,
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = vscode
|
||||
|
||||
@@ -279,7 +279,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [this.context.extensionUri],
|
||||
}
|
||||
webviewView.webview.html = this.getHtmlContent(webviewView.webview)
|
||||
|
||||
webviewView.webview.html =
|
||||
this.context.extensionMode === vscode.ExtensionMode.Development
|
||||
? this.getHMRHtmlContent(webviewView.webview)
|
||||
: this.getHtmlContent(webviewView.webview)
|
||||
|
||||
// Sets up an event listener to listen for messages passed from the webview view context
|
||||
// and executes code based on the message that is recieved
|
||||
@@ -403,6 +407,62 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
await this.view?.webview.postMessage(message)
|
||||
}
|
||||
|
||||
private getHMRHtmlContent(webview: vscode.Webview): string {
|
||||
const nonce = getNonce()
|
||||
|
||||
const stylesUri = getUri(webview, this.context.extensionUri, ["webview-ui", "build", "assets", "index.css"])
|
||||
const codiconsUri = getUri(webview, this.context.extensionUri, [
|
||||
"node_modules",
|
||||
"@vscode",
|
||||
"codicons",
|
||||
"dist",
|
||||
"codicon.css",
|
||||
])
|
||||
|
||||
const file = "src/index.tsx"
|
||||
const localPort = "5173"
|
||||
const localServerUrl = `localhost:${localPort}`
|
||||
const scriptUri = `http://${localServerUrl}/${file}`
|
||||
|
||||
const reactRefresh = /*html*/ `
|
||||
<script nonce="${nonce}" type="module">
|
||||
import RefreshRuntime from "http://localhost:${localPort}/@react-refresh"
|
||||
RefreshRuntime.injectIntoGlobalHook(window)
|
||||
window.$RefreshReg$ = () => {}
|
||||
window.$RefreshSig$ = () => (type) => type
|
||||
window.__vite_plugin_react_preamble_installed__ = true
|
||||
</script>
|
||||
`
|
||||
|
||||
const csp = [
|
||||
"default-src 'none'",
|
||||
`font-src ${webview.cspSource}`,
|
||||
`style-src ${webview.cspSource} 'unsafe-inline' https://* http://${localServerUrl} http://0.0.0.0:${localPort}`,
|
||||
`img-src ${webview.cspSource} data:`,
|
||||
`script-src 'unsafe-eval' https://* http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`,
|
||||
`connect-src https://* ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`,
|
||||
]
|
||||
|
||||
return /*html*/ `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
|
||||
<meta http-equiv="Content-Security-Policy" content="${csp.join("; ")}">
|
||||
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
||||
<link href="${codiconsUri}" rel="stylesheet" />
|
||||
<title>Roo Code</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
${reactRefresh}
|
||||
<script type="module" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines and returns the HTML that should be rendered within the webview panel.
|
||||
*
|
||||
@@ -558,7 +618,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
let currentConfigName = (await this.getGlobalState("currentApiConfigName")) as string
|
||||
const currentConfigName = (await this.getGlobalState("currentApiConfigName")) as string
|
||||
|
||||
if (currentConfigName) {
|
||||
if (!(await this.configManager.hasConfig(currentConfigName))) {
|
||||
@@ -1134,7 +1194,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
if (message.text && message.apiConfiguration) {
|
||||
try {
|
||||
await this.configManager.saveConfig(message.text, message.apiConfiguration)
|
||||
let listApiConfig = await this.configManager.listConfig()
|
||||
const listApiConfig = await this.configManager.listConfig()
|
||||
|
||||
await Promise.all([
|
||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
@@ -1159,7 +1219,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
await this.configManager.saveConfig(newName, message.apiConfiguration)
|
||||
await this.configManager.deleteConfig(oldName)
|
||||
|
||||
let listApiConfig = await this.configManager.listConfig()
|
||||
const listApiConfig = await this.configManager.listConfig()
|
||||
const config = listApiConfig?.find((c) => c.name === newName)
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
@@ -1217,7 +1277,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
|
||||
|
||||
// If this was the current config, switch to first available
|
||||
let currentApiConfigName = await this.getGlobalState("currentApiConfigName")
|
||||
const currentApiConfigName = await this.getGlobalState("currentApiConfigName")
|
||||
if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) {
|
||||
const apiConfig = await this.configManager.loadConfig(listApiConfig[0].name)
|
||||
await Promise.all([
|
||||
@@ -1237,7 +1297,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
break
|
||||
case "getListApiConfiguration":
|
||||
try {
|
||||
let listApiConfig = await this.configManager.listConfig()
|
||||
const listApiConfig = await this.configManager.listConfig()
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
|
||||
this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
|
||||
} catch (error) {
|
||||
@@ -1277,7 +1337,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
this.outputChannel.appendLine(
|
||||
`Failed to update timeout for ${message.serverName}: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
|
||||
)
|
||||
vscode.window.showErrorMessage(`Failed to update server timeout`)
|
||||
vscode.window.showErrorMessage("Failed to update server timeout")
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -1630,7 +1690,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
async refreshGlamaModels() {
|
||||
const glamaModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.glamaModels)
|
||||
|
||||
let models: Record<string, ModelInfo> = {}
|
||||
const models: Record<string, ModelInfo> = {}
|
||||
try {
|
||||
const response = await axios.get("https://glama.ai/api/gateway/v1/models")
|
||||
/*
|
||||
@@ -1720,7 +1780,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
GlobalFileNames.openRouterModels,
|
||||
)
|
||||
|
||||
let models: Record<string, ModelInfo> = {}
|
||||
const models: Record<string, ModelInfo> = {}
|
||||
try {
|
||||
const response = await axios.get("https://openrouter.ai/api/v1/models")
|
||||
/*
|
||||
|
||||
@@ -108,6 +108,11 @@ jest.mock("vscode", () => ({
|
||||
uriScheme: "vscode",
|
||||
language: "en",
|
||||
},
|
||||
ExtensionMode: {
|
||||
Production: 1,
|
||||
Development: 2,
|
||||
Test: 3,
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock sound utility
|
||||
|
||||
2
webview-ui/.gitignore
vendored
2
webview-ui/.gitignore
vendored
@@ -21,3 +21,5 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
*storybook.log
|
||||
|
||||
16
webview-ui/.storybook/main.ts
Normal file
16
webview-ui/.storybook/main.ts
Normal 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
|
||||
17
webview-ui/.storybook/preview.ts
Normal file
17
webview-ui/.storybook/preview.ts
Normal 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
|
||||
32
webview-ui/.storybook/vscode.css
Normal file
32
webview-ui/.storybook/vscode.css
Normal 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;
|
||||
}
|
||||
20
webview-ui/components.json
Normal file
20
webview-ui/components.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$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"
|
||||
}
|
||||
}
|
||||
2080
webview-ui/package-lock.json
generated
2080
webview-ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,14 +4,20 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src --ext ts,tsx",
|
||||
"test": "jest",
|
||||
"lint": "eslint src --ext ts,tsx"
|
||||
"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",
|
||||
@@ -24,36 +30,45 @@
|
||||
"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",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/vscode-webview": "^1.57.5",
|
||||
"@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"
|
||||
"vite": "6.0.11"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
@@ -93,20 +108,5 @@
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
|
||||
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all",
|
||||
"last 2 chrome version",
|
||||
"last 2 firefox version",
|
||||
"last 2 safari version"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
0
webview-ui/public/.gitkeep
Normal file
0
webview-ui/public/.gitkeep
Normal file
@@ -1,38 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
--></body>
|
||||
</html>
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -183,26 +183,28 @@ export const ChatRowContent = ({
|
||||
</div>
|
||||
)
|
||||
return [
|
||||
apiReqCancelReason !== null ? (
|
||||
apiReqCancelReason ? (
|
||||
apiReqCancelReason === "user_cancelled" ? (
|
||||
getIconSpan("error", cancelledColor)
|
||||
) : (
|
||||
getIconSpan("error", errorColor)
|
||||
)
|
||||
) : cost !== null ? (
|
||||
) : cost ? (
|
||||
getIconSpan("check", successColor)
|
||||
) : apiRequestFailedMessage ? (
|
||||
getIconSpan("error", errorColor)
|
||||
) : (
|
||||
<ProgressIndicator />
|
||||
),
|
||||
apiReqCancelReason !== null ? (
|
||||
apiReqCancelReason ? (
|
||||
apiReqCancelReason === "user_cancelled" ? (
|
||||
<span style={{ color: normalColor, fontWeight: "bold" }}>API Request Cancelled</span>
|
||||
) : (
|
||||
<span style={{ color: errorColor, fontWeight: "bold" }}>API Streaming Failed</span>
|
||||
<span style={{ color: errorColor, fontWeight: "bold" }}>
|
||||
API Streaming Failed ({JSON.stringify(apiReqCancelReason)})
|
||||
</span>
|
||||
)
|
||||
) : cost !== null ? (
|
||||
) : cost ? (
|
||||
<span style={{ color: normalColor, fontWeight: "bold" }}>API Request</span>
|
||||
) : apiRequestFailedMessage ? (
|
||||
<span style={{ color: errorColor, fontWeight: "bold" }}>API Request Failed</span>
|
||||
@@ -510,9 +512,7 @@ export const ChatRowContent = ({
|
||||
style={{
|
||||
...headerStyle,
|
||||
marginBottom:
|
||||
(cost === null && apiRequestFailedMessage) || apiReqStreamingFailedMessage
|
||||
? 10
|
||||
: 0,
|
||||
(!cost && apiRequestFailedMessage) || apiReqStreamingFailedMessage ? 10 : 0,
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
@@ -530,7 +530,7 @@ export const ChatRowContent = ({
|
||||
</div>
|
||||
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
|
||||
</div>
|
||||
{((cost === null && apiRequestFailedMessage) || apiReqStreamingFailedMessage) && (
|
||||
{((!cost && apiRequestFailedMessage) || apiReqStreamingFailedMessage) && (
|
||||
<>
|
||||
<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>
|
||||
{apiRequestFailedMessage || apiReqStreamingFailedMessage}
|
||||
|
||||
47
webview-ui/src/components/ui/button.tsx
Normal file
47
webview-ui/src/components/ui/button.tsx
Normal 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 }
|
||||
@@ -1,44 +1,71 @@
|
||||
@import "tailwindcss";
|
||||
/* @import "tailwindcss"; */
|
||||
|
||||
@layer theme, base, components, utilities;
|
||||
|
||||
@import "tailwindcss/theme.css" layer(theme);
|
||||
/* https://tailwindcss.com/docs/preflight */
|
||||
/* @import "tailwindcss/preflight.css" layer(base); */
|
||||
@import "tailwindcss/utilities.css" layer(utilities);
|
||||
|
||||
@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,9 +97,9 @@ 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 {
|
||||
@@ -163,10 +190,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 +202,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 +213,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 +227,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 +252,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 {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { StrictMode } from "react"
|
||||
import { createRoot } from "react-dom/client"
|
||||
|
||||
import "./index.css"
|
||||
import App from "./App"
|
||||
import "../../node_modules/@vscode/codicons/dist/codicon.css"
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
6
webview-ui/src/lib/utils.ts
Normal file
6
webview-ui/src/lib/utils.ts
Normal 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))
|
||||
}
|
||||
53
webview-ui/src/stories/Button.stories.ts
Normal file
53
webview-ui/src/stories/Button.stories.ts
Normal 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",
|
||||
},
|
||||
}
|
||||
7
webview-ui/src/stories/Welcome.mdx
Normal file
7
webview-ui/src/stories/Welcome.mdx
Normal 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.
|
||||
0
webview-ui/src/stories/assets/.gitkeep
Normal file
0
webview-ui/src/stories/assets/.gitkeep
Normal file
1
webview-ui/src/vite-env.d.ts
vendored
Normal file
1
webview-ui/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -14,7 +14,11 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "../src/shared"]
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
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]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
});
|
||||
36
webview-ui/vite.config.ts
Normal file
36
webview-ui/vite.config.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import path from "path"
|
||||
|
||||
import { defineConfig } from "vite"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
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: {
|
||||
hmr: {
|
||||
host: "localhost",
|
||||
protocol: "ws",
|
||||
},
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: "*",
|
||||
allowedHeaders: "*",
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user