Skip to content

Frontend Architecture

This document describes the frontend architecture of Thinkroid-Space for contributors. The frontend lives in Thinkroid_Space/<branch>/thinkroid-space-ui/.

Overview

ConcernTechnology
Build toolVite
UI frameworkReact 18 (JSX, no TypeScript)
Game enginePhaser 3 (pixel-art office scene)
State managementCentralized useState in App.jsx — no Redux or Zustand
Phaser ↔ React bridgemitt event bus (src/events.js)
StylingSingle file: src/index.css
i18nCustom hook useT(), three JSON dictionaries: en, zh, ja
AuthAuthProvider.jsx context, JWT session cookies
API callssrc/api.js fetch wrapper — handles 401 interception
Real-time updatesEventSource SSE stream at /api/events/stream

The Phaser canvas is mounted as a plain <div id="game-container">. All React panels float above it in <div class="ui-overlay">. When any panel is open, the canvas receives pointer-events: none so mouse events do not pass through to the game.


Application Structure

Entry point

src/main.jsx wraps the app in AuthProvider and LanguageProvider, then renders App. AuthProvider handles the full auth gate — it renders LoginPage in place of children when the user is not authenticated.

App.jsx — root component

App.jsx is the single source of truth for all panel visibility state. There is intentionally no state management library: every show* flag is a plain useState boolean at the top level of App.

Panel toggle pattern used throughout:

js
const [showTaskBoard, setShowTaskBoard] = useState(false)
// rendered conditionally:
{showTaskBoard && <TaskBoard onClose={() => setShowTaskBoard(false)} />}

All 20+ panel flags are declared at the top of App:

State varControls
selectedAgentAgentPanel (set by agent:clicked event)
memoryAgentMemoryPanel (set by memory:open event)
showDashboardDashboard
showTaskBoardTaskBoard
showBossChatBossChatPanel
showBulletinBulletinBoard
showMeetingMeetingRoom
showHirePanelHirePanel
showOnboardingOnboardingWizard
showBoardBoard (kanban)
showSpaceSettingsSpaceSettings
showApprovalsApprovalPanel
showChatLogChatLog
showRecordsRecordsPanel
showOuterChannelsOuterChannelsOverlay
showDepartmentsDepartmentPanel
showMessageCenterMessageCenter
showSkillLibrarySkillLibrary
showPromptEditorPromptEditor
showFileManagerFileManagerPanel
showContainerPanelContainerPanel
showCronPanelCronPanel
showUserMgmtUserManagement

Edit mode is a secondary sub-mode with its own state cluster: editMode, editTool, selectedEditRoom, selectedEditItem. Entering edit mode emits editMode:enter to Phaser; exiting emits editMode:exit.

Input blockinghasOverlay is a derived boolean that is true when any panel is open. A useLayoutEffect emits overlay:inputBlock synchronously whenever hasOverlay changes so Phaser can disable its input handler without a frame delay.

HUD layout:

  • HUD — always-visible top bar (space name, settings, edit mode toggle).
  • Left sidebar: collapsible button groups for Work, Team, Comm categories.
  • Right sidebar: collapsible Tools category.
  • Boss chat: fixed bottom-right floating button.
  • Mobile bottom nav: 4-category tab bar with pop-up item menus.
  • Mobile float buttons: Athena, minimap, boss chat, chat overlay (mutually exclusive).
  • StatsBar — always-visible bottom status bar (token usage, morale, etc.).
  • AthenaPanel — AI assistant overlay, always mounted, toggled via athena:toggle event or Cmd+/.
  • ChatOverlay — floating live chat feed (toggled via chatoverlay:toggle).

Phaser Game Scene

Entry: src/game/index.js

createGame(containerId) instantiates Phaser.Game with:

  • Renderer: Phaser.AUTO (WebGL preferred)
  • pixelArt: true
  • scale.mode: Phaser.Scale.RESIZE — canvas fills the viewport
  • Single scene: OfficeScene

The game instance is stored on window.__PHASER_GAME__ for debugging.

OfficeScene (src/game/scenes/OfficeScene.js)

The sole Phaser scene. It async create()s itself by fetching initial data from the API before drawing anything.

What it renders (in z-order):

  1. Outdoor area — grass/autumn tilemap surrounding the office.
  2. Office floor — configurable floor style (wood, carpet, etc.) from TILE_STYLES.
  3. Room boundaries — rooms fetched from /api/rooms, drawn as colored tile overlays.
  4. Room labels — Phaser Text objects for each room name.
  5. Interactive items — office furniture sprites from office-items spritesheet. Clicking a bulletin board, dashboard terminal, or outer-channels board opens the corresponding React panel via a mitt event.
  6. Door animationsdoor1 spritesheet animated on overlap.
  7. Agent sprites — walking characters with 4-direction animations. Clicking an agent sprite emits agent:clicked.
  8. Minimap — second Phaser.Camera rendered bottom-left (desktop) or toggled via floating button (mobile).
  9. Viewport indicator — rectangle drawn on the minimap camera showing current main camera position.

Agent lifecycle:

  • loadAgentsFromAPI() — fetches /api/agents, positions agents at their workspace tiles.
  • createAgents() — creates Phaser sprites and name labels for each agent.
  • startSimulation() — starts the idle movement tick.
  • reloadAgents() — called on office:reload event; destroys existing sprites and re-fetches.

Edit mode: When editMode:enter is received, the scene switches to a tile-editing pointer. Tools (room, wall, item, space-wall) are set via editTool:change. All edit mutations call backend REST endpoints and emit editSuccess / editError back to React for toast display.

Key methods:

MethodPurpose
preload()Loads avatar spritesheets (50+ characters) and tilesets
create()Async scene setup: fetch settings → draw map → load agents
createMap()Draws all tile layers (outdoor, floor, rooms, items)
createAgents()Instantiates agent sprites with walk animations
update()Per-frame: camera drag, minimap viewport indicator sync
loadAgentsFromAPI()Fetches agent roster and workspace positions
reloadAgents()Full agent sprite teardown and re-creation
handleTaskAssigned()Moves agent to workspace on task assignment
handleTaskExecuting()Visual "working" state for agent
handleTaskCompleted()Returns agent to idle state

Event Bus Protocol

src/events.js exports a single mitt() instance shared by both React and Phaser:

js
import mitt from 'mitt'
export const events = mitt()
export default events

All components import this singleton. There is no typed event registry; events are plain strings with an optional payload object.

React → Phaser

EventPayloadPurpose
overlay:inputBlockbooleanEnable/disable Phaser input when a panel is open
editMode:enterSwitch scene to edit mode
editMode:exitExit edit mode
editTool:change{ tool: string | null }Change active edit tool
editItem:startPlace{ registryItem }Begin item placement drag
editItem:cancelPlaceCancel ongoing placement
editItem:radiusChanged{ registryId }Update item interaction radius
editItem:rotateRotate selected item 90°
editItem:deleteDelete selected item
editRoom:refreshForce room tile redraw
editItem:refreshForce item sprite redraw
editRoom:deselectedReact closes the room properties panel
minimap:toggleShow/hide minimap (mobile float button)
minimap:hideHide minimap
chatoverlay:toggleToggle floating chat overlay
athena:toggleOpen/close Athena panel
athena:start-session{ initialMessage, uiContextOverride }Start a new Athena session (e.g., onboarding)
athena:restore-sessionPop the Athena session stack
theme:changedstringNotify Phaser to reload color palette
space:visualRefreshRedraw tiles after office dimension change
ambiance:updatedsettingsUpdate ambient audio settings
office:reloadDestroy and recreate the Phaser game
boss:openChatOpen BossChatPanel (from MessageCenter)

Phaser → React

EventPayloadPurpose
agent:clicked{ name, role, ... }Open AgentPanel for the clicked agent
bulletin:openOpen BulletinBoard panel
dashboard:openOpen Dashboard panel
outerChannels:openOpen OuterChannelsOverlay
board:openOpen Kanban Board
memory:open{ agentName }Open MemoryPanel for an agent
editRoom:selected{ room }Open RoomPropertiesPanel
editRoom:deselectedClose RoomPropertiesPanel
editItem:selected{ item }Highlight item in ItemShopPanel
editItem:deselectedClear item highlight
editSuccess{ message }Show success toast in EditToast
editError{ message }Show error toast in EditToast
minimap:openedNotify React to close chat overlay and mobile menu

SSE → mitt (bridged in App.jsx)

SSE events from /api/events/stream are re-emitted on the mitt bus:

SSE typemitt eventConsumers
agent:moveagent:server-moveOfficeScene (move sprite)
agent:morale-changedagent:morale-changedOfficeScene (update morale bar)
agent:statusagent:statusOfficeScene (update status icon)
agent:thinkingagent:thinkingOfficeScene (update thinking text)
stats:refreshstats:refreshStatsBar
task:created, task:updatedtask:list-changedTaskBoard, AgentTaskPanel
agent:chatagent:chatChatOverlay, ChatLog
bulletin:newbulletin:newBulletinBoard
approval:requestedapproval:requestedApprovalToast, App badge count
approval:resolvedapproval:resolvedApprovalToast, App badge count
meeting:startedmeeting:startedMeetingRoom
meeting:speechmeeting:speechMeetingRoom
meeting:concludedmeeting:concludedMeetingRoom
governance:janitorgovernance:janitorSpaceSettings debug tab
governance:budget_alertgovernance:budget_alertStatsBar
governance:reviewgovernance:reviewDashboard
notification:newnotification:newApp badge count, MessageCenter
cron:executedcron:executedCronPanel
cron:updatedcron:updatedCronPanel

Component Catalog

All .jsx files under src/components/ are listed below. "Key endpoints" lists the primary REST paths the component calls directly.

Core Panels

ComponentPurposeKey API Endpoints
HUD.jsxTop bar: space name, settings button, edit mode toggle, user menuGET /api/settings
StatsBar.jsxAlways-visible bottom bar: token budget, morale, task countsGET /api/stats
Dashboard.jsxAgent overview: status, morale, rest controls; governance review displayGET /api/stats, GET /api/agents, POST /api/agents/:name/rest
AgentPanel.jsxClicked-agent detail panel with tabs for chat, tasks, settings, memoryGET /api/settings/debug
AgentSettings.jsxFull per-agent configuration: models, tools, governance, avatar, personaGET /api/settings/avatars, GET /api/settings/providers, GET /api/tools, GET /api/agents/governance/*, GET /api/agents
AgentTaskPanel.jsxIn-panel task list and task creation for a specific agentGET /api/tasks, GET /api/projects, POST /api/tasks
AgentSkillsPanel.jsxPer-agent skill assignment: toggle built-in tools and MCP skillsGET /api/skills
MemoryPanel.jsxAgent long-term memory viewer and editor (multiple memory types)GET/PATCH /api/agents/:name/memory/:type, GET /api/agents/:name/memory-stats, POST /api/agents/:name/memory/consolidate
SpaceSettings.jsxGlobal settings: providers, office layout, theme, idle loop, governance, debugGET/PUT /api/settings, GET/POST /api/settings/providers, GET /api/settings/debug

Communication

ComponentPurposeKey API Endpoints
BossChatPanel.jsxBoss-only chat: filters to boss + dm:Boss:* channels, "New DM" dropdown with agent avatars, per-channel lazy loading (30/batch)GET /api/messages/all-chats?limit=50 (sidebar), GET /api/messages?channel=...&limit=30 (per-channel), GET /api/conversations/boss-unread
ChatWindow.jsxReusable single-thread chat UI inside AgentPanel, lazy loading (50/batch)GET /api/messages?channel=...&limit=50, POST /api/messages/chat
ChatLog.jsxFull chat history viewer across all agents, unread indicators, per-channel lazy loading (30/batch)GET /api/messages/all-chats?limit=50 (sidebar), GET /api/messages?channel=...&limit=30 (per-channel), GET /api/conversations/boss-unread
ChatOverlay.jsxFloating live feed of recent agent messages, lazy loading (30/batch)GET /api/messages/all-chats?limit=30&before=...
BulletinBoard.jsxRead/post messages on the public bulletin channelGET /api/messages?channel=bulletin, POST /api/messages
MeetingRoom.jsxStart and observe AI multi-agent meetings in real timeGET /api/meeting/list, GET /api/agents, POST /api/meeting/start
MessageCenter.jsxSystem notification inbox with read/unread managementGET /api/notifications, PUT /api/notifications/:id/read, PUT /api/notifications/read-all
OuterChannelsOverlay.jsxConfigure Discord/Telegram integrations and per-agent channel routingGET /api/outer-channels, GET /api/agents, POST/PUT /api/outer-channels, POST /api/outer-channels/test-config
AthenaPanel.jsxAI assistant overlay (Cmd+/): context-aware help, tool execution, multi-turn chatGET /api/athena/prompt-defaults, GET /api/athena/tools, POST /api/athena/chat, POST /api/athena/compact

Admin

ComponentPurposeKey API Endpoints
UserManagement.jsxUser accounts and role/permission administrationGET/POST /api/users, GET /api/users/permissions
DepartmentPanel.jsxDepartment (organization) create/edit and agent roster viewGET /api/organizations, GET /api/agents/roster, POST /api/departments
HirePanel.jsxAgent roster management: view, fire, open onboarding wizardGET /api/agents, GET /api/legacies
PermissionSettings.jsxPer-agent tool permission overrides (embedded inside AgentSettings)GET /api/agents

Task Management

ComponentPurposeKey API Endpoints
TaskBoard.jsxSimple task list with create/filter/assign, driven by SSE task:list-changedGET /api/tasks, GET /api/agents, GET /api/projects, POST /api/tasks
Board.jsxFull kanban board: projects, tasks by status, batch-executeGET /api/projects, GET /api/organizations, GET /api/agents, POST /api/tasks, POST /api/tasks/batch-execute
TaskForm.jsxReusable task creation form (modal)POST /api/tasks
TaskResult.jsxRead-only task result display (embedded in task detail views)
ApprovalPanel.jsxHuman-in-the-loop approval queue: review and decide pending requestsGET /api/approvals, POST /api/approvals/:id/decide
ApprovalToast.jsxAuto-dismiss toast banner when approvals arrive via SSE— (SSE-driven via approval:requested)
RecordsPanel.jsxSearchable archive of meeting conclusions, notes, and reportsGET /api/records

Technical / System Tools

ComponentPurposeKey API Endpoints
FileManagerPanel.jsxBrowse, upload, download, rename, and delete workspace filesGET /api/files/list, GET /api/files/search, POST /api/files/mkdir, POST /api/files/rename, POST /api/files/delete, POST /api/files/upload, GET /api/files/download
ContainerPanel.jsxDocker container status viewer and basic controlsGET /api/containers
CronPanel.jsxScheduled task manager: create cron jobs, view execution historyGET /api/cron, GET /api/agents
SkillLibrary.jsxManage custom skills and MCP servers; configure tool permissionsGET /api/skills, GET /api/skills/mcp-servers, GET /api/skills/builtin-tools, GET/PUT /api/settings/tool-permissions, POST /api/skills, POST /api/skills/mcp-servers
PromptEditor.jsxEdit global system prompt templates and memory block templatesGET/PUT /api/settings/scene-templates-default, GET /api/agents/Manager/scene-templates, GET /api/settings/memory-blocks-default, GET /api/settings/governance-blocks-default
DebugLogPanel.jsxLive debug log viewer with clear capability (embedded in SpaceSettings)DELETE /api/settings/debug/logs

Data / Debug Utilities

ComponentPurposeKey API Endpoints
JsonTree.jsxCollapsible JSON tree renderer (used in debug views and SpaceSettings)
BlockEditor.jsxRich block-based prompt section editor (used in PromptEditor)
OptionCards.jsxReusable card-grid selection UI (used in BossChatPanel and onboarding)

Space Config / Edit Mode

ComponentPurposeKey API Endpoints
EditToolbar.jsxEdit mode tool selector (room, wall, item, space-wall)
EditToast.jsxBrief success/error toast during edit operations (SSE editSuccess/editError)
ItemShopPanel.jsxItem placement picker for edit mode; shows item registryGET /api/items/registry
RoomPropertiesPanel.jsxRoom name, type, style and owner settings for the selected room— (edits via OfficeScene mutations)

Onboarding

ComponentPurposeKey API Endpoints
OnboardingWizard.jsxMulti-step wizard shell that sequences the step components
OnboardingStepBasicInfo.jsxStep 1 — agent name, avatar pickerGET /api/settings/avatars
OnboardingStepDepartment.jsxStep 2 — assign agent to department/orgGET /api/organizations
OnboardingStepPersona.jsxStep 3 — role, specialty, persona description
OnboardingStepSkills.jsxStep 4 — toggle skills and toolsGET /api/skills, GET /api/tools
OnboardingStepModels.jsxStep 5 — select brain and cerebellum AI providersGET /api/settings/providers
OnboardingStepGovernance.jsxStep 6 — governance capability selection
OnboardingStepReview.jsxStep 7 — review and submit: creates the agentGET /api/settings/providers, POST /api/agents
OnboardingStepLegacy.jsxLegacy import path for migrating older agent configsGET /api/legacies

Auth

ComponentPurposeKey API Endpoints
LoginPage.jsxFull-screen login/setup form with optional Cloudflare Turnstile(called via AuthProvider callbacks, not directly)

SSE Integration

The frontend opens a single persistent EventSource connection in the root App.useEffect:

js
const evtSource = new EventSource('/api/events/stream')
evtSource.onmessage = (e) => {
  const data = JSON.parse(e.data)
  // dispatch to mitt bus based on data.type
  events.emit(sseTypeToMittEvent(data.type), data)
}

The connection is closed on component unmount. Browser EventSource handles automatic reconnect; no custom reconnection logic is needed.

SSE event types handled:

agent:move, agent:morale-changed, agent:status, agent:thinking, stats:refresh, task:created, task:updated, agent:chat, bulletin:new, approval:requested, approval:resolved, meeting:started, meeting:speech, meeting:concluded, governance:janitor, governance:budget_alert, governance:review, governance:intervention, notification:new, cron:executed, cron:updated

See the Event Bus Protocol table above for the full SSE → mitt mapping.


CSS Architecture

All styles live in a single file: src/index.css. There are no CSS modules, no styled-components, and no Tailwind.

Structure (roughly top-to-bottom):

  1. CSS custom property definitions on :root (color tokens, spacing, fonts).
  2. Per-theme overrides using [data-theme="dark"] / [data-theme="classic"] etc. on document.documentElement.dataset.theme.
  3. Base resets and layout (#root, #game-container, .ui-overlay).
  4. Component-level blocks in the same order as the component catalog above.
  5. Mobile media query block at the bottom.

Class naming: flat BEM-influenced names, e.g. agent-panel, agent-panel__header, panel-toggle-btn, mobile-nav-tab. No strict BEM enforcement.

Mobile breakpoint: @media (max-width: 768px) — applied to switch from the left/right sidebar button groups to the bottom tab navigation, hide the chat overlay toggle, and adapt panel sizes to full-screen modals.