From 05ad409624c9ce6fe5ede72a1b264008551ae099 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 14:03:19 -0400 Subject: [PATCH] S3.10: office card shows the person's pixel portrait; provider policy explanations move into column tooltips Co-Authored-By: Claude Opus 5.5 --- src/renderer/src/pages/OfficePage.tsx | 4 ++-- .../src/pages/office/AgentDetailCard.tsx | 7 ++++--- .../src/pages/settings/ProviderPolicyTable.tsx | 16 +++++++--------- tests/e2e/office.spec.ts | 3 +++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/renderer/src/pages/OfficePage.tsx b/src/renderer/src/pages/OfficePage.tsx index e6f0f8a6..869e6e84 100644 --- a/src/renderer/src/pages/OfficePage.tsx +++ b/src/renderer/src/pages/OfficePage.tsx @@ -12,7 +12,7 @@ import { dayPart, season } from "../shared/palette-clock"; import { pixelScale } from "../shared/pixel-scale"; import type { Camera, OfficeAgentInput } from "./office/office-types"; import { officeStatus } from "./office/agent-state"; -import { officeRoster } from "./office/roster"; +import { ceoIdOf, officeRoster } from "./office/roster"; import { t } from "../i18n/i18n"; const FPS_INTERVAL = 1000 / 24; @@ -251,7 +251,7 @@ export function OfficePage({ snapshot }: PageProps) { /> {selectedAgent && (
- setSelectedId(null)} /> + setSelectedId(null)} />
)}
diff --git a/src/renderer/src/pages/office/AgentDetailCard.tsx b/src/renderer/src/pages/office/AgentDetailCard.tsx index 36ebd43c..153ab2cb 100644 --- a/src/renderer/src/pages/office/AgentDetailCard.tsx +++ b/src/renderer/src/pages/office/AgentDetailCard.tsx @@ -1,8 +1,9 @@ -import { Avatar, Icon, IconButton, Pill } from "../../shared/ui"; +import { Icon, IconButton, Pill } from "../../shared/ui"; import { officeStateLabel, roleLevelLabel } from "../../i18n/labels"; import { t } from "../../i18n/i18n"; import { TaskHoldNotice } from "../work/TaskHoldNotice"; import type { OfficeAgentInput } from "./office-types"; +import { PixelAvatar } from "./PixelAvatar"; import type { OfficeState } from "./agent-state"; const STATE_TONE: Record = { @@ -17,7 +18,7 @@ const STATE_TONE: Record = { * Floating profile card for the agent selected on the office floor. The page * owns its placement (it follows the sprite); the card only sizes itself. */ -export function AgentDetailCard({ agent, onClose }: { agent: OfficeAgentInput; onClose: () => void }) { +export function AgentDetailCard({ agent, isCeo, onClose }: { agent: OfficeAgentInput; isCeo: boolean; onClose: () => void }) { return (
- +
{agent.name}
{agent.title}
diff --git a/src/renderer/src/pages/settings/ProviderPolicyTable.tsx b/src/renderer/src/pages/settings/ProviderPolicyTable.tsx index 70e97a57..09e2d554 100644 --- a/src/renderer/src/pages/settings/ProviderPolicyTable.tsx +++ b/src/renderer/src/pages/settings/ProviderPolicyTable.tsx @@ -23,20 +23,18 @@ export function ProviderPolicyTable({ return ( <>
{t("Runtime policy")}
- {/* What each column governs, so a change is made knowing what it reaches. */} -
    -
  • {t("Scope: “Workspace” refuses every call that is not made for a workspace, including agent sessions on tasks without one.")}
  • -
  • {t("Data policy: zero data retention refuses background requests, so it also switches background off.")}
  • -
  • {t("Background: lets requests from the request console run in the provider's background mode.")}
  • -
  • {t("Monthly limit: counts every model call this company makes through the provider in the calendar month (UTC); at the limit, calls are refused and agent work waits until the month resets.")}
  • -
  • {t("Context window: the size new sessions fit prompts, skills, history and tool results into; sessions already running keep theirs.")}
  • -
-
{t("Provider")}
{t("Scope")}
{t("Data policy")}
{t("Background")}
{t("Monthly limit")}
{t("Context window (tokens)")}
{t("Usage")}
+
{t("Provider")}
+
{t("Scope")}
+
{t("Data policy")}
+
{t("Background")}
+
{t("Monthly limit")}
+
{t("Context window (tokens)")}
+
{t("Usage")}
{providers.map((provider, index) => ( { // Different people look different. const looks = await avatars.evaluateAll((canvases) => canvases.map((canvas) => (canvas as HTMLCanvasElement).toDataURL())); expect(new Set(looks).size).toBe(4); + const avaLook = await page.locator(".org-node-card", { hasText: "Ava Patel" }).locator("canvas.pixel-avatar").evaluate((canvas) => (canvas as HTMLCanvasElement).toDataURL()); await page.getByRole("button", { name: "Office", exact: true }).click(); await expect(canvas).toBeVisible(); await page.waitForTimeout(300); @@ -139,6 +140,8 @@ test("the office floor opens agent details on click", async () => { const avaCard = page.getByRole("dialog", { name: "Agent Ava Patel" }); await expect(avaCard).toBeVisible(); await expect(avaCard.getByText("Design", { exact: true })).toBeVisible(); + // The card shows the same pixel face the org chart drew for Ava. + expect(await avaCard.locator("canvas.pixel-avatar").evaluate((canvas) => (canvas as HTMLCanvasElement).toDataURL())).toBe(avaLook); const outDir = resolve("test-results/screenshots"); mkdirSync(outDir, { recursive: true });