From 5dfc2a6dc6dab32210368b96af2c2b21475d5f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 9 Oct 2026 12:45:15 -0400 Subject: [PATCH] S3.5: every page on the pixel grid: tokenised type, square corners, pixel charts with HTML axis labels Co-Authored-By: Claude Opus 5.5 --- src/renderer/src/app/AutonomySummary.tsx | 2 +- src/renderer/src/app/Onboarding.tsx | 20 ++--- .../src/app/OnboardingProviderStep.tsx | 6 +- src/renderer/src/app/StaleBanner.tsx | 2 +- src/renderer/src/i18n/zh.ts | 2 +- src/renderer/src/overlays.css | 8 +- src/renderer/src/pages/ApprovalsPage.tsx | 27 +++---- src/renderer/src/pages/ConnectorsPage.tsx | 2 +- src/renderer/src/pages/FilesPage.tsx | 2 +- src/renderer/src/pages/HomePage.tsx | 40 ++++----- src/renderer/src/pages/LibraryPage.tsx | 2 +- src/renderer/src/pages/RunsPage.tsx | 39 +++++---- src/renderer/src/pages/SettingsPage.tsx | 2 +- src/renderer/src/pages/TeamPage.tsx | 14 ++-- src/renderer/src/pages/WorkPage.tsx | 15 ++-- .../src/pages/approvals/ApprovalDialogs.tsx | 10 +-- src/renderer/src/pages/chat/ChatComposer.tsx | 4 +- .../src/pages/chat/ChatConversation.tsx | 14 ++-- .../src/pages/chat/ChatMessageRow.tsx | 26 +++--- src/renderer/src/pages/chat/ChatSidebar.tsx | 8 +- src/renderer/src/pages/chat/ChatTimeline.tsx | 12 +-- .../src/pages/chat/MentionTextarea.tsx | 7 +- .../src/pages/chat/NewSpaceDialog.tsx | 2 +- src/renderer/src/pages/chat/ThreadPanel.tsx | 2 +- .../src/pages/connectors/AccountSyncNote.tsx | 6 +- .../src/pages/connectors/ConnectedList.tsx | 6 +- .../src/pages/connectors/SignInStatus.tsx | 2 +- .../src/pages/connectors/ToolkitCatalog.tsx | 13 ++- src/renderer/src/pages/files/FileVisuals.tsx | 5 +- src/renderer/src/pages/files/FilesGrid.tsx | 8 +- src/renderer/src/pages/files/FilesTable.tsx | 10 +-- .../src/pages/home/DashboardCharts.tsx | 81 +++++++++++-------- src/renderer/src/pages/home/NextUpCard.tsx | 8 +- .../src/pages/library/DocumentsPanel.tsx | 4 +- .../src/pages/library/KnowledgePanel.tsx | 6 +- .../src/pages/office/AgentDetailCard.tsx | 7 +- .../src/pages/runs/RunRequestList.tsx | 4 +- .../src/pages/settings/BillingPanel.tsx | 18 ++--- .../src/pages/settings/CompanyDialog.tsx | 4 +- .../settings/DisconnectProviderDialog.tsx | 2 +- .../src/pages/settings/GoalAuditPanel.tsx | 6 +- .../src/pages/settings/LanguageField.tsx | 6 +- .../settings/ProviderConnectionDialog.tsx | 16 ++-- .../pages/settings/ProviderPolicyTable.tsx | 10 +-- .../pages/settings/ProviderRequestPanel.tsx | 4 +- .../src/pages/settings/ProvidersPanel.tsx | 18 ++--- src/renderer/src/pages/settings/ScopeNote.tsx | 2 +- .../src/pages/settings/WorkspacePanel.tsx | 2 +- .../src/pages/settings/WorkspaceRow.tsx | 6 +- src/renderer/src/pages/work/ListView.tsx | 4 +- src/renderer/src/pages/work/ProgressView.tsx | 15 ++-- .../src/pages/work/ProjectOverview.tsx | 4 +- .../src/pages/work/TaskDetailDialog.tsx | 40 ++++----- .../src/pages/work/TaskHoldNotice.tsx | 4 +- src/renderer/src/shared/DiscardConfirm.tsx | 2 +- src/renderer/src/shared/ErrorText.tsx | 2 +- src/renderer/src/shared/FailureToasts.tsx | 2 +- src/renderer/src/shared/ProviderLogo.tsx | 6 +- src/renderer/src/shared/SearchDialog.tsx | 14 ++-- src/renderer/src/styles.css | 23 +----- src/renderer/src/styles/components.css | 16 +--- src/renderer/src/styles/pixel.css | 2 +- tests/e2e/pixel-grid.ts | 20 +++++ tests/e2e/pixel-pages.spec.ts | 31 +++++++ tests/e2e/render-recovery.spec.ts | 2 + 65 files changed, 362 insertions(+), 347 deletions(-) create mode 100644 tests/e2e/pixel-grid.ts create mode 100644 tests/e2e/pixel-pages.spec.ts diff --git a/src/renderer/src/app/AutonomySummary.tsx b/src/renderer/src/app/AutonomySummary.tsx index 326e66f9..e2fceb38 100644 --- a/src/renderer/src/app/AutonomySummary.tsx +++ b/src/renderer/src/app/AutonomySummary.tsx @@ -8,7 +8,7 @@ const RISK_NAMES = { low: () => t("Low"), medium: () => t("Medium"), high: () => export function AutonomySummary({ mode }: { mode: "ai_ceo" | "ai_cofounder" }) { const gated = { level: RISK_NAMES[COMPANY_PRESETS.taskRiskCeiling](), amount: dollars(COMPANY_PRESETS.hireReviewAboveCents) }; return ( -
+
  • {mode === "ai_ceo" ? t("Approved without you: tasks above {level} risk, tasks that run commands in a workspace, and hires over {amount} a month.", gated) diff --git a/src/renderer/src/app/Onboarding.tsx b/src/renderer/src/app/Onboarding.tsx index dc106d70..b09d2512 100644 --- a/src/renderer/src/app/Onboarding.tsx +++ b/src/renderer/src/app/Onboarding.tsx @@ -168,15 +168,15 @@ export function Onboarding({
    A
    -
    AgentCompany
    +
    AgentCompany
    {cancel && step !== "connectors" && ( )}
    -

    {t("Start your AI company.")}

    -

    +

    {t("Start your AI company.")}

    +

    {t("Stand up the workspace, bind the provider, and queue the first operating loop.")}

    @@ -189,10 +189,10 @@ export function Onboarding({
    {t("Runtime selected")}
    - {selectedProvider.label} + {selectedProvider.label} {draft.providerModel && {draft.providerModel}}
    -
    +
    {t("We never embed the key in the build. Credentials encrypt locally via OS-protected storage.")}
    @@ -201,7 +201,7 @@ export function Onboarding({
    {restored && step !== "connectors" && ( -

    +

    {t("Your progress was restored. Keys are never saved before launch, so enter yours again.")}

    )} @@ -249,7 +249,7 @@ export function Onboarding({ /> )}
    - {error &&
    {error}
    } + {error &&
    {error}
    }
    @@ -284,10 +284,10 @@ function WelcomeStep({ />
    -

    +

    {t("AgentCompany sets up your operating loop in six steps. Bring an API key, pick a runtime for the founding CEO, and we'll wire up the first goal, project, and task.")}

    -
      +
      • {t("Connect one model provider (OpenAI, Anthropic, xAI, or Groq).")}
      • {t("Name the company and the human board chair.")}
      • {t("Pick execution mode and bind the workspace path.")}
      • @@ -333,7 +333,7 @@ function ModeStep({ draft, onChange, onBack, onNext }: StepProps) { return (

        {t("Mode")}

        -

        {t("How autonomous should the CEO be?")}

        +

        {t("How autonomous should the CEO be?")}

        {t("Provider")}

        -

        +

        {t("Pick the runtime for the founding CEO. You can attach more providers in Settings later.")}

        @@ -112,7 +112,7 @@ function ProviderCredentialFields({ return (
        {selected.credentialHelp && ( -
        {selected.credentialHelp}
        +
        {selected.credentialHelp}
        )} {isOAuthLike ? ( )} - {loadError &&
        {loadError}
        } + {loadError &&
        {loadError}
        } {(hasCredential || isCustom) && ( {fetched || showCuratedList ? ( diff --git a/src/renderer/src/app/StaleBanner.tsx b/src/renderer/src/app/StaleBanner.tsx index 7521cb88..efe5e766 100644 --- a/src/renderer/src/app/StaleBanner.tsx +++ b/src/renderer/src/app/StaleBanner.tsx @@ -9,7 +9,7 @@ export function StaleBanner({ failure, syncedAt, onRetry }: { failure: SyncFailu // Outside the app root, so it stays usable while a dialog makes the app inert. return createPortal(
        -
        +
        {t("Not up to date.")}{" "} {syncedAt ? t("Showing data from {time}.", { time: formatTime(syncedAt) }) : null}{" "} {t("Updating failed: {reason} (attempt {count}).", { reason: failure.message, count: failure.attempts })} diff --git a/src/renderer/src/i18n/zh.ts b/src/renderer/src/i18n/zh.ts index 9dc04b83..a59e1981 100644 --- a/src/renderer/src/i18n/zh.ts +++ b/src/renderer/src/i18n/zh.ts @@ -727,7 +727,7 @@ export const zh: Record = { "Copy failed": "复制失败", "Checking account status with Composio…": "正在向 Composio 核对账号状态…", "Account status checked with Composio at {time}.": "已于 {time} 向 Composio 核对账号状态。", - "Account status could not be checked with Composio: {reason}. The statuses below may be out of date.": "无法向 Composio 核对账号状态:{reason}。下方状态可能已过时。", + "Account status could not be checked with Composio, so the statuses below may be out of date. Reason: {reason}": "无法向 Composio 核对账号状态,下方状态可能已过时。原因:{reason}", "{name} ({label}) is connected.": "{name}({label})已连接。", "{name} ({label}) is still not connected. If you finished signing in, check again; otherwise connect again.": "{name}({label})仍未连接。如果已经完成登录,请再次检查;否则请重新连接。", "Check again": "再次检查", diff --git a/src/renderer/src/overlays.css b/src/renderer/src/overlays.css index b8ed57d5..51b8c0cd 100644 --- a/src/renderer/src/overlays.css +++ b/src/renderer/src/overlays.css @@ -24,7 +24,7 @@ to { opacity: 1; transform: none; } } /* Dialog name plate, as a speaker's name above an RPG text box. */ -.modal-title { display: inline-block; margin: 0 0 var(--sp-4); padding: 2px 10px; font-size: var(--fs-title); font-weight: 400; background: var(--btn-primary-bg); color: var(--btn-primary-fg); box-shadow: 2px 2px 0 0 var(--border-strong); } +.modal-title { display: inline-block; margin: 0 0 var(--sp-4); padding: 2px 10px; font-size: var(--fs-title); background: var(--btn-primary-bg); color: var(--btn-primary-fg); box-shadow: 2px 2px 0 0 var(--border-strong); } .modal-actions { display: flex; justify-content: flex-end; gap: var(--sp-2); margin-top: var(--sp-5); } .onboarding-window { @@ -104,7 +104,7 @@ .option-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); } .option-card:active { transform: scale(0.99); } .option-card.active { border-color: var(--text-1); box-shadow: inset 0 0 0 1px var(--text-1); } -.option-card .opt-title { font-size: var(--fs-body); font-weight: 540; } +.option-card .opt-title { font-size: var(--fs-body); } .option-card .opt-sub { font-size: var(--fs-caption); color: var(--text-3); margin-top: var(--sp-1); } .provider-inline-form { display: grid; @@ -142,8 +142,6 @@ .menu-label { padding: var(--sp-2) var(--sp-2) var(--sp-1); font-size: var(--fs-caption); - font-weight: 540; - letter-spacing: 0; color: var(--text-3); } .menu-row { @@ -161,7 +159,7 @@ transition: background var(--motion-fast) var(--ease-out), color var(--motion-fast) var(--ease-out); } .menu-row:hover { background: var(--hover); color: var(--text-1); } -.menu-row.active { background: var(--panel-3); color: var(--text-1); font-weight: 540; } +.menu-row.active { background: var(--panel-3); color: var(--text-1); } .menu-row.danger { color: var(--red); } .menu-row.danger:hover { background: rgba(210, 59, 59, 0.1); color: var(--red); } .menu-row .menu-check { margin-left: auto; color: var(--accent); display: inline-flex; } diff --git a/src/renderer/src/pages/ApprovalsPage.tsx b/src/renderer/src/pages/ApprovalsPage.tsx index 7ba91301..47fab3d3 100644 --- a/src/renderer/src/pages/ApprovalsPage.tsx +++ b/src/renderer/src/pages/ApprovalsPage.tsx @@ -158,7 +158,7 @@ export function ApprovalsPage({ snapshot, actions, navigate, focus }: PageProps) {decided.length > 0 && ( <>
        {t("Recently decided")}
        - {driftError &&
        {t("Could not check approved content: {message}", { message: driftError })}
        } + {driftError &&
        {t("Could not check approved content: {message}", { message: driftError })}
        }
        {shownDecided.map((approval, index) => ( {decided.length > decidedLimit && (
        - {t("Showing {shown} of {total}", { shown: Math.min(decidedLimit, decided.length), total: decided.length })} + {t("Showing {shown} of {total}", { shown: Math.min(decidedLimit, decided.length), total: decided.length })}
        )} @@ -232,7 +232,7 @@ function StatTile({ label, value, tone }: { label: string; value: string; tone?:
        {label}
        {value}
        @@ -266,7 +266,6 @@ function ApprovalRow({
        -
        +
        - {tone.label} + {tone.label} · {approvalKindLabel(approval.kind)}
        -
        {approval.title}
        -
        +
        {approval.title}
        +
        {approval.details}
        - {t("Requested by {requester}", { requester })} · {formatRelative(approval.createdAt)} + {t("Requested by {requester}", { requester })} · {formatRelative(approval.createdAt)}
        @@ -336,7 +335,7 @@ function DecidedRow({ style={{ gap: 12, padding: "12px 18px", - fontSize: 14, + fontSize: "var(--fs-body)", borderBottom: last ? "none" : "1px solid var(--border-soft)", background: focused ? "var(--accent-soft)" : undefined, }} @@ -347,16 +346,16 @@ function DecidedRow({ className="link-button" aria-label={t("View record of {title}", { title: approval.title })} onClick={onView} - style={{ flex: 1, fontWeight: 480, color: "var(--text-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", textAlign: "left" }} + style={{ flex: 1, color: "var(--text-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", textAlign: "left" }} > {approval.title} {approval.sourceEntityType === "task" && approval.sourceEntityId && ( - + )} {drift && {drift === "changed" ? t("Changed since approval") : t("Removed since approval")}} -
        +
        {decidedLabel} · {formatRelative(approval.decidedAt ?? approval.createdAt)}{superseded ? "" : ` · ${decider.split(" ")[0]}`}
        @@ -412,7 +411,7 @@ function BatchSummary({ total, result, onDismiss }: { total: number; result: Bat return (
        - {t("Approved {n} of {total}.", { n: result.approved.length, total })} + {t("Approved {n} of {total}.", { n: result.approved.length, total })}
        {result.failed.map((failure) => ( diff --git a/src/renderer/src/pages/ConnectorsPage.tsx b/src/renderer/src/pages/ConnectorsPage.tsx index 1678b8bf..eaacc9a4 100644 --- a/src/renderer/src/pages/ConnectorsPage.tsx +++ b/src/renderer/src/pages/ConnectorsPage.tsx @@ -46,7 +46,7 @@ export function ConnectorsPage({ snapshot, actions }: PageProps) { return (
        -

        {t("Connectors")}

        +

        {t("Connectors")}

        {connectError && } diff --git a/src/renderer/src/pages/FilesPage.tsx b/src/renderer/src/pages/FilesPage.tsx index d0f89139..1a0e2d45 100644 --- a/src/renderer/src/pages/FilesPage.tsx +++ b/src/renderer/src/pages/FilesPage.tsx @@ -119,7 +119,7 @@ export function FilesPage({ snapshot, actions, focus }: PageProps) {
        {labelFor(section)}
        -
        {t("Modified")} ↓
        +
        {t("Modified")} ↓
        {view === "list" ? ( diff --git a/src/renderer/src/pages/HomePage.tsx b/src/renderer/src/pages/HomePage.tsx index 88814dca..79768e03 100644 --- a/src/renderer/src/pages/HomePage.tsx +++ b/src/renderer/src/pages/HomePage.tsx @@ -115,12 +115,12 @@ function GoalCard({ return (
        {t("Quarter goal")}
        -
        +
        {goal ? goal.title : t("Set the first goal for {organizationName}.", { organizationName })}
        - {progress ? `${percent}%` : "—"} - {progress?.pace ? statusLabel(progress.pace) : t("no target date")} + {progress ? `${percent}%` : "—"} + {progress?.pace ? statusLabel(progress.pace) : t("no target date")} {goal && }
        @@ -152,21 +152,21 @@ function SpendCard({
        {t("Monthly budget")}
        - {money(totalBudgetCents)} - {capCents > 0 ? `/ ${money(capCents)}` : t("allocated")} + {money(totalBudgetCents)} + {capCents > 0 ? `/ ${money(capCents)}` : t("allocated")}
        {connected.slice(0, 3).map((provider) => { const allocatedCents = agents.filter((agent) => agent.modelProviderId === provider.id).reduce((sum, agent) => sum + agent.budgetCents, 0); return ( -
        +
        {provider.label} - {money(allocatedCents)} + {money(allocatedCents)}
        ); })} - {connected.length === 0 && {t("No providers connected")}} + {connected.length === 0 && {t("No providers connected")}}
        ); @@ -188,7 +188,7 @@ function RunActivityCard({
        {t("Agent runs")} - +
        {recent.map((run) => { @@ -198,15 +198,15 @@ function RunActivityCard({
        {agent && }
        -
        {task?.title ?? run.taskId}
        -
        {agent?.name ?? t("Agent")} · {runTriggerLabel(run.trigger)}
        +
        {task?.title ?? run.taskId}
        +
        {agent?.name ?? t("Agent")} · {runTriggerLabel(run.trigger)}
        {runStateLabel(run.state)} - {formatRelative(run.createdAt)} + {formatRelative(run.createdAt)}
        ); })} - {recent.length === 0 &&
        {t("No runs yet")}
        } + {recent.length === 0 &&
        {t("No runs yet")}
        }
        ); @@ -230,7 +230,7 @@ function RecentMessagesCard({
        {t("Recent messages")} - +
        {recent.map((msg) => { @@ -244,16 +244,16 @@ function RecentMessagesCard({
        - {name} - {space && #{space.name}} - {formatTime(msg.createdAt)} + {name} + {space && #{space.name}} + {formatTime(msg.createdAt)}
        -
        {preview}
        +
        {preview}
        ); })} - {recent.length === 0 &&
        {t("No messages yet")}
        } + {recent.length === 0 &&
        {t("No messages yet")}
        }
        ); @@ -313,7 +313,7 @@ function TargetDate({ goal, actions }: { goal: GoalRecord; actions: PageProps["a }; return ( - change(event.target.value)} style={{ height: 26, padding: "0 6px", fontSize: 12 }} /> + change(event.target.value)} style={{ height: 26, padding: "0 6px", fontSize: "var(--fs-body)" }} /> {error && {error}} ); diff --git a/src/renderer/src/pages/LibraryPage.tsx b/src/renderer/src/pages/LibraryPage.tsx index 8ac953dc..889e8cf5 100644 --- a/src/renderer/src/pages/LibraryPage.tsx +++ b/src/renderer/src/pages/LibraryPage.tsx @@ -31,7 +31,7 @@ export function LibraryPage({ snapshot, actions, focus }: PageProps) { return (