From ab861521c6953129ca39a196145d38a8daee8b71 Mon Sep 17 00:00:00 2001 From: ktmage Date: Sun, 1 Mar 2026 20:33:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20react-icons=20=E3=83=91=E3=83=83?= =?UTF-8?q?=E3=82=B1=E3=83=BC=E3=82=B8=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 13 +++++++++++-- package.json | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 114a42b..0f68f61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "@opencode-ai/sdk": "^1.2.10", "@types/dompurify": "^3.0.5", - "dompurify": "^3.3.1" + "dompurify": "^3.3.1", + "react-icons": "^5.5.0" }, "devDependencies": { "@biomejs/biome": "2.4.4", @@ -5669,7 +5670,6 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "dev": true, "license": "MIT", "peer": true, "engines": { @@ -5690,6 +5690,15 @@ "react": "^19.2.4" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 33ea5e7..d0d9c48 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "dependencies": { "@opencode-ai/sdk": "^1.2.10", "@types/dompurify": "^3.0.5", - "dompurify": "^3.3.1" + "dompurify": "^3.3.1", + "react-icons": "^5.5.0" } } From c62837c2739d6b22a9850b7703a7df6e35c6e38d Mon Sep 17 00:00:00 2001 From: ktmage Date: Sun, 1 Mar 2026 20:33:24 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E3=83=8F=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89SVG=E3=82=92=20react-icons/vsc=20?= =?UTF-8?q?=E3=81=AB=E7=BD=AE=E6=8F=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webview/components/atoms/icons/icons.tsx | 353 ++++++++--------------- 1 file changed, 127 insertions(+), 226 deletions(-) diff --git a/webview/components/atoms/icons/icons.tsx b/webview/components/atoms/icons/icons.tsx index 4ff99e8..09d8f46 100644 --- a/webview/components/atoms/icons/icons.tsx +++ b/webview/components/atoms/icons/icons.tsx @@ -1,154 +1,133 @@ import type { SVGProps } from "react"; +import type { IconBaseProps } from "react-icons"; +import { + VscAdd, + VscArrowLeft, + VscArrowRight, + VscAttach, + VscChecklist, + VscChevronRight, + VscClose, + VscDebugStop, + VscDiscard, + VscEdit, + VscError, + VscEye, + VscEyeClosed, + VscFile, + VscFolderOpened, + VscInfo, + VscLinkExternal, + VscListUnordered, + VscNewFile, + VscNote, + VscPerson, + VscPlay, + VscRepoForked, + VscSearch, + VscSend, + VscSettingsGear, + VscShare, + VscTerminal, + VscTrash, +} from "react-icons/vsc"; // Common props for all icon components. // Consumers can override width, height, className, and any standard SVG attribute. export type IconProps = SVGProps; +// react-icons の IconBaseProps は SVGAttributes を拡張しており、 +// SVGProps とほぼ互換。height を除外して size に統一する。 +function adapt({ width, height: _height, ...rest }: Omit, defaultSize: number): IconBaseProps { + return { size: width != null ? Number(width) : defaultSize, "aria-hidden": true, ...rest }; +} + // ─── Navigation / Header ───────────────────────────────────────────── /** Codicon: list-unordered */ -export function ListIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function ListIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } /** Codicon: add (16px plus) */ -export function AddIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function AddIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } /** Small plus icon (12px) used for quick-add buttons */ -export function PlusIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +export function PlusIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } /** Chevron right arrow — used for expand/collapse toggles */ -export function ChevronRightIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +export function ChevronRightIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } // ─── File / Document ────────────────────────────────────────────────── /** Codicon: file */ -export function FileIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +export function FileIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } -/** Codicon: paperclip / attach */ -export function ClipIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: attach */ +export function ClipIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } // ─── Action ─────────────────────────────────────────────────────────── /** Codicon: close (×) */ -export function CloseIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +export function CloseIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } /** Codicon: trash / delete */ -export function DeleteIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +export function DeleteIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } /** Codicon: edit / pencil */ -export function EditIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +export function EditIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } /** Codicon: send arrow */ -export function SendIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function SendIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** Codicon: stop (square) */ -export function StopIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: debug-stop (square) */ +export function StopIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** Revert / retry arrows — used for checkpoint revert */ -export function RevertIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +/** Codicon: discard — used for checkpoint revert */ +export function RevertIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } /** Codicon: repo-forked — セッション Fork 用の分岐アイコン */ -export function ForkIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +export function ForkIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } // ─── Input Area Actions ─────────────────────────────────────────────── /** Codicon: terminal */ -export function TerminalIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function TerminalIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** Codicon: gear / settings */ -export function GearIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: settings-gear */ +export function GearIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } // ─── Tool / Status ──────────────────────────────────────────────────── +// SpinnerIcon はアニメーション付きのカスタム SVG のため、react-icons に置き換えない /** Loading spinner — animated rotating arc */ export function SpinnerIcon({ width = 16, height = 16, className, ...props }: IconProps) { return ( @@ -167,185 +146,107 @@ export function SpinnerIcon({ width = 16, height = 16, className, ...props }: Ic ); } -/** Error circle with exclamation mark */ -export function ErrorCircleIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: error */ +export function ErrorCircleIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** Info circle — used for completed reasoning/thought indicator */ -export function InfoCircleIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: info — used for completed reasoning/thought indicator */ +export function InfoCircleIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Checkbox with checkmark */ -export function CheckboxIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: checklist */ +export function CheckboxIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } // ─── Tool Action Category Icons ─────────────────────────────────────── -/** Read action icon — open file/document */ -export function ReadActionIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: note — read action icon */ +export function ReadActionIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Edit action icon — pencil (same path as EditIcon) */ -export function EditActionIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: edit — edit action icon */ +export function EditActionIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Write/create action icon — file upload */ -export function WriteActionIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: new-file — write/create action icon */ +export function WriteActionIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Run action icon — play button in terminal */ -export function RunActionIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: play — run action icon */ +export function RunActionIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Search action icon — magnifying glass */ -export function SearchActionIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: search — search action icon */ +export function SearchActionIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Generic tool/file icon — default for uncategorized tools */ -export function ToolIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: file — default for uncategorized tools */ +export function ToolIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } // ─── Visibility ─────────────────────────────────────────────────────── -/** Codicon: diff — git compare icon for file changes */ -export function DiffIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: folder-opened — file changes icon */ +export function DiffIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** External link — open file in editor */ -export function ExternalLinkIcon({ width = 12, height = 12, ...props }: IconProps) { - return ( - - ); +/** Codicon: link-external — open file in editor */ +export function ExternalLinkIcon({ width = 12, height: _h, ...props }: IconProps) { + return ; } -/** Eye icon — visible / show */ -export function EyeIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: eye — visible / show */ +export function EyeIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } -/** Eye-off icon — hidden / hide */ -export function EyeOffIcon({ width = 14, height = 14, ...props }: IconProps) { - return ( - - ); +/** Codicon: eye-closed — hidden / hide */ +export function EyeOffIcon({ width = 14, height: _h, ...props }: IconProps) { + return ; } // ─── Child Session Navigation ───────────────────────────────────────── /** Codicon: arrow-left — back navigation */ -export function BackIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function BackIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } // ─── Share ──────────────────────────────────────────────────────────── -/** Codicon: link — share session */ -export function ShareIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: share — share session */ +export function ShareIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } /** Codicon: link-external — unshare / shared state */ -export function UnshareIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function UnshareIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } /** Codicon: person — agent/subagent icon */ -export function AgentIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +export function AgentIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } // ─── Undo / Redo ────────────────────────────────────────────────────── -/** Codicon: discard — 反時計回り矢印(Undo) */ -export function UndoIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: arrow-left — Undo */ +export function UndoIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } -/** Codicon: redo — 時計回り矢印(Redo) */ -export function RedoIcon({ width = 16, height = 16, ...props }: IconProps) { - return ( - - ); +/** Codicon: arrow-right — Redo */ +export function RedoIcon({ width = 16, height: _h, ...props }: IconProps) { + return ; } From 9b2b4fe8d0522f8a0a35e6f3e4366b8c05640b91 Mon Sep 17 00:00:00 2001 From: ktmage Date: Sun, 1 Mar 2026 20:33:29 +0900 Subject: [PATCH 3/3] =?UTF-8?q?test:=20=E6=9C=AA=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=81=A0=E3=81=A3=E3=81=9F7=E3=82=A2=E3=82=A4?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/components/atoms/icons.test.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/webview/__tests__/components/atoms/icons.test.tsx b/webview/__tests__/components/atoms/icons.test.tsx index 3c30d9a..e151806 100644 --- a/webview/__tests__/components/atoms/icons.test.tsx +++ b/webview/__tests__/components/atoms/icons.test.tsx @@ -4,6 +4,8 @@ import { describe, expect, it } from "vitest"; import type { IconProps } from "../../../components/atoms/icons"; import { AddIcon, + AgentIcon, + BackIcon, CheckboxIcon, ChevronRightIcon, ClipIcon, @@ -17,19 +19,24 @@ import { EyeIcon, EyeOffIcon, FileIcon, + ForkIcon, GearIcon, InfoCircleIcon, ListIcon, PlusIcon, ReadActionIcon, + RedoIcon, RevertIcon, RunActionIcon, SearchActionIcon, SendIcon, + ShareIcon, SpinnerIcon, StopIcon, TerminalIcon, ToolIcon, + UndoIcon, + UnshareIcon, WriteActionIcon, } from "../../../components/atoms/icons"; @@ -220,3 +227,31 @@ describe("DiffIcon", () => { describe("ExternalLinkIcon", () => { describeIconComponent(ExternalLinkIcon, 12, 12); }); + +describe("ForkIcon", () => { + describeIconComponent(ForkIcon, 12, 12); +}); + +describe("BackIcon", () => { + describeIconComponent(BackIcon, 16, 16); +}); + +describe("ShareIcon", () => { + describeIconComponent(ShareIcon, 16, 16); +}); + +describe("UnshareIcon", () => { + describeIconComponent(UnshareIcon, 16, 16); +}); + +describe("AgentIcon", () => { + describeIconComponent(AgentIcon, 16, 16); +}); + +describe("UndoIcon", () => { + describeIconComponent(UndoIcon, 16, 16); +}); + +describe("RedoIcon", () => { + describeIconComponent(RedoIcon, 16, 16); +});