Files
claude-office/src/components/HeaderBar.tsx
T
JianMiauandClaude Fable 5 1dc4920b32 RWD:直版改直向堆疊讓辦公室區域最大化
摘要:
直版/窄螢幕時辦公室改為滿寬顯示並鎖定 1200:700 比例,側邊面板移到下方;
另新增面板收合按鈕,桌面版也能全畫面看辦公室。

根本原因:
原本任何螢幕都是「辦公室 + 300px 側欄」左右排,SVG 以 meet 等比縮放,
直版視窗寬度受限時上下大量留白,辦公室被壓得很小。

影響:
直版(手機/平板)辦公室寬度從約一半提升到 100%;
窄橫版維持左右排但側欄縮為 232px;桌面版行為不變,多一顆 ⊞ 收合鈕。

修法:
- styles.css:orientation: portrait 時 main-row 改 column,
  floorplan-wrap 用 aspect-ratio 1200/7 00 撐滿寬度,不再靠 flex 留白;
  ≤900px 壓縮 header(隱藏圖例/副標、按鈕縮小、允許換行)
- store 新增 panelOpen/togglePanel,App 據此條件渲染 SidePanel

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 13:40:38 +08:00

80 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { STATUS_COLORS, STATUS_LABELS } from "@/lib/constants";
import { useOfficeStore } from "@/store/office-store";
import { getDirector, setMode } from "@/sim/runtime";
import type { AgentStatus } from "@/lib/types";
const LEGEND: AgentStatus[] = ["idle", "thinking", "tool_calling", "speaking", "spawning", "error"];
export function HeaderBar() {
const paused = useOfficeStore((s) => s.paused);
const setPaused = useOfficeStore((s) => s.setPaused);
const speed = useOfficeStore((s) => s.speed);
const setSpeed = useOfficeStore((s) => s.setSpeed);
const theme = useOfficeStore((s) => s.theme);
const setTheme = useOfficeStore((s) => s.setTheme);
const mode = useOfficeStore((s) => s.mode);
const agentCount = useOfficeStore((s) => s.agents.size);
const panelOpen = useOfficeStore((s) => s.panelOpen);
const isLive = mode === "live";
return (
<header className="header">
<div className="header-brand">
<span className="header-mark"></span>
<div>
<h1>Claude Office</h1>
<p>multi-agent 虛擬辦公室 · {isLive ? "實況模式" : "模擬模式"}</p>
</div>
</div>
<div className="header-legend">
{LEGEND.map((s) => (
<span key={s} className="legend-chip">
<span className="legend-dot" style={{ backgroundColor: STATUS_COLORS[s] }} />
{STATUS_LABELS[s]}
</span>
))}
</div>
<div className="header-controls">
<span className="agent-count">{agentCount} agents</span>
<button
className={`btn mode-btn ${isLive ? "live-on" : ""}`}
onClick={() => setMode(isLive ? "sim" : "live")}
title={isLive ? "切回劇本模擬" : "連上真實 Claude Code 活動 (需 npm run watch)"}
>
{isLive ? "● LIVE" : "▶ 模擬"}
</button>
{!isLive && (
<>
<button className="btn" onClick={() => getDirector().hireAgent()} title="招募一位 agent">
+ Agent
</button>
<button
className="btn"
onClick={() => setSpeed(speed >= 4 ? 1 : speed * 2)}
title="模擬速度"
>
{speed}×
</button>
</>
)}
<button className="btn" onClick={() => setPaused(!paused)} title={paused ? "繼續" : "暫停"}>
{paused ? "▶" : "⏸"}
</button>
<button className="btn" onClick={() => setTheme(theme === "light" ? "dark" : "light")} title="切換主題">
{theme === "light" ? "🌙" : "☀️"}
</button>
<button
className="btn"
onClick={() => useOfficeStore.getState().togglePanel()}
title={panelOpen ? "收合面板,放大辦公室" : "展開面板"}
>
{panelOpen ? "⊞" : "⊟"}
</button>
</div>
</header>
);
}