From 2abc3697de3f76ad41e438da2bf05b3393e79388 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Thu, 23 Apr 2026 22:29:18 +0800 Subject: [PATCH 1/3] feat(ui): replace chrome icon set with PawWork brand v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 74 chrome icons in packages/ui/src/components/icon.tsx with PawWork-drawn SVGs (vtracer-traced from imagegen batches, single DNA). Add 6 new icons: chevron-up, plugin, automation, remote-control, schedule, key. sidebar-active uses upstream-style left-panel semi-transparent fill; dot-grid keeps the 3-horizontal-dot form. Carve-out: packages/ui/src/components/icon.tsx is now accept-HEAD on upstream sync (see project memory: UI 层重写 carve-out). --- packages/ui/src/components/icon.tsx | 162 ++++++++++++++-------------- 1 file changed, 83 insertions(+), 79 deletions(-) diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 08726d0ff..d748c3249 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -2,106 +2,111 @@ import { splitProps, type ComponentProps } from "solid-js" const icons = { "align-right": ``, - "arrow-up": ``, - "arrow-left": ``, - "arrow-right": ``, - archive: ``, - "bubble-5": ``, + "arrow-up": ``, + "arrow-left": ``, + "arrow-right": ``, + archive: ``, + "bubble-5": ``, prompt: ``, - brain: ``, + brain: ``, fork: ``, - "bullet-list": ``, - "check-small": ``, - "chevron-down": ``, - "chevron-left": ``, - "chevron-right": ``, - "chevron-grabber-vertical": ``, + "bullet-list": ``, + "check-small": ``, + "chevron-down": ``, + "chevron-left": ``, + "chevron-right": ``, + "chevron-grabber-vertical": ``, "chevron-double-right": ``, - "circle-x": ``, - close: ``, - "close-small": ``, - checklist: ``, - console: ``, - terminal: ``, - "terminal-active": ` -`, - review: ``, - "review-active": ` -`, + "circle-x": ``, + close: ``, + "close-small": ``, + checklist: ``, + console: ``, + terminal: ``, + "terminal-active": ``, + review: ``, + "review-active": ``, expand: ``, collapse: ``, - code: ``, - "code-lines": ``, - "circle-ban-sign": ``, + code: ``, + "code-lines": ``, + "circle-ban-sign": ``, "edit-small-2": ``, eye: ``, - enter: ``, - folder: ``, - "file-tree": ``, - "file-tree-active": ` -`, - "magnifying-glass": ``, - "plus-small": ``, - plus: ``, - "new-session": ``, - "new-session-active": ` -`, - "pencil-line": ``, - mcp: ``, - glasses: ``, - "magnifying-glass-menu": ``, - "window-cursor": ``, - task: ``, - stop: ``, - status: ``, - "status-active": ` - -`, - sidebar: ``, - "sidebar-active": ` -`, + enter: ``, + folder: ``, + "file-tree": ``, + "file-tree-active": ``, + "magnifying-glass": ``, + "plus-small": ``, + plus: ``, + "new-session": ``, + "new-session-active": ``, + "pencil-line": ``, + mcp: ``, + glasses: ``, + "magnifying-glass-menu": ``, + "window-cursor": ``, + task: ``, + stop: ``, + status: ``, + "status-active": ``, + sidebar: ``, + "sidebar-active": ``, "layout-left": ``, "layout-left-partial": ``, "layout-left-full": ``, "layout-right": ``, "layout-right-partial": ``, "layout-right-full": ``, - "square-arrow-top-right": ``, - "open-file": ``, + "square-arrow-top-right": ``, + "open-file": ``, "speech-bubble": ``, - comment: ``, - "folder-add-left": ``, - github: ``, + comment: ``, + "folder-add-left": ``, + github: ``, discord: ``, "layout-bottom": ``, "layout-bottom-partial": ``, "layout-bottom-full": ``, - "dot-grid": ``, - "circle-check": ``, - copy: ``, - check: ``, + "dot-grid": ``, + "circle-check": ``, + copy: ``, + check: ``, photo: ``, share: ``, shield: ``, - download: ``, - menu: ``, + download: ``, + menu: ``, server: ``, - branch: ``, - edit: ``, - help: ``, - "settings-gear": ``, - dash: ``, - "cloud-upload": ``, - trash: ``, - sliders: ``, - keyboard: ``, - selector: ``, - "arrow-down-to-line": ``, - warning: ``, - reset: ``, + branch: ``, + edit: ``, + help: ``, + "settings-gear": ``, + dash: ``, + "cloud-upload": ``, + trash: ``, + sliders: ``, + keyboard: ``, + selector: ``, + "arrow-down-to-line": ``, + warning: ``, + reset: ``, link: ``, - providers: ``, - models: ``, + providers: ``, + models: ``, + + workspace: ``, + "doc-processing": ``, + "bar-chart": ``, + pin: ``, + "chevron-up": ``, + "plugin": ``, + "automation": ``, + "remote-control": ``, + "schedule": ``, + "key": ``, + } export interface IconProps extends ComponentProps<"svg"> { @@ -111,7 +116,6 @@ export interface IconProps extends ComponentProps<"svg"> { export function Icon(props: IconProps) { const [local, others] = splitProps(props, ["name", "size", "class", "classList"]) - const viewBox = () => (local.name === "magnifying-glass" ? "0 0 16 16" : "0 0 20 20") return (
{label()} - + } class="w-60 rounded-[10px] border border-border-strong bg-surface-base p-1 shadow-lg" @@ -90,7 +91,7 @@ export function WorkspaceChip() { setOpen(false) }} > - + {getFilename(workspace)} ) @@ -107,7 +108,7 @@ export function WorkspaceChip() { layoutPage.openProject() }} > - + {language.t("workspace.chip.add")}
@@ -116,36 +117,3 @@ export function WorkspaceChip() { ) } -function FolderIcon(props: { class?: string }) { - return ( -
- -
- ) -} - -function ChevronIcon(props: { class?: string }) { - return ( -
- -
- ) -} - -function PlusIcon(props: { class?: string }) { - return ( -
- -
- ) -} diff --git a/packages/app/src/components/session/pawwork-skill-meta.ts b/packages/app/src/components/session/pawwork-skill-meta.ts index bd775c080..1c43ac0ec 100644 --- a/packages/app/src/components/session/pawwork-skill-meta.ts +++ b/packages/app/src/components/session/pawwork-skill-meta.ts @@ -1,31 +1,45 @@ import type { IconProps } from "@opencode-ai/ui/icon" -import type { SkillIconName } from "./skill-icons" +import type { JSX } from "solid-js" -export const pawworkSkillCards = [ +type PawworkSkillCard = { + readonly name: "document-processing" | "data-analysis" | "writing-assistant" + readonly iconName: IconProps["name"] + readonly homeIcon: IconProps["name"] + readonly homeIconClass: string + readonly homeIconStyle?: JSX.CSSProperties + readonly titleKey: string + readonly descriptionKey: string +} + +// Writing accent (`#8B5FBF`) lives as inline style because Tailwind v4 is +// configured with `--color-*: initial`, so palette utilities like +// `text-violet-500` resolve to no CSS variable and render black. +export const pawworkSkillCards: readonly PawworkSkillCard[] = [ { name: "document-processing", - iconName: "folder" as IconProps["name"], - homeIcon: "folder" as SkillIconName, + iconName: "doc-processing", + homeIcon: "doc-processing", homeIconClass: "text-icon-warning-base", titleKey: "session.new.card.document.title", descriptionKey: "session.new.card.document.description", }, { name: "data-analysis", - iconName: "status" as IconProps["name"], - homeIcon: "bar-chart" as SkillIconName, + iconName: "bar-chart", + homeIcon: "bar-chart", homeIconClass: "text-icon-success-base", titleKey: "session.new.card.analysis.title", descriptionKey: "session.new.card.analysis.description", }, { name: "writing-assistant", - iconName: "pencil-line" as IconProps["name"], - homeIcon: "pencil" as SkillIconName, - homeIconClass: "text-violet-500", + iconName: "pencil-line", + homeIcon: "pencil-line", + homeIconClass: "", + homeIconStyle: { color: "#8B5FBF" }, titleKey: "session.new.card.writing.title", descriptionKey: "session.new.card.writing.description", }, -] as const +] -export type PawworkSkillName = (typeof pawworkSkillCards)[number]["name"] +export type PawworkSkillName = PawworkSkillCard["name"] diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx index 94a5c1a24..975debe45 100644 --- a/packages/app/src/components/session/session-new-view.tsx +++ b/packages/app/src/components/session/session-new-view.tsx @@ -1,7 +1,7 @@ +import { Icon } from "@opencode-ai/ui/icon" import { For, createSignal, Show, type JSX } from "solid-js" import { useLanguage } from "@/context/language" import { pawworkSkillCards, type PawworkSkillName } from "./pawwork-skill-meta" -import { SkillIcon } from "./skill-icons" type ComposerCtx = { onModeChange: (mode: "normal" | "shell") => void @@ -23,7 +23,7 @@ export function NewSessionView(props: { composer?: (ctx: ComposerCtx) => JSX.Ele

{language.t("session.new.title")}

{language.t("session.new.subtitle")}

-
+
{(card) => { const isSelected = () => mode() === "normal" && selectedSkill() === card.name @@ -33,14 +33,19 @@ export function NewSessionView(props: { composer?: (ctx: ComposerCtx) => JSX.Ele data-skill-card={card.name} aria-pressed={isSelected()} classList={{ - "flex items-center gap-2 rounded-xl border px-6 py-3 transition-colors": true, + "flex items-center justify-center gap-2 rounded-xl border px-6 py-3 transition-colors": true, "border-border-weaker-base bg-surface-base hover:border-border-weak-base hover:bg-surface-raised-base-hover": !isSelected(), "border-border-interactive-base bg-surface-raised-base shadow-sm": isSelected(), }} onClick={() => toggleSkill(card.name)} > - + {language.t(card.titleKey)} ) diff --git a/packages/app/src/components/session/skill-icons.tsx b/packages/app/src/components/session/skill-icons.tsx deleted file mode 100644 index 44497f650..000000000 --- a/packages/app/src/components/session/skill-icons.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { JSX } from "solid-js" - -const ICONS = { - folder: [ - "M760 1850 c-119 -11 -182 -48 -220 -132 -18 -41 -20 -70 -20 -518 0 -474 0 -476 22 -520 16 -31 38 -54 72 -75 l49 -30 541 0 541 0 40 27 c22 15 51 44 65 65 l25 37 3 385 c3 417 0 441 -51 489 -48 46 -83 51 -342 59 l-246 6 -37 45 c-94 113 -114 131 -165 150 -56 20 -147 24 -277 12z m276 -107 c14 -9 50 -49 79 -88 83 -113 90 -115 377 -115 222 0 237 -1 261 -20 28 -22 43 -74 32 -109 -6 -21 -7 -21 -578 -21 -315 0 -578 4 -585 9 -11 6 -13 40 -10 150 l3 143 37 34 38 34 160 0 c133 0 165 -3 186 -17z m745 -454 c3 -6 5 -136 5 -289 l-1 -280 -27 -27 c-38 -38 -91 -41 -611 -42 -456 -1 -469 0 -512 49 -19 21 -20 36 -20 309 l0 286 110 5 c190 9 1049 -1 1056 -11z", - ], - "bar-chart": [ - "M1575 1872 c-22 -15 -50 -43 -62 -61 l-23 -34 0 -576 c0 -553 1 -577 20 -605 33 -51 86 -76 160 -76 79 0 134 27 162 80 17 33 18 65 16 615 l-3 580 -23 33 c-53 73 -173 95 -247 44z m161 -93 c18 -20 19 -46 22 -570 l2 -549 -24 -25 c-32 -32 -96 -35 -126 -5 -20 20 -20 33 -20 579 l0 560 22 15 c33 24 101 21 124 -5z", - "M1110 1499 c-14 -6 -36 -20 -48 -32 -52 -48 -52 -49 -52 -459 0 -366 1 -384 20 -416 28 -45 89 -72 168 -72 69 0 103 13 142 55 l25 27 3 394 c3 442 3 440 -67 488 -31 22 -48 26 -102 25 -35 0 -75 -5 -89 -10z m146 -100 c18 -20 19 -41 19 -386 l-1 -365 -24 -20 c-31 -25 -106 -20 -127 8 -10 14 -13 98 -13 385 l0 368 22 15 c33 24 101 21 124 -5z", - "M630 1133 c-19 -10 -45 -33 -57 -52 -22 -32 -23 -45 -25 -220 -1 -237 -1 -240 29 -280 33 -42 78 -61 149 -61 65 0 114 20 149 62 25 29 25 30 25 244 0 231 -3 245 -55 294 -23 21 -38 25 -103 28 -59 2 -85 -1 -112 -15z m151 -93 c18 -10 19 -23 19 -205 0 -189 -1 -194 -22 -209 -34 -24 -85 -20 -114 9 l-25 25 3 180 c3 165 5 180 23 194 23 18 87 21 116 6z", - ], - pencil: [ - "M1423 1865 c-67 -19 -144 -67 -178 -110 -17 -23 -34 -35 -50 -35 -14 0 -32 -11 -45 -27 -34 -44 -334 -347 -389 -393 -53 -45 -63 -63 -85 -155 -8 -33 -21 -87 -30 -120 -17 -59 -93 -386 -103 -436 -4 -20 2 -31 23 -48 16 -13 37 -21 49 -18 11 2 135 34 276 70 l256 65 229 233 c126 128 239 243 251 256 16 17 29 22 47 17 37 -9 69 31 61 75 -6 30 -2 39 33 75 42 43 82 110 82 136 0 9 5 21 11 27 19 19 5 122 -25 190 -34 74 -113 154 -181 183 -63 27 -166 34 -232 15z m171 -89 c64 -19 111 -57 146 -119 24 -42 30 -64 30 -115 0 -76 -14 -113 -67 -172 l-40 -44 -27 18 c-25 16 -29 16 -52 1 -27 -18 -31 -42 -9 -71 8 -10 15 -22 15 -26 0 -3 -71 -77 -157 -165 -87 -87 -197 -199 -244 -250 l-86 -91 -124 -31 c-68 -16 -146 -37 -174 -45 -27 -9 -53 -12 -57 -8 -5 5 8 29 28 55 37 47 39 72 9 83 -9 4 -31 -8 -56 -31 -68 -61 -69 -41 -11 182 29 109 52 206 52 216 0 9 8 25 19 35 10 9 110 108 223 220 138 136 212 202 226 202 14 0 27 11 36 29 26 50 107 111 176 132 40 12 93 10 144 -5z", - "M1283 1618 c-13 -6 -23 -16 -23 -21 0 -14 200 -207 215 -207 16 0 35 21 35 40 0 17 -179 200 -195 199 -6 0 -20 -5 -32 -11z", - "M1083 1188 c-128 -128 -236 -241 -239 -252 -9 -28 40 -70 65 -57 31 17 466 471 469 490 3 18 -31 51 -52 51 -6 0 -115 -105 -243 -232z", - ], -} as const - -export type SkillIconName = keyof typeof ICONS - -export function SkillIcon(props: { name: SkillIconName; class?: string }): JSX.Element { - return ( - - ) -} diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 69f6941f1..3d5958639 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -616,12 +616,12 @@ export const dict = { "session.new.title": "Choose what to do", "session.new.subtitle": "PawWork helps you process files, analyze information, write content, and tackle everyday tasks.", "session.new.reassurance": "Files and conversations stay on your computer", - "session.new.card.document.title": "Process documents", + "session.new.card.document.title": "Process docs", "session.new.card.document.description": "Edit, convert, and extract from Word, Excel, PowerPoint, and PDF files.", "session.new.card.analysis.title": "Analyze data", "session.new.card.analysis.description": "Summarize spreadsheets, build charts, and answer questions from your files.", - "session.new.card.writing.title": "Write faster", + "session.new.card.writing.title": "Start writing", "session.new.card.writing.description": "Draft emails, reports, plans, and polished work copy.", "session.new.placeholder.document": "Share a document and tell me what to organize, convert, or extract…", "session.new.placeholder.analysis": "Share your data and tell me what to summarize, chart, or answer…", From 856cbf247b90a50af116cc4b6c4cc51b780d2371 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Thu, 23 Apr 2026 22:29:38 +0800 Subject: [PATCH 3/3] feat(app): wire -active tab icons + refine sidebar icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pass active state to RightPanelShellIcon so Review / Terminal tabs render review-active / terminal-active when selected (previously both variants lived in icon.tsx but were unused). - Fix Add-tab dropdown's "Open file" menu item icon: was folder-add-left (duplicating Open-project's glyph), now open-file. - Remove the 20×20 magnifying-glass override on the sidebar search button so it matches the 16×16 of the new-session button (was 25% larger). Closes part of #181 (sidebar icon set alignment, slice 3 of 3). --- .../app/src/pages/layout/pawwork-sidebar.tsx | 22 ++++--------------- .../src/pages/session/session-side-panel.tsx | 12 +++++----- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/packages/app/src/pages/layout/pawwork-sidebar.tsx b/packages/app/src/pages/layout/pawwork-sidebar.tsx index 7d6b886b2..7f2979db8 100644 --- a/packages/app/src/pages/layout/pawwork-sidebar.tsx +++ b/packages/app/src/pages/layout/pawwork-sidebar.tsx @@ -2,6 +2,7 @@ import type { Session } from "@opencode-ai/sdk/v2/client" import { Button } from "@opencode-ai/ui/button" import { ContextMenu } from "@opencode-ai/ui/context-menu" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Icon } from "@opencode-ai/ui/icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { createEffect, createMemo, createSignal, For, Show, type Accessor, type JSX } from "solid-js" @@ -26,21 +27,6 @@ const FilterIcon = (props: { size?: number }) => { ) } -const PinIcon = (props: { filled?: boolean; size?: number }) => { - const size = props.size ?? 16 - return ( - - ) -} - export const PawworkSidebar = (props: { scope?: "main" | "peek" mobile?: boolean @@ -145,12 +131,12 @@ export const PawworkSidebar = (props: { }} classList={{ "inline-flex size-6 items-center justify-center rounded transition-colors": true, - "text-accent-brand opacity-100 pointer-events-auto": isPinned(), + "text-text-strong opacity-100 pointer-events-auto": isPinned(), "text-text-weak opacity-0 pointer-events-none group-hover/session:opacity-100 group-hover/session:pointer-events-auto group-focus-within/session:opacity-100 group-focus-within/session:pointer-events-auto hover:text-text-base": !isPinned(), }} > - + )} titleContent={({ session: rowSession, title }) => ( @@ -264,7 +250,7 @@ export const PawworkSidebar = (props: { size="large" variant="ghost" icon="magnifying-glass" - class="w-full [&>[data-component=icon]]:!size-5" + class="w-full" onClick={props.onSearch} > {language.t("sidebar.pawwork.search")} diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 5217a286b..520433b84 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -69,7 +69,7 @@ export function openReviewShellTab(sidePanel: { openTab: (tab: "review") => void } /** Maps right-panel tab names to their shell icon components. */ -function RightPanelShellIcon(props: { icon: ShellTabIcon }) { +function RightPanelShellIcon(props: { icon: ShellTabIcon; active?: boolean }) { return ( @@ -82,10 +82,10 @@ function RightPanelShellIcon(props: { icon: ShellTabIcon }) { - + - + ) @@ -317,7 +317,7 @@ export function SessionSidePanel(props: { closable={tab.closable} onClose={view().sidePanel.closeTab} > - + {tab.label} } @@ -328,7 +328,7 @@ export function SessionSidePanel(props: { closable={tab.closable} onClose={view().sidePanel.closeTab} > - + {tab.label} @@ -349,7 +349,7 @@ export function SessionSidePanel(props: { openFilePicker(showAllFiles)}> - + {language.t("command.file.open")} {command.keybind("file.open")}