bmad-visual 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/bin/bmad-visual.js +255 -0
  2. package/package.json +31 -0
  3. package/templates/.agent/workflows/analyst.md +20 -0
  4. package/templates/.agent/workflows/architect.md +20 -0
  5. package/templates/.agent/workflows/bmad-advanced-elicitation.md +5 -0
  6. package/templates/.agent/workflows/bmad-brainstorming.md +5 -0
  7. package/templates/.agent/workflows/bmad-check-implementation-readiness.md +5 -0
  8. package/templates/.agent/workflows/bmad-checkpoint-preview.md +5 -0
  9. package/templates/.agent/workflows/bmad-code-review.md +5 -0
  10. package/templates/.agent/workflows/bmad-correct-course.md +5 -0
  11. package/templates/.agent/workflows/bmad-create-architecture.md +5 -0
  12. package/templates/.agent/workflows/bmad-create-epics-and-stories.md +5 -0
  13. package/templates/.agent/workflows/bmad-create-prd.md +5 -0
  14. package/templates/.agent/workflows/bmad-create-story.md +5 -0
  15. package/templates/.agent/workflows/bmad-create-ux-design.md +5 -0
  16. package/templates/.agent/workflows/bmad-dev-story.md +5 -0
  17. package/templates/.agent/workflows/bmad-distillator.md +5 -0
  18. package/templates/.agent/workflows/bmad-document-project.md +5 -0
  19. package/templates/.agent/workflows/bmad-domain-research.md +5 -0
  20. package/templates/.agent/workflows/bmad-edit-prd.md +5 -0
  21. package/templates/.agent/workflows/bmad-editorial-review-prose.md +5 -0
  22. package/templates/.agent/workflows/bmad-editorial-review-structure.md +5 -0
  23. package/templates/.agent/workflows/bmad-generate-project-context.md +5 -0
  24. package/templates/.agent/workflows/bmad-help-full.md +5 -0
  25. package/templates/.agent/workflows/bmad-help.md +5 -0
  26. package/templates/.agent/workflows/bmad-index-docs.md +5 -0
  27. package/templates/.agent/workflows/bmad-market-research.md +5 -0
  28. package/templates/.agent/workflows/bmad-party-mode.md +5 -0
  29. package/templates/.agent/workflows/bmad-prfaq.md +5 -0
  30. package/templates/.agent/workflows/bmad-product-brief.md +5 -0
  31. package/templates/.agent/workflows/bmad-qa-generate-e2e-tests.md +5 -0
  32. package/templates/.agent/workflows/bmad-quick-dev.md +5 -0
  33. package/templates/.agent/workflows/bmad-retrospective.md +5 -0
  34. package/templates/.agent/workflows/bmad-review-adversarial-general.md +5 -0
  35. package/templates/.agent/workflows/bmad-review-edge-case-hunter.md +5 -0
  36. package/templates/.agent/workflows/bmad-shard-doc.md +5 -0
  37. package/templates/.agent/workflows/bmad-sprint-planning.md +5 -0
  38. package/templates/.agent/workflows/bmad-sprint-status.md +5 -0
  39. package/templates/.agent/workflows/bmad-technical-research.md +5 -0
  40. package/templates/.agent/workflows/bmad-validate-prd.md +5 -0
  41. package/templates/.agent/workflows/dev.md +20 -0
  42. package/templates/.agent/workflows/pm.md +20 -0
  43. package/templates/.agent/workflows/qa.md +20 -0
  44. package/templates/.agent/workflows/sm.md +20 -0
  45. package/templates/.agent/workflows/solo-dev.md +20 -0
  46. package/templates/.agent/workflows/ux.md +20 -0
  47. package/templates/.claude/settings.local.json +50 -0
  48. package/templates/.claude/skills/analyst/SKILL.md +77 -0
  49. package/templates/.claude/skills/architect/SKILL.md +72 -0
  50. package/templates/.claude/skills/bmad-advanced-elicitation/SKILL.md +24 -0
  51. package/templates/.claude/skills/bmad-brainstorming/SKILL.md +26 -0
  52. package/templates/.claude/skills/bmad-check-implementation-readiness/SKILL.md +22 -0
  53. package/templates/.claude/skills/bmad-checkpoint-preview/SKILL.md +17 -0
  54. package/templates/.claude/skills/bmad-code-review/SKILL.md +22 -0
  55. package/templates/.claude/skills/bmad-correct-course/SKILL.md +17 -0
  56. package/templates/.claude/skills/bmad-create-architecture/SKILL.md +22 -0
  57. package/templates/.claude/skills/bmad-create-epics-and-stories/SKILL.md +22 -0
  58. package/templates/.claude/skills/bmad-create-prd/SKILL.md +30 -0
  59. package/templates/.claude/skills/bmad-create-story/SKILL.md +21 -0
  60. package/templates/.claude/skills/bmad-create-ux-design/SKILL.md +23 -0
  61. package/templates/.claude/skills/bmad-dev-story/SKILL.md +27 -0
  62. package/templates/.claude/skills/bmad-distillator/SKILL.md +22 -0
  63. package/templates/.claude/skills/bmad-document-project/SKILL.md +27 -0
  64. package/templates/.claude/skills/bmad-domain-research/SKILL.md +21 -0
  65. package/templates/.claude/skills/bmad-edit-prd/SKILL.md +18 -0
  66. package/templates/.claude/skills/bmad-editorial-review-prose/SKILL.md +21 -0
  67. package/templates/.claude/skills/bmad-editorial-review-structure/SKILL.md +25 -0
  68. package/templates/.claude/skills/bmad-generate-project-context/SKILL.md +20 -0
  69. package/templates/.claude/skills/bmad-help/SKILL.md +63 -0
  70. package/templates/.claude/skills/bmad-help-full/SKILL.md +23 -0
  71. package/templates/.claude/skills/bmad-index-docs/SKILL.md +16 -0
  72. package/templates/.claude/skills/bmad-market-research/SKILL.md +22 -0
  73. package/templates/.claude/skills/bmad-party-mode/SKILL.md +24 -0
  74. package/templates/.claude/skills/bmad-prfaq/SKILL.md +36 -0
  75. package/templates/.claude/skills/bmad-product-brief/SKILL.md +36 -0
  76. package/templates/.claude/skills/bmad-qa-generate-e2e-tests/SKILL.md +21 -0
  77. package/templates/.claude/skills/bmad-quick-dev/SKILL.md +23 -0
  78. package/templates/.claude/skills/bmad-retrospective/SKILL.md +17 -0
  79. package/templates/.claude/skills/bmad-review-adversarial-general/SKILL.md +21 -0
  80. package/templates/.claude/skills/bmad-review-edge-case-hunter/SKILL.md +22 -0
  81. package/templates/.claude/skills/bmad-shard-doc/SKILL.md +17 -0
  82. package/templates/.claude/skills/bmad-sprint-planning/SKILL.md +21 -0
  83. package/templates/.claude/skills/bmad-sprint-status/SKILL.md +17 -0
  84. package/templates/.claude/skills/bmad-technical-research/SKILL.md +21 -0
  85. package/templates/.claude/skills/bmad-validate-prd/SKILL.md +22 -0
  86. package/templates/.claude/skills/dev/SKILL.md +71 -0
  87. package/templates/.claude/skills/pm/SKILL.md +76 -0
  88. package/templates/.claude/skills/qa/SKILL.md +64 -0
  89. package/templates/.claude/skills/sm/SKILL.md +74 -0
  90. package/templates/.claude/skills/solo-dev/SKILL.md +64 -0
  91. package/templates/.claude/skills/ux/SKILL.md +71 -0
  92. package/templates/CLAUDE.md +60 -0
  93. package/templates/dashboard/index.html +15 -0
  94. package/templates/dashboard/package.json +30 -0
  95. package/templates/dashboard/public/assets/avatars/Female1_1wave.png +0 -0
  96. package/templates/dashboard/public/assets/avatars/Female1_2wave.png +0 -0
  97. package/templates/dashboard/public/assets/avatars/Female1_blink.png +0 -0
  98. package/templates/dashboard/public/assets/avatars/Female1_talk.png +0 -0
  99. package/templates/dashboard/public/assets/avatars/Female2_1wave.png +0 -0
  100. package/templates/dashboard/public/assets/avatars/Female2_2wave.png +0 -0
  101. package/templates/dashboard/public/assets/avatars/Female2_blink.png +0 -0
  102. package/templates/dashboard/public/assets/avatars/Female2_talk.png +0 -0
  103. package/templates/dashboard/public/assets/avatars/Female3_blink.png +0 -0
  104. package/templates/dashboard/public/assets/avatars/Female3_talk.png +0 -0
  105. package/templates/dashboard/public/assets/avatars/Female3_wave.png +0 -0
  106. package/templates/dashboard/public/assets/avatars/Female4_blink.png +0 -0
  107. package/templates/dashboard/public/assets/avatars/Female4_talk.png +0 -0
  108. package/templates/dashboard/public/assets/avatars/Female4_wave.png +0 -0
  109. package/templates/dashboard/public/assets/avatars/Female5_blink.png +0 -0
  110. package/templates/dashboard/public/assets/avatars/Female5_talk.png +0 -0
  111. package/templates/dashboard/public/assets/avatars/Female5_wave.png +0 -0
  112. package/templates/dashboard/public/assets/avatars/Female6_blink.png +0 -0
  113. package/templates/dashboard/public/assets/avatars/Female6_talk.png +0 -0
  114. package/templates/dashboard/public/assets/avatars/Female6_wave.png +0 -0
  115. package/templates/dashboard/public/assets/avatars/Male1_1wave.png +0 -0
  116. package/templates/dashboard/public/assets/avatars/Male1_2wave.png +0 -0
  117. package/templates/dashboard/public/assets/avatars/Male1_blink.png +0 -0
  118. package/templates/dashboard/public/assets/avatars/Male1_talk.png +0 -0
  119. package/templates/dashboard/public/assets/avatars/Male2_1wave.png +0 -0
  120. package/templates/dashboard/public/assets/avatars/Male2_2wave.png +0 -0
  121. package/templates/dashboard/public/assets/avatars/Male2_blink.png +0 -0
  122. package/templates/dashboard/public/assets/avatars/Male2_talk.png +0 -0
  123. package/templates/dashboard/public/assets/avatars/Male3_blink.png +0 -0
  124. package/templates/dashboard/public/assets/avatars/Male3_talk.png +0 -0
  125. package/templates/dashboard/public/assets/avatars/Male3_wave.png +0 -0
  126. package/templates/dashboard/public/assets/avatars/Male4_blink.png +0 -0
  127. package/templates/dashboard/public/assets/avatars/Male4_talk.png +0 -0
  128. package/templates/dashboard/public/assets/avatars/Male4_wave.png +0 -0
  129. package/templates/dashboard/public/assets/desks/desktop_set_black_down.png +0 -0
  130. package/templates/dashboard/public/assets/desks/desktop_set_black_down_coding-1.png +0 -0
  131. package/templates/dashboard/public/assets/desks/desktop_set_black_down_coding.png +0 -0
  132. package/templates/dashboard/public/assets/desks/desktop_set_black_up.png +0 -0
  133. package/templates/dashboard/public/assets/desks/desktop_set_white_down.png +0 -0
  134. package/templates/dashboard/public/assets/desks/desktop_set_white_down_coding-1.png +0 -0
  135. package/templates/dashboard/public/assets/desks/desktop_set_white_down_coding.png +0 -0
  136. package/templates/dashboard/public/assets/desks/desktop_set_white_up.png +0 -0
  137. package/templates/dashboard/public/assets/furniture/armchair_tan.png +0 -0
  138. package/templates/dashboard/public/assets/furniture/armchair_tan_down.png +0 -0
  139. package/templates/dashboard/public/assets/furniture/backpack_blue.png +0 -0
  140. package/templates/dashboard/public/assets/furniture/backpack_red.png +0 -0
  141. package/templates/dashboard/public/assets/furniture/blinds.png +0 -0
  142. package/templates/dashboard/public/assets/furniture/blinds_large_closed_white.png +0 -0
  143. package/templates/dashboard/public/assets/furniture/bookshelf.png +0 -0
  144. package/templates/dashboard/public/assets/furniture/bookshelf_purple_tall.png +0 -0
  145. package/templates/dashboard/public/assets/furniture/bulletin_board.png +0 -0
  146. package/templates/dashboard/public/assets/furniture/clock.png +0 -0
  147. package/templates/dashboard/public/assets/furniture/coffee_mug.png +0 -0
  148. package/templates/dashboard/public/assets/furniture/coffee_mug_blue.png +0 -0
  149. package/templates/dashboard/public/assets/furniture/coffee_table.png +0 -0
  150. package/templates/dashboard/public/assets/furniture/coffeepot_right.png +0 -0
  151. package/templates/dashboard/public/assets/furniture/coffeetable_black_horizontal.png +0 -0
  152. package/templates/dashboard/public/assets/furniture/couch.png +0 -0
  153. package/templates/dashboard/public/assets/furniture/couch_tan_down.png +0 -0
  154. package/templates/dashboard/public/assets/furniture/cushion_blue.png +0 -0
  155. package/templates/dashboard/public/assets/furniture/cushion_tan.png +0 -0
  156. package/templates/dashboard/public/assets/furniture/desk_wood.png +0 -0
  157. package/templates/dashboard/public/assets/furniture/fancy_rug.png +0 -0
  158. package/templates/dashboard/public/assets/furniture/fancy_rug_wide.png +0 -0
  159. package/templates/dashboard/public/assets/furniture/flowers1.png +0 -0
  160. package/templates/dashboard/public/assets/furniture/flowers2.png +0 -0
  161. package/templates/dashboard/public/assets/furniture/lamp_tan.png +0 -0
  162. package/templates/dashboard/public/assets/furniture/lantern.png +0 -0
  163. package/templates/dashboard/public/assets/furniture/monstera.png +0 -0
  164. package/templates/dashboard/public/assets/furniture/monstera_small.png +0 -0
  165. package/templates/dashboard/public/assets/furniture/picture_frame.png +0 -0
  166. package/templates/dashboard/public/assets/furniture/plant1.png +0 -0
  167. package/templates/dashboard/public/assets/furniture/plant2.png +0 -0
  168. package/templates/dashboard/public/assets/furniture/plant3.png +0 -0
  169. package/templates/dashboard/public/assets/furniture/plant_poof.png +0 -0
  170. package/templates/dashboard/public/assets/furniture/plant_spindly.png +0 -0
  171. package/templates/dashboard/public/assets/furniture/poster_blue.png +0 -0
  172. package/templates/dashboard/public/assets/furniture/rug.png +0 -0
  173. package/templates/dashboard/public/assets/furniture/succulent_blue.png +0 -0
  174. package/templates/dashboard/public/assets/furniture/succulent_green.png +0 -0
  175. package/templates/dashboard/public/assets/furniture/treasurechest_closed_gold.png +0 -0
  176. package/templates/dashboard/public/assets/furniture/water_cooler_better.png +0 -0
  177. package/templates/dashboard/public/assets/furniture/whiteboard.png +0 -0
  178. package/templates/dashboard/public/assets/furniture/whiteboard_stand_graph.png +0 -0
  179. package/templates/dashboard/public/assets/furniture/window_blinds_open.png +0 -0
  180. package/templates/dashboard/public/assets/logo.png +0 -0
  181. package/templates/dashboard/src/App.tsx +119 -0
  182. package/templates/dashboard/src/components/SquadCard.tsx +47 -0
  183. package/templates/dashboard/src/components/SquadSelector.tsx +61 -0
  184. package/templates/dashboard/src/components/StatusBadge.tsx +32 -0
  185. package/templates/dashboard/src/components/StatusBar.tsx +97 -0
  186. package/templates/dashboard/src/hooks/useSquadSocket.ts +137 -0
  187. package/templates/dashboard/src/lib/formatTime.ts +16 -0
  188. package/templates/dashboard/src/lib/normalizeState.ts +25 -0
  189. package/templates/dashboard/src/main.tsx +14 -0
  190. package/templates/dashboard/src/office/AgentSprite.ts +249 -0
  191. package/templates/dashboard/src/office/OfficeScene.ts +577 -0
  192. package/templates/dashboard/src/office/PhaserGame.tsx +101 -0
  193. package/templates/dashboard/src/office/RoomBuilder.ts +190 -0
  194. package/templates/dashboard/src/office/assetKeys.ts +150 -0
  195. package/templates/dashboard/src/office/palette.ts +32 -0
  196. package/templates/dashboard/src/plugin/squadWatcher.ts +260 -0
  197. package/templates/dashboard/src/store/useSquadStore.ts +63 -0
  198. package/templates/dashboard/src/styles/globals.css +41 -0
  199. package/templates/dashboard/src/types/state.ts +69 -0
  200. package/templates/dashboard/src/vite-env.d.ts +1 -0
  201. package/templates/dashboard/tsconfig.json +24 -0
  202. package/templates/dashboard/tsconfig.tsbuildinfo +1 -0
  203. package/templates/dashboard/vite.config.ts +13 -0
  204. package/templates/squads/.gitkeep +0 -0
  205. package/templates/squads/bmad/agents/analyst.agent.md +87 -0
  206. package/templates/squads/bmad/agents/architect.agent.md +82 -0
  207. package/templates/squads/bmad/agents/developer.agent.md +91 -0
  208. package/templates/squads/bmad/agents/pm.agent.md +86 -0
  209. package/templates/squads/bmad/agents/qa-engineer.agent.md +84 -0
  210. package/templates/squads/bmad/agents/scrum-master.agent.md +84 -0
  211. package/templates/squads/bmad/agents/solo-dev.agent.md +81 -0
  212. package/templates/squads/bmad/agents/tech-writer.agent.md +86 -0
  213. package/templates/squads/bmad/agents/ux-designer.agent.md +81 -0
  214. package/templates/squads/bmad/squad.yaml +70 -0
  215. package/templates/squads/bmad/state.json +108 -0
@@ -0,0 +1,249 @@
1
+ import Phaser from 'phaser';
2
+ import { avatarKeys, DESK_KEYS, FURNITURE_KEYS, type CharacterName } from './assetKeys';
3
+ import { COLORS } from './palette';
4
+ import type { Agent, AgentStatus } from '@/types/state';
5
+
6
+ // Avatar display scale — characters should be prominent at desk
7
+ const AVATAR_SCALE = 0.8;
8
+
9
+ // Status → badge color mapping
10
+ const STATUS_COLORS: Record<AgentStatus, number> = {
11
+ idle: COLORS.statusIdle,
12
+ working: COLORS.statusWorking,
13
+ done: COLORS.statusDone,
14
+ checkpoint: COLORS.statusCheckpoint,
15
+ delivering: COLORS.statusWorking,
16
+ };
17
+
18
+ // Status → display label
19
+ const STATUS_LABELS: Record<AgentStatus, string> = {
20
+ idle: 'Parado',
21
+ working: 'Trabalhando',
22
+ done: 'Concluído',
23
+ checkpoint: 'Aprovação',
24
+ delivering: 'Entregando',
25
+ };
26
+
27
+ export class AgentSprite {
28
+ private scene: Phaser.Scene;
29
+ private deskTable: Phaser.GameObjects.Image;
30
+ private deskShadow: Phaser.GameObjects.Graphics;
31
+ private desk: Phaser.GameObjects.Image;
32
+ private coffeeMug: Phaser.GameObjects.Image;
33
+ private avatar: Phaser.GameObjects.Image;
34
+ private nameText: Phaser.GameObjects.Text;
35
+ private badgeBg: Phaser.GameObjects.Graphics;
36
+ private statusDot: Phaser.GameObjects.Graphics;
37
+ private statusText: Phaser.GameObjects.Text;
38
+ private animTimer?: Phaser.Time.TimerEvent;
39
+ private agent: Agent;
40
+ private characterName: CharacterName;
41
+ private deskVariant: 'black' | 'white';
42
+ private avatarDisplayH: number = 0;
43
+
44
+ constructor(
45
+ scene: Phaser.Scene,
46
+ x: number,
47
+ y: number,
48
+ characterName: CharacterName,
49
+ deskVariant: 'black' | 'white',
50
+ agent: Agent,
51
+ ) {
52
+ this.scene = scene;
53
+ this.agent = agent;
54
+ this.characterName = characterName;
55
+ this.deskVariant = deskVariant;
56
+
57
+ // === VERTICAL LAYOUT (sprites, top to bottom on screen) ===
58
+ // desk_wood: 96x64 @ 1.3x = 125x83px → y-42 to y+42
59
+ // desktop_set: ~48x40 @ 1.3x = 62x52px → y-56 to y-4 (center at y-30)
60
+ // avatar: 48x51 @ 0.8x = 38x41px → y-91 to y-50 (center at y-70)
61
+ //
62
+ // Depth order (low = behind, high = front):
63
+ // avatar → y (seated character, lowest — desk will cover lower body)
64
+ // desk_wood → y+1 (desk surface IN FRONT of avatar → covers avatar's lower half)
65
+ // monitor → y+2 (on desk surface, screen faces viewer)
66
+ // coffee mug → y+3 (foreground item on front desk edge)
67
+ // label → 900/901 (always on top)
68
+ //
69
+ // Result: avatar fully visible above monitor, lower body hidden by desk → seated look
70
+ // =========================================
71
+
72
+ // Avatar — positioned further behind the desk so head/torso is clearly visible
73
+ const avatarKey = this.getAvatarKey(agent.status);
74
+ this.avatar = scene.add.image(x, y - 70, avatarKey)
75
+ .setOrigin(0.5, 0.5)
76
+ .setScale(AVATAR_SCALE)
77
+ .setDepth(y); // LOWEST depth — desk and monitor render in front
78
+ // Lock display height so texture swaps between frames of different pixel dimensions
79
+ // don't cause a visible scale jump (e.g. Male1 blink=56px tall vs talk=51px tall).
80
+ // Width is NOT locked — each frame scales proportionally from this height reference.
81
+ this.avatarDisplayH = this.avatar.displayHeight;
82
+
83
+ // Desk table surface — renders IN FRONT of avatar (covers lower body)
84
+ this.deskTable = scene.add.image(x, y, FURNITURE_KEYS.deskWood)
85
+ .setOrigin(0.5, 0.5)
86
+ .setScale(1.3)
87
+ .setDepth(y + 1);
88
+
89
+ // Monitor — screen-facing (_down orientation), sits on desk surface
90
+ const deskKey = this.getDeskKey(agent.status);
91
+ this.desk = scene.add.image(x, y - 30, deskKey)
92
+ .setOrigin(0.5, 0.5)
93
+ .setScale(1.3)
94
+ .setDepth(y + 2); // On top of desk surface, screen visible to viewer
95
+
96
+ // Coffee mug — right side of desk, away from monitor
97
+ this.coffeeMug = scene.add.image(x + 42, y + 8, 'furniture_coffee_mug')
98
+ .setOrigin(0.5, 1).setScale(1.4).setDepth(y + 3);
99
+
100
+ // Shadow (unused graphics object kept for destroy() compatibility)
101
+ this.deskShadow = scene.add.graphics();
102
+ this.deskShadow.setDepth(y - 1);
103
+
104
+ // Name badge — above avatar head (avatar center y-70, head top ≈ y-91, badge at y-140)
105
+ // badge height = 44px → badge bottom at y-96, leaving 5px gap above avatar top
106
+ const labelY = y - 140;
107
+
108
+ // Background pill behind name + status
109
+ this.badgeBg = scene.add.graphics();
110
+
111
+ // Name text — bold, clean, high contrast
112
+ this.nameText = scene.add.text(x, labelY + 5, agent.name, {
113
+ fontFamily: '"Segoe UI", "Helvetica Neue", Arial, sans-serif',
114
+ fontSize: '16px',
115
+ fontStyle: 'bold',
116
+ color: '#ffffff',
117
+ align: 'center',
118
+ stroke: '#000000',
119
+ strokeThickness: 4,
120
+ resolution: 2,
121
+ }).setOrigin(0.5, 0);
122
+ this.nameText.setDepth(901);
123
+
124
+ // Status dot
125
+ this.statusDot = scene.add.graphics();
126
+
127
+ // Status text — colored with outline
128
+ const statusColor = this.getStatusHexColor(agent.status);
129
+ this.statusText = scene.add.text(x, labelY + 24, STATUS_LABELS[agent.status], {
130
+ fontFamily: '"Segoe UI", "Helvetica Neue", Arial, sans-serif',
131
+ fontSize: '13px',
132
+ fontStyle: 'bold',
133
+ color: statusColor,
134
+ align: 'center',
135
+ stroke: '#000000',
136
+ strokeThickness: 3,
137
+ resolution: 2,
138
+ }).setOrigin(0.5, 0);
139
+ this.statusText.setDepth(901);
140
+
141
+ // Draw background and status dot
142
+ this.drawLabelBackground(x, labelY);
143
+ this.drawStatusDot(x, labelY + 22, agent.status);
144
+
145
+ this.startAnimation(agent.status);
146
+ }
147
+
148
+ private getStatusHexColor(status: AgentStatus): string {
149
+ const num = STATUS_COLORS[status] ?? COLORS.statusIdle;
150
+ return '#' + num.toString(16).padStart(6, '0');
151
+ }
152
+
153
+ private getDeskKey(_status: AgentStatus): string {
154
+ // Always show coding desk — all agents are always working
155
+ return this.deskVariant === 'black' ? DESK_KEYS.blackCoding : DESK_KEYS.whiteCoding;
156
+ }
157
+
158
+ private getAvatarKey(_status: AgentStatus): string {
159
+ // Always start in talk frame — animation will cycle from there
160
+ return avatarKeys(this.characterName).talk;
161
+ }
162
+
163
+ private drawLabelBackground(x: number, labelY: number): void {
164
+ const nameW = Math.max(this.nameText.width, this.statusText.width + 18);
165
+ const bgW = nameW + 20;
166
+ const bgH = 44;
167
+ // Solid dark background with rounded corners
168
+ this.badgeBg.fillStyle(0x0f1629, 0.95);
169
+ this.badgeBg.fillRoundedRect(x - bgW / 2, labelY, bgW, bgH, 5);
170
+ // Subtle border
171
+ this.badgeBg.lineStyle(1, 0x1a2540, 0.6);
172
+ this.badgeBg.strokeRoundedRect(x - bgW / 2, labelY, bgW, bgH, 4);
173
+ this.badgeBg.setDepth(900);
174
+ }
175
+
176
+ private drawStatusDot(x: number, _statusY: number, status: AgentStatus): void {
177
+ const dotColor = STATUS_COLORS[status] ?? COLORS.statusIdle;
178
+ const textW = Math.max(this.statusText.width, 24);
179
+ this.statusDot.fillStyle(dotColor, 1);
180
+ this.statusDot.fillCircle(x - textW / 2 - 5, this.statusText.y + this.statusText.height / 2, 3);
181
+ this.statusDot.setDepth(901);
182
+ }
183
+
184
+ private setAvatarFrame(key: string): void {
185
+ this.avatar.setTexture(key);
186
+ // Scale uniformly so height matches the reference (talk frame) — preserves aspect ratio
187
+ this.avatar.setScale(this.avatarDisplayH / this.avatar.height);
188
+ }
189
+
190
+ private startAnimation(_status: AgentStatus): void {
191
+ // Always run the working animation regardless of status
192
+ const keys = avatarKeys(this.characterName);
193
+ let frame = 0;
194
+ this.animTimer = this.scene.time.addEvent({
195
+ delay: 500,
196
+ loop: true,
197
+ callback: () => {
198
+ frame = (frame + 1) % 2;
199
+ this.setAvatarFrame(frame === 0 ? keys.talk : keys.blink);
200
+ },
201
+ });
202
+ }
203
+
204
+ updateStatus(agent: Agent): void {
205
+ if (this.agent.status === agent.status) return;
206
+ this.agent = agent;
207
+
208
+ this.desk.setTexture(this.getDeskKey(agent.status));
209
+ this.setAvatarFrame(this.getAvatarKey(agent.status));
210
+
211
+ this.animTimer?.destroy();
212
+ this.startAnimation(agent.status);
213
+
214
+ // Update status text and dot
215
+ this.statusText.setText(STATUS_LABELS[agent.status]);
216
+ this.statusText.setColor(this.getStatusHexColor(agent.status));
217
+
218
+ this.statusDot.clear();
219
+ const dotColor = STATUS_COLORS[agent.status] ?? COLORS.statusIdle;
220
+ this.statusDot.fillStyle(dotColor, 1);
221
+ const textW = Math.max(this.statusText.width, 24);
222
+ this.statusDot.fillCircle(
223
+ this.statusText.x - textW / 2 - 5,
224
+ this.statusText.y + this.statusText.height / 2,
225
+ 3,
226
+ );
227
+ }
228
+
229
+ hideAvatar(): void {
230
+ this.avatar.setVisible(false);
231
+ }
232
+
233
+ showAvatar(): void {
234
+ this.avatar.setVisible(true);
235
+ }
236
+
237
+ destroy(): void {
238
+ this.animTimer?.destroy();
239
+ this.deskTable.destroy();
240
+ this.deskShadow.destroy();
241
+ this.desk.destroy();
242
+ this.coffeeMug.destroy();
243
+ this.avatar.destroy();
244
+ this.nameText.destroy();
245
+ this.badgeBg.destroy();
246
+ this.statusDot.destroy();
247
+ this.statusText.destroy();
248
+ }
249
+ }