agmon 0.2.0 → 0.2.2

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 (294) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/fallback-build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +8 -8
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  11. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +15 -13
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +15 -13
  15. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +8 -6
  16. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  17. package/.next/server/app/api/media/route.js.nft.json +1 -1
  18. package/.next/server/app/api/reports/tokens/route.js.nft.json +1 -1
  19. package/.next/server/app/api/sessions/[id]/transcript/route.js +1 -1
  20. package/.next/server/app/api/sessions/[id]/transcript/route.js.nft.json +1 -1
  21. package/.next/server/app/api/traces/stream/route.js +1 -1
  22. package/.next/server/app/api/traces/stream/route.js.nft.json +1 -1
  23. package/.next/server/app/api/version/route.js.nft.json +1 -1
  24. package/.next/server/app/api/workspace/file-content/route.js.nft.json +1 -1
  25. package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
  26. package/.next/server/app/chat/page.js.nft.json +1 -1
  27. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  28. package/.next/server/app/chat.html +1 -1
  29. package/.next/server/app/chat.rsc +20 -19
  30. package/.next/server/app/chat.segments/_full.segment.rsc +20 -19
  31. package/.next/server/app/chat.segments/_tree.segment.rsc +2 -2
  32. package/.next/server/app/chat.segments/chat/__PAGE__.segment.rsc +22 -20
  33. package/.next/server/app/index.html +1 -1
  34. package/.next/server/app/index.rsc +20 -18
  35. package/.next/server/app/index.segments/__PAGE__.segment.rsc +10 -9
  36. package/.next/server/app/index.segments/_full.segment.rsc +20 -18
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/page.js.nft.json +1 -1
  39. package/.next/server/app/page_client-reference-manifest.js +1 -1
  40. package/.next/server/app/reports/page.js.nft.json +1 -1
  41. package/.next/server/app/reports/page_client-reference-manifest.js +1 -1
  42. package/.next/server/app/reports.html +1 -1
  43. package/.next/server/app/reports.rsc +4 -4
  44. package/.next/server/app/reports.segments/_full.segment.rsc +4 -4
  45. package/.next/server/app/reports.segments/_tree.segment.rsc +2 -2
  46. package/.next/server/app/reports.segments/reports/__PAGE__.segment.rsc +6 -5
  47. package/.next/server/app/store/page.js.nft.json +1 -1
  48. package/.next/server/app/store/page_client-reference-manifest.js +1 -1
  49. package/.next/server/app/store.html +1 -1
  50. package/.next/server/app/store.rsc +5 -5
  51. package/.next/server/app/store.segments/_full.segment.rsc +5 -5
  52. package/.next/server/app/store.segments/_tree.segment.rsc +2 -2
  53. package/.next/server/app/store.segments/store/__PAGE__.segment.rsc +7 -6
  54. package/.next/server/chunks/[root-of-the-server]__0dw97_pahi8uf._.js +3 -0
  55. package/.next/server/chunks/[root-of-the-server]__0dw97_pahi8uf._.js.map +1 -0
  56. package/.next/server/chunks/_0ao-_t4jkb46h._.js +2 -2
  57. package/.next/server/chunks/_0ao-_t4jkb46h._.js.map +1 -1
  58. package/.next/server/chunks/_1-8ipqazoyrrk._.js +1 -1
  59. package/.next/server/chunks/_1-8ipqazoyrrk._.js.map +1 -1
  60. package/.next/server/chunks/ssr/[root-of-the-server]__0i2rbwbkxbdog._.js +3 -0
  61. package/.next/server/chunks/ssr/[root-of-the-server]__0i2rbwbkxbdog._.js.map +1 -0
  62. package/.next/server/chunks/ssr/[root-of-the-server]__0qzscpxebh28_._.js +3 -0
  63. package/.next/server/chunks/ssr/[root-of-the-server]__0qzscpxebh28_._.js.map +1 -0
  64. package/.next/server/chunks/ssr/[root-of-the-server]__1jwplflwix8wi._.js +427 -0
  65. package/.next/server/chunks/ssr/[root-of-the-server]__1jwplflwix8wi._.js.map +1 -0
  66. package/.next/server/chunks/ssr/[root-of-the-server]__1kt7h7bz59mdn._.js +1 -1
  67. package/.next/server/chunks/ssr/[root-of-the-server]__1kt7h7bz59mdn._.js.map +1 -1
  68. package/.next/server/chunks/ssr/[root-of-the-server]__1otsvjy81s8my._.js +427 -0
  69. package/.next/server/chunks/ssr/[root-of-the-server]__1otsvjy81s8my._.js.map +1 -0
  70. package/.next/server/chunks/ssr/_0e3frtjrk-wi9._.js +3 -0
  71. package/.next/server/chunks/ssr/_0e3frtjrk-wi9._.js.map +1 -0
  72. package/.next/server/chunks/ssr/_0h29jdf4relbq._.js +3 -0
  73. package/.next/server/chunks/ssr/_0h29jdf4relbq._.js.map +1 -0
  74. package/.next/server/chunks/ssr/_0zel-n0h--cnl._.js +3 -0
  75. package/.next/server/chunks/ssr/_0zel-n0h--cnl._.js.map +1 -0
  76. package/.next/server/chunks/ssr/_1487-zi23c5eb._.js +1 -1
  77. package/.next/server/chunks/ssr/_1487-zi23c5eb._.js.map +1 -1
  78. package/.next/server/chunks/ssr/_1vgwoe-shb9z7._.js +3 -0
  79. package/.next/server/chunks/ssr/_1vgwoe-shb9z7._.js.map +1 -0
  80. package/.next/server/chunks/ssr/_1zm4m-s8k7oe3._.js +3 -0
  81. package/.next/server/chunks/ssr/_1zm4m-s8k7oe3._.js.map +1 -0
  82. package/.next/server/chunks/ssr/_202tntue-jj6s._.js +3 -0
  83. package/.next/server/chunks/ssr/_202tntue-jj6s._.js.map +1 -0
  84. package/.next/server/chunks/ssr/{node_modules_1wlwrgnt65zus._.js → node_modules_00ebs67b4u61w._.js} +1 -1
  85. package/.next/server/chunks/ssr/{node_modules_0f773uqgzj7y7._.js → node_modules_01avo0bde3tpo._.js} +1 -1
  86. package/.next/server/chunks/ssr/{node_modules_0z3elum1675ve._.js → node_modules_01l0208e5kqic._.js} +1 -1
  87. package/.next/server/chunks/ssr/{node_modules_1c8i3jrmo4393._.js → node_modules_033x29yqle_sj._.js} +1 -1
  88. package/.next/server/chunks/ssr/{node_modules_1kr1c-ncf8q9t._.js → node_modules_03wbg__aslvui._.js} +1 -1
  89. package/.next/server/chunks/ssr/{node_modules_02jwra-d9iqim._.js → node_modules_06mgkab1v5iox._.js} +2 -2
  90. package/.next/server/chunks/ssr/{node_modules_0z7rdbaf13024._.js → node_modules_08-bqkrz7sh0l._.js} +1 -1
  91. package/.next/server/chunks/ssr/{node_modules_13nsmuhey_ti_._.js → node_modules_089w9jm7s2qa3._.js} +2 -2
  92. package/.next/server/chunks/ssr/{node_modules_0m3ihe65818y_._.js → node_modules_08q6a-f2itxyo._.js} +1 -1
  93. package/.next/server/chunks/ssr/{node_modules_0__m9_odts7_e._.js → node_modules_0cy47-s63xjzh._.js} +2 -2
  94. package/.next/server/chunks/ssr/{node_modules_0b8ndnuql44z1._.js → node_modules_0czh0jcnsm5e6._.js} +2 -2
  95. package/.next/server/chunks/ssr/{node_modules_0fs8mckuwk2tj._.js → node_modules_0e6qoh__e4a69._.js} +1 -1
  96. package/.next/server/chunks/ssr/{node_modules_05w7qhtrzvnk6._.js → node_modules_0fv321j1nslkb._.js} +1 -1
  97. package/.next/server/chunks/ssr/{node_modules_061uaylfjwqk7._.js → node_modules_0g-wb7h-hsdwc._.js} +1 -1
  98. package/.next/server/chunks/ssr/{node_modules_1ll-58c1c-qab._.js → node_modules_0hb0q0epwvp--._.js} +1 -1
  99. package/.next/server/chunks/ssr/{node_modules_0dq0dsblt-q0g._.js → node_modules_0l-y0kqtu_r3o._.js} +1 -1
  100. package/.next/server/chunks/ssr/{node_modules_1zs2nou5uogby._.js → node_modules_0l814qpw677vm._.js} +1 -1
  101. package/.next/server/chunks/ssr/{node_modules_1xyxigbqzb8vs._.js → node_modules_0o63z1gd17rhb._.js} +1 -1
  102. package/.next/server/chunks/ssr/{node_modules_1-sz_-qddwilq._.js → node_modules_0oz_zzlh8exwx._.js} +1 -1
  103. package/.next/server/chunks/ssr/{node_modules_0oukyh0iq21ow._.js → node_modules_0so2agzqd9p8l._.js} +2 -2
  104. package/.next/server/chunks/ssr/{node_modules_12qw2ucebdvol._.js → node_modules_0w2w61b8bh769._.js} +1 -1
  105. package/.next/server/chunks/ssr/{node_modules_1dff7b_kvngr-._.js → node_modules_0wd6szix_zku7._.js} +2 -2
  106. package/.next/server/chunks/ssr/{node_modules_0fd1swhk15cx3._.js → node_modules_0wp7omf0xblv8._.js} +1 -1
  107. package/.next/server/chunks/ssr/{node_modules_0nde84kzuwv2n._.js → node_modules_0yayrqhzfk8wy._.js} +1 -1
  108. package/.next/server/chunks/ssr/{node_modules_0f4xh33v3iltc._.js → node_modules_0zb0i39mnjdf3._.js} +1 -1
  109. package/.next/server/chunks/ssr/{node_modules_1iovyom0-y7o_._.js → node_modules_10c51o2qv6mbi._.js} +1 -1
  110. package/.next/server/chunks/ssr/{node_modules_02nv3yff2pqh8._.js → node_modules_10g1mmrf63d8a._.js} +2 -2
  111. package/.next/server/chunks/ssr/{node_modules_0-6sb1gkbjo93._.js → node_modules_152txg4c7ose3._.js} +2 -2
  112. package/.next/server/chunks/ssr/{node_modules_12qn4bepdou5v._.js → node_modules_185fuy4cv7loa._.js} +2 -2
  113. package/.next/server/chunks/ssr/{node_modules_1-iyhsn_1p9nx._.js → node_modules_19vqq7rvf-_ar._.js} +2 -2
  114. package/.next/server/chunks/ssr/{node_modules_0hqa9oxz17hip._.js → node_modules_1hd7j-tdb8drq._.js} +1 -1
  115. package/.next/server/chunks/ssr/{node_modules_1rlnehdn16ay-._.js → node_modules_1hu83gui7_nj2._.js} +1 -1
  116. package/.next/server/chunks/ssr/{node_modules_07maxxr-t28ql._.js → node_modules_1i3x2csuq_-jt._.js} +1 -1
  117. package/.next/server/chunks/ssr/{node_modules_0kovk4b6czb22._.js → node_modules_1j8hpoiygrs5e._.js} +1 -1
  118. package/.next/server/chunks/ssr/{node_modules_0zc49fdcopqu9._.js → node_modules_1l2g7btuh3e4s._.js} +2 -2
  119. package/.next/server/chunks/ssr/{node_modules_0t4h1rucl35hl._.js → node_modules_1n4kig4mj6n4s._.js} +2 -2
  120. package/.next/server/chunks/ssr/{node_modules_0vn0kjn_84lmn._.js → node_modules_1nz3ckv6tu_66._.js} +1 -1
  121. package/.next/server/chunks/ssr/{node_modules_0-nvg0c4pgfb8._.js → node_modules_1qnypguag6s_l._.js} +1 -1
  122. package/.next/server/chunks/ssr/{node_modules_1v4lexrfhnlhs._.js → node_modules_1r6w-jlme6d10._.js} +1 -1
  123. package/.next/server/chunks/ssr/{node_modules_1f4geuvkvlxj1._.js → node_modules_1s8oxeqa5_i3k._.js} +1 -1
  124. package/.next/server/chunks/ssr/{node_modules_1216007ogd5om._.js → node_modules_1tskcaktx6ods._.js} +1 -1
  125. package/.next/server/chunks/ssr/{node_modules_02vtgweceovke._.js → node_modules_1xz1dstlo5ri-._.js} +1 -1
  126. package/.next/server/chunks/ssr/{node_modules_1kv6lsckqs-m0._.js → node_modules_1zii7uk468cay._.js} +2 -2
  127. package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0bmuudv_6eypr._.js → node_modules_katex_dist_katex_mjs_067omoj5szuys._.js} +1 -1
  128. package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0qyy6pq9uzpk0._.js → node_modules_katex_dist_katex_mjs_1604s1ky8db8u._.js} +1 -1
  129. package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_1d8fezw1qsiob._.js → node_modules_lucide-react_dist_esm_0-5bcc79665lq._.js} +2 -2
  130. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0-5bcc79665lq._.js.map +1 -0
  131. package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_0pezg1352q13j._.js → node_modules_lucide-react_dist_esm_0axoa1eg51x1-._.js} +2 -2
  132. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0axoa1eg51x1-._.js.map +1 -0
  133. package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_131i-tjzse3ye._.js → node_modules_lucide-react_dist_esm_18ei1-na3kusb._.js} +2 -2
  134. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_18ei1-na3kusb._.js.map +1 -0
  135. package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_1to-9cv1ysvs6._.js → node_modules_lucide-react_dist_esm_1tx493x2_auzs._.js} +2 -2
  136. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1tx493x2_auzs._.js.map +1 -0
  137. package/.next/server/chunks/ssr/node_modules_mermaid_dist_mermaid_core_mjs_06it7ktosm1dr._.js +3 -0
  138. package/.next/server/chunks/ssr/node_modules_mermaid_dist_mermaid_core_mjs_06it7ktosm1dr._.js.map +1 -0
  139. package/.next/server/chunks/ssr/src_app_page_1vqfk0-j72fp6.js +1 -1
  140. package/.next/server/chunks/ssr/src_app_page_1vqfk0-j72fp6.js.map +1 -1
  141. package/.next/server/chunks/ssr/src_components_reports_TokenReportView_jsx_1g3p_enbbsadt._.js +1 -1
  142. package/.next/server/chunks/ssr/src_components_reports_TokenReportView_jsx_1g3p_enbbsadt._.js.map +1 -1
  143. package/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/server/pages/404.html +1 -1
  145. package/.next/server/pages/500.html +1 -1
  146. package/.next/server/preview-props.json +3 -3
  147. package/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/server/server-reference-manifest.json +1 -1
  149. package/.next/static/chunks/0jo6fy2c_gmqn.js +1 -0
  150. package/.next/static/chunks/0r9apkv3exqv-.js +1 -0
  151. package/.next/static/chunks/1n_lb7wmn81jj.js +1 -0
  152. package/.next/static/chunks/2-w116ewoshm1.js +1 -0
  153. package/.next/static/chunks/23uup1e8841av.js +1 -0
  154. package/.next/static/chunks/2q9cwykv6ox1t.css +1 -0
  155. package/.next/static/chunks/2rx7xvi55ohln.js +1 -0
  156. package/.next/static/chunks/2zf44j8sjo0w0.js +425 -0
  157. package/.next/static/chunks/3rtpnpqd981z9.js +1 -0
  158. package/.next/trace +1 -1
  159. package/.next/trace-build +1 -1
  160. package/bin/cli.js +12 -0
  161. package/package.json +1 -1
  162. package/src/app/api/traces/stream/route.js +41 -5
  163. package/src/app/chat/page.js +14 -14
  164. package/src/app/globals.css +128 -0
  165. package/src/app/layout.js +8 -2
  166. package/src/app/page.js +18 -2
  167. package/src/components/chat/ChatMessageItem.js +12 -12
  168. package/src/components/chat/CodeBlock.js +5 -5
  169. package/src/components/chat/DiffViewer.js +27 -27
  170. package/src/components/chat/MarkdownRenderer.js +19 -19
  171. package/src/components/chat/SessionChatView.js +42 -42
  172. package/src/components/chat/SessionSearch.js +8 -8
  173. package/src/components/chat/ThinkingAccordion.js +3 -3
  174. package/src/components/chat/ToolCallCard.js +6 -6
  175. package/src/components/chat/ToolOutputView.js +19 -19
  176. package/src/components/factory/office-canvas.js +165 -3
  177. package/src/components/factory/scene/characters.js +267 -32
  178. package/src/components/layout/BottomPanel.jsx +6 -6
  179. package/src/components/layout/CommandPalette.jsx +12 -0
  180. package/src/components/layout/EditorTabs.jsx +17 -0
  181. package/src/components/layout/LayoutToggles.jsx +31 -2
  182. package/src/components/layout/LeftSidebar.jsx +88 -6
  183. package/src/components/layout/RightSidebar.jsx +8 -8
  184. package/src/components/layout/StatusBar.jsx +1 -1
  185. package/src/components/layout/TitleBar.jsx +76 -2
  186. package/src/components/layout/VSCodeWorkbench.jsx +99 -14
  187. package/src/components/store/Item3DRenderer.jsx +602 -48
  188. package/src/components/store/LiveFittingRoom.jsx +55 -791
  189. package/src/components/store/LiveReactAgent.jsx +1637 -0
  190. package/src/components/store/SupporterStoreModal.jsx +3 -2
  191. package/src/components/store/SupporterStoreView.jsx +94 -34
  192. package/src/components/theme/ThemeSettingsModal.jsx +40 -40
  193. package/src/components/ui/Button.jsx +4 -4
  194. package/src/components/ui/Input.jsx +1 -1
  195. package/src/components/ui/Modal.jsx +6 -6
  196. package/src/components/ui/Select.jsx +3 -3
  197. package/src/lib/catalog/auras.js +226 -1199
  198. package/src/lib/catalog/index.js +60 -1
  199. package/src/lib/catalog/pets.js +291 -2398
  200. package/src/lib/catalog/props.js +488 -4199
  201. package/src/lib/catalog/skins.js +773 -3249
  202. package/src/lib/catalog/trophies.js +226 -1199
  203. package/src/lib/mockTraces.js +28 -26
  204. package/src/lib/parsers/codexParser.js +373 -0
  205. package/src/lib/parsers/transcriptParser.js +35 -3
  206. package/src/lib/supporterStore.js +1 -1
  207. package/src/lib/traceContract.js +1 -1
  208. package/src/lib/useFactoryTraces.js +70 -5
  209. package/src/lib/watchers/antigravityWatcher.js +15 -1
  210. package/src/lib/watchers/claudeWatcher.js +86 -70
  211. package/src/lib/watchers/codexWatcher.js +482 -0
  212. package/src/lib/watchers/watcherManager.js +46 -30
  213. package/.next/server/chunks/src_lib_watchers_antigravityWatcher_0w1-atg39y__p.js +0 -3
  214. package/.next/server/chunks/src_lib_watchers_antigravityWatcher_0w1-atg39y__p.js.map +0 -1
  215. package/.next/server/chunks/ssr/[root-of-the-server]__19s2v5rryc14o._.js +0 -3
  216. package/.next/server/chunks/ssr/[root-of-the-server]__19s2v5rryc14o._.js.map +0 -1
  217. package/.next/server/chunks/ssr/[root-of-the-server]__1_jerxrctq6ev._.js +0 -427
  218. package/.next/server/chunks/ssr/[root-of-the-server]__1_jerxrctq6ev._.js.map +0 -1
  219. package/.next/server/chunks/ssr/[root-of-the-server]__1pvso-uyf--97._.js +0 -427
  220. package/.next/server/chunks/ssr/[root-of-the-server]__1pvso-uyf--97._.js.map +0 -1
  221. package/.next/server/chunks/ssr/[root-of-the-server]__1y78dc7oiz7ks._.js +0 -3
  222. package/.next/server/chunks/ssr/[root-of-the-server]__1y78dc7oiz7ks._.js.map +0 -1
  223. package/.next/server/chunks/ssr/_0ozao7omdwbvd._.js +0 -3
  224. package/.next/server/chunks/ssr/_0ozao7omdwbvd._.js.map +0 -1
  225. package/.next/server/chunks/ssr/_0rcmpdyxc1qwj._.js +0 -3
  226. package/.next/server/chunks/ssr/_0rcmpdyxc1qwj._.js.map +0 -1
  227. package/.next/server/chunks/ssr/_0wv0xnhun3mk7._.js +0 -3
  228. package/.next/server/chunks/ssr/_0wv0xnhun3mk7._.js.map +0 -1
  229. package/.next/server/chunks/ssr/_17rmaft1zusua._.js +0 -3
  230. package/.next/server/chunks/ssr/_17rmaft1zusua._.js.map +0 -1
  231. package/.next/server/chunks/ssr/_1e-d4pcj59a7a._.js +0 -3
  232. package/.next/server/chunks/ssr/_1e-d4pcj59a7a._.js.map +0 -1
  233. package/.next/server/chunks/ssr/_1qr5gnh64z8pg._.js +0 -3
  234. package/.next/server/chunks/ssr/_1qr5gnh64z8pg._.js.map +0 -1
  235. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0pezg1352q13j._.js.map +0 -1
  236. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_131i-tjzse3ye._.js.map +0 -1
  237. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1d8fezw1qsiob._.js.map +0 -1
  238. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1to-9cv1ysvs6._.js.map +0 -1
  239. package/.next/static/chunks/0_3s_e4gd9xl0.js +0 -1
  240. package/.next/static/chunks/0i4kx4yl-dwfg.js +0 -425
  241. package/.next/static/chunks/0z0jbobmmvqoc.js +0 -1
  242. package/.next/static/chunks/1_a_gmob5qzho.js +0 -1
  243. package/.next/static/chunks/1ifqr4qo4697b.js +0 -1
  244. package/.next/static/chunks/2ccq9i8ev4dsx.js +0 -1
  245. package/.next/static/chunks/2mif7onk_9jy-.css +0 -1
  246. package/.next/static/chunks/40wyf-mv0tiu3.js +0 -1
  247. /package/.next/server/chunks/ssr/{node_modules_1c8i3jrmo4393._.js.map → node_modules_00ebs67b4u61w._.js.map} +0 -0
  248. /package/.next/server/chunks/ssr/{node_modules_0f773uqgzj7y7._.js.map → node_modules_01avo0bde3tpo._.js.map} +0 -0
  249. /package/.next/server/chunks/ssr/{node_modules_0z3elum1675ve._.js.map → node_modules_01l0208e5kqic._.js.map} +0 -0
  250. /package/.next/server/chunks/ssr/{node_modules_1wlwrgnt65zus._.js.map → node_modules_033x29yqle_sj._.js.map} +0 -0
  251. /package/.next/server/chunks/ssr/{node_modules_1kr1c-ncf8q9t._.js.map → node_modules_03wbg__aslvui._.js.map} +0 -0
  252. /package/.next/server/chunks/ssr/{node_modules_02jwra-d9iqim._.js.map → node_modules_06mgkab1v5iox._.js.map} +0 -0
  253. /package/.next/server/chunks/ssr/{node_modules_0fs8mckuwk2tj._.js.map → node_modules_08-bqkrz7sh0l._.js.map} +0 -0
  254. /package/.next/server/chunks/ssr/{node_modules_0-6sb1gkbjo93._.js.map → node_modules_089w9jm7s2qa3._.js.map} +0 -0
  255. /package/.next/server/chunks/ssr/{node_modules_02vtgweceovke._.js.map → node_modules_08q6a-f2itxyo._.js.map} +0 -0
  256. /package/.next/server/chunks/ssr/{node_modules_0__m9_odts7_e._.js.map → node_modules_0cy47-s63xjzh._.js.map} +0 -0
  257. /package/.next/server/chunks/ssr/{node_modules_02nv3yff2pqh8._.js.map → node_modules_0czh0jcnsm5e6._.js.map} +0 -0
  258. /package/.next/server/chunks/ssr/{node_modules_0z7rdbaf13024._.js.map → node_modules_0e6qoh__e4a69._.js.map} +0 -0
  259. /package/.next/server/chunks/ssr/{node_modules_0-nvg0c4pgfb8._.js.map → node_modules_0fv321j1nslkb._.js.map} +0 -0
  260. /package/.next/server/chunks/ssr/{node_modules_061uaylfjwqk7._.js.map → node_modules_0g-wb7h-hsdwc._.js.map} +0 -0
  261. /package/.next/server/chunks/ssr/{node_modules_0t4h1rucl35hl._.js.map → node_modules_0hb0q0epwvp--._.js.map} +0 -0
  262. /package/.next/server/chunks/ssr/{node_modules_0dq0dsblt-q0g._.js.map → node_modules_0l-y0kqtu_r3o._.js.map} +0 -0
  263. /package/.next/server/chunks/ssr/{node_modules_0zc49fdcopqu9._.js.map → node_modules_0l814qpw677vm._.js.map} +0 -0
  264. /package/.next/server/chunks/ssr/{node_modules_1xyxigbqzb8vs._.js.map → node_modules_0o63z1gd17rhb._.js.map} +0 -0
  265. /package/.next/server/chunks/ssr/{node_modules_1-iyhsn_1p9nx._.js.map → node_modules_0oz_zzlh8exwx._.js.map} +0 -0
  266. /package/.next/server/chunks/ssr/{node_modules_0oukyh0iq21ow._.js.map → node_modules_0so2agzqd9p8l._.js.map} +0 -0
  267. /package/.next/server/chunks/ssr/{node_modules_07maxxr-t28ql._.js.map → node_modules_0w2w61b8bh769._.js.map} +0 -0
  268. /package/.next/server/chunks/ssr/{node_modules_1dff7b_kvngr-._.js.map → node_modules_0wd6szix_zku7._.js.map} +0 -0
  269. /package/.next/server/chunks/ssr/{node_modules_0fd1swhk15cx3._.js.map → node_modules_0wp7omf0xblv8._.js.map} +0 -0
  270. /package/.next/server/chunks/ssr/{node_modules_0nde84kzuwv2n._.js.map → node_modules_0yayrqhzfk8wy._.js.map} +0 -0
  271. /package/.next/server/chunks/ssr/{node_modules_0f4xh33v3iltc._.js.map → node_modules_0zb0i39mnjdf3._.js.map} +0 -0
  272. /package/.next/server/chunks/ssr/{node_modules_1-sz_-qddwilq._.js.map → node_modules_10c51o2qv6mbi._.js.map} +0 -0
  273. /package/.next/server/chunks/ssr/{node_modules_0b8ndnuql44z1._.js.map → node_modules_10g1mmrf63d8a._.js.map} +0 -0
  274. /package/.next/server/chunks/ssr/{node_modules_13nsmuhey_ti_._.js.map → node_modules_152txg4c7ose3._.js.map} +0 -0
  275. /package/.next/server/chunks/ssr/{node_modules_12qn4bepdou5v._.js.map → node_modules_185fuy4cv7loa._.js.map} +0 -0
  276. /package/.next/server/chunks/ssr/{node_modules_1iovyom0-y7o_._.js.map → node_modules_19vqq7rvf-_ar._.js.map} +0 -0
  277. /package/.next/server/chunks/ssr/{node_modules_0hqa9oxz17hip._.js.map → node_modules_1hd7j-tdb8drq._.js.map} +0 -0
  278. /package/.next/server/chunks/ssr/{node_modules_1rlnehdn16ay-._.js.map → node_modules_1hu83gui7_nj2._.js.map} +0 -0
  279. /package/.next/server/chunks/ssr/{node_modules_12qw2ucebdvol._.js.map → node_modules_1i3x2csuq_-jt._.js.map} +0 -0
  280. /package/.next/server/chunks/ssr/{node_modules_0kovk4b6czb22._.js.map → node_modules_1j8hpoiygrs5e._.js.map} +0 -0
  281. /package/.next/server/chunks/ssr/{node_modules_1ll-58c1c-qab._.js.map → node_modules_1l2g7btuh3e4s._.js.map} +0 -0
  282. /package/.next/server/chunks/ssr/{node_modules_1zs2nou5uogby._.js.map → node_modules_1n4kig4mj6n4s._.js.map} +0 -0
  283. /package/.next/server/chunks/ssr/{node_modules_0vn0kjn_84lmn._.js.map → node_modules_1nz3ckv6tu_66._.js.map} +0 -0
  284. /package/.next/server/chunks/ssr/{node_modules_05w7qhtrzvnk6._.js.map → node_modules_1qnypguag6s_l._.js.map} +0 -0
  285. /package/.next/server/chunks/ssr/{node_modules_1v4lexrfhnlhs._.js.map → node_modules_1r6w-jlme6d10._.js.map} +0 -0
  286. /package/.next/server/chunks/ssr/{node_modules_1f4geuvkvlxj1._.js.map → node_modules_1s8oxeqa5_i3k._.js.map} +0 -0
  287. /package/.next/server/chunks/ssr/{node_modules_1216007ogd5om._.js.map → node_modules_1tskcaktx6ods._.js.map} +0 -0
  288. /package/.next/server/chunks/ssr/{node_modules_0m3ihe65818y_._.js.map → node_modules_1xz1dstlo5ri-._.js.map} +0 -0
  289. /package/.next/server/chunks/ssr/{node_modules_1kv6lsckqs-m0._.js.map → node_modules_1zii7uk468cay._.js.map} +0 -0
  290. /package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0bmuudv_6eypr._.js.map → node_modules_katex_dist_katex_mjs_067omoj5szuys._.js.map} +0 -0
  291. /package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0qyy6pq9uzpk0._.js.map → node_modules_katex_dist_katex_mjs_1604s1ky8db8u._.js.map} +0 -0
  292. /package/.next/static/{sQnXeiP8rV6y2v0az_xw3 → dGN2VKPk5TJgezxS3JvRc}/_buildManifest.js +0 -0
  293. /package/.next/static/{sQnXeiP8rV6y2v0az_xw3 → dGN2VKPk5TJgezxS3JvRc}/_clientMiddlewareManifest.js +0 -0
  294. /package/.next/static/{sQnXeiP8rV6y2v0az_xw3 → dGN2VKPk5TJgezxS3JvRc}/_ssgManifest.js +0 -0
@@ -64,25 +64,25 @@ export default function ToolOutputView({ output = "", onImageClick }) {
64
64
  };
65
65
 
66
66
  return (
67
- <div className="mt-2 rounded-xl border border-slate-800 bg-slate-950/80 overflow-hidden text-xs font-mono">
67
+ <div className="mt-2 rounded-xl border border-[var(--border-card)] bg-[var(--bg-chat-code)] overflow-hidden text-xs font-mono">
68
68
  {/* Header bar */}
69
- <div className="flex items-center justify-between px-3 py-1.5 bg-slate-900/80 border-b border-slate-800 text-[11px] text-slate-400">
69
+ <div className="flex items-center justify-between px-3 py-1.5 bg-[var(--bg-card-inner)] border-b border-[var(--border-subtle)] text-[11px] text-[var(--text-muted)]">
70
70
  <div className="flex items-center gap-2">
71
71
  <Terminal className="w-3.5 h-3.5 text-cyan-400" />
72
- <span className="font-medium text-slate-300">Tool Output</span>
73
- <span className="text-[10px] text-slate-500 font-mono">({totalLines} lines)</span>
72
+ <span className="font-medium text-[var(--text-bright)]">Tool Output</span>
73
+ <span className="text-[10px] text-[var(--text-muted)] font-mono">({totalLines} lines)</span>
74
74
  </div>
75
75
 
76
76
  <div className="flex items-center gap-1.5 font-sans">
77
77
  {/* JSON Tree vs Raw Text Switcher */}
78
78
  {parsedJson && (
79
- <div className="flex items-center bg-slate-950 rounded-lg p-0.5 border border-slate-800 mr-1">
79
+ <div className="flex items-center bg-[var(--bg-card)] rounded-lg p-0.5 border border-[var(--border-card)] mr-1">
80
80
  <button
81
81
  onClick={() => setViewMode("raw")}
82
- className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] transition-colors ${
82
+ className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] transition-colors cursor-pointer ${
83
83
  viewMode === "raw"
84
- ? "bg-slate-800 text-slate-200"
85
- : "text-slate-400 hover:text-slate-300"
84
+ ? "bg-[var(--bg-selection)] text-[var(--text-bright)] font-semibold"
85
+ : "text-[var(--text-muted)] hover:text-[var(--text-bright)]"
86
86
  }`}
87
87
  >
88
88
  <AlignLeft className="w-3 h-3" />
@@ -90,10 +90,10 @@ export default function ToolOutputView({ output = "", onImageClick }) {
90
90
  </button>
91
91
  <button
92
92
  onClick={() => setViewMode("json")}
93
- className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] transition-colors ${
93
+ className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] transition-colors cursor-pointer ${
94
94
  viewMode === "json"
95
95
  ? "bg-indigo-600/30 text-indigo-300 border border-indigo-500/40"
96
- : "text-slate-400 hover:text-slate-300"
96
+ : "text-[var(--text-muted)] hover:text-[var(--text-bright)]"
97
97
  }`}
98
98
  >
99
99
  <Braces className="w-3 h-3 text-indigo-400" />
@@ -105,7 +105,7 @@ export default function ToolOutputView({ output = "", onImageClick }) {
105
105
  {/* Copy Button */}
106
106
  <button
107
107
  onClick={handleCopy}
108
- className="flex items-center gap-1 px-2 py-0.5 rounded hover:bg-slate-800 text-slate-400 hover:text-slate-200 transition-colors border border-slate-800/80"
108
+ className="flex items-center gap-1 px-2 py-0.5 rounded hover:bg-[var(--bg-hover)] text-[var(--text-muted)] hover:text-[var(--text-bright)] transition-colors border border-[var(--border-subtle)] cursor-pointer"
109
109
  title="Copy output"
110
110
  >
111
111
  {copied ? (
@@ -115,7 +115,7 @@ export default function ToolOutputView({ output = "", onImageClick }) {
115
115
  </>
116
116
  ) : (
117
117
  <>
118
- <Copy className="w-3 h-3" />
118
+ <Copy className="w-3.5 h-3.5" />
119
119
  <span className="text-[10px]">Copy</span>
120
120
  </>
121
121
  )}
@@ -125,8 +125,8 @@ export default function ToolOutputView({ output = "", onImageClick }) {
125
125
 
126
126
  {/* Detected Image Thumbnails / Chips */}
127
127
  {imageMatches.length > 0 && (
128
- <div className="flex flex-wrap items-center gap-1.5 px-3 py-1.5 bg-slate-900/40 border-b border-slate-800/80 text-[11px]">
129
- <span className="text-slate-400 flex items-center gap-1">
128
+ <div className="flex flex-wrap items-center gap-1.5 px-3 py-1.5 bg-[var(--bg-card-inner)] border-b border-[var(--border-subtle)] text-[11px]">
129
+ <span className="text-[var(--text-muted)] flex items-center gap-1">
130
130
  <ImageIcon className="w-3.5 h-3.5 text-cyan-400" />
131
131
  <span>Image output:</span>
132
132
  </span>
@@ -137,7 +137,7 @@ export default function ToolOutputView({ output = "", onImageClick }) {
137
137
  src: `/api/media?path=${encodeURIComponent(imgP)}`,
138
138
  alt: imgP.split("/").pop()
139
139
  })}
140
- className="flex items-center gap-1 px-2 py-0.5 rounded bg-cyan-950/60 hover:bg-cyan-900/80 border border-cyan-500/40 text-cyan-300 font-mono transition-colors text-[10px]"
140
+ className="flex items-center gap-1 px-2 py-0.5 rounded bg-[var(--bg-chat-user)] hover:brightness-110 border border-[var(--border-chat-user)] text-[var(--accent-secondary)] font-mono transition-colors text-[10px] cursor-pointer"
141
141
  title="Click to view full image popup"
142
142
  >
143
143
  <span>{imgP.split("/").pop()}</span>
@@ -148,22 +148,22 @@ export default function ToolOutputView({ output = "", onImageClick }) {
148
148
 
149
149
  {/* JSON Inspector View */}
150
150
  {parsedJson && viewMode === "json" ? (
151
- <div className="p-2 bg-black/60">
151
+ <div className="p-2 bg-[var(--bg-chat-tool)]">
152
152
  <JsonInspector data={parsedJson} title="JSON Output View" />
153
153
  </div>
154
154
  ) : (
155
155
  /* Raw Output text */
156
- <pre className="p-3 text-[11px] text-slate-300 leading-relaxed overflow-x-auto max-h-80 overflow-y-auto whitespace-pre font-mono">
156
+ <pre className="p-3 text-[11px] text-[var(--text-chat-code)] leading-relaxed overflow-x-auto max-h-80 overflow-y-auto whitespace-pre font-mono">
157
157
  {displayedText}
158
158
  </pre>
159
159
  )}
160
160
 
161
161
  {/* Expand/Collapse Footer for Raw View */}
162
162
  {viewMode === "raw" && hasManyLines && (
163
- <div className="px-3 py-1.5 bg-slate-900/60 border-t border-slate-800 text-center">
163
+ <div className="px-3 py-1.5 bg-[var(--bg-card-inner)] border-t border-[var(--border-subtle)] text-center">
164
164
  <button
165
165
  onClick={() => setExpanded(!expanded)}
166
- className="text-[11px] text-cyan-400 hover:text-cyan-300 underline transition-colors"
166
+ className="text-[11px] text-[var(--accent-secondary)] hover:underline transition-colors cursor-pointer"
167
167
  >
168
168
  {expanded ? "Show less (first 50 lines)" : `Show all ${totalLines} lines`}
169
169
  </button>
@@ -1,14 +1,29 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useRef, useState } from "react";
4
- import { ZoomIn, ZoomOut, Maximize2 } from "lucide-react";
4
+ import { ZoomIn, ZoomOut, Maximize2, Minimize2, Sparkles, Volume2, VolumeX, Maximize, ListFilter, Clock } from "lucide-react";
5
5
  import { mountOfficeScene } from "./office-scene";
6
6
  import { SUPPORTER_CHANGE_EVENT } from "@/lib/supporterStore";
7
7
 
8
- export default function OfficeCanvas({ traces = [], onStats, onSelect, selectedId = null }) {
8
+ export default function OfficeCanvas({
9
+ traces = [],
10
+ totalCount = 0,
11
+ top = 20,
12
+ onTopChange = null,
13
+ timeframe = "24h",
14
+ onTimeframeChange = null,
15
+ onStats,
16
+ onSelect,
17
+ selectedId = null,
18
+ isZenFullscreen = false,
19
+ onToggleZenFullscreen = null,
20
+ soundEnabled = false,
21
+ onToggleSound = null,
22
+ }) {
9
23
  const hostRef = useRef(null);
10
24
  const sceneRef = useRef(null);
11
25
  const [zoomPct, setZoomPct] = useState(100);
26
+ const [showToast, setShowToast] = useState(false);
12
27
  const latest = useRef({ onSelect, selectedId, traces });
13
28
 
14
29
  useEffect(() => {
@@ -111,8 +126,49 @@ export default function OfficeCanvas({ traces = [], onStats, onSelect, selectedI
111
126
  return () => clearInterval(id);
112
127
  }, [onStats]);
113
128
 
129
+ // Re-fit canvas layout whenever fullscreen mode changes
130
+ useEffect(() => {
131
+ const timer = setTimeout(() => {
132
+ if (typeof window !== "undefined") {
133
+ window.dispatchEvent(new Event("resize"));
134
+ }
135
+ sceneRef.current?.fit?.();
136
+ }, 60);
137
+ return () => clearTimeout(timer);
138
+ }, [isZenFullscreen]);
139
+
140
+ // Toast notification when entering Chill Mode
141
+ useEffect(() => {
142
+ if (isZenFullscreen) {
143
+ setShowToast(true);
144
+ const timer = setTimeout(() => setShowToast(false), 3500);
145
+ return () => clearTimeout(timer);
146
+ } else {
147
+ setShowToast(false);
148
+ }
149
+ }, [isZenFullscreen]);
150
+
151
+ // Global ESC key listener to exit Chill Mode
152
+ useEffect(() => {
153
+ if (!isZenFullscreen) return;
154
+ const handleKeyDown = (e) => {
155
+ if (e.key === "Escape") {
156
+ e.preventDefault();
157
+ onToggleZenFullscreen?.(false);
158
+ }
159
+ };
160
+ window.addEventListener("keydown", handleKeyDown);
161
+ return () => window.removeEventListener("keydown", handleKeyDown);
162
+ }, [isZenFullscreen, onToggleZenFullscreen]);
163
+
114
164
  return (
115
- <div className="relative h-full w-full overflow-hidden rounded-xl border border-slate-800 bg-[#080d14] shadow-2xl">
165
+ <div
166
+ className={`relative h-full w-full overflow-hidden bg-[#080d14] ${
167
+ isZenFullscreen
168
+ ? "!fixed !inset-0 !z-[99] !w-screen !h-screen !rounded-none !border-none"
169
+ : "rounded-xl border border-slate-800 shadow-2xl"
170
+ }`}
171
+ >
116
172
  <div ref={hostRef} className="absolute inset-0" />
117
173
  {error && (
118
174
  <div className="absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-rose-400">
@@ -120,6 +176,67 @@ export default function OfficeCanvas({ traces = [], onStats, onSelect, selectedI
120
176
  </div>
121
177
  )}
122
178
 
179
+ {/* Floating Canvas Filter Badge (Top-left) */}
180
+ {!isZenFullscreen && (
181
+ <div
182
+ data-testid="canvas-filter-pill"
183
+ className="absolute top-3 left-3 z-10 hidden sm:flex items-center gap-1.5 px-2.5 py-1 rounded-lg border border-slate-800/80 bg-slate-950/80 backdrop-blur-md shadow-xl text-slate-300 text-xs select-none pointer-events-none"
184
+ >
185
+ <ListFilter className="w-3 h-3 text-cyan-400 shrink-0" />
186
+ <span className="font-semibold text-[11px] text-slate-200">
187
+ {top === "all" ? "All Desks" : `Top ${top}`}
188
+ </span>
189
+ <span className="text-slate-600">·</span>
190
+ <Clock className="w-3 h-3 text-amber-400 shrink-0" />
191
+ <span className="text-[11px] text-slate-300">
192
+ {timeframe === "all" ? "All time" : timeframe}
193
+ </span>
194
+ {totalCount > 0 && traces.length > 0 && traces.length < totalCount && (
195
+ <span
196
+ title={`Showing ${traces.length} of ${totalCount} recorded conversations`}
197
+ className="ml-0.5 px-1.5 py-0.2 rounded text-[10px] font-mono bg-cyan-950/60 text-cyan-300 border border-cyan-800/40"
198
+ >
199
+ {traces.length}/{totalCount}
200
+ </span>
201
+ )}
202
+ </div>
203
+ )}
204
+
205
+ {/* Chill Mode Entry Toast Banner */}
206
+ {showToast && (
207
+ <div className="absolute top-5 left-1/2 -translate-x-1/2 z-30 pointer-events-none transition-all duration-300 animate-in fade-in slide-in-from-top-2">
208
+ <div className="flex items-center gap-2 px-4 py-2 rounded-xl bg-slate-950/90 border border-cyan-500/40 text-cyan-200 text-xs shadow-2xl backdrop-blur-md">
209
+ <Sparkles className="w-4 h-4 text-cyan-400 shrink-0" />
210
+ <span>Chế độ Chill toàn màn hình · Nhấn <kbd className="px-1.5 py-0.5 rounded bg-slate-800 text-amber-300 font-mono text-[10px]">ESC</kbd> hoặc click nút góc trên để thoát</span>
211
+ </div>
212
+ </div>
213
+ )}
214
+
215
+ {/* Chill Mode Floating Top Control Bar */}
216
+ {isZenFullscreen && (
217
+ <div className="absolute top-4 right-4 z-30 flex items-center gap-2 animate-in fade-in duration-200">
218
+ <div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-slate-950/70 border border-slate-800/80 backdrop-blur-md shadow-xl text-xs text-slate-300 select-none">
219
+ <span className="relative flex h-2 w-2">
220
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" />
221
+ <span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" />
222
+ </span>
223
+ <span className="font-medium text-[11px]">Virtual Office · Chill Mode</span>
224
+ <span className="text-[10px] text-slate-400 font-mono">({traces.length} Bots)</span>
225
+ </div>
226
+
227
+ <button
228
+ type="button"
229
+ data-testid="exit-zen-fullscreen-btn"
230
+ onClick={() => onToggleZenFullscreen?.(false)}
231
+ title="Thoát chế độ toàn màn hình Chill Mode (ESC)"
232
+ className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-slate-900/80 hover:bg-slate-800 border border-slate-700/60 hover:border-amber-500/50 text-slate-300 hover:text-white backdrop-blur-md shadow-xl transition-all cursor-pointer text-xs font-medium group"
233
+ >
234
+ <Minimize2 className="w-3.5 h-3.5 text-amber-400 group-hover:scale-110 transition-transform" />
235
+ <span className="text-[11px]">Thoát ESC</span>
236
+ </button>
237
+ </div>
238
+ )}
239
+
123
240
  {/* Floating Canvas Camera Controls */}
124
241
  <div className="absolute bottom-3 left-3 z-10 flex items-center gap-1 rounded-lg border border-slate-800/80 bg-slate-950/80 p-1 backdrop-blur-md shadow-xl text-slate-400">
125
242
  <button
@@ -151,6 +268,51 @@ export default function OfficeCanvas({ traces = [], onStats, onSelect, selectedI
151
268
  >
152
269
  <Maximize2 className="w-3.5 h-3.5" />
153
270
  </button>
271
+
272
+ {onToggleZenFullscreen && (
273
+ <>
274
+ <div className="w-[1px] h-3.5 bg-slate-800 my-auto" />
275
+ <button
276
+ type="button"
277
+ data-testid="canvas-camera-zen-fullscreen-btn"
278
+ onClick={() => onToggleZenFullscreen(!isZenFullscreen)}
279
+ title={
280
+ isZenFullscreen
281
+ ? "Thoát toàn màn hình (ESC)"
282
+ : "Chế độ Chill toàn màn hình Virtual Office (ESC để thoát)"
283
+ }
284
+ className={`p-1.5 rounded-md transition-colors cursor-pointer ${
285
+ isZenFullscreen
286
+ ? "bg-amber-500/20 text-amber-300 hover:bg-amber-500/30"
287
+ : "hover:bg-slate-800 hover:text-cyan-300 text-slate-400"
288
+ }`}
289
+ >
290
+ {isZenFullscreen ? (
291
+ <Minimize2 className="w-3.5 h-3.5 text-amber-400" />
292
+ ) : (
293
+ <Maximize className="w-3.5 h-3.5" />
294
+ )}
295
+ </button>
296
+ </>
297
+ )}
298
+
299
+ {onToggleSound && (
300
+ <>
301
+ <div className="w-[1px] h-3.5 bg-slate-800 my-auto" />
302
+ <button
303
+ type="button"
304
+ onClick={onToggleSound}
305
+ title={soundEnabled ? "Âm thanh: BẬT" : "Âm thanh: TẮT"}
306
+ className={`p-1.5 rounded-md transition-colors cursor-pointer ${
307
+ soundEnabled
308
+ ? "text-emerald-400 hover:bg-slate-800"
309
+ : "text-slate-500 hover:text-slate-300 hover:bg-slate-800"
310
+ }`}
311
+ >
312
+ {soundEnabled ? <Volume2 className="w-3.5 h-3.5" /> : <VolumeX className="w-3.5 h-3.5" />}
313
+ </button>
314
+ </>
315
+ )}
154
316
  </div>
155
317
  </div>
156
318
  );
@@ -51,24 +51,81 @@ export function createCharacter({ color, depth = 1, scale = 1, seed = 0, trimCol
51
51
  const skinItem = typeof skin === "string" ? getItemById(skin) : null;
52
52
  const hasCustomSkin = Boolean(skinItem || (skin && skin !== "classic" && skin !== "none"));
53
53
  const skinArchetype = skinItem?.archetype || (skin === "cat" ? "cyber_cat" : skin === "ninja" ? "stealth_ninja" : skin === "hacker" ? "matrix_hacker" : skin || "cyber_classic");
54
+ const skinVariant = skinItem?.variant || "tactical_visor";
54
55
  const skinColor = skinItem?.color ? parseHexColor(skinItem.color, trimColor ?? defaultTrim) : (trimColor ?? defaultTrim);
56
+ const skinSecondaryColor = skinItem?.secondaryColor ? parseHexColor(skinItem.secondaryColor, 0x111827) : 0x111827;
57
+ const skinAccentColor = skinItem?.accentColor ? parseHexColor(skinItem.accentColor, 0xffffff) : 0xffffff;
55
58
  const trim = skinColor;
56
59
  const SHELL = hasCustomSkin ? 0x1e2430 : 0xf3f5f9;
57
60
  const SHADE = hasCustomSkin ? 0x111622 : 0xc9d0dc;
58
61
 
59
- // Torso: two stacked shells with a coloured band between them.
62
+ // Torso: 5 Distinct Staff Uniforms & Silhouettes
60
63
  const body = new Graphics();
61
- body.roundRect(-17, -30, 34, 22, 11).fill(vgrad(0xffffff, SHELL, SHADE));
62
- body.roundRect(-17, -30, 34, 8, 8).fill({ color: 0xffffff, alpha: 0.6 });
63
- body.roundRect(-14, -12, 28, 8, 4).fill({ color: trim });
64
- body.roundRect(-16, -8, 32, 16, 8).fill(vgrad(SHELL, SHADE, 0x8c97a9));
65
- body.roundRect(-16, 2, 32, 6, 4).fill({ color: SHADE, alpha: 0.6 });
66
-
67
- // Chest arc reactor for equipped custom skins
68
- if (hasCustomSkin) {
69
- body.circle(0, -18, 4).fill({ color: 0x0f172a });
70
- body.circle(0, -18, 4).stroke({ width: 1.2, color: skinColor });
71
- body.circle(0, -18, 2).fill({ color: skinColor });
64
+ if (skinArchetype === "mecha_pilot") {
65
+ // Heavy Industrial Exoskeleton Vest
66
+ body.poly([-22, -30, 22, -30, 18, -6, -18, -6]).fill({ color: 0x18181b }).stroke({ width: 1.5, color: skinColor });
67
+ // Pauldrons
68
+ body.poly([-27, -29, -20, -33, -17, -25, -25, -20]).fill({ color: 0x27272a }).stroke({ width: 1, color: skinColor });
69
+ body.poly([27, -29, 20, -33, 17, -25, 25, -20]).fill({ color: 0x27272a }).stroke({ width: 1, color: skinColor });
70
+ // Hazard Chevrons
71
+ body.poly([-12, -26, -7, -26, -3, -20, -8, -20]).fill({ color: 0xeab308 });
72
+ body.poly([2, -26, 7, -26, 11, -20, 6, -20]).fill({ color: 0xeab308 });
73
+ // Heavy Tactical Harness Straps
74
+ body.rect(-13, -30, 2, 24).fill({ color: skinColor });
75
+ body.rect(11, -30, 2, 24).fill({ color: skinColor });
76
+ // Central Fusion Core with Cage Grille
77
+ body.circle(0, -14, 5.5).fill({ color: 0x09090b }).stroke({ width: 1.4, color: skinColor });
78
+ body.circle(0, -14, 3).fill({ color: skinColor });
79
+ body.rect(-4, -15, 8, 2).fill({ color: 0xffffff });
80
+ } else if (skinArchetype === "stealth_ninja") {
81
+ // Sleek Form-Fitting Shinobi Gi
82
+ body.poly([-16, -30, 16, -30, 13, -6, -13, -6]).fill({ color: 0x090d16 }).stroke({ width: 1.4, color: skinColor });
83
+ // Crossed Kimono Lapels
84
+ body.poly([-16, -30, 5, -14, 2, -14, -16, -28]).fill({ color: skinColor });
85
+ // Diagonal Tactical Sash
86
+ body.poly([-16, -28, -11, -30, 14, -9, 9, -7]).fill({ color: skinColor, alpha: 0.9 });
87
+ // Obi Waist Sash & Knot
88
+ body.roundRect(-14, -12, 28, 6, 2).fill({ color: 0x1e1b4b }).stroke({ width: 1.2, color: skinColor });
89
+ body.poly([3, -7, 6, 1, 4, 6, 1, 1, 3, -7]).fill({ color: skinColor });
90
+ } else if (skinArchetype === "matrix_hacker") {
91
+ // Loose Oversized Slouchy Techwear Hoodie Body
92
+ body.roundRect(-21, -30, 42, 25, 7).fill({ color: 0x121217 }).stroke({ width: 1.5, color: skinColor });
93
+ // Cowl Neck Collar
94
+ body.roundRect(-18, -32, 36, 10, 4).fill({ color: 0x1f1f28 }).stroke({ width: 1.2, color: skinColor });
95
+ // Hanging Drawstrings with Metal Aglets
96
+ body.rect(-7, -23, 2, 16).fill({ color: 0xffffff });
97
+ body.rect(-7.5, -7, 3, 4).fill({ color: skinColor });
98
+ body.rect(5, -23, 2, 16).fill({ color: 0xffffff });
99
+ body.rect(4.5, -7, 3, 4).fill({ color: skinColor });
100
+ // Kangaroo Pouch Pocket
101
+ body.poly([-15, -14, 15, -14, 13, -6, -13, -6]).fill({ color: 0x09090d }).stroke({ width: 1, color: 0x27272a });
102
+ } else if (skinArchetype === "celestial_astro") {
103
+ // Pressurized Flight Suit Body
104
+ body.poly([-18, -30, 18, -30, 16, -6, -16, -6]).fill({ color: 0xf1f5f9 }).stroke({ width: 1.4, color: skinColor });
105
+ // Hermetic Helmet Seal Ring
106
+ body.roundRect(-14, -33, 28, 5, 2).fill({ color: 0x334155 }).stroke({ width: 1.2, color: skinColor });
107
+ // Gold Command Epaulets on Shoulders
108
+ body.roundRect(-24, -31, 8, 4.5, 1).fill({ color: 0xf59e0b }).stroke({ width: 0.8, color: 0x78350f });
109
+ body.roundRect(16, -31, 8, 4.5, 1).fill({ color: 0xf59e0b }).stroke({ width: 0.8, color: 0x78350f });
110
+ // Dual Atmospheric Pressure Regulator Dials
111
+ body.circle(-7, -17, 3.5).fill({ color: 0x0f172a }).stroke({ width: 1.2, color: skinColor });
112
+ body.circle(-7, -17, 1.5).fill({ color: 0x22c55e });
113
+ body.circle(7, -17, 3.5).fill({ color: 0x0f172a }).stroke({ width: 1.2, color: skinColor });
114
+ body.circle(7, -17, 1.5).fill({ color: 0x38bdf8 });
115
+ } else {
116
+ // Default Cyber Suit (Corporate Tech Field Ops / Executive Security)
117
+ body.poly([-18, -30, 18, -30, 15, -6, -15, -6]).fill(vgrad(0xffffff, SHELL, SHADE)).stroke({ width: 1.2, color: skinColor });
118
+ // Sharp Lapels
119
+ body.poly([-17, -30, -7, -14, -13, -14]).fill({ color: 0x0d1117 });
120
+ body.poly([17, -30, 7, -14, 13, -14]).fill({ color: 0x0d1117 });
121
+ // Security ID Badge Lanyard
122
+ body.poly([-5, -30, 0, -18, 5, -30]).stroke({ width: 1.2, color: skinAccentColor, alpha: 0.85 });
123
+ body.roundRect(-4.5, -19, 9, 12, 1.5).fill({ color: 0x090d16 }).stroke({ width: 1, color: skinColor });
124
+ body.rect(-3, -17.5, 6, 4.5).fill({ color: skinColor });
125
+ body.rect(-3, -11, 6, 1.5).fill({ color: 0xffffff });
126
+ // Corporate Belt & Buckle
127
+ body.rect(-15, -10, 30, 4).fill({ color: 0x020617 });
128
+ body.roundRect(-3.5, -11, 7, 6, 1).fill({ color: 0x1e293b }).stroke({ width: 1, color: skinColor });
72
129
  }
73
130
 
74
131
  const neck = new Graphics();
@@ -108,42 +165,220 @@ export function createCharacter({ color, depth = 1, scale = 1, seed = 0, trimCol
108
165
  head.ellipse(0, -53, 20, 15.5).fill({ color: 0x0f141c });
109
166
  head.ellipse(-5, -58, 11, 5).fill({ color: 0xffffff, alpha: 0.06 });
110
167
 
111
- // Custom Skin Accessories on Head according to 5 Archetypes
112
- if (skinArchetype === "cyber_suit") {
113
- // Cyber Suit: Angular Tactical Brow Visor & Wing Antennas
168
+ // Custom Skin Accessories on Head matching 50 Curated Variants
169
+ // 1. CYBER SUIT VARIANTS
170
+ if (skinVariant === "tactical_visor") {
114
171
  head.roundRect(-24, -66, 48, 7, 2.5).fill({ color: 0x0f172a });
115
172
  head.roundRect(-24, -66, 48, 7, 2.5).stroke({ width: 1.6, color: skinColor });
116
173
  head.poly([-26, -66, -34, -76, -26, -72]).fill({ color: skinColor });
117
174
  head.poly([26, -66, 34, -76, 26, -72]).fill({ color: skinColor });
118
- head.circle(0, -62.5, 2.5).fill({ color: skinColor });
119
- } else if (skinArchetype === "stealth_ninja" || skinArchetype === "ninja") {
120
- // Ninja Headband & Laser Visor
121
- head.roundRect(-24, -66, 48, 8, 3).fill({ color: 0x1e1b4b });
122
- head.roundRect(-24, -66, 48, 8, 3).stroke({ width: 1.5, color: skinColor });
175
+ head.circle(0, -62.5, 2.5).fill({ color: skinAccentColor });
176
+ } else if (skinVariant === "laser_scout") {
177
+ head.circle(8, -57, 7).stroke({ width: 1.6, color: skinColor });
178
+ head.circle(8, -57, 2.5).fill({ color: 0xff0000 });
179
+ } else if (skinVariant === "riot_shield") {
180
+ head.roundRect(-25, -68, 50, 9, 3).fill({ color: 0x1e2430 });
181
+ head.roundRect(-25, -68, 50, 9, 3).stroke({ width: 1.8, color: skinColor });
182
+ head.circle(-16, -64, 2.5).fill({ color: skinAccentColor });
183
+ head.circle(16, -64, 2.5).fill({ color: skinAccentColor });
184
+ } else if (skinVariant === "cyber_horns") {
185
+ head.poly([-20, -70, -30, -88, -14, -76]).fill({ color: skinColor });
186
+ head.poly([20, -70, 30, -88, 14, -76]).fill({ color: skinColor });
187
+ } else if (skinVariant === "crown_radiator") {
188
+ head.poly([-16, -72, -14, -86, -8, -74]).fill({ color: skinColor });
189
+ head.poly([-4, -74, 0, -92, 4, -74]).fill({ color: skinColor });
190
+ head.poly([8, -74, 14, -86, 16, -72]).fill({ color: skinColor });
191
+ head.circle(0, -70, 3).fill({ color: skinAccentColor });
192
+ } else if (skinVariant === "gas_respirator") {
193
+ head.circle(-14, -44, 6).fill({ color: 0x1e2430 }).stroke({ width: 1.5, color: skinColor });
194
+ head.circle(14, -44, 6).fill({ color: 0x1e2430 }).stroke({ width: 1.5, color: skinColor });
195
+ head.circle(-14, -44, 3).fill({ color: skinColor });
196
+ head.circle(14, -44, 3).fill({ color: skinColor });
197
+ } else if (skinVariant === "prism_goggles") {
198
+ head.poly([-16, -62, -8, -66, -2, -62, -2, -52, -8, -48, -16, -52]).fill({ color: 0x0c4a6e }).stroke({ width: 1.6, color: skinColor });
199
+ head.poly([2, -62, 8, -66, 16, -62, 16, -52, 8, -48, 2, -52]).fill({ color: 0x0c4a6e }).stroke({ width: 1.6, color: skinColor });
200
+ head.circle(-9, -57, 2.5).fill({ color: skinAccentColor });
201
+ head.circle(9, -57, 2.5).fill({ color: skinAccentColor });
202
+ } else if (skinVariant === "samurai_crest") {
203
+ head.poly([-22, -72, 0, -88, 22, -72, 0, -80]).fill({ color: skinColor });
204
+ head.circle(0, -78, 3).fill({ color: skinAccentColor });
205
+ } else if (skinVariant === "overdrive_vents") {
206
+ head.poly([-18, -72, -28, -90, -14, -76]).fill({ color: skinColor });
207
+ head.poly([18, -72, 28, -90, 14, -76]).fill({ color: skinColor });
208
+ } else if (skinVariant === "holo_shroud") {
209
+ head.ellipse(0, -66, 28, 12).stroke({ width: 1.8, color: skinColor, alpha: 0.8 });
210
+ head.circle(0, -78, 4).fill({ color: skinAccentColor });
211
+ }
212
+ // 2. MECHA PILOT VARIANTS
213
+ else if (skinVariant === "v_fin") {
214
+ head.poly([-24, -88, -18, -92, 0, -76, 18, -92, 24, -88, 0, -72]).fill({ color: skinColor });
215
+ head.poly([-4, -72, 0, -78, 4, -72, 0, -68]).fill({ color: 0xef4444 });
216
+ } else if (skinVariant === "blast_shield") {
217
+ head.roundRect(-24, -68, 48, 14, 3).fill({ color: 0x0f172a }).stroke({ width: 2, color: skinColor });
218
+ head.circle(-18, -80, 3.5).fill({ color: 0xfde047 });
219
+ head.circle(18, -80, 3.5).fill({ color: 0xfde047 });
220
+ } else if (skinVariant === "eva_horn") {
221
+ head.poly([-3, -74, 0, -98, 3, -74]).fill({ color: skinColor });
222
+ head.roundRect(-28, -48, 5, 18, 2).fill({ color: skinColor });
223
+ head.roundRect(23, -48, 5, 18, 2).fill({ color: skinColor });
224
+ } else if (skinVariant === "cage_grille") {
225
+ head.roundRect(-16, -52, 32, 14, 2).fill({ color: 0x18181b }).stroke({ width: 1.6, color: skinColor });
226
+ head.roundRect(-22, -86, 4, 16, 1).fill({ color: 0x27272a }).stroke({ width: 1, color: skinColor });
227
+ head.roundRect(18, -86, 4, 16, 1).fill({ color: 0x27272a }).stroke({ width: 1, color: skinColor });
228
+ } else if (skinVariant === "missile_pod") {
229
+ head.roundRect(-30, -42, 8, 12, 2).fill({ color: 0x1c1917 }).stroke({ width: 1.5, color: skinColor });
230
+ head.roundRect(22, -42, 8, 12, 2).fill({ color: 0x1c1917 }).stroke({ width: 1.5, color: skinColor });
231
+ head.circle(-26, -38, 1.5).fill({ color: 0xef4444 });
232
+ head.circle(26, -38, 1.5).fill({ color: 0xef4444 });
233
+ } else if (skinVariant === "quad_array") {
234
+ head.poly([-22, -72, -26, -90, -20, -72]).fill({ color: skinColor });
235
+ head.poly([-12, -76, -14, -94, -10, -76]).fill({ color: skinColor });
236
+ head.poly([10, -76, 14, -94, 12, -76]).fill({ color: skinColor });
237
+ head.poly([20, -72, 26, -90, 22, -72]).fill({ color: skinColor });
238
+ head.circle(-26, -90, 2).fill({ color: skinAccentColor });
239
+ head.circle(26, -90, 2).fill({ color: skinAccentColor });
240
+ } else if (skinVariant === "halo_shield") {
241
+ head.poly([-32, -42, -26, -46, -20, -42, -20, -34, -26, -30, -32, -34]).fill({ color: 0x064e3b }).stroke({ width: 1.5, color: skinColor });
242
+ head.poly([20, -42, 26, -46, 32, -42, 32, -34, 26, -30, 20, -34]).fill({ color: 0x064e3b }).stroke({ width: 1.5, color: skinColor });
243
+ } else if (skinVariant === "ram_horns") {
244
+ head.poly([-18, -72, -34, -86, -18, -92, -14, -72]).fill({ color: skinColor });
245
+ head.poly([18, -72, 34, -86, 18, -92, 14, -72]).fill({ color: skinColor });
246
+ } else if (skinVariant === "radar_dish") {
247
+ head.ellipse(-24, -84, 7, 12).fill({ color: 0x18181b }).stroke({ width: 1.5, color: skinColor });
248
+ head.circle(-24, -84, 2).fill({ color: skinAccentColor });
249
+ } else if (skinVariant === "hyper_wings") {
250
+ head.poly([-26, -46, -44, -64, -34, -40]).fill({ color: skinColor });
251
+ head.poly([26, -46, 44, -64, 34, -40]).fill({ color: skinColor });
252
+ }
253
+ // 3. STEALTH NINJA VARIANTS
254
+ else if (skinVariant === "flowing_headband") {
255
+ head.roundRect(-24, -66, 48, 8, 2).fill({ color: 0x1e1b4b }).stroke({ width: 1.5, color: skinColor });
256
+ head.circle(0, -62, 3).fill({ color: 0x38bdf8 });
257
+ head.poly([24, -62, 38, -56, 46, -46, 36, -48, 24, -56]).fill({ color: skinColor });
258
+ } else if (skinVariant === "tactical_cowl") {
259
+ head.roundRect(-24, -68, 48, 24, 6).fill({ color: 0x0f172a, alpha: 0.8 }).stroke({ width: 1.4, color: skinColor });
260
+ head.circle(6, -57, 4.5).stroke({ width: 1.8, color: 0x22c55e });
261
+ head.circle(6, -57, 1.5).fill({ color: 0x22c55e });
262
+ } else if (skinVariant === "oni_mask") {
263
+ head.poly([-16, -72, -24, -88, -12, -76]).fill({ color: 0xef4444 });
264
+ head.poly([16, -72, 24, -88, 12, -76]).fill({ color: 0xef4444 });
265
+ head.poly([-16, -46, -10, -40, 0, -42, 10, -40, 16, -46, 12, -36, -12, -36]).fill({ color: 0xffffff });
266
+ } else if (skinVariant === "kitsune_ears") {
267
+ head.poly([-22, -72, -14, -88, -6, -76]).fill({ color: 0x1e2430 });
268
+ head.poly([-20, -73, -14, -85, -8, -76]).fill({ color: skinColor });
269
+ head.poly([6, -76, 14, -88, 22, -72]).fill({ color: 0x1e2430 });
270
+ head.poly([8, -76, 14, -85, 20, -73]).fill({ color: skinColor });
271
+ } else if (skinVariant === "conical_kasa") {
272
+ head.poly([-36, -72, 0, -92, 36, -72]).fill({ color: 0x18181b }).stroke({ width: 1.6, color: skinColor });
273
+ head.roundRect(-36, -72, 72, 2.5, 1).fill({ color: skinAccentColor });
274
+ } else if (skinVariant === "twin_katanas") {
275
+ head.poly([-24, -32, 28, -88, 25, -88, -27, -32]).fill({ color: skinColor });
276
+ head.poly([24, -32, -28, -88, -25, -88, 27, -32]).fill({ color: skinColor });
277
+ } else if (skinVariant === "tengu_beak") {
278
+ head.poly([-12, -48, 0, -34, 12, -48, 0, -44]).fill({ color: 0x0f172a }).stroke({ width: 1.5, color: skinColor });
279
+ } else if (skinVariant === "veil_shroud") {
280
+ head.roundRect(-22, -62, 44, 26, 6).fill({ color: 0x0f172a, alpha: 0.85 }).stroke({ width: 1.2, color: skinColor });
281
+ } else if (skinVariant === "scythe_crest") {
282
+ head.poly([-14, -66, 0, -88, 14, -66, 0, -82]).fill({ color: skinColor });
283
+ } else if (skinVariant === "void_mask") {
284
+ head.roundRect(-18, -62, 36, 20, 4).fill({ color: 0x000000 }).stroke({ width: 1.8, color: skinColor });
285
+ head.circle(0, -52, 3).fill({ color: skinColor });
286
+ }
287
+ // 4. MATRIX HACKER VARIANTS
288
+ else if (skinVariant === "hoodie_cowl") {
289
+ head.roundRect(-28, -74, 56, 46, 14).fill({ color: 0x051f0b, alpha: 0.9 }).stroke({ width: 1.8, color: skinColor });
290
+ } else if (skinVariant === "vr_goggles") {
291
+ head.roundRect(-22, -64, 44, 15, 3).fill({ color: 0x083344 }).stroke({ width: 1.8, color: skinColor });
292
+ head.circle(-16, -78, 2.5).fill({ color: skinAccentColor });
293
+ head.poly([-16, -64, -16, -78, -14, -78, -14, -64]).fill({ color: skinColor });
294
+ } else if (skinVariant === "daemon_horns") {
295
+ head.poly([-16, -70, -22, -86, -10, -74]).fill({ color: skinColor });
296
+ head.poly([16, -70, 22, -86, 10, -74]).fill({ color: skinColor });
297
+ } else if (skinVariant === "glitch_halo") {
298
+ head.rect(-28, -76, 56, 42).stroke({ width: 1.5, color: skinColor, alpha: 0.8 });
299
+ head.rect(-18, -82, 12, 6).fill({ color: skinColor, alpha: 0.7 });
300
+ } else if (skinVariant === "floating_hud") {
301
+ head.roundRect(24, -78, 30, 16, 2).fill({ color: 0x09090b }).stroke({ width: 1, color: skinColor });
302
+ } else if (skinVariant === "respirator_eq") {
303
+ head.roundRect(-18, -46, 36, 10, 3).fill({ color: 0x0f172a }).stroke({ width: 1.2, color: skinColor });
304
+ head.rect(-12, -43, 4, 5).fill({ color: 0x22c55e });
305
+ head.rect(-4, -45, 4, 7).fill({ color: 0xeab308 });
306
+ head.rect(4, -45, 4, 7).fill({ color: 0xef4444 });
307
+ head.rect(12, -43, 4, 5).fill({ color: 0x22c55e });
308
+ } else if (skinVariant === "heatsink_fins") {
309
+ head.rect(-18, -84, 4, 14).fill({ color: 0xea580c });
310
+ head.rect(-10, -88, 4, 18).fill({ color: 0xea580c });
311
+ head.rect(-2, -90, 4, 20).fill({ color: 0xea580c });
312
+ head.rect(6, -88, 4, 18).fill({ color: 0xea580c });
313
+ head.rect(14, -84, 4, 14).fill({ color: 0xea580c });
314
+ } else if (skinVariant === "cable_dreads") {
315
+ head.poly([-22, -62, -32, -48, -28, -12, -26, -12, -30, -48, -20, -62]).fill({ color: skinColor });
316
+ head.poly([22, -62, 32, -48, 28, -12, 26, -12, 30, -48, 20, -62]).fill({ color: skinColor });
317
+ } else if (skinVariant === "cyber_skull") {
318
+ head.circle(-7, -55, 4).fill({ color: 0x000000 }).stroke({ width: 1.5, color: skinColor });
319
+ head.circle(7, -55, 4).fill({ color: 0x000000 }).stroke({ width: 1.5, color: skinColor });
320
+ head.poly([-8, -40, 8, -40, 8, -38, -8, -38]).fill({ color: skinColor });
321
+ } else if (skinVariant === "wireframe_cube") {
322
+ head.rect(-24, -76, 48, 42).stroke({ width: 1.6, color: skinColor });
323
+ head.rect(-16, -68, 32, 26).stroke({ width: 1.2, color: skinAccentColor, alpha: 0.8 });
324
+ }
325
+ // 5. CELESTIAL ASTRO VARIANTS
326
+ else if (skinVariant === "bubble_dome") {
327
+ head.circle(0, -56, 28).stroke({ width: 2.5, color: skinColor, alpha: 0.9 });
328
+ head.circle(0, -84, 3.5).fill({ color: 0xfef08a });
329
+ } else if (skinVariant === "lunar_pack") {
330
+ head.rect(-20, -86, 2, 18).fill({ color: skinColor });
331
+ head.circle(-19, -86, 2).fill({ color: 0xffffff });
332
+ head.ellipse(0, -54, 24, 18).stroke({ width: 2, color: 0xe2e8f0 });
333
+ } else if (skinVariant === "sunburst_halo") {
334
+ head.ellipse(0, -64, 30, 8).stroke({ width: 2, color: skinColor, alpha: 0.85 });
335
+ head.circle(0, -76, 3).fill({ color: 0xfef08a });
336
+ head.circle(-22, -70, 2).fill({ color: 0xfef08a });
337
+ head.circle(22, -70, 2).fill({ color: 0xfef08a });
338
+ } else if (skinVariant === "saturn_ring") {
339
+ head.ellipse(0, -56, 36, 10).stroke({ width: 2.2, color: skinColor });
340
+ head.circle(28, -62, 3).fill({ color: skinAccentColor });
341
+ } else if (skinVariant === "star_crown") {
342
+ head.poly([-16, -74, -12, -86, -8, -76]).fill({ color: skinColor });
343
+ head.poly([-6, -76, 0, -92, 6, -76]).fill({ color: skinColor });
344
+ head.poly([8, -76, 12, -86, 16, -74]).fill({ color: skinColor });
345
+ head.circle(0, -74, 2.5).fill({ color: 0xffffff });
346
+ } else if (skinVariant === "eclipse_corona") {
347
+ head.ellipse(0, -56, 32, 18).stroke({ width: 3, color: skinColor, alpha: 0.6 });
348
+ head.circle(0, -56, 22).fill({ color: 0x000000 }).stroke({ width: 1.5, color: skinColor });
349
+ } else if (skinVariant === "pulsar_spires") {
350
+ head.poly([-1.5, -78, 1.5, -78, 1.5, -98, -1.5, -98]).fill({ color: skinColor });
351
+ head.circle(0, -98, 3).fill({ color: 0xffffff });
352
+ head.poly([-1.5, -34, 1.5, -34, 1.5, -18, -1.5, -18]).fill({ color: skinColor });
353
+ head.circle(0, -18, 3).fill({ color: 0xffffff });
354
+ } else if (skinVariant === "aurora_ribbons") {
355
+ head.ellipse(0, -78, 24, 6).stroke({ width: 2.5, color: skinColor, alpha: 0.75 });
356
+ } else if (skinVariant === "angel_wings") {
357
+ head.poly([-26, -46, -48, -78, -32, -38]).fill({ color: skinColor, alpha: 0.85 });
358
+ head.poly([26, -46, 48, -78, 32, -38]).fill({ color: skinColor, alpha: 0.85 });
359
+ head.ellipse(0, -80, 16, 5).stroke({ width: 1.6, color: skinAccentColor });
360
+ } else if (skinVariant === "singularity_core") {
361
+ head.ellipse(0, -56, 30, 26).stroke({ width: 2, color: 0xffffff, alpha: 0.9 });
362
+ head.circle(-22, -70, 2.5).fill({ color: skinAccentColor });
363
+ head.circle(22, -42, 2.5).fill({ color: skinAccentColor });
364
+ }
365
+ // Archetype fallbacks
366
+ else if (skinArchetype === "stealth_ninja" || skinArchetype === "ninja") {
367
+ head.roundRect(-24, -66, 48, 8, 3).fill({ color: 0x1e1b4b }).stroke({ width: 1.5, color: skinColor });
123
368
  head.circle(0, -62, 3).fill({ color: 0x38bdf8 });
124
- head.poly([24, -62, 38, -56, 32, -50, 22, -56]).fill({ color: skinColor, alpha: 0.9 });
125
369
  } else if (skinArchetype === "mecha_pilot") {
126
- // Dual Heavy Antennas
127
370
  head.poly([-22, -72, -18, -90, -12, -70]).fill({ color: skinColor });
128
371
  head.poly([12, -70, 18, -90, 22, -72]).fill({ color: skinColor });
129
- head.circle(-18, -90, 2.5).fill({ color: 0xffffff });
130
- head.circle(18, -90, 2.5).fill({ color: 0xffffff });
131
372
  } else if (skinArchetype === "celestial_astro") {
132
- // Golden Solar Halo
133
373
  head.ellipse(0, -64, 30, 8).stroke({ width: 2, color: skinColor, alpha: 0.85 });
134
- head.circle(0, -72, 3).fill({ color: 0xfef08a });
135
374
  } else if (skinArchetype === "matrix_hacker" || skinArchetype === "hacker") {
136
- // Hacker Matrix Hoodie Cowl
137
- head.roundRect(-27, -78, 54, 48, 16).fill({ color: 0x111827 });
138
- head.roundRect(-27, -78, 54, 48, 16).stroke({ width: 1.6, color: skinColor });
375
+ head.roundRect(-27, -78, 54, 48, 16).fill({ color: 0x111827 }).stroke({ width: 1.6, color: skinColor });
139
376
  } else if (skinArchetype === "cyber_cat" || skin === "cat") {
140
- // Cute Cat Ears
141
377
  head.poly([-22, -72, -14, -86, -6, -76]).fill({ color: SHELL });
142
- head.poly([-20, -73, -14, -83, -8, -76]).fill({ color: 0xf472b6, alpha: 0.8 }); // pink inner ear
378
+ head.poly([-20, -73, -14, -83, -8, -76]).fill({ color: 0xf472b6, alpha: 0.8 });
143
379
  head.poly([6, -76, 14, -86, 22, -72]).fill({ color: SHELL });
144
380
  head.poly([8, -76, 14, -83, 20, -73]).fill({ color: 0xf472b6, alpha: 0.8 });
145
381
  } else {
146
- // High-Tech Crest
147
382
  head.poly([-8, -68, 0, -78, 8, -68]).fill({ color: skinColor });
148
383
  }
149
384