fix(office-ui): resolve blank/frozen office canvas and add sidebar collapse

Office canvas fixes:
- Lazy-create the Phaser game via ResizeObserver on the first non-zero
  layout instead of creating it inside the display:none office page with
  inline px fallback sizes. Phaser's RESIZE-mode 500ms parent poll has no
  zero guard, so the old path shrank the canvas to 0x0 (blue-black screen)
  and later restored it to a stale wrong size (clipped office).
- On unhide, re-measure with scale.getParentBounds() before scale.refresh();
  plain scale.resize() is clobbered by the stale cached parentSize in
  RESIZE mode.
- Fix whole-game freeze when clicking an office card: camera effects
  resolve ease names via EaseMap, which has no 'Cubic.Out' key, leaving
  effect.ease undefined and killing the RAF loop with a per-frame
  TypeError. Use 'Cubic.easeOut' for cam.pan in panToOffice/resetCameraView.
- Bound GameBridge queues (latest snapshot supersedes, event queue capped)
  since game creation is now deferred until the Office page is first opened.

Sidebar:
- Add a collapse/expand handle on the canvas/sidebar boundary with a 220ms
  grid transition; state persists in localStorage. The canvas follows the
  column change automatically through the ResizeObserver path. Stacked
  (<=1024px) layout collapses the bottom panel and moves the handle to the
  bottom edge.

README:
- Add Simplified Chinese translation (README.zh-CN.md) with a language
  switcher in both files; fix stale TOC entries in the English README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LZH-YS1998
2026-07-07 11:45:11 +08:00
parent 137e382097
commit ee79331d48
12 changed files with 1087 additions and 202 deletions
+17 -1
View File
@@ -459,6 +459,14 @@ export default function App() {
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null)
const [theme, setTheme] = useState<ThemeName>('openopc')
const [showSubagents, setShowSubagents] = useState(true)
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
try { return localStorage.getItem('opc_office_sidebar_collapsed') === '1' } catch { return false }
})
const toggleSidebar = () => setSidebarCollapsed(v => {
const next = !v
try { localStorage.setItem('opc_office_sidebar_collapsed', next ? '1' : '0') } catch { /* private mode */ }
return next
})
const [eventTypeFilter, setEventTypeFilter] = useState('all')
const [activePage, setActivePage] = useState<AppPage>('workspace')
const [swarmAgents, setSwarmAgents] = useState<AgentInfo[]>([])
@@ -2408,13 +2416,21 @@ export default function App() {
)}
{/* Main Grid */}
<main className={`main-grid${activePage !== 'office' ? ' hidden' : ''}`}>
<main className={`main-grid${activePage !== 'office' ? ' hidden' : ''}${sidebarCollapsed ? ' sidebar-collapsed' : ''}`}>
{/* Phaser Game Canvas */}
<section className="canvas-wrap">
<PhaserGame bridge={bridgeRef.current} />
<button className="canvas-float-btn" onClick={() => setShowSubagents((v) => !v)} title={showSubagents ? 'Hide sub-agents' : 'Show sub-agents'}>
{showSubagents ? '👥' : '👤'}
</button>
<button
className="sidebar-collapse-btn"
onClick={toggleSidebar}
title={sidebarCollapsed ? 'Show side panel' : 'Hide side panel'}
aria-label={sidebarCollapsed ? 'Show side panel' : 'Hide side panel'}
>
<span className="collapse-glyph">{sidebarCollapsed ? '' : ''}</span>
</button>
</section>
{/* Sidebar */}