S3.10: office card shows the person's pixel portrait; provider policy explanations move into column tooltips
CI / Typecheck, lint, tests, e2e (push) Failing after 6m37s
CI / Typecheck, lint, tests, e2e (push) Failing after 6m37s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e5a4e87300
commit
05ad409624
4 files changed
+16
-14
No files matched your search
@@ -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 && (
|
||||
<div ref={cardBoxRef} style={{ position: "absolute", left: 0, top: 0, willChange: "transform", visibility: "hidden" }}>
|
||||
<AgentDetailCard agent={selectedAgent} onClose={() => setSelectedId(null)} />
|
||||
<AgentDetailCard agent={selectedAgent} isCeo={selectedAgent.id === ceoIdOf(inputs)} onClose={() => setSelectedId(null)} />
|
||||
</div>
|
||||
)}
|
||||
<div className="office-toolbar" role="toolbar" aria-label={t("Office tools")}>
|
||||
|
||||
@@ -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<OfficeState, "green" | "amber" | "red" | undefined> = {
|
||||
@@ -17,7 +18,7 @@ const STATE_TONE: Record<OfficeState, "green" | "amber" | "red" | undefined> = {
|
||||
* 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 (
|
||||
<div
|
||||
role="dialog"
|
||||
@@ -31,7 +32,7 @@ export function AgentDetailCard({ agent, onClose }: { agent: OfficeAgentInput; o
|
||||
}}
|
||||
>
|
||||
<div className="row" style={{ gap: 10, alignItems: "flex-start" }}>
|
||||
<Avatar name={agent.name} size="lg" bot />
|
||||
<PixelAvatar id={agent.id} isCeo={isCeo} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: "var(--fs-body)", lineHeight: 1.3, wordBreak: "break-word" }}>{agent.name}</div>
|
||||
<div style={{ fontSize: "var(--fs-body)", color: "var(--text-2)", marginTop: 1, wordBreak: "break-word" }}>{agent.title}</div>
|
||||
|
||||
@@ -23,20 +23,18 @@ export function ProviderPolicyTable({
|
||||
return (
|
||||
<>
|
||||
<div className="section-label" style={{ marginBottom: 6 }}>{t("Runtime policy")}</div>
|
||||
{/* What each column governs, so a change is made knowing what it reaches. */}
|
||||
<ul style={{ fontSize: "var(--fs-body)", color: "var(--text-3)", lineHeight: 1.6, margin: "0 0 10px", paddingLeft: 18 }}>
|
||||
<li>{t("Scope: “Workspace” refuses every call that is not made for a workspace, including agent sessions on tasks without one.")}</li>
|
||||
<li>{t("Data policy: zero data retention refuses background requests, so it also switches background off.")}</li>
|
||||
<li>{t("Background: lets requests from the request console run in the provider's background mode.")}</li>
|
||||
<li>{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.")}</li>
|
||||
<li>{t("Context window: the size new sessions fit prompts, skills, history and tool results into; sessions already running keep theirs.")}</li>
|
||||
</ul>
|
||||
<div className="card" style={{ padding: 0, overflowX: "auto", marginBottom: 28 }}>
|
||||
<div
|
||||
className="table-row head"
|
||||
style={{ gridTemplateColumns: POLICY_GRID, minWidth: 940 }}
|
||||
>
|
||||
<div>{t("Provider")}</div><div>{t("Scope")}</div><div>{t("Data policy")}</div><div>{t("Background")}</div><div>{t("Monthly limit")}</div><div>{t("Context window (tokens)")}</div><div>{t("Usage")}</div>
|
||||
<div>{t("Provider")}</div>
|
||||
<div title={t("Scope: “Workspace” refuses every call that is not made for a workspace, including agent sessions on tasks without one.")}>{t("Scope")}</div>
|
||||
<div title={t("Data policy: zero data retention refuses background requests, so it also switches background off.")}>{t("Data policy")}</div>
|
||||
<div title={t("Background: lets requests from the request console run in the provider's background mode.")}>{t("Background")}</div>
|
||||
<div title={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("Monthly limit")}</div>
|
||||
<div title={t("Context window: the size new sessions fit prompts, skills, history and tool results into; sessions already running keep theirs.")}>{t("Context window (tokens)")}</div>
|
||||
<div>{t("Usage")}</div>
|
||||
</div>
|
||||
{providers.map((provider, index) => (
|
||||
<ProviderPolicyRow
|
||||
|
||||
@@ -128,6 +128,7 @@ test("the office floor opens agent details on click", async () => {
|
||||
// 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 });
|
||||
|
||||
Reference in new issue
Block a user