diff --git a/README.md b/README.md index 747179e..f88a41b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,38 @@ + +# Icon library +This repo uses https://github.com/microsoft/vscode-codicons +https://microsoft.github.io/vscode-codicons/dist/codicon.html + + +# Styling VSCode Webview UI Toolkit Components + +## Understanding Styling Constraints + +When working with the VSCode Webview UI Toolkit, it's important to understand the styling constraints imposed by the underlying architecture. The toolkit uses Microsoft's FAST framework, which utilizes Shadow DOM for component encapsulation. This approach ensures consistency with VSCode's design language but introduces some limitations in custom styling. + +### Key Points: + +- **Shadow DOM Encapsulation**: The toolkit components use Shadow DOM, which encapsulates the internal structure of components. This means that traditional CSS selectors cannot directly target elements within the component. + +- **Wrapper vs. Shadow Element**: When you apply styles to a toolkit component, you're typically styling the wrapper element, not the shadow element inside. This can lead to unexpected results if you're trying to modify the internal appearance of a component. + +- **Use Props for Behavior Modification**: Instead of relying on custom styles, you should primarily use the props provided by the toolkit components to modify their behavior and appearance. This ensures consistency with VSCode's design language and prevents potential conflicts. + +- **Limited Direct Styling**: While it's possible to style some internal elements using the `::part()` pseudo-element selector, this approach is not officially supported or documented by the toolkit. Using it may lead to inconsistencies with VSCode's native UI. + +## Best Practices + +1. **Stick to Provided Props**: Whenever possible, use the props and attributes provided by the toolkit components to customize their appearance and behavior. + +2. **Avoid Custom Styles**: Refrain from applying custom styles that significantly alter the appearance of toolkit components. This helps maintain consistency with VSCode's native UI. + +3. **Use Wrapper Styles Carefully**: If you need to apply styles, focus on the wrapper element (e.g., positioning, margins) rather than trying to modify the internal shadow elements. + +### Sources +- https://github.com/microsoft/vscode-webview-ui-toolkit/issues/376#issuecomment-1191881962 +- https://github.com/microsoft/vscode-webview-ui-toolkit/issues/550#issuecomment-2148407785 + + # claude-dev README This is the README for your extension "claude-dev". After writing up a brief description, we recommend including the following sections. diff --git a/package-lock.json b/package-lock.json index c4322d2..2bccc73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "claude-dev", "version": "0.0.1", + "dependencies": { + "@vscode/codicons": "^0.0.36" + }, "devDependencies": { "@types/mocha": "^10.0.7", "@types/node": "20.x", @@ -933,6 +936,12 @@ "dev": true, "license": "ISC" }, + "node_modules/@vscode/codicons": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.36.tgz", + "integrity": "sha512-wsNOvNMMJ2BY8rC2N2MNBG7yOowV3ov8KlvUE/AiVUlHKTfWsw3OgAOQduX7h0Un6GssKD3aoTVH+TF3DSQwKQ==", + "license": "CC-BY-4.0" + }, "node_modules/@vscode/test-cli": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.9.tgz", diff --git a/package.json b/package.json index 5377528..9fb4290 100644 --- a/package.json +++ b/package.json @@ -77,5 +77,8 @@ "eslint": "^8.57.0", "npm-run-all": "^4.1.5", "typescript": "^5.4.5" + }, + "dependencies": { + "@vscode/codicons": "^0.0.36" } } diff --git a/src/providers/SidebarProvider.ts b/src/providers/SidebarProvider.ts index 2e790fb..d09aee7 100644 --- a/src/providers/SidebarProvider.ts +++ b/src/providers/SidebarProvider.ts @@ -56,6 +56,12 @@ export class SidebarProvider implements vscode.WebviewViewProvider { // The JS file from the React build output const scriptUri = getUri(webview, this._extensionUri, ["webview-ui", "build", "static", "js", "main.js"]) + // The codicon font from the React build output + // https://github.com/microsoft/vscode-extension-samples/blob/main/webview-codicons-sample/src/extension.ts + // we installed this package in the extension so that we can access it how its intended from the extension (the font file is likely bundled in vscode), and we just import the css fileinto our react app we don't have access to it + // don't forget to add font-src ${webview.cspSource}; + const codiconsUri = getUri(webview, this._extensionUri, ["node_modules", "@vscode", "codicons", "dist", "codicon.css"]) + // const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.js")) // const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "reset.css")) @@ -84,8 +90,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider { - + + Claude Dev diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 1302088..b856788 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -2,83 +2,43 @@ import React from "react" import logo from "./logo.svg" import "./App.css" + import { vscode } from "./utilities/vscode" import { + VSCodeBadge, VSCodeButton, + VSCodeCheckbox, VSCodeDataGrid, - VSCodeDataGridRow, VSCodeDataGridCell, - VSCodeTextField, + VSCodeDataGridRow, + VSCodeDivider, + VSCodeDropdown, + VSCodeLink, + VSCodeOption, + VSCodePanels, + VSCodePanelTab, + VSCodePanelView, VSCodeProgressRing, + VSCodeRadio, + VSCodeRadioGroup, + VSCodeTag, + VSCodeTextArea, + VSCodeTextField, } from "@vscode/webview-ui-toolkit/react" +import ChatSidebar from "./components/ChatSidebar" -function App() { - function handleHowdyClick() { +const App: React.FC = () => { + const handleHowdyClick = () => { vscode.postMessage({ command: "hello", text: "Hey there partner! 🤠", }) } - const rowData = [ - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - ] - return ( -
-

Hello World!

- Howdy! - -
- - - - A Custom Header Title - - - Another Custom Title - - - Title Is Custom - - - Custom Title - - - {rowData.map((row) => ( - - {row.cell1} - {row.cell2} - {row.cell3} - {row.cell4} - - ))} - - - - - - Add - Remove - -
+ // REMOVE COLOR +
+
) } diff --git a/webview-ui/src/components/ChatSidebar.tsx b/webview-ui/src/components/ChatSidebar.tsx new file mode 100644 index 0000000..d3ae701 --- /dev/null +++ b/webview-ui/src/components/ChatSidebar.tsx @@ -0,0 +1,94 @@ +import React, { useState, useRef, useEffect, useCallback } from "react" +import { VSCodeButton, VSCodeTextArea, VSCodeDivider, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { vscode } from "../utilities/vscode" +import ResizingTextArea from "./ResizingTextArea" + +interface Message { + id: number + text: string + sender: "user" | "assistant" +} + +const ChatSidebar = () => { + const [messages, setMessages] = useState([]) + const [inputValue, setInputValue] = useState("") + const messagesEndRef = useRef(null) + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) + } + + useEffect(scrollToBottom, [messages]) + + const handleSendMessage = () => { + if (inputValue.trim()) { + const newMessage: Message = { + id: Date.now(), + text: inputValue.trim(), + sender: "user", + } + setMessages([...messages, newMessage]) + setInputValue("") + // if (textAreaRef.current) { + // textAreaRef.current.style.height = "auto" + // } + + // Here you would typically send the message to your extension's backend + vscode.postMessage({ + command: "sendMessage", + text: newMessage.text, + }) + } + } + + return ( +
+
+ {messages.map((message) => ( +
+ {message.text} +
+ ))} +
+
+ +
+ + Send + +
+ + + + + + + + + +
+
+ + Send +
+
+ ) +} + +export default ChatSidebar diff --git a/webview-ui/src/components/Demo.tsx b/webview-ui/src/components/Demo.tsx new file mode 100644 index 0000000..69b2b43 --- /dev/null +++ b/webview-ui/src/components/Demo.tsx @@ -0,0 +1,132 @@ + +import { vscode } from "../utilities/vscode" +import { + VSCodeBadge, + VSCodeButton, + VSCodeCheckbox, + VSCodeDataGrid, + VSCodeDataGridCell, + VSCodeDataGridRow, + VSCodeDivider, + VSCodeDropdown, + VSCodeLink, + VSCodeOption, + VSCodePanels, + VSCodePanelTab, + VSCodePanelView, + VSCodeProgressRing, + VSCodeRadio, + VSCodeRadioGroup, + VSCodeTag, + VSCodeTextArea, + VSCodeTextField, +} from "@vscode/webview-ui-toolkit/react" + +function Demo() { + function handleHowdyClick() { + vscode.postMessage({ + command: "hello", + text: "Hey there partner! 🤠", + }) + } + + const rowData = [ + { + cell1: "Cell Data", + cell2: "Cell Data", + cell3: "Cell Data", + cell4: "Cell Data", + }, + { + cell1: "Cell Data", + cell2: "Cell Data", + cell3: "Cell Data", + cell4: "Cell Data", + }, + { + cell1: "Cell Data", + cell2: "Cell Data", + cell3: "Cell Data", + cell4: "Cell Data", + }, + ] + + return ( +
+

Hello World!

+ Howdy! + +
+ + + + A Custom Header Title + + + Another Custom Title + + + Title Is Custom + + + Custom Title + + + {rowData.map((row, index) => ( + + {row.cell1} + {row.cell2} + {row.cell3} + {row.cell4} + + ))} + + + +
+ + + + + + + + + +
+
+ + + + + + Add + Remove + + + Badge + Checkbox + + + Option 1 + Option 2 + + Link + + Tab 1 + Tab 2 + Panel View 1 + Panel View 2 + + + Radio 1 + Radio 2 + + Tag + +
+
+ ) +} + +export default Demo diff --git a/webview-ui/src/components/ResizingTextArea.tsx b/webview-ui/src/components/ResizingTextArea.tsx new file mode 100644 index 0000000..183d3b7 --- /dev/null +++ b/webview-ui/src/components/ResizingTextArea.tsx @@ -0,0 +1,46 @@ +import React, { TextareaHTMLAttributes, CSSProperties, useRef, useEffect } from "react" + +interface ResizingTextAreaProps extends Omit, "onChange"> { + onChange: (value: string) => void +} + +const ResizingTextArea= ({ style, value, onChange, ...props }: ResizingTextAreaProps) => { + const textAreaRef = useRef(null) + + const textareaStyle: CSSProperties = { + width: "100%", + minHeight: "60px", + backgroundColor: "var(--vscode-input-background, #3c3c3c)", + color: "var(--vscode-input-foreground, #cccccc)", + border: "1px solid var(--vscode-input-border, #3c3c3c)", + borderRadius: "2px", + padding: "4px 8px", + outline: "none", + fontFamily: "var(--vscode-editor-font-family)", + fontSize: "var(--vscode-editor-font-size, 13px)", + lineHeight: "var(--vscode-editor-line-height, 1.5)", + resize: "none", + overflow: "hidden", + ...style, + } + + const adjustTextAreaHeight = () => { + if (textAreaRef.current) { + textAreaRef.current.style.height = "auto" + textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px` + } + } + + const handleInputChange = (event: React.ChangeEvent) => { + onChange(event.target.value) + adjustTextAreaHeight() + } + + useEffect(() => { + adjustTextAreaHeight() + }, [value]) + + return