refactor(ui): unify typography on 13/12 and tighten sidebar geometry#320
Conversation
Re-points .text-14-medium and .text-14-mono to a new --font-size-hierarchy token (initialized to 14px). No visual change; prepares for flipping --font-size-base to 13px without dragging hierarchy callsites.
- Change --font-size-base from 14px to 13px - Add --font-size-x-small: 12px token - Add .text-12-regular / .text-12-medium / .text-12-mono utilities Hierarchy callsites (.text-14-medium / .text-14-mono) remain 14px via --font-size-hierarchy. .text-14-regular callsites now render at 13px; they will be renamed to .text-13-regular in the next commit.
Mass sed across packages/app/src and packages/ui/src (.tsx only, excluding .stories.tsx). 30 files updated. The .text-14-regular class definition stays in utilities.css as a transitional shim; no .tsx file references it anymore. Behavioral note: callsites previously rendered at 14px / line-height 171% now render at 13px / line-height 167% (the .text-13-regular metrics). This is the intended body-copy density change.
…kens Comment-driven decision rule applied across 12 component CSS files: - Rules with /* text-14-medium */ neighbor → var(--font-size-hierarchy) (button.css, list.css, tabs.css, tag.css, toast.css) - Rules with /* text-14-regular */ neighbor → var(--font-size-base) (dialog.css, list.css, text-field.css, message-nav.css) - No-comment rules judged by selector and weight: titles/headers → hierarchy; row bodies, hints, descriptions, inputs → base (basic-tool.css, message-part.css, diff-changes.css, markdown.css) The [data-slot="permission-patterns"] code rule at message-part.css:803 is intentionally left at 14px; Task 6 takes ownership and routes it to 12px alongside the other code-styling changes.
- markdown.css .shiki: 13px → 12px (fenced code blocks) - markdown.css :not(pre) > code: uncomment and set to 12px (inline code) - message-part.css permission-patterns code: 14px → 12px reasoning-part defensive 13px override left in place.
Add data-leading-slot attribute to 3 leading-icon wrappers in the sidebar: SessionRow indicator, NewSessionItem leading slot, and the ProjectPreviewPanel branch icon. New sidebar.css force-sizes any svg or [data-size="small"] descendant to 14×14 inside those wrappers, without touching the global Icon size="small" default (16px) used elsewhere. The branch icon wrapper stays size-6 (24px container) here; resizing to size-5 is owned by the action-container task.
Switch SessionRow and NewSessionItem leading slots from a 20×20 centered container to 14×14 left-anchored. With the row's existing pl-2 (8px), every leading visual (PIN dot, status dots, leading icon, spinner) now starts at the same X=8 left edge, removing the per-shape centering offset that previously pushed icons to X=10 and dots to X=15. Spinner shrunk 15→14 to fit the new slot; gap-2 keeps text X at 8 + 14 + 8 = 30. Branch row resize is owned by the next commit.
Replace the top-of-sidebar New session and Search Buttons with the same row JSX shape SessionRow uses: pl-2 row padding, 14×14 data-leading-slot, gap-2, text-13-medium label. New session keeps the primary surface fill and the brand-color leading icon; Search uses the same hover background as session rows. Container gap-2 → gap-0.5 to match the rest of the session list spacing now that both buttons are list rows. The empty-state "Open project" CTA and titlebar IconButtons are unchanged.
- Resize size-6 (24px) to size-5 (20px) for: SessionItem archive IconButton, SessionItem reserved action area (w-6 → w-5), PawworkSidebar dot-grid menu trigger, and the project-preview branch icon container. - Bump the ALL group header from pr-2 to pr-3 so the sort/filter button's right hit-edge lines up with the row archive button's right edge (both at the panel's pr-3 rail). The branch icon glyph automatically renders at 14×14 inside the 20×20 container via the data-leading-slot CSS rule introduced earlier.
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR shifts the UI typography baseline (14px → 13px, adds 12px), replaces many hard-coded font sizes with theme variables, tightens component sizing (buttons, inputs, sidebar icons/leading slots), and removes several PawworkSidebar help/open-project props and footer controls. Several i18n strings and an e2e assertion were also updated. Changes
Sequence Diagram(s)(omitted — changes are primarily styling, theme, and minor API removal without new multi-actor control flow) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Review rate limit: 0/3 reviews remaining, refill in 9 minutes and 32 seconds. Comment |
- Bump inter-row gap from 2px (gap-0.5) to 4px (gap-1) and row vertical padding from py-1 to py-1.5, so the sessions list and the top New session / Search rows feel less squeezed. - Bump the leading icons on top New session, Search, and Settings rows to 16x16 (default Icon size="small") for visual weight; SessionRow indicators stay at 14x14 via the data-leading-slot override. - Drop the orange brand fill on New session so it matches the Search and Settings rows; brand differentiation now lives only in the icon glyph, not the row background. - Replace the three-IconButton footer (open project / help / settings) with a single Settings row that aligns with the sidebar's left rail and uses the same row shape as the top buttons. - Swap the workspace chip menu's "add directory" leading icon from plus-small to folder-add-left so the chip menu carries the open-workspace affordance the footer button used to. - Drop the now-dead onOpenHelp / helpLabel / openProjectLabel / openProjectKeybind props on PawworkSidebar, the unused PAWWORK_GITHUB_ISSUE_URL import in layout.tsx, the unused Tooltip import in pawwork-sidebar.tsx, and the orphan sidebar.help i18n key in zh and en. diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 25f386a..0cf99a5 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -758,7 +758,6 @@ export const dict = { "sidebar.menu.toggle": "Toggle menu", "sidebar.nav.projectsAndSessions": "Projects and sessions", "sidebar.settings": "Settings", - "sidebar.help": "Help", "sidebar.workspaces.enable": "Enable workspaces", "sidebar.workspaces.disable": "Disable workspaces", "sidebar.gettingStarted.title": "Getting started", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index adc8ef9..eff9aee 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -672,7 +672,6 @@ export const dict = { "sidebar.menu.toggle": "切换菜单", "sidebar.nav.projectsAndSessions": "项目和会话", "sidebar.settings": "设置", - "sidebar.help": "帮助", "sidebar.workspaces.enable": "启用工作区", "sidebar.workspaces.disable": "禁用工作区", "sidebar.gettingStarted.title": "入门", diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 0940ac7..9797e0c 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -18,7 +18,6 @@ import { useNavigate, useParams } from "@solidjs/router" import { useLayout, LocalProject } from "@/context/layout" import { useGlobalSync } from "@/context/global-sync" import { Persist, persisted } from "@/utils/persist" -import { PAWWORK_GITHUB_ISSUE_URL } from "@/utils/support-links" import { base64Encode } from "@opencode-ai/util/encode" import { decode64 } from "@/utils/base64" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" @@ -2170,7 +2169,7 @@ export default function Layout(props: ParentProps) { <div class="flex-1 min-h-0 -mt-4 flex items-center justify-center px-6 pb-64 text-center"> <div class="mt-8 flex max-w-60 flex-col items-center gap-6 text-center"> <div class="flex flex-col gap-3"> - <div class="text-14-medium text-text-strong">{language.t("sidebar.empty.title")}</div> + <div class="text-13-medium text-text-strong">{language.t("sidebar.empty.title")}</div> <div class="text-13-regular text-text-base" style={{ "line-height": "var(--line-height-normal)" }}> {language.t("sidebar.empty.description")} </div> @@ -2195,8 +2194,8 @@ export default function Layout(props: ParentProps) { if (!item) return renameProject(item, next) }} - class="text-14-medium text-text-strong truncate" - displayClass="text-14-medium text-text-strong truncate" + class="text-13-medium text-text-strong truncate" + displayClass="text-13-medium text-text-strong truncate" stopPropagation /> @@ -2376,7 +2375,7 @@ export default function Layout(props: ParentProps) { <div class="rounded-xl bg-background-base shadow-xs-border-base" data-component="getting-started"> <div class="p-3 flex flex-col gap-6"> <div class="flex flex-col gap-2"> - <div class="text-14-medium text-text-strong">{language.t("sidebar.gettingStarted.title")}</div> + <div class="text-13-medium text-text-strong">{language.t("sidebar.gettingStarted.title")}</div> <div class="text-13-regular text-text-base" style={{ "line-height": "var(--line-height-normal)" }}> {language.t("sidebar.gettingStarted.line1")} </div> @@ -2425,12 +2424,8 @@ export default function Layout(props: ParentProps) { onSearch={() => command.show()} onOpenProject={chooseProject} onOpenSettings={openSettings} - onOpenHelp={() => platform.openLink(PAWWORK_GITHUB_ISSUE_URL)} - openProjectLabel={() => language.t("command.project.open")} - openProjectKeybind={() => command.keybind("project.open")} settingsLabel={() => language.t("sidebar.settings")} settingsKeybind={() => command.keybind("settings.open")} - helpLabel={() => language.t("sidebar.help")} /> ) const sidebarContent = (mobile?: boolean) => diff --git a/packages/app/src/pages/layout/pawwork-sidebar.tsx b/packages/app/src/pages/layout/pawwork-sidebar.tsx index 4a0c4e4..7400d2e 100644 --- a/packages/app/src/pages/layout/pawwork-sidebar.tsx +++ b/packages/app/src/pages/layout/pawwork-sidebar.tsx @@ -4,7 +4,7 @@ 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 { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { createEffect, createMemo, createSignal, For, Show, type Accessor, type JSX } from "solid-js" import { useLanguage } from "@/context/language" import { createInlineEditorController } from "./inline-editor" @@ -48,12 +48,8 @@ export const PawworkSidebar = (props: { onSearch: () => void onOpenProject: () => void onOpenSettings: () => void - onOpenHelp: () => void - openProjectLabel: Accessor<string> - openProjectKeybind: Accessor<string | undefined> settingsLabel: Accessor<string> settingsKeybind: Accessor<string | undefined> - helpLabel: Accessor<string> }): JSX.Element => { const language = useLanguage() const editor = createInlineEditorController() @@ -242,25 +238,25 @@ export const PawworkSidebar = (props: { class="flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background-weak" > <div class="shrink-0 px-3 pt-3"> - <div class="flex flex-col gap-0.5"> + <div class="flex flex-col gap-1"> <button type="button" data-action="pawwork-session-new" onClick={props.onNew} - class="w-full flex items-center gap-2 pl-2 pr-3 py-1 rounded-xl bg-surface-interactive-base hover:bg-surface-interactive-hover transition-colors text-left focus:outline-none" + class="w-full flex items-center gap-2 pl-2 pr-3 py-1.5 rounded-xl hover:bg-surface-raised-base-hover transition-colors text-left focus:outline-none" > - <span data-leading-slot class="shrink-0 w-[14px] h-[14px] flex items-center" style={{ color: "var(--accent-brand)" }}> - <Icon name="new-session" size="small" /> + <span class="shrink-0 w-4 h-4 flex items-center"> + <Icon name="new-session" size="small" class="text-icon-base" /> </span> - <span class="text-13-medium text-text-strong min-w-0 flex-1 truncate">{language.t("command.session.new")}</span> + <span class="text-13-medium text-text-base min-w-0 flex-1 truncate">{language.t("command.session.new")}</span> </button> <button type="button" data-action="pawwork-session-search" onClick={props.onSearch} - class="w-full flex items-center gap-2 pl-2 pr-3 py-1 rounded-xl hover:bg-surface-raised-base-hover transition-colors text-left focus:outline-none" + class="w-full flex items-center gap-2 pl-2 pr-3 py-1.5 rounded-xl hover:bg-surface-raised-base-hover transition-colors text-left focus:outline-none" > - <span data-leading-slot class="shrink-0 w-[14px] h-[14px] flex items-center"> + <span class="shrink-0 w-4 h-4 flex items-center"> <Icon name="magnifying-glass" size="small" class="text-icon-base" /> </span> <span class="text-13-medium text-text-base min-w-0 flex-1 truncate">{language.t("sidebar.pawwork.search")}</span> @@ -273,7 +269,7 @@ export const PawworkSidebar = (props: { fallback={ <div class="flex flex-1 items-center px-3"> <div class="flex w-full flex-col gap-3 rounded-xl border border-border-weak-base bg-surface-base p-4"> - <div class="text-14-medium text-text-strong">{language.t("sidebar.empty.title")}</div> + <div class="text-13-medium text-text-strong">{language.t("sidebar.empty.title")}</div> <p class="text-13-regular text-text-weak">{language.t("sidebar.pawwork.empty.description")}</p> <Button data-action="pawwork-open-project" size="large" onClick={props.onOpenProject}> {language.t("command.project.open")} @@ -294,9 +290,9 @@ export const PawworkSidebar = (props: { when={props.sessions().length > 0} fallback={<div class="px-2 text-13-regular text-text-weak">{language.t("sidebar.pawwork.empty.sessions")}</div>} > - <nav class="flex flex-col gap-0.5"> + <nav class="flex flex-col gap-1"> <Show when={pinnedRows().length > 0}> - <section data-component="pawwork-sidebar-pinned" class="flex flex-col gap-0.5"> + <section data-component="pawwork-sidebar-pinned" class="flex flex-col gap-1"> <div class="px-2 pb-0.5 text-13-medium text-text-weak">{language.t("sidebar.pawwork.pinned")}</div> <For each={pinnedRows()}>{(entry) => renderSessionItem(entry)}</For> </section> @@ -326,7 +322,7 @@ export const PawworkSidebar = (props: { <Show when={props.sortMode() === "project"}> <For each={groupedRows()}> {(group) => ( - <section class="flex flex-col gap-0.5"> + <section class="flex flex-col gap-1"> <div data-component="pawwork-group-header" class="px-2 pt-2 pb-0.5 text-13-medium text-text-weak"> {group.label} </div> @@ -342,48 +338,26 @@ export const PawworkSidebar = (props: { <div data-component="pawwork-sidebar-footer" - class="shrink-0 border-t border-border-weaker-base px-3 py-2 flex items-center justify-between" + class="shrink-0 border-t border-border-weaker-base px-3 py-2" > <TooltipKeybind placement={tooltipPlacement()} - title={props.openProjectLabel()} - keybind={props.openProjectKeybind() ?? ""} + title={props.settingsLabel()} + keybind={props.settingsKeybind() ?? ""} > - <IconButton - icon="folder-add-left" - variant="ghost" - size="large" - data-action="pawwork-open-project" - onClick={props.onOpenProject} - aria-label={props.openProjectLabel()} - /> - </TooltipKeybind> - <div class="flex items-center gap-1"> - <Tooltip placement={tooltipPlacement()} value={props.helpLabel()}> - <IconButton - icon="help" - variant="ghost" - size="large" - data-action="pawwork-open-help" - onClick={props.onOpenHelp} - aria-label={props.helpLabel()} - /> - </Tooltip> - <TooltipKeybind - placement={tooltipPlacement()} - title={props.settingsLabel()} - keybind={props.settingsKeybind() ?? ""} + <button + type="button" + data-action="pawwork-open-settings" + onClick={props.onOpenSettings} + aria-label={props.settingsLabel()} + class="w-full flex items-center gap-2 pl-2 pr-3 py-1.5 rounded-xl hover:bg-surface-raised-base-hover transition-colors text-left focus:outline-none" > - <IconButton - icon="settings-gear" - variant="ghost" - size="large" - data-action="pawwork-open-settings" - onClick={props.onOpenSettings} - aria-label={props.settingsLabel()} - /> - </TooltipKeybind> - </div> + <span class="shrink-0 w-4 h-4 flex items-center"> + <Icon name="settings-gear" size="small" class="text-icon-base" /> + </span> + <span class="text-13-medium text-text-base min-w-0 flex-1 truncate">{props.settingsLabel()}</span> + </button> + </TooltipKeybind> </div> </section> ) diff --git a/packages/app/src/pages/layout/sidebar-items.tsx b/packages/app/src/pages/layout/sidebar-items.tsx index 7ca7ada..a25d479 100644 --- a/packages/app/src/pages/layout/sidebar-items.tsx +++ b/packages/app/src/pages/layout/sidebar-items.tsx @@ -110,7 +110,7 @@ const SessionRow = (props: { return ( <A href={`/${props.slug}/session/${props.session.id}`} - class={`flex items-center gap-2 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`} + class={`flex items-center gap-2 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-1" : "py-1.5"}`} onPointerDown={props.warmPress} onFocus={props.warmFocus} onClick={() => { @@ -293,7 +293,7 @@ export const NewSessionItem = (props: { <A href={`/${props.slug}/session`} end - class={`flex items-center gap-2 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`} + class={`flex items-center gap-2 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-1" : "py-1.5"}`} onClick={() => { if (layout.sidebar.opened()) return props.clearHoverProjectSoon() diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx index ada8787..1bcfff5 100644 --- a/packages/app/src/pages/layout/sidebar-project.tsx +++ b/packages/app/src/pages/layout/sidebar-project.tsx @@ -198,7 +198,7 @@ const ProjectPreviewPanel = (props: { }): JSX.Element => ( <div class="-m-3 p-2 flex flex-col w-72"> <div class="px-4 pt-2 pb-1 flex items-center gap-2"> - <div class="text-14-medium text-text-strong truncate grow">{displayName(props.project)}</div> + <div class="text-13-medium text-text-strong truncate grow">{displayName(props.project)}</div> </div> <div class="px-4 pb-2 text-13-medium text-text-weak">{props.language.t("sidebar.project.recentSessions")}</div> <div class="px-2 pb-2 flex flex-col gap-2"> @@ -229,7 +229,7 @@ const ProjectPreviewPanel = (props: { <div data-leading-slot class="shrink-0 size-5 flex items-center justify-center"> <Icon name="branch" size="small" class="text-icon-base" /> </div> - <span class="truncate text-14-medium text-text-base">{props.label(directory)}</span> + <span class="truncate text-13-medium text-text-base">{props.label(directory)}</span> </div> <For each={sessions().slice(0, 2)}> {(session) => (
- Model, variant, and workspace chips: h-[32px] → h-[28px], label text-14-medium → text-13-medium. Same shape, less vertical weight. - Plus-attach button and send button: size-8 → size-7. The plus icon inside the attach button shrinks size-4.5 → size-4 to match the smaller container. - Workspace chip popover menu items: text-14-medium → text-13-medium to stay consistent with the chip surface. Visually puts the composer rail at the same density as the new 13px-driven sidebar and chat body. diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index a64e660..2c6feaf 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1121,7 +1121,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { as="div" variant="ghost" size="normal" - class="h-[32px]! min-w-0 w-44 px-3 justify-start! text-14-medium! text-text-base group rounded-full! border! border-border-strong-base! transition-colors hover:bg-surface-base-hover" + class="h-[28px]! min-w-0 w-44 px-3 justify-start! text-13-medium! text-text-base group rounded-full! border! border-border-strong-base! transition-colors hover:bg-surface-base-hover" style={triggerStyle()} onClick={() => { void import("@/components/dialog-select-model-unpaid").then((x) => { @@ -1158,7 +1158,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { size: "normal", style: triggerStyle(), class: - "h-[32px]! min-w-0 w-44 px-3 justify-start! text-14-medium! text-text-base group rounded-full! border! border-border-strong-base! transition-colors hover:bg-surface-base-hover", + "h-[28px]! min-w-0 w-44 px-3 justify-start! text-13-medium! text-text-base group rounded-full! border! border-border-strong-base! transition-colors hover:bg-surface-base-hover", "data-action": "prompt-model", }} onClose={restoreFocus} @@ -1202,7 +1202,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { "aria-haspopup": "menu", style: triggerStyle(), class: - "h-[32px] px-3 max-w-[140px] inline-flex items-center gap-1.5 rounded-full border border-border-strong-base text-14-medium text-text-base transition-colors hover:bg-surface-base-hover", + "h-[28px] px-3 max-w-[140px] inline-flex items-center gap-1.5 rounded-full border border-border-strong-base text-13-medium text-text-base transition-colors hover:bg-surface-base-hover", } as any } trigger={ @@ -1225,7 +1225,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { type="button" role="menuitemradio" aria-checked={active()} - class="flex w-full items-center justify-between gap-2 rounded-md px-2 py-1.5 text-left text-14-medium text-text-strong outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" + class="flex w-full items-center justify-between gap-2 rounded-md px-2 py-1.5 text-left text-13-medium text-text-strong outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" classList={{ "font-medium": active() }} onClick={() => { local.model.variant.set(variant === "default" ? undefined : variant) @@ -1561,14 +1561,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => { data-action="prompt-attach" type="button" variant="ghost" - class="size-8 shrink-0 p-0" + class="size-7 shrink-0 p-0" style={buttons()} onClick={pick} disabled={store.mode !== "normal"} tabIndex={store.mode === "normal" ? undefined : -1} aria-label={language.t("prompt.action.attachFile")} > - <Icon name="plus" class="size-4.5" /> + <Icon name="plus" class="size-4" /> </Button> </TooltipKeybind> <Show when={store.mode === "normal"}> diff --git a/packages/app/src/components/prompt-input/send-button.tsx b/packages/app/src/components/prompt-input/send-button.tsx index 92ada1a..8168ce5 100644 --- a/packages/app/src/components/prompt-input/send-button.tsx +++ b/packages/app/src/components/prompt-input/send-button.tsx @@ -20,7 +20,7 @@ export const SendButton: Component<SendButtonProps> = (props) => { aria-label={props["aria-label"]} onClick={props.onClick} style={props.style} - class="inline-flex size-8 items-center justify-center rounded-full bg-button-brand-base shadow-[0_1px_3px_var(--button-brand-shadow)] transition-colors duration-150 hover:bg-button-brand-hover disabled:bg-border-weak-base disabled:cursor-not-allowed disabled:shadow-none" + class="inline-flex size-7 items-center justify-center rounded-full bg-button-brand-base shadow-[0_1px_3px_var(--button-brand-shadow)] transition-colors duration-150 hover:bg-button-brand-hover disabled:bg-border-weak-base disabled:cursor-not-allowed disabled:shadow-none" > <Icon name={props.stopping ? "stop-square" : "arrow-up"} diff --git a/packages/app/src/components/prompt-input/workspace-chip.tsx b/packages/app/src/components/prompt-input/workspace-chip.tsx index f45a2a0..5e67527 100644 --- a/packages/app/src/components/prompt-input/workspace-chip.tsx +++ b/packages/app/src/components/prompt-input/workspace-chip.tsx @@ -63,7 +63,7 @@ export function WorkspaceChip(props: { style?: JSX.CSSProperties | string } = {} "aria-label": language.t("workspace.chip.ariaLabel"), "aria-haspopup": "menu", class: - "h-[32px] px-3 inline-flex items-center gap-1.5 rounded-full border border-border-strong-base text-14-medium text-text-base transition-colors hover:bg-surface-base-hover", + "h-[28px] px-3 inline-flex items-center gap-1.5 rounded-full border border-border-strong-base text-13-medium text-text-base transition-colors hover:bg-surface-base-hover", style: props.style, } as any } @@ -96,7 +96,7 @@ export function WorkspaceChip(props: { style?: JSX.CSSProperties | string } = {} type="button" role="menuitemradio" aria-checked={active()} - class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-14-medium outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" + class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-13-medium outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" onClick={() => { navigate(`/${base64Encode(workspace.path)}/session`) setOpen(false) @@ -122,13 +122,13 @@ export function WorkspaceChip(props: { style?: JSX.CSSProperties | string } = {} type="button" role="menuitem" data-action="workspace-chip-add" - class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-14-medium text-text-base outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" + class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-13-medium text-text-base outline-none hover:bg-surface-raised-base-hover focus-visible:bg-surface-raised-base-hover" onClick={() => { setOpen(false) layoutPage.openProject() }} > - <Icon name="plus-small" size="small" class="shrink-0 text-text-weak" /> + <Icon name="folder-add-left" size="small" class="shrink-0 text-text-weak" /> <span class="min-w-0 flex-1 truncate">{language.t("workspace.chip.add")}</span> </button> </div>
PawWork's product direction does not need a separate 14px hierarchy size on top of the 13px body size. Visual hierarchy is carried by weight (medium vs regular), so the size layer was redundant and only created drift. Three coordinated changes: - Mass rename text-14-medium → text-13-medium across all .tsx callsites in packages/app/src and packages/ui/src (excluding .stories.tsx). Covers settings section heads, settings item titles, dialog headers, home recent-projects header, sidebar workspace labels, sidebar empty state, file-tabs filenames, session review header, message timeline group titles, and the context-tool group title (the "已探索" / "Explored" wrapper). - Re-classify three component-CSS hierarchy escapes back to body: basic-tool tool-title, basic-tool task-tool-title, and message-part message-part-title now read --font-size-base (13) instead of --font-size-hierarchy. - Flip the --font-size-hierarchy token from 14px to 13px so the remaining primitives that still resolve through it (Button labels, Tag chips, Tabs labels, Toast titles, markdown h4-h6, .text-14-mono) also fall to 13. The token stays defined as a permanent carve-out point in case PawWork ever wants to bring back a hierarchy step.
Reword the context-gathering wrapper and its child counts so the
group reads as a coherent sentence in both languages instead of a
bag of words.
zh:
- gatheredContext: 已探索 → 探索完毕 (matches the 完毕 pattern)
- read: {{count}} 次读取 → 阅读 {{count}} 篇
- search: {{count}} 次搜索 → 搜索 {{count}} 次
- list: {{count}} 个列表 → 浏览 {{count}} 处
All counts now use a verb + count + classifier shape that mirrors
how 通义灵码 documents the same tools (阅读 / 检索) and stays
4-character symmetric.
en:
- read: {{count}} read/reads → Read {{count}} file/files
- list: {{count}} list/lists → Listed {{count}} dir/dirs
- search: kept as {{count}} search/searches (already noun-natural)
The wrapper "Exploring/Explored" is unchanged; "explore the codebase"
is established dev jargon (Cursor docs, opencode subagent name) and
already conveys goal-directed pre-task investigation.
diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts
index 929f25f..9bed434 100644
--- a/packages/ui/src/i18n/en.ts
+++ b/packages/ui/src/i18n/en.ts
@@ -73,12 +73,12 @@ export const dict: Record<string, string> = {
"ui.messagePart.review.title": "Review your answers",
"ui.messagePart.questions.dismissed": "Questions dismissed",
"ui.messagePart.compaction": "Session compacted",
- "ui.messagePart.context.read.one": "{{count}} read",
- "ui.messagePart.context.read.other": "{{count}} reads",
+ "ui.messagePart.context.read.one": "Read {{count}} file",
+ "ui.messagePart.context.read.other": "Read {{count}} files",
"ui.messagePart.context.search.one": "{{count}} search",
"ui.messagePart.context.search.other": "{{count}} searches",
- "ui.messagePart.context.list.one": "{{count}} list",
- "ui.messagePart.context.list.other": "{{count}} lists",
+ "ui.messagePart.context.list.one": "Listed {{count}} dir",
+ "ui.messagePart.context.list.other": "Listed {{count}} dirs",
"ui.list.loading": "Loading",
"ui.list.empty": "No results",
diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts
index df466b9..0bfb4ec 100644
--- a/packages/ui/src/i18n/zh.ts
+++ b/packages/ui/src/i18n/zh.ts
@@ -55,7 +55,7 @@ export const dict = {
"ui.sessionTurn.status.delegating": "正在委派工作",
"ui.sessionTurn.status.planning": "正在规划下一步",
"ui.sessionTurn.status.gatheringContext": "正在探索",
- "ui.sessionTurn.status.gatheredContext": "已探索",
+ "ui.sessionTurn.status.gatheredContext": "探索完毕",
"ui.sessionTurn.status.searchingCodebase": "正在搜索代码库",
"ui.sessionTurn.status.searchingWeb": "正在搜索网页",
"ui.sessionTurn.status.makingEdits": "正在修改",
@@ -67,12 +67,12 @@ export const dict = {
"ui.messagePart.questions.dismissed": "问题已忽略",
"ui.messagePart.compaction": "会话已压缩",
- "ui.messagePart.context.read.one": "{{count}} 次读取",
- "ui.messagePart.context.read.other": "{{count}} 次读取",
- "ui.messagePart.context.search.one": "{{count}} 次搜索",
- "ui.messagePart.context.search.other": "{{count}} 次搜索",
- "ui.messagePart.context.list.one": "{{count}} 个列表",
- "ui.messagePart.context.list.other": "{{count}} 个列表",
+ "ui.messagePart.context.read.one": "阅读 {{count}} 篇",
+ "ui.messagePart.context.read.other": "阅读 {{count}} 篇",
+ "ui.messagePart.context.search.one": "搜索 {{count}} 次",
+ "ui.messagePart.context.search.other": "搜索 {{count}} 次",
+ "ui.messagePart.context.list.one": "浏览 {{count}} 处",
+ "ui.messagePart.context.list.other": "浏览 {{count}} 处",
"ui.messagePart.diagnostic.error": "错误",
"ui.messagePart.title.edit": "编辑",
"ui.messagePart.title.write": "写入",
35c896a to
7a885f1
Compare
There was a problem hiding this comment.
Code Review
This pull request implements a broad UI update focused on reducing the base font size from 14px to 13px and tightening the layout across the application. Key modifications include updating global CSS variables, utility classes, and adjusting component-specific styles and icon sizes. Additionally, the 'Help' link has been removed from the sidebar. Feedback was provided regarding the replacement of custom Button components with native button elements in the sidebar, which may negatively impact accessibility and styling consistency; it is recommended to use style overrides on the existing components instead.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/app/src/pages/layout/pawwork-sidebar.tsx`:
- Around line 246-247: The row buttons in pawwork-sidebar.tsx (the New, Search,
and Settings rows) remove default keyboard focus by including
"focus:outline-none" but do not add any focus-visible replacement; update the
class strings on those row button elements (the ones containing
"focus:outline-none") to keep the visual outline for keyboard users by replacing
the blanket removal with accessible focus-visible utilities (e.g., keep or add
"focus-visible:outline" and an appropriate size/color such as
"focus-visible:outline-2" and "focus-visible:outline-primary" or your design
system equivalents) so keyboard focus is visible while mouse focus remains
unchanged.
In `@packages/app/src/pages/layout/sidebar.css`:
- Around line 1-6: Sidebar leading-slot container is using size-5 (20px) causing
asymmetric padding with the 14×14 icon rule; update the container in
sidebar-project.tsx (around the leading-slot element at the component rendering)
to replace the `size-5` class with `w-[14px] h-[14px]` so it matches the icon
constraint and mirrors the usage in sidebar-items.tsx.
In `@packages/ui/src/components/markdown.css`:
- Line 124: Replace the hard-coded "font-size: 12px;" occurrences with the
shared x-small design token (e.g. use var(--font-size-x-small) or the project's
equivalent token name) wherever "font-size: 12px;" appears (the occurrences
currently at the two "font-size: 12px;" lines in markdown.css); update both
places to reference the token to ensure consistency and maintainability.
In `@packages/ui/src/components/message-part.css`:
- Line 803: Replace the literal font-size: 12px in message-part.css (the rule
that sets permission pattern/code text) with the shared x-small design token
used across the project; change the declaration to use the token (for example
var(--font-size-x-small) or the project's equivalent --x-small token) so the
component stays in sync with the design system.
In `@packages/ui/src/i18n/en.ts`:
- Around line 80-81: Replace the terse jargon "dir"/"dirs" in the localization
entries with a full user-facing term; update the translation keys
"ui.messagePart.context.list.one" and "ui.messagePart.context.list.other" in
en.ts to use "directory" and "directories" (or "folder"/"folders" if you prefer)
so the UI reads clearly for users.
In `@packages/ui/src/i18n/zh.ts`:
- Around line 70-75: Update the Chinese copy entries for the context labels to
use explicit nouns matching existing tool terminology: replace values for
"ui.messagePart.context.read.one" and "ui.messagePart.context.read.other" with
phrasing that explicitly mentions files (e.g., "读取文件 {{count}} 个"), and replace
"ui.messagePart.context.list.one" and "ui.messagePart.context.list.other" with
phrasing that explicitly mentions directories or listings (e.g., "列出目录 {{count}}
个" or "列出目录 {{count}} 次"), ensuring both .one and .other variants are consistent
and grammatically correct for numeric interpolation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5464f4d5-e526-4f3d-b64a-80c470506f82
📒 Files selected for processing (59)
packages/app/src/app.tsxpackages/app/src/components/dialog-connect-provider.tsxpackages/app/src/components/dialog-connect-websearch.tsxpackages/app/src/components/dialog-custom-provider.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/dialog-release-notes.tsxpackages/app/src/components/dialog-select-directory.tsxpackages/app/src/components/dialog-select-file.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-select-provider.tsxpackages/app/src/components/dialog-select-server.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/components/prompt-input/drag-overlay.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/prompt-input/slash-popover.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/server/server-row.tsxpackages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/status-popover-body.tsxpackages/app/src/i18n/en.tspackages/app/src/i18n/zh.tspackages/app/src/pages/home.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/pages/layout/sidebar.csspackages/app/src/pages/session.tsxpackages/app/src/pages/session/composer/session-revert-dock.tsxpackages/app/src/pages/session/composer/session-todo-dock.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/pages/session/session-side-panel.tsxpackages/app/src/pages/session/terminal-panel.tsxpackages/ui/src/components/basic-tool.csspackages/ui/src/components/button.csspackages/ui/src/components/dialog.csspackages/ui/src/components/diff-changes.csspackages/ui/src/components/file-media.tsxpackages/ui/src/components/file-search.tsxpackages/ui/src/components/list.csspackages/ui/src/components/markdown.csspackages/ui/src/components/message-nav.csspackages/ui/src/components/message-part.csspackages/ui/src/components/message-part.tsxpackages/ui/src/components/tabs.csspackages/ui/src/components/tag.csspackages/ui/src/components/text-field.csspackages/ui/src/components/toast.csspackages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/styles/theme.csspackages/ui/src/styles/utilities.css
💤 Files with no reviewable changes (2)
- packages/app/src/i18n/en.ts
- packages/app/src/i18n/zh.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-desktop
- GitHub Check: smoke-macos-arm64
- GitHub Check: e2e-artifacts
- GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (1)
packages/app/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/app/AGENTS.md)
Always prefer
createStoreover multiplecreateSignalcalls in SolidJS
Files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/dialog-select-server.tsxpackages/app/src/components/dialog-custom-provider.tsxpackages/app/src/components/prompt-input/drag-overlay.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/server/server-row.tsxpackages/app/src/components/dialog-select-provider.tsxpackages/app/src/components/dialog-release-notes.tsxpackages/app/src/pages/session/terminal-panel.tsxpackages/app/src/app.tsxpackages/app/src/pages/session/composer/session-revert-dock.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/components/dialog-select-directory.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session/session-side-panel.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/prompt-input/slash-popover.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-connect-websearch.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/components/dialog-select-file.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/pages/session.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/status-popover-body.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-connect-provider.tsxpackages/app/src/pages/session/composer/session-todo-dock.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsxpackages/app/src/components/prompt-input.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/patch/index.ts:337-346
Timestamp: 2026-04-28T04:38:05.946Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/patch/index.ts`), the BOM-transition surfacing gap — where `Bom.split` strips BOM before building `unified_diff`/`new_content` but `Bom.join` later re-attaches BOM on disk write, meaning BOM changes are not reflected in the diff payload — is intentionally deferred. PR `#270` is an upstream-sync graft; fixing the issue here would mix refactor + bugfix intents and drift the diff from the upstream baseline needed for clean future grafts. A dedicated follow-up PR (or upstream report) will address this. Do NOT re-flag the missing BOM-change surfacing in `ApplyPatchFileUpdate`/`ApplyPatchFileChange` as a blocking issue in PR `#270` or in future sync PRs that carry the same upstream baseline.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/todo.ts:9-18
Timestamp: 2026-04-28T06:51:54.812Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork), `TodoItem.status` and `TodoItem.priority` are intentionally declared as plain `Schema.String` rather than closed literal unions. This matches the upstream opencode baseline (`dev:packages/opencode/src/tool/todo.ts`). The tightening — `Schema.Literals(["pending","in_progress","completed","cancelled"])` for `status` and `Schema.Literals(["high","medium","low"])` for `priority` — is tracked as a follow-up under the harness/tool-set-v1 series (issue `#129`) to land either as part of a tool-schema tightening sweep or upstream-first. Do NOT re-flag the free-form strings for these fields in upstream-sync PRs; flag it only in a PawWork-authored PR or the dedicated sweep that touches `TodoItem` schema.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
📚 Learning: 2026-04-23T07:23:23.849Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 180
File: packages/app/src/components/session/session-new-view.tsx:13-18
Timestamp: 2026-04-23T07:23:23.849Z
Learning: In pawwork (Astro-Han/pawwork), prefer using `createStore` instead of multiple `createSignal` calls only when the signals represent **coupled** object state that is updated together (i.e., there is at least one shared batch-update site where the state is changed in the same transaction). If the state fields are **independent** and are mutated by separate handlers (e.g., one handler updates only `selectedSkill` while another updates only `mode`), keep them as individual `createSignal` calls—using `createStore` for truly independent fields adds boilerplate without behavioral benefit.
Applied to files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/dialog-select-server.tsxpackages/app/src/components/dialog-custom-provider.tsxpackages/app/src/components/prompt-input/drag-overlay.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/server/server-row.tsxpackages/app/src/components/dialog-select-provider.tsxpackages/app/src/components/dialog-release-notes.tsxpackages/app/src/pages/session/terminal-panel.tsxpackages/app/src/app.tsxpackages/app/src/pages/session/composer/session-revert-dock.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/components/dialog-select-directory.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session/session-side-panel.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/prompt-input/slash-popover.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-connect-websearch.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/components/dialog-select-file.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/pages/session.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/status-popover-body.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-connect-provider.tsxpackages/app/src/pages/session/composer/session-todo-dock.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsxpackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-23T15:10:21.635Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 191
File: packages/app/src/components/session/pawwork-skill-meta.ts:38-39
Timestamp: 2026-04-23T15:10:21.635Z
Learning: This repo configures Tailwind v4 with `--color-*: initial`, which effectively breaks standard Tailwind palette utilities (e.g., `text-violet-500` can resolve to no CSS variable and render as a no-op/black). For brand/accent colors that are not backed by semantic design tokens, use inline styles with the exact hex value (e.g., `style={{ color: '#8B5FBF' }}` / `homeIconStyle: { color: '#8B5FBF' }`) and add a short comment explaining that Tailwind palette utilities won’t work due to the `--color-*: initial` setup. Do not suggest replacing these inline hex colors with Tailwind palette classes anywhere in this repo.
Applied to files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/dialog-select-server.tsxpackages/app/src/components/dialog-custom-provider.tsxpackages/app/src/components/prompt-input/drag-overlay.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/server/server-row.tsxpackages/app/src/components/dialog-select-provider.tsxpackages/app/src/components/dialog-release-notes.tsxpackages/app/src/pages/session/terminal-panel.tsxpackages/app/src/app.tsxpackages/app/src/pages/session/composer/session-revert-dock.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/components/dialog-select-directory.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session/session-side-panel.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/prompt-input/slash-popover.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-connect-websearch.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/components/dialog-select-file.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/pages/session.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/status-popover-body.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-connect-provider.tsxpackages/app/src/pages/session/composer/session-todo-dock.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsxpackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-23T17:02:35.873Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 198
File: packages/app/src/index.css:95-97
Timestamp: 2026-04-23T17:02:35.873Z
Learning: In the pawwork repository, Stylelint is intentionally not configured (per AGENTS.md, only linting is enforced and the Biome formatter is disabled). When reviewing CSS files, do not raise Stylelint rule-based issues (e.g., `declaration-empty-line-before`) because they are false positives and not enforced by the project toolchain.
Applied to files:
packages/app/src/pages/layout/sidebar.csspackages/ui/src/components/toast.csspackages/ui/src/components/dialog.csspackages/ui/src/components/button.csspackages/ui/src/components/tabs.csspackages/ui/src/components/tag.csspackages/ui/src/components/diff-changes.csspackages/ui/src/components/text-field.csspackages/ui/src/components/markdown.csspackages/ui/src/styles/theme.csspackages/ui/src/components/basic-tool.csspackages/ui/src/components/list.csspackages/ui/src/styles/utilities.csspackages/ui/src/components/message-nav.csspackages/ui/src/components/message-part.css
📚 Learning: 2026-04-24T05:39:58.329Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Applied to files:
packages/app/src/components/prompt-input/drag-overlay.tsxpackages/app/src/pages/session/composer/session-revert-dock.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/prompt-input/slash-popover.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/pages/session.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsxpackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-24T05:48:39.493Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/e2e/app/composer-parity.spec.ts:0-0
Timestamp: 2026-04-24T05:48:39.493Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, the Model chip trigger button carries `data-action="prompt-model"` (around line 1187) and the Variant chip trigger button carries `data-action="prompt-model-variant"` (around line 1231), both set via `triggerProps`. These are therefore already captured by any `[data-action]` selector sweep in E2E tests and do not need a separate `[data-component]` query to be included in parity assertions — though unioning both is kept as belt-and-suspenders in `collectBarSet`.
Applied to files:
packages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-22T05:32:29.012Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:32:29.012Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/ui/src/i18n/zh.tspackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsx
📚 Learning: 2026-04-22T09:32:58.310Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Applied to files:
packages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout.tsx
📚 Learning: 2026-04-28T07:27:41.971Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/file/ripgrep.ts:29-118
Timestamp: 2026-04-28T07:27:41.971Z
Learning: In `packages/opencode/src/file/ripgrep.ts` (Astro-Han/pawwork), the internal Effect schemas `TimeStats`, `Stats`, `PathText`, `Begin`, `End`, `Summary`, and the exported `SearchMatch`/`Match`/`Result` union are all declared as `Schema.Struct` — matching upstream `dev:packages/opencode/src/file/ripgrep.ts` lines 60-118. PR `#270` is an upstream-sync graft per `project_upstream_strategy.md`. Converting these to `Schema.Class` would diverge from the upstream baseline and create recurring merge friction on future syncs. The `Schema.Class` convention applies to PawWork-authored schemas, not to upstream-grafted shapes in this file. Do NOT re-flag the use of `Schema.Struct` for these schemas in upstream-sync PRs; flag it only in a PR that simultaneously converts the upstream side, or in a dedicated PawWork carve-out PR for ripgrep schemas.
Applied to files:
packages/app/src/pages/layout/sidebar-workspace.tsx
📚 Learning: 2026-04-28T03:01:37.478Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 282
File: packages/ui/src/i18n/zh.ts:109-109
Timestamp: 2026-04-28T03:01:37.478Z
Learning: In Astro-Han/pawwork PR `#282` (`packages/ui/src/i18n/zh.ts`), the translation for `ui.tool.questions` is intentionally `提出问题` (verb-object structure), NOT `向你提问` (prepositional-phrase structure). The choice was made after a UI smoke run to keep consistent verb-object phrasing across all zh tool-card labels (`执行命令`, `列出目录`, `查找文件`, `搜索文本`, `读取文件`, `读取网页`, `批量修改`, `查看待办`). Do NOT flag `提出问题` as incorrect for this key.
Applied to files:
packages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-28T06:51:54.812Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/todo.ts:9-18
Timestamp: 2026-04-28T06:51:54.812Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork), `TodoItem.status` and `TodoItem.priority` are intentionally declared as plain `Schema.String` rather than closed literal unions. This matches the upstream opencode baseline (`dev:packages/opencode/src/tool/todo.ts`). The tightening — `Schema.Literals(["pending","in_progress","completed","cancelled"])` for `status` and `Schema.Literals(["high","medium","low"])` for `priority` — is tracked as a follow-up under the harness/tool-set-v1 series (issue `#129`) to land either as part of a tool-schema tightening sweep or upstream-first. Do NOT re-flag the free-form strings for these fields in upstream-sync PRs; flag it only in a PawWork-authored PR or the dedicated sweep that touches `TodoItem` schema.
Applied to files:
packages/app/src/components/status-popover-body.tsxpackages/app/src/pages/session/composer/session-todo-dock.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T15:26:07.250Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `data-component`, `data-action`, or semantic roles for selectors instead of CSS class names or IDs
Applied to files:
packages/app/src/pages/session/composer/session-todo-dock.tsx
📚 Learning: 2026-04-28T08:14:31.436Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/edit.ts:48-48
Timestamp: 2026-04-28T08:14:31.436Z
Learning: In `packages/opencode/src/tool/edit.ts` (Astro-Han/pawwork), the `filePath` schema description (`"The absolute path to the file to modify"`) is upstream-inherited from `dev:packages/opencode/src/tool/edit.ts:48`. The runtime actually accepts relative paths (resolved via `Instance.directory` at lines 79-81), but the description fix is intentionally deferred to a single PawWork-authored description-cleanup PR that will also cover the identical mismatch in `packages/opencode/src/tool/write.ts:24`. Do NOT re-flag the too-narrow `filePath` description in upstream-sync PRs; flag it only in the dedicated description-cleanup PR.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T07:28:14.317Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/lsp.ts:23-32
Timestamp: 2026-04-28T07:28:14.317Z
Learning: In `packages/opencode/src/tool/lsp.ts` (Astro-Han/pawwork, PR `#270`), the `Parameters` schema requires `line` and `character` for all operations, including `workspaceSymbol` and `documentSymbol` which never use coordinates. This matches the upstream `dev:packages/opencode/src/tool/lsp.ts:23-32` baseline exactly — both fields are declared as required `Schema.Number` with `>= 1` checks. The fix (per-operation schema split, or making `line`/`character` optional with handler-side presence validation for operations that need them like `goToDefinition`/`findReferences`) is deferred to a follow-up PR or upstream report to avoid mixing refactor + bug-fix intents and drifting the diff from the upstream baseline. Do NOT re-flag the required coordinates on `workspaceSymbol`/`documentSymbol` in upstream-sync PRs; flag it only in a PawWork-authored PR that directly touches `lsp.ts` parameter validation.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T04:56:13.350Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/tool.ts:8-10
Timestamp: 2026-04-28T04:56:13.350Z
Learning: In `packages/opencode/src/tool/tool.ts` (Astro-Han/pawwork, PR `#270`), the `Metadata` interface uses `[key: string]: any` as its index signature. This is upstream-inherited code adopted wholesale via the graft strategy (per `project_upstream_strategy.md`). The fix — tightening to `Record<string, unknown>` and explicitly narrowing framework-owned fields like `truncated` — is intentionally deferred to a follow-up PR or upstream report. Do NOT re-flag the `any` index signature on `Metadata` in PR `#270` or in future upstream-sync PRs that carry the same upstream baseline.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-25T09:51:18.951Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 231
File: packages/desktop-electron/src/main/menu-template.ts:178-189
Timestamp: 2026-04-25T09:51:18.951Z
Learning: In Astro-Han/pawwork (`packages/desktop-electron/src/main/menu-template.ts`), the `buildMacosMenuTemplate` and `buildWindowsMenuTemplate` functions are intentionally kept as separate, literal per-platform templates rather than sharing extracted submenu helpers. The split exists to allow future per-platform divergence; the templates already differ structurally (File menu: macOS omits `quit` because it lives in the App menu, Windows places `quit` next to `close`; Window submenu differs; macOS has an App menu with no Windows equivalent; View differs on terminal accelerator `Ctrl+\`` vs `CmdOrCtrl+\``). Do NOT suggest extracting shared submenu builder helpers (e.g., `buildEditSubmenu`, `buildViewSubmenu`) as YAGNI — the literal duplication is intentional and easy to scan.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
🔇 Additional comments (56)
packages/ui/src/components/file-search.tsx (1)
34-34: Typography update is consistent and safe.
text-13-regularhere aligns with the PR-wide 13px body baseline and matches the adjacent counter text scale in this component.packages/ui/src/components/file-media.tsx (1)
255-255: Typography update is consistent with the 13px baseline refactor.This class change is aligned with the PR’s body-text unification goal and is safe (style-only, no behavior impact).
packages/ui/src/i18n/en.ts (1)
76-77: Good pluralized read-copy update.This keeps
{{count}}interpolation and improves readability without changing key structure.packages/ui/src/i18n/zh.ts (1)
58-58:gatheredContextwording looks good.“探索完毕” is clear and reads naturally as a completed state label.
packages/ui/src/styles/theme.css (1)
8-11: Typography token shift is coherent and safely centralized.
--font-size-x-small,--font-size-base, and--font-size-hierarchyare aligned with the intended 12/13 scale and provide a clean tokenized base for downstream components.packages/ui/src/styles/utilities.css (2)
46-72: New 12px utility set is consistent with existing typography primitives.The added regular/medium/mono variants are structured correctly and reuse the shared font/spacing tokens as expected.
113-114: Hierarchy-token wiring for 14-medium/mono is correct.Switching these to
--font-size-hierarchymatches the new token-driven typography strategy.Also applies to: 123-124
packages/ui/src/components/basic-tool.css (1)
73-74: Font-size token migration is applied consistently across tool text slots.Good consolidation on
--font-size-base; this keeps the component aligned with the global 13px baseline.Also applies to: 98-99, 123-124, 169-170, 238-239
packages/ui/src/components/tabs.css (1)
52-55: Tabs trigger typography now correctly follows hierarchy tokenization.This change is in line with the typography unification and keeps the component-level rule consistent with shared tokens.
packages/ui/src/components/toast.css (1)
122-125: Toast title size now correctly depends on shared hierarchy token.The tokenized size update is clean and consistent with the rest of the typography migration.
packages/ui/src/components/dialog.css (1)
90-93: Dialog description typography is correctly migrated to base token.This keeps dialog body text aligned with the global 13px baseline.
packages/ui/src/components/message-nav.css (1)
74-74: Message-nav title preview correctly uses base typography token.Good replacement of fixed sizing with the shared scale.
packages/ui/src/components/tag.css (1)
29-32: Large tag typography is correctly routed through hierarchy token.This is consistent with the broader tokenized typography refactor.
packages/ui/src/components/button.css (1)
162-162: Tokenized hierarchy size looks correct.Line 162 correctly moves large-button typography to
--font-size-hierarchy, keeping the component aligned with the shared scale.packages/ui/src/components/diff-changes.css (1)
10-10: Good token migration for diff counters.Lines 10 and 22 now inherit base typography from theme tokens, which prevents future drift from hard-coded sizes.
Also applies to: 22-22
packages/ui/src/components/text-field.css (1)
10-10: Input typography update is consistent.Lines 10 and 90 correctly standardize text-field font sizing on
--font-size-base.Also applies to: 90-90
packages/ui/src/components/list.css (1)
164-164: List typography tokenization is well applied.These replacements cleanly unify list text sizing on the theme scale without changing layout logic.
Also applies to: 202-202, 243-243, 318-318
packages/ui/src/components/markdown.css (1)
26-26: Heading hierarchy token usage is correct.Line 26 aligns markdown heading sizing with the shared hierarchy token.
packages/ui/src/components/message-part.css (1)
440-440: Token migration across message-part typography is solid.These updates consistently move user-facing copy to
--font-size-base/--font-size-hierarchywithout changing behavior.Also applies to: 761-761, 778-778, 858-858, 944-944, 1070-1070, 1078-1078, 1106-1106
packages/ui/src/components/message-part.tsx (1)
923-923: Class swap is correct for the new text scale.Line 923 cleanly aligns the context tool group title with the 13px typography baseline.
packages/app/src/components/prompt-input/drag-overlay.tsx (1)
20-20: Overlay label typography update looks good.Line 20 correctly applies the new 13px body class for consistency with the rest of the prompt UI.
packages/app/src/components/dialog-select-server.tsx (1)
558-558: LGTM — typography update aligns with the PR baseline shift.The server default status badge now uses
text-13-regular, matching the PR-wide typography downscale from 14px to 13px.packages/app/src/pages/session/terminal-panel.tsx (1)
247-247: LGTM — terminal typography updates match the baseline shift.Both the loading fallback chip (line 247) and the drag overlay label (line 328) now use
text-13-regular, consistent with the PR-wide typography downscale.Also applies to: 328-328
packages/app/src/components/dialog-manage-models.tsx (1)
37-37: LGTM — dialog action button typography updated correctly.The "Connect Provider" button now uses
text-13-medium, aligning with the PR-wide typography baseline shift.packages/app/src/components/session/session-sortable-tab.tsx (1)
25-25: LGTM — file tab label typography updated correctly.The filename display now uses
text-13-medium, matching the PR-wide typography baseline shift.packages/app/src/pages/session/message-timeline.tsx (1)
641-641: LGTM — session header and dialog typography updated consistently.All session title elements (parent link, separator, child title, draft input) and the delete confirmation dialog text now use
text-13-*variants, aligning with the PR-wide typography baseline shift.Also applies to: 759-759, 766-766, 795-795, 809-809
packages/app/src/components/server/server-row.tsx (1)
85-85: LGTM — server version typography updated correctly.The server version display now uses
text-13-regular, matching the PR-wide typography baseline shift.packages/app/src/pages/session/files-tab.tsx (1)
115-115: LGTM — files tab typography updated consistently.Both the empty state text (line 115) and the filename display (line 133) now use
text-13-*variants, aligning with the PR-wide typography baseline shift.Also applies to: 133-133
packages/app/src/pages/session/composer/session-revert-dock.tsx (1)
47-49: Typography downscale is consistent and safe.These class updates are purely presentational and align with the 13px hierarchy objective without affecting dock behavior.
packages/app/src/components/settings-general.tsx (1)
383-383: Heading/title typography normalization looks good.The updated
text-13-mediumusage is consistent across settings sections and row titles, with no functional regressions introduced.Also applies to: 492-492, 536-536, 589-589, 655-655, 688-688, 728-728
packages/app/src/app.tsx (1)
344-344: Connection error panel typography update is clean.Both text class changes are consistent with the 13px baseline and do not alter retry/server-selection behavior.
Also applies to: 364-364
packages/app/src/components/status-popover-body.tsx (1)
305-305: Popover text hierarchy is now consistent across tabs.Good consolidation to
text-13-regularfor labels and empty states; behavior remains unchanged.Also applies to: 348-348, 375-375, 401-401, 414-414, 428-428, 434-434
packages/app/src/components/dialog-custom-provider.tsx (1)
184-184: Description text update is correct and low-risk.This is a straight visual adjustment and preserves dialog form behavior.
packages/app/src/components/dialog-select-model-unpaid.tsx (1)
46-46: Unpaid model dialog typography changes are cohesive.The updated text sizing is consistent across headings, provider annotations, and the “view all” row without changing interaction behavior.
Also applies to: 90-90, 112-112, 119-119, 126-126, 133-133
packages/app/src/components/prompt-input/send-button.tsx (1)
23-23: Send button density tweak matches the composer tightening pass.The size reduction is localized to styling and keeps existing submit/stop behavior intact.
packages/app/src/components/dialog-select-provider.tsx (1)
69-69: Provider-note typography change is solid.These updates are consistent with the global 13px body baseline and preserve existing provider-list behavior.
Also applies to: 77-77
packages/app/src/components/settings-keybinds.tsx (1)
411-411: Typography downshift is consistent and behavior-preserving.These class updates align with the 13px baseline and do not alter rendering logic or interactions.
Also applies to: 416-416, 446-446, 448-448
packages/app/src/pages/layout/sidebar-project.tsx (1)
201-201: Leading-slot + typography adjustments look correct.The slot marker and size reduction are coherent with sidebar rail unification, and text class changes are safely scoped.
Also applies to: 229-229, 232-232
packages/app/src/components/dialog-select-model.tsx (1)
246-246: Dialog action typography change is clean and isolated.This is a safe visual-only update with no behavior impact.
packages/app/src/pages/layout/sidebar-workspace.tsx (1)
78-78: Workspace typography updates are consistent and low-risk.All edits are class-level density adjustments with unchanged behavior paths.
Also applies to: 103-103, 109-109, 123-124, 277-277
packages/app/src/pages/session/session-side-panel.tsx (1)
471-471: Side-panel fallback typography changes are good.These changes are purely presentational and align with the global 13px shift.
Also applies to: 505-505
packages/app/src/components/prompt-input/slash-popover.tsx (1)
68-68: Popover typography adjustments are consistent and safe.No interaction or filtering behavior was altered; the class updates are clean.
Also applies to: 97-97, 99-99
packages/app/src/components/dialog-select-directory.tsx (1)
366-366: Directory row typography migration looks good.Both branches now consistently use the new text scale without affecting logic.
Also applies to: 378-378
packages/app/src/pages/session/composer/session-todo-dock.tsx (1)
138-138: Todo dock typography update is consistent and non-disruptive.The revised text scale is applied uniformly across related todo text surfaces.
Also applies to: 170-170, 253-253
packages/app/src/components/dialog-release-notes.tsx (1)
77-77: Typography update is clean and behavior-preserving.This class change is scoped to presentation and is consistent with the PR’s 13px baseline goal.
packages/app/src/components/dialog-select-file.tsx (1)
447-447: LGTM on the typography consolidation in list rows.These updates stay purely presentational and keep the command/file/session rendering paths intact.
Also applies to: 460-463, 476-477, 483-484
packages/app/src/components/dialog-connect-websearch.tsx (1)
102-102: State-copy typography updates look consistent across all dialog branches.The modified lines are presentation-only and do not alter any save/remove/status behavior.
Also applies to: 161-161, 169-169, 174-174, 180-180, 229-237
packages/app/src/components/settings-providers.tsx (1)
140-140: Provider settings typography tightening is solid.All touched segments remain behavior-neutral and align with the 13px standardization.
Also applies to: 145-146, 159-159, 165-166, 182-182, 194-194, 227-227, 249-249
packages/app/src/pages/home.tsx (1)
77-77: Home page typography adjustments look good.These are clean visual refinements with no behavioral side effects.
Also applies to: 92-92, 107-107, 128-128
packages/app/src/pages/layout/sidebar-items.tsx (1)
103-103: Sidebar rail/density refinements are coherent and safe.The updated leading-slot/action geometry is consistent, and interaction logic remains intact.
Also applies to: 113-113, 122-123, 236-236, 252-252, 296-296, 302-302
packages/app/src/components/settings-models.tsx (1)
19-19: Models settings typography pass looks good.Scoped presentation-only updates with no behavior changes.
Also applies to: 27-30, 103-103, 112-112
packages/app/src/components/prompt-input/workspace-chip.tsx (1)
66-66: Workspace chip visual updates are consistent and non-breaking.Typography and icon swap are well-scoped UI refinements; interaction behavior remains unchanged.
Also applies to: 99-99, 125-125, 131-131
packages/app/src/pages/session.tsx (1)
1315-1315: Typography downshift is consistent and safe.The updated
text-13-*callsites in these review-empty and selector-value surfaces align cleanly with the PR’s density objective without changing behavior.Also applies to: 1322-1322, 1329-1330, 1361-1361
packages/app/src/components/prompt-input.tsx (1)
1124-1124: Prompt-input density updates look coherent.The class changes are internally consistent with the 13/12 typography rollout and do not alter interaction behavior in the modified segments.
Also applies to: 1161-1161, 1205-1205, 1228-1228, 1505-1505, 1515-1515, 1564-1564, 1571-1571
packages/app/src/components/dialog-connect-provider.tsx (1)
279-279: Dialog typography changes are clean.These updates are consistent visual downscales only; no behavioral risks were introduced in the touched states.
Also applies to: 303-303, 364-364, 427-430, 439-439, 500-500, 558-558, 570-570, 606-606, 617-617, 628-628
packages/app/src/pages/layout.tsx (1)
1754-1758: Layout text-scale updates are consistent with the new baseline.The changed typography callsites are coherent with the PR scope and don’t affect behavior.
Also applies to: 1828-1833, 2172-2174, 2197-2198, 2378-2383
The footer button was removed in 9ad00d1 (folded into the workspace chip menu via the folder-add-left icon). Update home.spec.ts so it no longer waits for a button that no longer exists.
Astro-Han
left a comment
There was a problem hiding this comment.
Review pass focused on edge cases, regressions, and cross-module impact.
The New session, Search, and Settings rows kept focus:outline-none without a focus-visible replacement, leaving keyboard users without any visible focus affordance. Add focus-visible:bg-surface-raised-base-hover so keyboard focus reuses the same hover treatment the rows already have for mouse hover. Addresses coderabbitai review on #320.
Replace the three literal 12px declarations introduced for code styling (markdown.css .shiki and inline code, message-part.css permission-patterns code) with var(--font-size-x-small). The token was added to theme.css in the same PR; routing through it keeps the design system consistent and lets a future code-size change ripple from one place. Addresses coderabbitai review on #320.
en: "Listed N dir / dirs" was implementation jargon; use the full word "directory / directories" so the UI reads cleanly for non-technical users (PawWork's "for everyone" positioning). zh: collapse the prior verb-only labels (阅读 N 篇 / 浏览 N 处) to verb-object form that matches PR #282's tool-card labels (读取文件 / 列出目录) and stays visually symmetric across all three counts: - 读取 N 个文件 (was 阅读 N 篇) - 搜索 N 处匹配 (was 搜索 N 次, now matches verb+量+名 shape) - 列出 N 个目录 (was 浏览 N 处) "处" stays as the search measure word because grep-style search returns hits at file:line locations, which is exactly what 处 denotes (an occurrence at a position) — more concrete than 项. Addresses coderabbitai review on #320.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/app/src/pages/layout/sidebar-items.tsx (1)
121-127:⚠️ Potential issue | 🟠 MajorKeep the leading slot width in sync with the pin control.
props.leadingSlotnow lands in a 14px rail, but the pin button rendered frompackages/app/src/pages/layout/pawwork-sidebar.tsx:115-137is stillsize-5. That control will overflow the slot and reintroduce horizontal drift in the tightened sidebar.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app/src/pages/layout/sidebar-items.tsx` around lines 121 - 127, The leading slot in sidebar-items.tsx is fixed to 14px causing overflow because the pin control in pawwork-sidebar.tsx renders at size-5; update the leading slot (the div with data-leading-slot and indicator()) to use the same sizing as the pin button instead of hardcoded 14px—either replace w-[14px] h-[14px] with the same utility/class/variable used by the pin button (or a shared CSS variable/token), or adjust the pin button to match the slot, so props.leadingSlot and indicator() align with the pin control size-5.
♻️ Duplicate comments (4)
packages/ui/src/i18n/en.ts (1)
80-81:⚠️ Potential issue | 🟡 MinorReplace
dir/dirswith user-facing wording.
dir/dirsreads as internal shorthand in UI copy.💡 Suggested wording
- "ui.messagePart.context.list.one": "Listed {{count}} dir", - "ui.messagePart.context.list.other": "Listed {{count}} dirs", + "ui.messagePart.context.list.one": "Listed {{count}} directory", + "ui.messagePart.context.list.other": "Listed {{count}} directories",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/i18n/en.ts` around lines 80 - 81, The UI copy uses internal shorthand "dir/dirs" in the i18n keys "ui.messagePart.context.list.one" and "ui.messagePart.context.list.other"; update both translation strings to user-facing wording (e.g., "Listed {{count}} directory" and "Listed {{count}} directories" or another user-friendly term like "item/items" or "entry/entries") so the text reads naturally to end users and preserves the {{count}} variable.packages/ui/src/i18n/zh.ts (1)
70-75:⚠️ Potential issue | 🟡 MinorPrefer explicit file/directory nouns in context counters.
These strings are less specific than the existing zh tool terms in this file (
读取文件,列出目录), so the summary can feel semantically drifted.💡 Suggested copy alignment
- "ui.messagePart.context.read.one": "阅读 {{count}} 篇", - "ui.messagePart.context.read.other": "阅读 {{count}} 篇", + "ui.messagePart.context.read.one": "读取 {{count}} 个文件", + "ui.messagePart.context.read.other": "读取 {{count}} 个文件", "ui.messagePart.context.search.one": "搜索 {{count}} 次", "ui.messagePart.context.search.other": "搜索 {{count}} 次", - "ui.messagePart.context.list.one": "浏览 {{count}} 处", - "ui.messagePart.context.list.other": "浏览 {{count}} 处", + "ui.messagePart.context.list.one": "列出 {{count}} 个目录", + "ui.messagePart.context.list.other": "列出 {{count}} 个目录",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/i18n/zh.ts` around lines 70 - 75, The context-count strings are too generic and should use explicit nouns to match existing terminology—update the translations for the keys ui.messagePart.context.read.one, ui.messagePart.context.read.other, ui.messagePart.context.list.one, and ui.messagePart.context.list.other to reference 文件 for read counters and 目录 for list counters (aligning with existing terms like 读取文件 and 列出目录), so the summaries stay semantically consistent across the file.packages/ui/src/components/message-part.css (1)
803-803: 🧹 Nitpick | 🔵 TrivialUse a font-size token instead of a literal at Line 803.
font-size: 12px;is still hardcoded incodestyles; this should use the shared x-small token for consistency with the design system.Proposed refactor
- code { - font-size: 12px; + code { + font-size: var(--font-size-x-small); line-height: var(--line-height-large); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/components/message-part.css` at line 803, Replace the hardcoded font-size: 12px in the code styles inside message-part.css with the shared "x-small" font-size design token; locate the selector targeting inline code (the `code` styles in message-part.css) and swap the literal 12px for the project's x-small token (for example use the project's token variable such as var(--font-size-x-small) or the equivalent token reference used elsewhere) so the component uses the centralized typography token instead of a literal.packages/app/src/pages/layout/pawwork-sidebar.tsx (1)
242-359:⚠️ Potential issue | 🟠 MajorRestore visible focus styling on the raw sidebar buttons.
The New/Search/Settings
<button>s still remove the browser outline viafocus:outline-nonebut do not add afocus-visiblereplacement, so keyboard focus disappears on the primary sidebar actions. This is the same accessibility gap already flagged in the prior review.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app/src/pages/layout/pawwork-sidebar.tsx` around lines 242 - 359, The sidebar buttons (data-action="pawwork-session-new", "pawwork-session-search", "pawwork-open-settings") currently remove the native focus outline via "focus:outline-none" and lack a keyboard-visible replacement; update these elements to restore visible focus by replacing or augmenting "focus:outline-none" with appropriate focus-visible utility classes (e.g., Tailwind's focus-visible:outline-* / focus-visible:ring-* or your design system's focus class) so keyboard users see a clear focus indicator while preserving the visual style for mouse focus.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/app/src/pages/layout/sidebar-items.tsx`:
- Around line 121-127: The leading slot in sidebar-items.tsx is fixed to 14px
causing overflow because the pin control in pawwork-sidebar.tsx renders at
size-5; update the leading slot (the div with data-leading-slot and indicator())
to use the same sizing as the pin button instead of hardcoded 14px—either
replace w-[14px] h-[14px] with the same utility/class/variable used by the pin
button (or a shared CSS variable/token), or adjust the pin button to match the
slot, so props.leadingSlot and indicator() align with the pin control size-5.
---
Duplicate comments:
In `@packages/app/src/pages/layout/pawwork-sidebar.tsx`:
- Around line 242-359: The sidebar buttons (data-action="pawwork-session-new",
"pawwork-session-search", "pawwork-open-settings") currently remove the native
focus outline via "focus:outline-none" and lack a keyboard-visible replacement;
update these elements to restore visible focus by replacing or augmenting
"focus:outline-none" with appropriate focus-visible utility classes (e.g.,
Tailwind's focus-visible:outline-* / focus-visible:ring-* or your design
system's focus class) so keyboard users see a clear focus indicator while
preserving the visual style for mouse focus.
In `@packages/ui/src/components/message-part.css`:
- Line 803: Replace the hardcoded font-size: 12px in the code styles inside
message-part.css with the shared "x-small" font-size design token; locate the
selector targeting inline code (the `code` styles in message-part.css) and swap
the literal 12px for the project's x-small token (for example use the project's
token variable such as var(--font-size-x-small) or the equivalent token
reference used elsewhere) so the component uses the centralized typography token
instead of a literal.
In `@packages/ui/src/i18n/en.ts`:
- Around line 80-81: The UI copy uses internal shorthand "dir/dirs" in the i18n
keys "ui.messagePart.context.list.one" and "ui.messagePart.context.list.other";
update both translation strings to user-facing wording (e.g., "Listed {{count}}
directory" and "Listed {{count}} directories" or another user-friendly term like
"item/items" or "entry/entries") so the text reads naturally to end users and
preserves the {{count}} variable.
In `@packages/ui/src/i18n/zh.ts`:
- Around line 70-75: The context-count strings are too generic and should use
explicit nouns to match existing terminology—update the translations for the
keys ui.messagePart.context.read.one, ui.messagePart.context.read.other,
ui.messagePart.context.list.one, and ui.messagePart.context.list.other to
reference 文件 for read counters and 目录 for list counters (aligning with existing
terms like 读取文件 and 列出目录), so the summaries stay semantically consistent across
the file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 56899805-07cc-494f-8309-bd7e8577c856
📒 Files selected for processing (29)
packages/app/e2e/app/home.spec.tspackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/i18n/en.tspackages/app/src/i18n/zh.tspackages/app/src/pages/home.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/pages/session.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/ui/src/components/basic-tool.csspackages/ui/src/components/message-part.csspackages/ui/src/components/message-part.tsxpackages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/styles/theme.css
💤 Files with no reviewable changes (3)
- packages/app/src/i18n/en.ts
- packages/app/e2e/app/home.spec.ts
- packages/app/src/i18n/zh.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
packages/app/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/app/AGENTS.md)
Always prefer
createStoreover multiplecreateSignalcalls in SolidJS
Files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/patch/index.ts:337-346
Timestamp: 2026-04-28T04:38:05.946Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/patch/index.ts`), the BOM-transition surfacing gap — where `Bom.split` strips BOM before building `unified_diff`/`new_content` but `Bom.join` later re-attaches BOM on disk write, meaning BOM changes are not reflected in the diff payload — is intentionally deferred. PR `#270` is an upstream-sync graft; fixing the issue here would mix refactor + bugfix intents and drift the diff from the upstream baseline needed for clean future grafts. A dedicated follow-up PR (or upstream report) will address this. Do NOT re-flag the missing BOM-change surfacing in `ApplyPatchFileUpdate`/`ApplyPatchFileChange` as a blocking issue in PR `#270` or in future sync PRs that carry the same upstream baseline.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/todo.ts:9-18
Timestamp: 2026-04-28T06:51:54.812Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork), `TodoItem.status` and `TodoItem.priority` are intentionally declared as plain `Schema.String` rather than closed literal unions. This matches the upstream opencode baseline (`dev:packages/opencode/src/tool/todo.ts`). The tightening — `Schema.Literals(["pending","in_progress","completed","cancelled"])` for `status` and `Schema.Literals(["high","medium","low"])` for `priority` — is tracked as a follow-up under the harness/tool-set-v1 series (issue `#129`) to land either as part of a tool-schema tightening sweep or upstream-first. Do NOT re-flag the free-form strings for these fields in upstream-sync PRs; flag it only in a PawWork-authored PR or the dedicated sweep that touches `TodoItem` schema.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/lsp.ts:23-32
Timestamp: 2026-04-28T07:28:14.317Z
Learning: In `packages/opencode/src/tool/lsp.ts` (Astro-Han/pawwork, PR `#270`), the `Parameters` schema requires `line` and `character` for all operations, including `workspaceSymbol` and `documentSymbol` which never use coordinates. This matches the upstream `dev:packages/opencode/src/tool/lsp.ts:23-32` baseline exactly — both fields are declared as required `Schema.Number` with `>= 1` checks. The fix (per-operation schema split, or making `line`/`character` optional with handler-side presence validation for operations that need them like `goToDefinition`/`findReferences`) is deferred to a follow-up PR or upstream report to avoid mixing refactor + bug-fix intents and drifting the diff from the upstream baseline. Do NOT re-flag the required coordinates on `workspaceSymbol`/`documentSymbol` in upstream-sync PRs; flag it only in a PawWork-authored PR that directly touches `lsp.ts` parameter validation.
📚 Learning: 2026-04-23T07:23:23.849Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 180
File: packages/app/src/components/session/session-new-view.tsx:13-18
Timestamp: 2026-04-23T07:23:23.849Z
Learning: In pawwork (Astro-Han/pawwork), prefer using `createStore` instead of multiple `createSignal` calls only when the signals represent **coupled** object state that is updated together (i.e., there is at least one shared batch-update site where the state is changed in the same transaction). If the state fields are **independent** and are mutated by separate handlers (e.g., one handler updates only `selectedSkill` while another updates only `mode`), keep them as individual `createSignal` calls—using `createStore` for truly independent fields adds boilerplate without behavioral benefit.
Applied to files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T15:10:21.635Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 191
File: packages/app/src/components/session/pawwork-skill-meta.ts:38-39
Timestamp: 2026-04-23T15:10:21.635Z
Learning: This repo configures Tailwind v4 with `--color-*: initial`, which effectively breaks standard Tailwind palette utilities (e.g., `text-violet-500` can resolve to no CSS variable and render as a no-op/black). For brand/accent colors that are not backed by semantic design tokens, use inline styles with the exact hex value (e.g., `style={{ color: '#8B5FBF' }}` / `homeIconStyle: { color: '#8B5FBF' }`) and add a short comment explaining that Tailwind palette utilities won’t work due to the `--color-*: initial` setup. Do not suggest replacing these inline hex colors with Tailwind palette classes anywhere in this repo.
Applied to files:
packages/app/src/components/session/session-sortable-tab.tsxpackages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/dialog-select-model.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/pages/session/files-tab.tsxpackages/app/src/pages/home.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/components/settings-models.tsxpackages/app/src/pages/session.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/components/settings-general.tsxpackages/app/src/components/settings-providers.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-24T05:39:58.329Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Applied to files:
packages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/pages/session.tsxpackages/app/src/components/prompt-input.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-24T05:48:39.493Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/e2e/app/composer-parity.spec.ts:0-0
Timestamp: 2026-04-24T05:48:39.493Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, the Model chip trigger button carries `data-action="prompt-model"` (around line 1187) and the Variant chip trigger button carries `data-action="prompt-model-variant"` (around line 1231), both set via `triggerProps`. These are therefore already captured by any `[data-action]` selector sweep in E2E tests and do not need a separate `[data-component]` query to be included in parity assertions — though unioning both is kept as belt-and-suspenders in `collectBarSet`.
Applied to files:
packages/app/src/components/prompt-input/send-button.tsxpackages/app/src/components/dialog-manage-models.tsxpackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/prompt-input.tsx
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `data-component`, `data-action`, or semantic roles for selectors instead of CSS class names or IDs
Applied to files:
packages/app/src/components/dialog-manage-models.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-22T05:32:29.012Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:32:29.012Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/app/src/pages/layout.tsx
📚 Learning: 2026-04-28T04:56:18.533Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/test/file/ripgrep.test.ts:172-175
Timestamp: 2026-04-28T04:56:18.533Z
Learning: In `packages/opencode/test/file/ripgrep.test.ts` (Astro-Han/pawwork, PR `#270`), the `files dies on nonexistent directory` test hardcodes `/tmp/nonexistent-dir-12345` as the missing-directory path. This is upstream-inherited behaviour adopted wholesale via the graft strategy (`project_upstream_strategy.md`). The fix — replacing the hardcoded path with a platform-neutral guaranteed-nonexistent child path derived from the `tmpdir` fixture — is intentionally deferred to a follow-up PR or upstream report, to avoid mixing bugfix + refactor intents and drifting the diff from the upstream baseline future syncs need. Do NOT re-flag the hardcoded `/tmp/nonexistent-dir-12345` path in this file as a blocking or actionable issue until the follow-up PR lands.
Applied to files:
packages/ui/src/i18n/en.ts
📚 Learning: 2026-04-28T08:14:31.436Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/edit.ts:48-48
Timestamp: 2026-04-28T08:14:31.436Z
Learning: In `packages/opencode/src/tool/edit.ts` (Astro-Han/pawwork), the `filePath` schema description (`"The absolute path to the file to modify"`) is upstream-inherited from `dev:packages/opencode/src/tool/edit.ts:48`. The runtime actually accepts relative paths (resolved via `Instance.directory` at lines 79-81), but the description fix is intentionally deferred to a single PawWork-authored description-cleanup PR that will also cover the identical mismatch in `packages/opencode/src/tool/write.ts:24`. Do NOT re-flag the too-narrow `filePath` description in upstream-sync PRs; flag it only in the dedicated description-cleanup PR.
Applied to files:
packages/ui/src/i18n/en.tspackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T03:01:37.478Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 282
File: packages/ui/src/i18n/zh.ts:109-109
Timestamp: 2026-04-28T03:01:37.478Z
Learning: In Astro-Han/pawwork PR `#282` (`packages/ui/src/i18n/zh.ts`), the translation for `ui.tool.questions` is intentionally `提出问题` (verb-object structure), NOT `向你提问` (prepositional-phrase structure). The choice was made after a UI smoke run to keep consistent verb-object phrasing across all zh tool-card labels (`执行命令`, `列出目录`, `查找文件`, `搜索文本`, `读取文件`, `读取网页`, `批量修改`, `查看待办`). Do NOT flag `提出问题` as incorrect for this key.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-22T09:32:58.310Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Applied to files:
packages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/ui/src/styles/theme.csspackages/app/src/components/prompt-input/workspace-chip.tsxpackages/app/src/components/settings-keybinds.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/app/src/pages/layout/sidebar-workspace.tsxpackages/ui/src/i18n/zh.tspackages/app/src/components/dialog-select-model-unpaid.tsxpackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T17:02:35.873Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 198
File: packages/app/src/index.css:95-97
Timestamp: 2026-04-23T17:02:35.873Z
Learning: In the pawwork repository, Stylelint is intentionally not configured (per AGENTS.md, only linting is enforced and the Biome formatter is disabled). When reviewing CSS files, do not raise Stylelint rule-based issues (e.g., `declaration-empty-line-before`) because they are false positives and not enforced by the project toolchain.
Applied to files:
packages/ui/src/styles/theme.csspackages/ui/src/components/basic-tool.csspackages/ui/src/components/message-part.css
📚 Learning: 2026-04-23T15:26:07.250Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T07:28:14.317Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/lsp.ts:23-32
Timestamp: 2026-04-28T07:28:14.317Z
Learning: In `packages/opencode/src/tool/lsp.ts` (Astro-Han/pawwork, PR `#270`), the `Parameters` schema requires `line` and `character` for all operations, including `workspaceSymbol` and `documentSymbol` which never use coordinates. This matches the upstream `dev:packages/opencode/src/tool/lsp.ts:23-32` baseline exactly — both fields are declared as required `Schema.Number` with `>= 1` checks. The fix (per-operation schema split, or making `line`/`character` optional with handler-side presence validation for operations that need them like `goToDefinition`/`findReferences`) is deferred to a follow-up PR or upstream report to avoid mixing refactor + bug-fix intents and drifting the diff from the upstream baseline. Do NOT re-flag the required coordinates on `workspaceSymbol`/`documentSymbol` in upstream-sync PRs; flag it only in a PawWork-authored PR that directly touches `lsp.ts` parameter validation.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T06:51:54.812Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/todo.ts:9-18
Timestamp: 2026-04-28T06:51:54.812Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork), `TodoItem.status` and `TodoItem.priority` are intentionally declared as plain `Schema.String` rather than closed literal unions. This matches the upstream opencode baseline (`dev:packages/opencode/src/tool/todo.ts`). The tightening — `Schema.Literals(["pending","in_progress","completed","cancelled"])` for `status` and `Schema.Literals(["high","medium","low"])` for `priority` — is tracked as a follow-up under the harness/tool-set-v1 series (issue `#129`) to land either as part of a tool-schema tightening sweep or upstream-first. Do NOT re-flag the free-form strings for these fields in upstream-sync PRs; flag it only in a PawWork-authored PR or the dedicated sweep that touches `TodoItem` schema.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T04:56:13.350Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/tool.ts:8-10
Timestamp: 2026-04-28T04:56:13.350Z
Learning: In `packages/opencode/src/tool/tool.ts` (Astro-Han/pawwork, PR `#270`), the `Metadata` interface uses `[key: string]: any` as its index signature. This is upstream-inherited code adopted wholesale via the graft strategy (per `project_upstream_strategy.md`). The fix — tightening to `Record<string, unknown>` and explicitly narrowing framework-owned fields like `truncated` — is intentionally deferred to a follow-up PR or upstream report. Do NOT re-flag the `any` index signature on `Metadata` in PR `#270` or in future upstream-sync PRs that carry the same upstream baseline.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T17:02:39.474Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 198
File: packages/ui/src/theme/themes/pawwork.json:32-33
Timestamp: 2026-04-23T17:02:39.474Z
Learning: In Astro-Han/pawwork, `#FF5910` is the locked PawWork brand orange used for `button-primary-base` (and `--accent-brand` in `index.css`, theme token `interactive`/`primary`). The `text-on-interactive-base: `#FFFFFF`` on `button-primary-base: `#FF5910`` combination produces a contrast ratio of ~3.14:1. This intentionally fails WCAG AA normal-text (4.5:1) but passes AA non-text (SC 1.4.11, 3:1) and AA large-text (SC 1.4.3, 3:1). The tradeoff is accepted for brand consistency; do NOT flag this contrast pair as a WCAG violation in future reviews. If a specific surface requires stricter contrast, it should be addressed individually (e.g., larger text, ghost variant, outline button) rather than changing the palette.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
🔇 Additional comments (24)
packages/ui/src/i18n/zh.ts (1)
58-58: Good status copy update.
"探索完毕"reads naturally as the completed state paired with"正在探索".packages/ui/src/i18n/en.ts (1)
76-77: Nice phrasing improvement for read summary.
Read {{count}} file/filesis clearer and more natural than the previous count-leading form.packages/ui/src/components/message-part.tsx (1)
922-922: Context tool group typography change is safe and consistent.Line 922 is a presentation-only downshift to
text-13-medium; behavior and rendering logic remain intact.packages/app/src/components/dialog-manage-models.tsx (1)
37-37: Manage-models action button update looks good.Line 37 is a clean visual-only class adjustment to
text-13-mediumwith no behavioral risk.packages/app/src/pages/session/message-timeline.tsx (1)
641-641: Session timeline typography pass is consistent and non-invasive.These line edits are strictly presentational (
text-13-*) and do not affect timeline interaction or mutation flows.Also applies to: 759-759, 766-766, 795-795, 809-809
packages/app/src/components/session/session-sortable-tab.tsx (1)
25-25: File tab label sizing update is good.Line 25 is a straightforward typography adjustment with no behavioral impact.
packages/app/src/components/prompt-input/send-button.tsx (1)
23-23: Send button size reduction is correctly scoped.Line 23 tightens geometry only; submit behavior, disabled state, and accessibility attributes remain unchanged.
packages/app/src/components/dialog-select-model.tsx (1)
246-246: Dialog select-model action typography update is solid.Line 246 applies the expected text-size downshift without affecting dialog behavior.
packages/app/src/pages/session/files-tab.tsx (1)
115-115: Files tab typography adjustments are clean and low-risk.The updated text classes are strictly visual and keep existing file-state/render behavior intact.
Also applies to: 133-133
packages/app/src/pages/layout/sidebar-project.tsx (1)
201-201: Sidebar project preview alignment updates are well implemented.The
data-leading-slotaddition at Line 229 correctly integrates with shared sidebar leading-slot icon sizing, and the Line 201/232 typography changes stay consistent with the new 13px baseline.Also applies to: 229-229, 232-232
packages/app/src/components/settings-general.tsx (1)
383-383: Typography downshift is consistent and safe.These class updates are cohesive with the 13px baseline and do not alter runtime behavior.
Also applies to: 492-492, 536-536, 589-589, 655-655, 688-688, 728-728
packages/app/src/components/settings-keybinds.tsx (1)
411-411: Keybinds typography updates look good.The 13px class migration is applied consistently in headers, row labels, and empty state text.
Also applies to: 416-416, 446-446, 448-448
packages/app/src/components/dialog-select-model-unpaid.tsx (1)
46-46: Dialog typography refactor is clean.The updated
text-13-*usage is consistent across title, notes, and CTA label without affecting flow.Also applies to: 90-90, 112-112, 119-120, 126-126, 133-133
packages/app/src/pages/session.tsx (1)
1315-1315: Session review empty-state typography updates are solid.These changes keep behavior intact while aligning with the 13px hierarchy target.
Also applies to: 1322-1322, 1329-1330, 1361-1361
packages/ui/src/components/message-part.css (1)
440-440: Token-based typography migration looks correct.These updates consistently move affected text to shared font-size tokens.
Also applies to: 761-761, 778-778, 858-858, 944-944, 1070-1070, 1078-1078, 1106-1106
packages/app/src/components/settings-providers.tsx (1)
140-140: Providers settings typography updates are coherent.The 13px substitutions are consistently applied across headings, labels, and helper text.
Also applies to: 145-145, 159-159, 165-165, 182-182, 194-194, 227-227, 249-249
packages/app/src/components/settings-models.tsx (1)
19-19: Settings models typography changes are safe and aligned.The updated classes maintain behavior while matching the new type scale.
Also applies to: 27-27, 29-29, 103-103, 112-112
packages/app/src/pages/home.tsx (1)
77-77: Home page typography adjustments look good.The updated text scale is consistent and does not impact behavior.
Also applies to: 92-92, 107-107, 128-128
packages/ui/src/styles/theme.css (1)
8-11: Token update looks consistent.The new 13px base/hierarchy tokens and 12px x-small token line up with the PR-wide typography downshift.
packages/ui/src/components/basic-tool.css (1)
69-238: Typography now follows the shared base token.Using
--font-size-basehere keeps the tool and task card text aligned with the 13px theme token.packages/app/src/components/prompt-input/workspace-chip.tsx (1)
66-131: Workspace chip density matches the new composer scale.The 28px trigger, 13px text, and add-workspace icon update all fit the tighter layout, and the shell-mode gating remains intact.
packages/app/src/components/prompt-input.tsx (1)
1124-1571: Prompt input density changes look coherent.The 28px controls and 13px text classes are consistent with the rest of the composer refactor, and the existing shell-mode/send-button behavior stays unchanged.
packages/app/src/pages/layout/sidebar-workspace.tsx (1)
103-358: Workspace sidebar typography and spacing stay consistent.The 13px text updates and tighter load-more row match the shared sidebar scale.
packages/app/src/pages/layout.tsx (1)
2172-2428: Layout callsite updates line up with the sidebar API change.The reduced typography and
PawworkSidebarprop cleanup match the refactor.
…ttern en: "Listed N dir / dirs" was implementation jargon; use the full word "directory / directories" so the UI reads cleanly for non-technical users (PawWork's "for everyone" positioning). zh: collapse the prior verb-only labels (阅读 N 篇 / 浏览 N 处) to verb-object form that matches PR #282's tool-card labels (读取文件 / 列出目录) and stays visually symmetric across all three counts: - 读取 N 个文件 (was 阅读 N 篇) - 搜索 N 处匹配 (was 搜索 N 次, now matches verb+量+名 shape) - 列出 N 个目录 (was 浏览 N 处) "处" stays as the search measure word because grep-style search returns hits at file:line locations, which is exactly what 处 denotes (an occurrence at a position) — more concrete than 项. Addresses coderabbitai review on #320.
# Conflicts: # packages/app/e2e/app/home.spec.ts
The previous class="size-5 rounded-md" did not actually shrink IconButton: the [data-component="icon-button"][data-size="normal"] selector in icon-button.css beats Tailwind's .size-5, so the button was still laid out at 24x24 and only visually clipped to 20x20 by the parent's w-5 overflow-hidden. This cropped the hover/focus affordance and gave the action column an inconsistent hit target. Pass size="small" (the IconButton API for 20x20) on both the SessionRow archive button and the pawwork-sidebar dot-grid trigger so the layout actually matches the slot.
The session-row pin button rendered through SessionRow's leadingSlot was size-5 (20px) but the slot container is 14x14, so the button overflowed the slot and reintroduced horizontal drift in the tightened sidebar. Match the slot dimensions: w-[14px] h-[14px]. The Icon inside is already auto-shrunk to 14x14 by the [data-leading-slot] CSS rule, so the button container, content, and slot now all line up at the same 14x14. Addresses coderabbitai outside-diff comment on #320.
fb22931 to
d61be8e
Compare
|
@coderabbitai outside-diff finding addressed in d61be8e. The session-row pin button ( |
|
Tip For best results, initiate chat on the files or code changes.
✏️ Learnings added
🧠 Learnings used |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ui/src/components/markdown.css (1)
6-10:⚠️ Potential issue | 🟡 MinorMinor: Update the stale “14px” comment on the markdown base font-size.
Line 8 uses
font-size: var(--font-size-base); /* 14px */, but the theme snippet indicates--font-size-baseis13px. This comment will mislead future maintainers and can cause follow-up review churn.Proposed fix
[data-component="markdown"] { - font-size: var(--font-size-base); /* 14px */ + font-size: var(--font-size-base); /* 13px */ line-height: 160%; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/components/markdown.css` around lines 6 - 10, Update the stale inline comment that says "14px" next to the CSS rule "font-size: var(--font-size-base);" in the markdown base styles; change the comment to reflect the actual theme value "13px" so it reads e.g. "/* 13px */" (locate the rule in packages/ui/src/components/markdown.css where font-size: var(--font-size-base) is defined).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ui/src/i18n/en.ts`:
- Around line 78-79: The locale keys "ui.messagePart.context.search.one" and
"ui.messagePart.context.search.other" use a count-first format; change their
values to a verb-led format to match the surrounding keys (e.g., use "Search
{{count}}" for both plural forms) so that when rendered with other keys like
"ui.messagePart.context.read" and "ui.messagePart.context.list" the phrasing and
ordering are consistent; update the strings for the two keys in
packages/ui/src/i18n/en.ts accordingly.
---
Outside diff comments:
In `@packages/ui/src/components/markdown.css`:
- Around line 6-10: Update the stale inline comment that says "14px" next to the
CSS rule "font-size: var(--font-size-base);" in the markdown base styles; change
the comment to reflect the actual theme value "13px" so it reads e.g. "/* 13px
*/" (locate the rule in packages/ui/src/components/markdown.css where font-size:
var(--font-size-base) is defined).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 219df0b9-4784-454b-a257-d6bf41eca5c1
📒 Files selected for processing (7)
packages/app/e2e/app/home.spec.tspackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout/sidebar-items.tsxpackages/ui/src/components/markdown.csspackages/ui/src/components/message-part.csspackages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
💤 Files with no reviewable changes (1)
- packages/app/e2e/app/home.spec.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-opencode
- GitHub Check: unit-app
- GitHub Check: unit-desktop
- GitHub Check: typecheck
- GitHub Check: smoke-macos-arm64
- GitHub Check: analyze-js-ts
- GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (1)
packages/app/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/app/AGENTS.md)
Always prefer
createStoreover multiplecreateSignalcalls in SolidJS
Files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
🧠 Learnings (30)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 320
File: packages/app/src/pages/layout/sidebar.css:1-6
Timestamp: 2026-04-29T04:23:50.832Z
Learning: In `packages/app/src/pages/layout/sidebar-project.tsx` (Astro-Han/pawwork), the branch-icon container at line 229 uses `size-5` (20px) with `justify-center items-center` and `data-leading-slot` by design. This is intentional: (1) the 3px padding is symmetric, not asymmetric; (2) the branch row lives inside the project-preview header card — a different visual context from the X=8 left rail (SessionRow/NewSessionItem/New session/Search/Settings). The `data-leading-slot` + 20×20 container combination is the deliberate mechanism to render a 14×14 icon glyph (via sidebar.css) inside a larger touch-target without affecting global Icon size="small". Do NOT flag this size-5 container as misaligned or asymmetric in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/patch/index.ts:337-346
Timestamp: 2026-04-28T04:38:05.946Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/patch/index.ts`), the BOM-transition surfacing gap — where `Bom.split` strips BOM before building `unified_diff`/`new_content` but `Bom.join` later re-attaches BOM on disk write, meaning BOM changes are not reflected in the diff payload — is intentionally deferred. PR `#270` is an upstream-sync graft; fixing the issue here would mix refactor + bugfix intents and drift the diff from the upstream baseline needed for clean future grafts. A dedicated follow-up PR (or upstream report) will address this. Do NOT re-flag the missing BOM-change surfacing in `ApplyPatchFileUpdate`/`ApplyPatchFileChange` as a blocking issue in PR `#270` or in future sync PRs that carry the same upstream baseline.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 0
File: :0-0
Timestamp: 2026-04-29T04:31:25.068Z
Learning: In `packages/app/src/pages/layout/pawwork-sidebar.tsx` (Astro-Han/pawwork), the session-row pin button container must be sized `w-[14px] h-[14px]` to match the 14×14 leading slot declared in `sidebar-items.tsx`. The Icon glyph inside is auto-shrunk to 14×14 by the `[data-leading-slot]` CSS rule in `sidebar.css`, so all three dimensions (button wrapper, icon content, slot frame) align at 14×14. Do NOT suggest widening the pin button container beyond 14×14 in this context.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
📚 Learning: 2026-04-29T04:23:45.886Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 320
File: packages/ui/src/i18n/zh.ts:0-0
Timestamp: 2026-04-29T04:23:45.886Z
Learning: In Astro-Han/pawwork `packages/ui/src/i18n/zh.ts`, the canonical zh translations for message-part context counters (commit fa6771a35, PR `#320`) are:
- `ui.messagePart.context.read.one/.other` → `读取 {{count}} 个文件`
- `ui.messagePart.context.search.one/.other` → `搜索 {{count}} 处匹配` (`处` is the correct measure word for grep-style file:line hits, chosen for verb+count+量词+名词 symmetry with the other two labels)
- `ui.messagePart.context.list.one/.other` → `列出 {{count}} 个目录`
Do NOT suggest `搜索 {{count}} 次` or `浏览 {{count}} 处` for these keys in future reviews.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-28T04:56:18.533Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/test/file/ripgrep.test.ts:172-175
Timestamp: 2026-04-28T04:56:18.533Z
Learning: In `packages/opencode/test/file/ripgrep.test.ts` (Astro-Han/pawwork, PR `#270`), the `files dies on nonexistent directory` test hardcodes `/tmp/nonexistent-dir-12345` as the missing-directory path. This is upstream-inherited behaviour adopted wholesale via the graft strategy (`project_upstream_strategy.md`). The fix — replacing the hardcoded path with a platform-neutral guaranteed-nonexistent child path derived from the `tmpdir` fixture — is intentionally deferred to a follow-up PR or upstream report, to avoid mixing bugfix + refactor intents and drifting the diff from the upstream baseline future syncs need. Do NOT re-flag the hardcoded `/tmp/nonexistent-dir-12345` path in this file as a blocking or actionable issue until the follow-up PR lands.
Applied to files:
packages/ui/src/i18n/en.ts
📚 Learning: 2026-04-28T08:14:31.436Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/edit.ts:48-48
Timestamp: 2026-04-28T08:14:31.436Z
Learning: In `packages/opencode/src/tool/edit.ts` (Astro-Han/pawwork), the `filePath` schema description (`"The absolute path to the file to modify"`) is upstream-inherited from `dev:packages/opencode/src/tool/edit.ts:48`. The runtime actually accepts relative paths (resolved via `Instance.directory` at lines 79-81), but the description fix is intentionally deferred to a single PawWork-authored description-cleanup PR that will also cover the identical mismatch in `packages/opencode/src/tool/write.ts:24`. Do NOT re-flag the too-narrow `filePath` description in upstream-sync PRs; flag it only in the dedicated description-cleanup PR.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T08:29:02.858Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/file/ripgrep.ts:469-470
Timestamp: 2026-04-28T08:29:02.858Z
Learning: In `packages/opencode/src/file/ripgrep.ts` (Astro-Han/pawwork, PR `#270`), the `tree()` function filters files with `if (file.includes(".opencode")) continue`, which is a raw substring match that would also drop unrelated paths whose names merely contain `.opencode` (e.g., `.opencode-backup`). The correct fix is `if (file.split(path.sep).includes(".opencode")) continue` to match only the exact path segment. However, upstream `dev:packages/opencode/src/file/ripgrep.ts` carries the identical false-positive filter. PR `#270` is an upstream-sync graft; fixing it here would diverge from the baseline. The fix is deferred to a PawWork ripgrep filter cleanup PR or an upstream-first fix, to land alongside the other ripgrep deferrals (Schema.Class, bytes-union, abort short-circuit, mixed-separator). Do NOT re-flag the `.opencode` substring filter in upstream-sync PRs.
Applied to files:
packages/ui/src/i18n/en.ts
📚 Learning: 2026-04-28T03:01:37.478Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 282
File: packages/ui/src/i18n/zh.ts:109-109
Timestamp: 2026-04-28T03:01:37.478Z
Learning: In Astro-Han/pawwork PR `#282` (`packages/ui/src/i18n/zh.ts`), the translation for `ui.tool.questions` is intentionally `提出问题` (verb-object structure), NOT `向你提问` (prepositional-phrase structure). The choice was made after a UI smoke run to keep consistent verb-object phrasing across all zh tool-card labels (`执行命令`, `列出目录`, `查找文件`, `搜索文本`, `读取文件`, `读取网页`, `批量修改`, `查看待办`). Do NOT flag `提出问题` as incorrect for this key.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-28T07:28:14.317Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/lsp.ts:23-32
Timestamp: 2026-04-28T07:28:14.317Z
Learning: In `packages/opencode/src/tool/lsp.ts` (Astro-Han/pawwork, PR `#270`), the `Parameters` schema requires `line` and `character` for all operations, including `workspaceSymbol` and `documentSymbol` which never use coordinates. This matches the upstream `dev:packages/opencode/src/tool/lsp.ts:23-32` baseline exactly — both fields are declared as required `Schema.Number` with `>= 1` checks. The fix (per-operation schema split, or making `line`/`character` optional with handler-side presence validation for operations that need them like `goToDefinition`/`findReferences`) is deferred to a follow-up PR or upstream report to avoid mixing refactor + bug-fix intents and drifting the diff from the upstream baseline. Do NOT re-flag the required coordinates on `workspaceSymbol`/`documentSymbol` in upstream-sync PRs; flag it only in a PawWork-authored PR that directly touches `lsp.ts` parameter validation.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.csspackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T06:51:54.812Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/todo.ts:9-18
Timestamp: 2026-04-28T06:51:54.812Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork), `TodoItem.status` and `TodoItem.priority` are intentionally declared as plain `Schema.String` rather than closed literal unions. This matches the upstream opencode baseline (`dev:packages/opencode/src/tool/todo.ts`). The tightening — `Schema.Literals(["pending","in_progress","completed","cancelled"])` for `status` and `Schema.Literals(["high","medium","low"])` for `priority` — is tracked as a follow-up under the harness/tool-set-v1 series (issue `#129`) to land either as part of a tool-schema tightening sweep or upstream-first. Do NOT re-flag the free-form strings for these fields in upstream-sync PRs; flag it only in a PawWork-authored PR or the dedicated sweep that touches `TodoItem` schema.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.csspackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.csspackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-28T07:27:49.810Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/file/ripgrep.ts:45-72
Timestamp: 2026-04-28T07:27:49.810Z
Learning: In `packages/opencode/src/file/ripgrep.ts` (Astro-Han/pawwork), `PathText`, the `lines` field inside `SearchMatch`, and `submatches[].match` accept only `{text: string}` and do NOT handle ripgrep's `{bytes: base64}` JSON variant (emitted for non-UTF-8 paths/match text). This matches the upstream `dev:packages/opencode/src/file/ripgrep.ts` baseline exactly. The fix — adding `Schema.Union([Schema.Struct({text: Schema.String}), Schema.Struct({bytes: Schema.String})])` for each location plus base64-decode in downstream consumers — is intentionally deferred to either a dedicated PawWork PR or an upstream-first fix inherited via the next sync. Do NOT re-flag the missing `{bytes: ...}` union variants in upstream-sync PRs; flag it only in a PawWork-authored PR that directly touches these schema definitions or their downstream consumers.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-28T04:38:11.727Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/edit.ts:35-45
Timestamp: 2026-04-28T04:38:11.727Z
Learning: In `packages/opencode/src/tool/edit.ts` (Astro-Han/pawwork, PR `#270`), the module-scoped `Map<string, Semaphore.Semaphore>` (`locks`) and `lock(filePath)` helper are intentionally left as a global, never-cleaned registry. PR `#270` is a pure upstream-sync graft and mixing in a bugfix would drift the diff from the upstream baseline. The fix (moving per-file semaphores into `InstanceState`/`ScopedCache` for per-instance lifecycle management) is tracked as a follow-up to land in its own PR or be reported upstream. Do NOT re-flag the absence of InstanceState wiring for the lock map in this file until the follow-up PR is opened.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.css
📚 Learning: 2026-04-28T04:38:05.946Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/patch/index.ts:337-346
Timestamp: 2026-04-28T04:38:05.946Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/patch/index.ts`), the BOM-transition surfacing gap — where `Bom.split` strips BOM before building `unified_diff`/`new_content` but `Bom.join` later re-attaches BOM on disk write, meaning BOM changes are not reflected in the diff payload — is intentionally deferred. PR `#270` is an upstream-sync graft; fixing the issue here would mix refactor + bugfix intents and drift the diff from the upstream baseline needed for clean future grafts. A dedicated follow-up PR (or upstream report) will address this. Do NOT re-flag the missing BOM-change surfacing in `ApplyPatchFileUpdate`/`ApplyPatchFileChange` as a blocking issue in PR `#270` or in future sync PRs that carry the same upstream baseline.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/i18n/zh.tspackages/ui/src/components/markdown.csspackages/ui/src/components/message-part.css
📚 Learning: 2026-04-28T04:38:11.771Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/agent.ts:23-27
Timestamp: 2026-04-28T04:38:11.771Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/tool/agent.ts`), the `subagent_session_id` field in the `Parameters` schema accepts any `Schema.String` rather than a branded `SessionID`. This is inherited upstream behavior (adopted in PR `#270`, an upstream-sync graft of upstream PR `#23244`). The fix — validating `subagent_session_id` as a `SessionID` brand up front so malformed/typo'd IDs fail explicitly rather than silently forking a new subagent session — is intentionally deferred to a follow-up PR or upstream report. Do NOT re-flag this as a blocking issue in PR `#270` or in future upstream-sync PRs that carry the same schema; flag it only in a PawWork-authored PR that touches `agent.ts` parameter validation.
Applied to files:
packages/ui/src/i18n/en.tspackages/ui/src/components/message-part.css
📚 Learning: 2026-04-28T06:47:20.342Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/question.ts:6-16
Timestamp: 2026-04-28T06:47:20.342Z
Learning: In Astro-Han/pawwork, `packages/opencode/src/tool/question.ts` is a permanent PawWork carve-out: `Question.Prompt` (defined in `packages/opencode/src/question/index.ts`) remains a Zod schema, NOT an Effect Schema. The `ts-expect-error` boundary in `tool/registry.ts` marks the Zod ↔ Effect Schema seam. The dropped `packages/opencode/test/tool/question.test.ts` covered the old Zod-mixed surface. Schema-bound regression tests for this tool are intentionally deferred until `question/index.ts` migrates to Effect Schema in its own dedicated PR. Do NOT request restoring question tool tests in upstream-sync PRs; flag it only in the PR that migrates `question/index.ts` to Effect Schema.
Applied to files:
packages/ui/src/i18n/en.ts
📚 Learning: 2026-04-22T05:32:29.012Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:32:29.012Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.
Applied to files:
packages/ui/src/i18n/zh.ts
📚 Learning: 2026-04-29T04:23:50.832Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 320
File: packages/app/src/pages/layout/sidebar.css:1-6
Timestamp: 2026-04-29T04:23:50.832Z
Learning: In `packages/app/src/pages/layout/sidebar-project.tsx` (Astro-Han/pawwork), the branch-icon container at line 229 uses `size-5` (20px) with `justify-center items-center` and `data-leading-slot` by design. This is intentional: (1) the 3px padding is symmetric, not asymmetric; (2) the branch row lives inside the project-preview header card — a different visual context from the X=8 left rail (SessionRow/NewSessionItem/New session/Search/Settings). The `data-leading-slot` + 20×20 container combination is the deliberate mechanism to render a 14×14 icon glyph (via sidebar.css) inside a larger touch-target without affecting global Icon size="small". Do NOT flag this size-5 container as misaligned or asymmetric in future reviews.
Applied to files:
packages/ui/src/components/markdown.csspackages/app/src/pages/layout/sidebar-items.tsxpackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-22T09:32:58.310Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Applied to files:
packages/ui/src/components/markdown.csspackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T17:02:35.873Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 198
File: packages/app/src/index.css:95-97
Timestamp: 2026-04-23T17:02:35.873Z
Learning: In the pawwork repository, Stylelint is intentionally not configured (per AGENTS.md, only linting is enforced and the Biome formatter is disabled). When reviewing CSS files, do not raise Stylelint rule-based issues (e.g., `declaration-empty-line-before`) because they are false positives and not enforced by the project toolchain.
Applied to files:
packages/ui/src/components/markdown.csspackages/ui/src/components/message-part.css
📚 Learning: 2026-04-24T05:39:58.329Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:58.329Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Applied to files:
packages/ui/src/components/markdown.csspackages/app/src/pages/layout/sidebar-items.tsxpackages/ui/src/components/message-part.csspackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T15:26:07.250Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/src/pages/layout/sidebar-items.tsx:102-107
Timestamp: 2026-04-23T15:26:07.250Z
Learning: In Astro-Han/pawwork (`packages/app/src/pages/layout/sidebar-items.tsx`), the `indicator()` function in `SessionRow` intentionally renders `props.leadingSlot` (the pin button) only as a fallback when no status indicator (running/permission/error/unseen) is active. When a higher-priority status wins the slot, the pin button is removed from the DOM — this is a deliberate design choice for the merged leading slot (`#150`). The keyboard unpin path is preserved via: (1) focusing the row anchor triggers `group-focus-within` which reveals the dots menu trigger, then Tab → Enter → "Unpin Session"; (2) the context menu (right-click / Shift+F10) exposes "Unpin Session". The "always render + CSS overlay" approach was considered but rejected due to z-index/pointer-events complexity; residual `...` slot behavior is tracked in `#192`. Do NOT flag the absence of the pin button from the DOM when a status is active as an accessibility regression.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-29T04:31:25.068Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 0
File: :0-0
Timestamp: 2026-04-29T04:31:25.068Z
Learning: In `packages/app/src/pages/layout/pawwork-sidebar.tsx` (Astro-Han/pawwork), the session-row pin button container must be sized `w-[14px] h-[14px]` to match the 14×14 leading slot declared in `sidebar-items.tsx`. The Icon glyph inside is auto-shrunk to 14×14 by the `[data-leading-slot]` CSS rule in `sidebar.css`, so all three dimensions (button wrapper, icon content, slot frame) align at 14×14. Do NOT suggest widening the pin button container beyond 14×14 in this context.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-24T05:48:39.493Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/e2e/app/composer-parity.spec.ts:0-0
Timestamp: 2026-04-24T05:48:39.493Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, the Model chip trigger button carries `data-action="prompt-model"` (around line 1187) and the Variant chip trigger button carries `data-action="prompt-model-variant"` (around line 1231), both set via `triggerProps`. These are therefore already captured by any `[data-action]` selector sweep in E2E tests and do not need a separate `[data-component]` query to be included in parity assertions — though unioning both is kept as belt-and-suspenders in `collectBarSet`.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T07:23:23.849Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 180
File: packages/app/src/components/session/session-new-view.tsx:13-18
Timestamp: 2026-04-23T07:23:23.849Z
Learning: In pawwork (Astro-Han/pawwork), prefer using `createStore` instead of multiple `createSignal` calls only when the signals represent **coupled** object state that is updated together (i.e., there is at least one shared batch-update site where the state is changed in the same transaction). If the state fields are **independent** and are mutated by separate handlers (e.g., one handler updates only `selectedSkill` while another updates only `mode`), keep them as individual `createSignal` calls—using `createStore` for truly independent fields adds boilerplate without behavioral benefit.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T15:10:21.635Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 191
File: packages/app/src/components/session/pawwork-skill-meta.ts:38-39
Timestamp: 2026-04-23T15:10:21.635Z
Learning: This repo configures Tailwind v4 with `--color-*: initial`, which effectively breaks standard Tailwind palette utilities (e.g., `text-violet-500` can resolve to no CSS variable and render as a no-op/black). For brand/accent colors that are not backed by semantic design tokens, use inline styles with the exact hex value (e.g., `style={{ color: '#8B5FBF' }}` / `homeIconStyle: { color: '#8B5FBF' }`) and add a short comment explaining that Tailwind palette utilities won’t work due to the `--color-*: initial` setup. Do not suggest replacing these inline hex colors with Tailwind palette classes anywhere in this repo.
Applied to files:
packages/app/src/pages/layout/sidebar-items.tsxpackages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/ui/src/components/message-part.css
📚 Learning: 2026-04-28T04:56:13.350Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 270
File: packages/opencode/src/tool/tool.ts:8-10
Timestamp: 2026-04-28T04:56:13.350Z
Learning: In `packages/opencode/src/tool/tool.ts` (Astro-Han/pawwork, PR `#270`), the `Metadata` interface uses `[key: string]: any` as its index signature. This is upstream-inherited code adopted wholesale via the graft strategy (per `project_upstream_strategy.md`). The fix — tightening to `Record<string, unknown>` and explicitly narrowing framework-owned fields like `truncated` — is intentionally deferred to a follow-up PR or upstream report. Do NOT re-flag the `any` index signature on `Metadata` in PR `#270` or in future upstream-sync PRs that carry the same upstream baseline.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `data-component`, `data-action`, or semantic roles for selectors instead of CSS class names or IDs
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-23T17:02:39.474Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 198
File: packages/ui/src/theme/themes/pawwork.json:32-33
Timestamp: 2026-04-23T17:02:39.474Z
Learning: In Astro-Han/pawwork, `#FF5910` is the locked PawWork brand orange used for `button-primary-base` (and `--accent-brand` in `index.css`, theme token `interactive`/`primary`). The `text-on-interactive-base: `#FFFFFF`` on `button-primary-base: `#FF5910`` combination produces a contrast ratio of ~3.14:1. This intentionally fails WCAG AA normal-text (4.5:1) but passes AA non-text (SC 1.4.11, 3:1) and AA large-text (SC 1.4.3, 3:1). The tradeoff is accepted for brand consistency; do NOT flag this contrast pair as a WCAG violation in future reviews. If a specific surface requires stricter contrast, it should be addressed individually (e.g., larger text, ghost variant, outline button) rather than changing the palette.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/app/src/pages/layout/pawwork-sidebar.tsx
🔇 Additional comments (23)
packages/ui/src/components/message-part.css (9)
433-446: LGTM: message-part title font-size now uses the shared base token.Switching
[data-slot="message-part-title"]tofont-size: var(--font-size-base)keeps it aligned to the 13px baseline while preserving the existing line-height/weight semantics.
759-767: LGTM: permission header title uses hierarchy sizing token.
[data-slot="permission-header-title"]moving tovar(--font-size-hierarchy)is consistent with the PR’s 13px hierarchy intent and matches the theme variable definition.
776-783: LGTM: permission hint uses shared base token.Updating
[data-slot="permission-hint"]tofont-size: var(--font-size-base)ensures consistent 13px body sizing across the component.
802-806: LGTM: permission-patternscodenow uses the shared x-small token (12px).
[data-slot="permission-patterns"] codenow usesvar(--font-size-x-small), which matches the design-system 12px intent for code-in-permission-patterns text.
856-869: LGTM: question header title uses hierarchy sizing token.
[data-slot="question-header-title"]usingvar(--font-size-hierarchy)is consistent with the updated typography scale and the theme token values.
942-950: LGTM: question text uses shared base token.
[data-slot="question-text"]now usesfont-size: var(--font-size-base)(13px), aligning with the unified body sizing direction.
1068-1074: LGTM: option label font-size now uses shared base token.
[data-slot="option-label"]updated tovar(--font-size-base)keeps option labels on the unified 13px baseline.
1076-1085: LGTM: option description font-size now uses shared base token.
[data-slot="option-description"]updated tovar(--font-size-base)maintains consistent typography between label/description and matches the theme variables.
1097-1110: LGTM: custom input text font-size now uses shared base token.
[data-slot="question-custom-input"]usingvar(--font-size-base)keeps the input text aligned with the surrounding question typography.packages/ui/src/components/markdown.css (3)
19-32: LGTM: Headings now consistently use the hierarchy token.Switching
h1–h6tofont-size: var(--font-size-hierarchy)matches the intended typography baseline unification and avoids reintroducing hard-coded sizes.
123-128: LGTM:.shikiuses the x-small token for 12px code blocks.Using
font-size: var(--font-size-x-small)on.shikialigns fenced code styling with the PR’s 12px code size target and with the theme token source.
214-220: LGTM: Inline code explicitly sets the x-small font-size.The added
font-size: var(--font-size-x-small)on:not(pre) > coderemoves ambiguity about whether inline code inherited a larger base size previously.packages/app/src/pages/layout/sidebar-items.tsx (3)
103-123: Leading-slot normalization looks correct.The 14px spinner plus
data-leading-slot+w-[14px] h-[14px]container keeps status/icon rendering aligned with the tightened sidebar hierarchy.
236-253: Right action rail sizing is now consistent.Using
w-5for the action column andIconButton size="small"for archive resolves the previous 20px/24px mismatch and keeps the action lane stable.
297-303: New session row is aligned with SessionRow density/geometry.The
py-1/py-1.5density and 14px leading slot treatment match the session row primitives cleanly.packages/ui/src/i18n/zh.ts (2)
58-58: Status wording update is clear and natural.
探索完毕reads as a proper completion state and pairs well with the in-progress正在探索.
70-75: Context counter phrasing is consistent and specific.The read/search/list strings now use explicit objects and consistent grammatical structure across
.one/.other.packages/app/src/pages/layout/pawwork-sidebar.tsx (3)
130-130: Session row controls are correctly tightened to small geometry.The 14×14 pin wrapper and
IconButton size="small"menu trigger are consistent with the updated sidebar slot/action sizing.Also applies to: 156-157
242-264: Top action rows are cleanly unified.The New/Search rows now share a consistent row primitive (icon slot, typography, spacing, focus-visible behavior), matching the sidebar list style.
342-342: Footer simplification is coherent and accessible.Single-row Settings with
TooltipKeybind, clear labeling, and visible keyboard focus state keeps footer behavior consistent with top rows.Also applies to: 346-361
packages/ui/src/i18n/en.ts (3)
76-77: LGTM:context.readpluralization/casing looks consistent.“Read {{count}} file/files” preserves
{{count}}interpolation and correctly uses singular/plural underone/other. No changes needed based on the provided snippet.
80-81: LGTM:context.listcopy format is correct.“Listed {{count}} directory/directories” preserves interpolation and matches
one/otherpluralization. No changes needed.
1-167: Overall: i18n dictionary structure is sound.
export const dict: Record<string, string>and the provided entries are syntactically correct and consistent with typical i18n-map patterns.
Two small follow-ups to coderabbit's review on #320. - packages/ui/src/i18n/en.ts: switch the search count from the count-led "{{count}} search / {{count}} searches" to past-tense verb-led "Searched {{count}} time / Searched {{count}} times" so it lines up with the existing "Read N file" / "Listed N directory" siblings and mirrors the zh "搜索 N 次" register. - packages/ui/src/components/markdown.css: update the inline "/* 14px */" comment next to font-size: var(--font-size-base) to "/* 13px */", which is the actual value after the hierarchy collapse landed in this PR.
|
@coderabbitai outside-diff finding fixed in 0202473. Updated the inline comment in |
|
Tip For best results, initiate chat on the files or code changes.
🧠 Learnings used |
Summary
Merge of #315 (sidebar geometry) and #316 (typography 14→13 body / 12 code) into one bundled visual PR. Bundling avoids phantom diffs that would appear if the two were landed sequentially against the same sidebar files. After review feedback the PR also collapses the 14px hierarchy size layer down to 13 across primitives, reshapes the sidebar footer, and tightens the composer rail to the new density.
Why
PawWork's positioning is "open-source AI agent for everyone". The 14px / 13px split inherited from opencode was creating a denser / lighter rhythm that did not survive PawWork's actual usage: visual hierarchy in this product is carried by weight, not size, and the size layer mostly created drift between TSX class callsites and the inline rules in primitive component CSS. Sidebar geometry was also drifting (X=8 dot vs X=10 icon vs X=15 PIN, size-6 action containers, mismatched right rail) and needed a single unifying pass.
The end state: a single 13px body across chat, sidebar, settings, dialogs, and the composer; 12px for code (fenced, inline, permission patterns); a sidebar left rail where every leading visual lines up at X=8 and every right-side action lands on the same pr-3 column; and a sidebar footer that is just one Settings row in the same shape as the top New session and Search rows.
Related Issue
Closes #315 and #316. Out of scope: the regression in #319 (manually stopping a turn clears the todo dock) is filed separately and will be fixed in its own PR.
How To Verify
Manual visual sweep performed locally on macOS dev:desktop with the regression script (markdown rendering, list/glob/grep tool cards, diff changes summary, permission dialog, question dialog) — all surfaces match the new 13px / 12px / X=8 / pr-3 grid. Toast surface was not exercised in the manual run because no flow surfaced one; risk is low because the toast title rule was re-pointed from a 14px literal to var(--font-size-hierarchy) which is now 13px, no shape changes.
Verification gates:
grep -rE 'text-14-regular' packages/app/src packages/ui/src --include='*.tsx' --exclude='*.stories.tsx'returns 0 hitsgrep -rE 'text-14-medium' packages/app/src packages/ui/src --include='*.tsx' --exclude='*.stories.tsx'returns 0 hitsgrep -rnE 'font-size:\s*14px' packages/app/src packages/ui/src --include='*.css'returns 0 hitsgrep -rE 'fontSize:\s*14' packages/app/srcstill returns the two xterm callsites (settings.tsx:122 and terminal.tsx:368) — intentionally out of scope, xterm is a separate font systemScreenshots or Recordings
Not attached. The change is broad-but-shallow density work and is most legible by running the dev:desktop build directly.
Checklist
dev, and my PR title and commit messages use Conventional Commits in EnglishSummary by CodeRabbit
Style
Localization
Tests