Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/app/e2e/app/home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test("@smoke home renders the hero composer and starter cards", async ({ page, p

const home = page.locator('[data-component="session-new-home"]')
const composer = home.locator(sessionComposerDockSelector)
const firstCard = home.getByRole("button", { name: /Process documents/i })
const firstCard = home.getByRole("button", { name: /Process docs/i })
const workspaceChip = page.getByRole("button", { name: /Switch workspace|切换工作目录/i })
await expect(home).toBeVisible()
await expect(page.getByRole("button", { name: "Open project" }).first()).toBeVisible()
Expand All @@ -31,7 +31,7 @@ test("@smoke home renders the hero composer and starter cards", async ({ page, p
await expect(home.locator(promptSelector)).toBeVisible()
await expect(firstCard).toBeVisible()
await expect(page.getByRole("button", { name: /Analyze data/i })).toBeVisible()
await expect(page.getByRole("button", { name: /Write faster/i })).toBeVisible()
await expect(page.getByRole("button", { name: /Start writing/i })).toBeVisible()
await expect(page.getByRole("button", { name: "Right utility panel" })).toBeVisible()
await expect(workspaceChip).toBeVisible()

Expand Down
42 changes: 5 additions & 37 deletions packages/app/src/components/prompt-input/workspace-chip.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Icon } from "@opencode-ai/ui/icon"
import { Popover } from "@opencode-ai/ui/popover"
import { base64Encode } from "@opencode-ai/util/encode"
import { getFilename } from "@opencode-ai/util/path"
Expand Down Expand Up @@ -60,9 +61,9 @@ export function WorkspaceChip() {
}}
trigger={
<>
<FolderIcon class="text-text-weak" />
<Icon name="workspace" size="small" class="text-text-weak" />
<span class="leading-none">{label()}</span>
<ChevronIcon class="text-text-weak" />
<Icon name="chevron-down" size="small" class="text-text-weak" />
</>
}
class="w-60 rounded-[10px] border border-border-strong bg-surface-base p-1 shadow-lg"
Expand Down Expand Up @@ -90,7 +91,7 @@ export function WorkspaceChip() {
setOpen(false)
}}
>
<FolderIcon class="text-text-weak" />
<Icon name="workspace" size="small" class="text-text-weak" />
<span class="min-w-0 flex-1 truncate">{getFilename(workspace)}</span>
</button>
)
Expand All @@ -107,7 +108,7 @@ export function WorkspaceChip() {
layoutPage.openProject()
}}
>
<PlusIcon class="text-text-weak" />
<Icon name="plus-small" size="small" class="text-text-weak" />
<span class="min-w-0 flex-1 truncate">{language.t("workspace.chip.add")}</span>
</button>
</div>
Expand All @@ -116,36 +117,3 @@ export function WorkspaceChip() {
)
}

function FolderIcon(props: { class?: string }) {
return (
<div data-component="icon" data-size="small" class={props.class}>
<svg data-slot="icon-svg" viewBox="0 0 14 14" fill="none" aria-hidden="true">
<path
d="M1.5 3.5a1 1 0 0 1 1-1h3l1.5 1.5h4.5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1V3.5z"
stroke="currentColor"
stroke-width="1.1"
/>
</svg>
</div>
)
}

function ChevronIcon(props: { class?: string }) {
return (
<div data-component="icon" data-size="small" class={props.class}>
<svg data-slot="icon-svg" viewBox="0 0 14 14" fill="none" aria-hidden="true">
<path d="M3.5 5.5l3.5 3 3.5-3" stroke="currentColor" stroke-width="1.1" fill="none" />
</svg>
</div>
)
}

function PlusIcon(props: { class?: string }) {
return (
<div data-component="icon" data-size="small" class={props.class}>
<svg data-slot="icon-svg" viewBox="0 0 14 14" fill="none" aria-hidden="true">
<path d="M7 3.5v7M3.5 7h7" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" />
</svg>
</div>
)
}
36 changes: 25 additions & 11 deletions packages/app/src/components/session/pawwork-skill-meta.ts
Original file line number Diff line number Diff line change
@@ -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" },
Comment thread
Astro-Han marked this conversation as resolved.
titleKey: "session.new.card.writing.title",
Comment thread
Astro-Han marked this conversation as resolved.
Comment thread
Astro-Han marked this conversation as resolved.
descriptionKey: "session.new.card.writing.description",
},
] as const
]

export type PawworkSkillName = (typeof pawworkSkillCards)[number]["name"]
export type PawworkSkillName = PawworkSkillCard["name"]
13 changes: 9 additions & 4 deletions packages/app/src/components/session/session-new-view.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,7 +23,7 @@ export function NewSessionView(props: { composer?: (ctx: ComposerCtx) => JSX.Ele
<h1 class="text-20-medium text-text-strong">{language.t("session.new.title")}</h1>
<p class="mt-3 text-14-regular text-text-weak">{language.t("session.new.subtitle")}</p>

<div class="mt-8 flex w-full max-w-[640px] flex-wrap justify-center gap-3">
<div class="mt-8 grid w-fit max-w-[640px] grid-cols-1 gap-3 sm:grid-cols-3">
<For each={pawworkSkillCards}>
Comment thread
Astro-Han marked this conversation as resolved.
{(card) => {
const isSelected = () => mode() === "normal" && selectedSkill() === card.name
Expand All @@ -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)}
>
<SkillIcon name={card.homeIcon} class={`h-6 w-6 shrink-0 ${card.homeIconClass}`} />
<Icon
name={card.homeIcon}
size="normal"
class={`shrink-0 ${card.homeIconClass ?? ""}`}
style={card.homeIconStyle}
/>
Comment thread
Astro-Han marked this conversation as resolved.
<span class="text-14-medium text-text-strong">{language.t(card.titleKey)}</span>
</button>
)
Expand Down
37 changes: 0 additions & 37 deletions packages/app/src/components/session/skill-icons.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Comment thread
Astro-Han marked this conversation as resolved.
"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…",
Expand Down
22 changes: 4 additions & 18 deletions packages/app/src/pages/layout/pawwork-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -26,21 +27,6 @@ const FilterIcon = (props: { size?: number }) => {
)
}

const PinIcon = (props: { filled?: boolean; size?: number }) => {
Comment thread
Astro-Han marked this conversation as resolved.
const size = props.size ?? 16
return (
<svg width={size} height={size} viewBox="0 0 20 20" fill="none" aria-hidden="true">
<path
d="M11 2L18 9L14 11L11 14L6 9L9 6Z"
stroke="currentColor"
stroke-linejoin="miter"
fill={props.filled ? "currentColor" : "none"}
/>
<path d="M8 11L2 18" stroke="currentColor" stroke-linecap="square" />
</svg>
)
}

export const PawworkSidebar = (props: {
scope?: "main" | "peek"
mobile?: boolean
Expand Down Expand Up @@ -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(),
Comment thread
Astro-Han marked this conversation as resolved.
"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":
Comment thread
Astro-Han marked this conversation as resolved.
!isPinned(),
}}
>
<PinIcon filled={isPinned()} />
<Icon name="pin" size="small" />
Comment thread
Astro-Han marked this conversation as resolved.
</button>
Comment thread
Astro-Han marked this conversation as resolved.
)}
titleContent={({ session: rowSession, title }) => (
Expand Down Expand Up @@ -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}
Comment thread
Astro-Han marked this conversation as resolved.
>
{language.t("sidebar.pawwork.search")}
Expand Down
12 changes: 6 additions & 6 deletions packages/app/src/pages/session/session-side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Switch>
<Match when={props.icon.kind === "indicator"}>
Expand All @@ -82,10 +82,10 @@ function RightPanelShellIcon(props: { icon: ShellTabIcon }) {
<Icon name="folder" size="small" class="text-text-weaker" />
</Match>
<Match when={props.icon.kind === "icon" && props.icon.name === "review"}>
<Icon name="review" size="small" class="text-text-weaker" />
<Icon name={props.active ? "review-active" : "review"} size="small" class="text-text-weaker" />
</Match>
Comment thread
Astro-Han marked this conversation as resolved.
<Match when={props.icon.kind === "icon" && props.icon.name === "terminal"}>
<Icon name="terminal" size="small" class="text-text-weaker" />
<Icon name={props.active ? "terminal-active" : "terminal"} size="small" class="text-text-weaker" />
</Match>
</Switch>
)
Expand Down Expand Up @@ -317,7 +317,7 @@ export function SessionSidePanel(props: {
closable={tab.closable}
onClose={view().sidePanel.closeTab}
>
<RightPanelShellIcon icon={tab.icon} />
<RightPanelShellIcon icon={tab.icon} active={activeTab() === tab.value} />
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<span>{tab.label}</span>
</ShellTab>
}
Expand All @@ -328,7 +328,7 @@ export function SessionSidePanel(props: {
closable={tab.closable}
onClose={view().sidePanel.closeTab}
>
<RightPanelShellIcon icon={tab.icon} />
<RightPanelShellIcon icon={tab.icon} active={activeTab() === tab.value} />
<span>{tab.label}</span>
</SortableShellTab>
</Show>
Expand All @@ -349,7 +349,7 @@ export function SessionSidePanel(props: {
<DropdownMenu.Portal>
<DropdownMenu.Content>
<DropdownMenu.Item onSelect={() => openFilePicker(showAllFiles)}>
<Icon name="folder-add-left" size="small" />
<Icon name="open-file" size="small" />
<DropdownMenu.ItemLabel>{language.t("command.file.open")}</DropdownMenu.ItemLabel>
<span class="ml-auto text-12-regular text-text-weaker">{command.keybind("file.open")}</span>
</DropdownMenu.Item>
Expand Down
Loading
Loading