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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +8 -8
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +15 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +15 -13
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +8 -6
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/media/route.js.nft.json +1 -1
- package/.next/server/app/api/reports/tokens/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/transcript/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/transcript/route.js.nft.json +1 -1
- package/.next/server/app/api/traces/stream/route.js +1 -1
- package/.next/server/app/api/traces/stream/route.js.nft.json +1 -1
- package/.next/server/app/api/version/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/file-content/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
- package/.next/server/app/chat/page.js.nft.json +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/chat.html +1 -1
- package/.next/server/app/chat.rsc +20 -19
- package/.next/server/app/chat.segments/_full.segment.rsc +20 -19
- package/.next/server/app/chat.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/chat.segments/chat/__PAGE__.segment.rsc +22 -20
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +20 -18
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +10 -9
- package/.next/server/app/index.segments/_full.segment.rsc +20 -18
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/reports/page.js.nft.json +1 -1
- package/.next/server/app/reports/page_client-reference-manifest.js +1 -1
- package/.next/server/app/reports.html +1 -1
- package/.next/server/app/reports.rsc +4 -4
- package/.next/server/app/reports.segments/_full.segment.rsc +4 -4
- package/.next/server/app/reports.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/reports.segments/reports/__PAGE__.segment.rsc +6 -5
- package/.next/server/app/store/page.js.nft.json +1 -1
- package/.next/server/app/store/page_client-reference-manifest.js +1 -1
- package/.next/server/app/store.html +1 -1
- package/.next/server/app/store.rsc +5 -5
- package/.next/server/app/store.segments/_full.segment.rsc +5 -5
- package/.next/server/app/store.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/store.segments/store/__PAGE__.segment.rsc +7 -6
- package/.next/server/chunks/[root-of-the-server]__0dw97_pahi8uf._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0dw97_pahi8uf._.js.map +1 -0
- package/.next/server/chunks/_0ao-_t4jkb46h._.js +2 -2
- package/.next/server/chunks/_0ao-_t4jkb46h._.js.map +1 -1
- package/.next/server/chunks/_1-8ipqazoyrrk._.js +1 -1
- package/.next/server/chunks/_1-8ipqazoyrrk._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0i2rbwbkxbdog._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0i2rbwbkxbdog._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0qzscpxebh28_._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0qzscpxebh28_._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1jwplflwix8wi._.js +427 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1jwplflwix8wi._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1kt7h7bz59mdn._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1kt7h7bz59mdn._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1otsvjy81s8my._.js +427 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1otsvjy81s8my._.js.map +1 -0
- package/.next/server/chunks/ssr/_0e3frtjrk-wi9._.js +3 -0
- package/.next/server/chunks/ssr/_0e3frtjrk-wi9._.js.map +1 -0
- package/.next/server/chunks/ssr/_0h29jdf4relbq._.js +3 -0
- package/.next/server/chunks/ssr/_0h29jdf4relbq._.js.map +1 -0
- package/.next/server/chunks/ssr/_0zel-n0h--cnl._.js +3 -0
- package/.next/server/chunks/ssr/_0zel-n0h--cnl._.js.map +1 -0
- package/.next/server/chunks/ssr/_1487-zi23c5eb._.js +1 -1
- package/.next/server/chunks/ssr/_1487-zi23c5eb._.js.map +1 -1
- package/.next/server/chunks/ssr/_1vgwoe-shb9z7._.js +3 -0
- package/.next/server/chunks/ssr/_1vgwoe-shb9z7._.js.map +1 -0
- package/.next/server/chunks/ssr/_1zm4m-s8k7oe3._.js +3 -0
- package/.next/server/chunks/ssr/_1zm4m-s8k7oe3._.js.map +1 -0
- package/.next/server/chunks/ssr/_202tntue-jj6s._.js +3 -0
- package/.next/server/chunks/ssr/_202tntue-jj6s._.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_1wlwrgnt65zus._.js → node_modules_00ebs67b4u61w._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0f773uqgzj7y7._.js → node_modules_01avo0bde3tpo._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0z3elum1675ve._.js → node_modules_01l0208e5kqic._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1c8i3jrmo4393._.js → node_modules_033x29yqle_sj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1kr1c-ncf8q9t._.js → node_modules_03wbg__aslvui._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_02jwra-d9iqim._.js → node_modules_06mgkab1v5iox._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0z7rdbaf13024._.js → node_modules_08-bqkrz7sh0l._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_13nsmuhey_ti_._.js → node_modules_089w9jm7s2qa3._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0m3ihe65818y_._.js → node_modules_08q6a-f2itxyo._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0__m9_odts7_e._.js → node_modules_0cy47-s63xjzh._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0b8ndnuql44z1._.js → node_modules_0czh0jcnsm5e6._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0fs8mckuwk2tj._.js → node_modules_0e6qoh__e4a69._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_05w7qhtrzvnk6._.js → node_modules_0fv321j1nslkb._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_061uaylfjwqk7._.js → node_modules_0g-wb7h-hsdwc._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ll-58c1c-qab._.js → node_modules_0hb0q0epwvp--._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0dq0dsblt-q0g._.js → node_modules_0l-y0kqtu_r3o._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1zs2nou5uogby._.js → node_modules_0l814qpw677vm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1xyxigbqzb8vs._.js → node_modules_0o63z1gd17rhb._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1-sz_-qddwilq._.js → node_modules_0oz_zzlh8exwx._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0oukyh0iq21ow._.js → node_modules_0so2agzqd9p8l._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_12qw2ucebdvol._.js → node_modules_0w2w61b8bh769._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1dff7b_kvngr-._.js → node_modules_0wd6szix_zku7._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0fd1swhk15cx3._.js → node_modules_0wp7omf0xblv8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0nde84kzuwv2n._.js → node_modules_0yayrqhzfk8wy._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0f4xh33v3iltc._.js → node_modules_0zb0i39mnjdf3._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1iovyom0-y7o_._.js → node_modules_10c51o2qv6mbi._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_02nv3yff2pqh8._.js → node_modules_10g1mmrf63d8a._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0-6sb1gkbjo93._.js → node_modules_152txg4c7ose3._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_12qn4bepdou5v._.js → node_modules_185fuy4cv7loa._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_1-iyhsn_1p9nx._.js → node_modules_19vqq7rvf-_ar._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0hqa9oxz17hip._.js → node_modules_1hd7j-tdb8drq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1rlnehdn16ay-._.js → node_modules_1hu83gui7_nj2._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_07maxxr-t28ql._.js → node_modules_1i3x2csuq_-jt._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0kovk4b6czb22._.js → node_modules_1j8hpoiygrs5e._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0zc49fdcopqu9._.js → node_modules_1l2g7btuh3e4s._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0t4h1rucl35hl._.js → node_modules_1n4kig4mj6n4s._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0vn0kjn_84lmn._.js → node_modules_1nz3ckv6tu_66._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0-nvg0c4pgfb8._.js → node_modules_1qnypguag6s_l._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1v4lexrfhnlhs._.js → node_modules_1r6w-jlme6d10._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1f4geuvkvlxj1._.js → node_modules_1s8oxeqa5_i3k._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1216007ogd5om._.js → node_modules_1tskcaktx6ods._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_02vtgweceovke._.js → node_modules_1xz1dstlo5ri-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1kv6lsckqs-m0._.js → node_modules_1zii7uk468cay._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0bmuudv_6eypr._.js → node_modules_katex_dist_katex_mjs_067omoj5szuys._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_katex_dist_katex_mjs_0qyy6pq9uzpk0._.js → node_modules_katex_dist_katex_mjs_1604s1ky8db8u._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_1d8fezw1qsiob._.js → node_modules_lucide-react_dist_esm_0-5bcc79665lq._.js} +2 -2
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0-5bcc79665lq._.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_lucide-react_dist_esm_0pezg1352q13j._.js → node_modules_lucide-react_dist_esm_0axoa1eg51x1-._.js} +2 -2
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0axoa1eg51x1-._.js.map +1 -0
- 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
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_18ei1-na3kusb._.js.map +1 -0
- 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
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1tx493x2_auzs._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_mermaid_dist_mermaid_core_mjs_06it7ktosm1dr._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_mermaid_dist_mermaid_core_mjs_06it7ktosm1dr._.js.map +1 -0
- package/.next/server/chunks/ssr/src_app_page_1vqfk0-j72fp6.js +1 -1
- package/.next/server/chunks/ssr/src_app_page_1vqfk0-j72fp6.js.map +1 -1
- package/.next/server/chunks/ssr/src_components_reports_TokenReportView_jsx_1g3p_enbbsadt._.js +1 -1
- package/.next/server/chunks/ssr/src_components_reports_TokenReportView_jsx_1g3p_enbbsadt._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/preview-props.json +3 -3
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0jo6fy2c_gmqn.js +1 -0
- package/.next/static/chunks/0r9apkv3exqv-.js +1 -0
- package/.next/static/chunks/1n_lb7wmn81jj.js +1 -0
- package/.next/static/chunks/2-w116ewoshm1.js +1 -0
- package/.next/static/chunks/23uup1e8841av.js +1 -0
- package/.next/static/chunks/2q9cwykv6ox1t.css +1 -0
- package/.next/static/chunks/2rx7xvi55ohln.js +1 -0
- package/.next/static/chunks/2zf44j8sjo0w0.js +425 -0
- package/.next/static/chunks/3rtpnpqd981z9.js +1 -0
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/bin/cli.js +12 -0
- package/package.json +1 -1
- package/src/app/api/traces/stream/route.js +41 -5
- package/src/app/chat/page.js +14 -14
- package/src/app/globals.css +128 -0
- package/src/app/layout.js +8 -2
- package/src/app/page.js +18 -2
- package/src/components/chat/ChatMessageItem.js +12 -12
- package/src/components/chat/CodeBlock.js +5 -5
- package/src/components/chat/DiffViewer.js +27 -27
- package/src/components/chat/MarkdownRenderer.js +19 -19
- package/src/components/chat/SessionChatView.js +42 -42
- package/src/components/chat/SessionSearch.js +8 -8
- package/src/components/chat/ThinkingAccordion.js +3 -3
- package/src/components/chat/ToolCallCard.js +6 -6
- package/src/components/chat/ToolOutputView.js +19 -19
- package/src/components/factory/office-canvas.js +165 -3
- package/src/components/factory/scene/characters.js +267 -32
- package/src/components/layout/BottomPanel.jsx +6 -6
- package/src/components/layout/CommandPalette.jsx +12 -0
- package/src/components/layout/EditorTabs.jsx +17 -0
- package/src/components/layout/LayoutToggles.jsx +31 -2
- package/src/components/layout/LeftSidebar.jsx +88 -6
- package/src/components/layout/RightSidebar.jsx +8 -8
- package/src/components/layout/StatusBar.jsx +1 -1
- package/src/components/layout/TitleBar.jsx +76 -2
- package/src/components/layout/VSCodeWorkbench.jsx +99 -14
- package/src/components/store/Item3DRenderer.jsx +602 -48
- package/src/components/store/LiveFittingRoom.jsx +55 -791
- package/src/components/store/LiveReactAgent.jsx +1637 -0
- package/src/components/store/SupporterStoreModal.jsx +3 -2
- package/src/components/store/SupporterStoreView.jsx +94 -34
- package/src/components/theme/ThemeSettingsModal.jsx +40 -40
- package/src/components/ui/Button.jsx +4 -4
- package/src/components/ui/Input.jsx +1 -1
- package/src/components/ui/Modal.jsx +6 -6
- package/src/components/ui/Select.jsx +3 -3
- package/src/lib/catalog/auras.js +226 -1199
- package/src/lib/catalog/index.js +60 -1
- package/src/lib/catalog/pets.js +291 -2398
- package/src/lib/catalog/props.js +488 -4199
- package/src/lib/catalog/skins.js +773 -3249
- package/src/lib/catalog/trophies.js +226 -1199
- package/src/lib/mockTraces.js +28 -26
- package/src/lib/parsers/codexParser.js +373 -0
- package/src/lib/parsers/transcriptParser.js +35 -3
- package/src/lib/supporterStore.js +1 -1
- package/src/lib/traceContract.js +1 -1
- package/src/lib/useFactoryTraces.js +70 -5
- package/src/lib/watchers/antigravityWatcher.js +15 -1
- package/src/lib/watchers/claudeWatcher.js +86 -70
- package/src/lib/watchers/codexWatcher.js +482 -0
- package/src/lib/watchers/watcherManager.js +46 -30
- package/.next/server/chunks/src_lib_watchers_antigravityWatcher_0w1-atg39y__p.js +0 -3
- package/.next/server/chunks/src_lib_watchers_antigravityWatcher_0w1-atg39y__p.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__19s2v5rryc14o._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__19s2v5rryc14o._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1_jerxrctq6ev._.js +0 -427
- package/.next/server/chunks/ssr/[root-of-the-server]__1_jerxrctq6ev._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1pvso-uyf--97._.js +0 -427
- package/.next/server/chunks/ssr/[root-of-the-server]__1pvso-uyf--97._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1y78dc7oiz7ks._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1y78dc7oiz7ks._.js.map +0 -1
- package/.next/server/chunks/ssr/_0ozao7omdwbvd._.js +0 -3
- package/.next/server/chunks/ssr/_0ozao7omdwbvd._.js.map +0 -1
- package/.next/server/chunks/ssr/_0rcmpdyxc1qwj._.js +0 -3
- package/.next/server/chunks/ssr/_0rcmpdyxc1qwj._.js.map +0 -1
- package/.next/server/chunks/ssr/_0wv0xnhun3mk7._.js +0 -3
- package/.next/server/chunks/ssr/_0wv0xnhun3mk7._.js.map +0 -1
- package/.next/server/chunks/ssr/_17rmaft1zusua._.js +0 -3
- package/.next/server/chunks/ssr/_17rmaft1zusua._.js.map +0 -1
- package/.next/server/chunks/ssr/_1e-d4pcj59a7a._.js +0 -3
- package/.next/server/chunks/ssr/_1e-d4pcj59a7a._.js.map +0 -1
- package/.next/server/chunks/ssr/_1qr5gnh64z8pg._.js +0 -3
- package/.next/server/chunks/ssr/_1qr5gnh64z8pg._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_0pezg1352q13j._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_131i-tjzse3ye._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1d8fezw1qsiob._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_1to-9cv1ysvs6._.js.map +0 -1
- package/.next/static/chunks/0_3s_e4gd9xl0.js +0 -1
- package/.next/static/chunks/0i4kx4yl-dwfg.js +0 -425
- package/.next/static/chunks/0z0jbobmmvqoc.js +0 -1
- package/.next/static/chunks/1_a_gmob5qzho.js +0 -1
- package/.next/static/chunks/1ifqr4qo4697b.js +0 -1
- package/.next/static/chunks/2ccq9i8ev4dsx.js +0 -1
- package/.next/static/chunks/2mif7onk_9jy-.css +0 -1
- package/.next/static/chunks/40wyf-mv0tiu3.js +0 -1
- /package/.next/server/chunks/ssr/{node_modules_1c8i3jrmo4393._.js.map → node_modules_00ebs67b4u61w._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0f773uqgzj7y7._.js.map → node_modules_01avo0bde3tpo._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0z3elum1675ve._.js.map → node_modules_01l0208e5kqic._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1wlwrgnt65zus._.js.map → node_modules_033x29yqle_sj._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1kr1c-ncf8q9t._.js.map → node_modules_03wbg__aslvui._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_02jwra-d9iqim._.js.map → node_modules_06mgkab1v5iox._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0fs8mckuwk2tj._.js.map → node_modules_08-bqkrz7sh0l._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0-6sb1gkbjo93._.js.map → node_modules_089w9jm7s2qa3._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_02vtgweceovke._.js.map → node_modules_08q6a-f2itxyo._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0__m9_odts7_e._.js.map → node_modules_0cy47-s63xjzh._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_02nv3yff2pqh8._.js.map → node_modules_0czh0jcnsm5e6._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0z7rdbaf13024._.js.map → node_modules_0e6qoh__e4a69._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0-nvg0c4pgfb8._.js.map → node_modules_0fv321j1nslkb._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_061uaylfjwqk7._.js.map → node_modules_0g-wb7h-hsdwc._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0t4h1rucl35hl._.js.map → node_modules_0hb0q0epwvp--._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0dq0dsblt-q0g._.js.map → node_modules_0l-y0kqtu_r3o._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0zc49fdcopqu9._.js.map → node_modules_0l814qpw677vm._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1xyxigbqzb8vs._.js.map → node_modules_0o63z1gd17rhb._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1-iyhsn_1p9nx._.js.map → node_modules_0oz_zzlh8exwx._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0oukyh0iq21ow._.js.map → node_modules_0so2agzqd9p8l._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_07maxxr-t28ql._.js.map → node_modules_0w2w61b8bh769._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1dff7b_kvngr-._.js.map → node_modules_0wd6szix_zku7._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0fd1swhk15cx3._.js.map → node_modules_0wp7omf0xblv8._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0nde84kzuwv2n._.js.map → node_modules_0yayrqhzfk8wy._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0f4xh33v3iltc._.js.map → node_modules_0zb0i39mnjdf3._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1-sz_-qddwilq._.js.map → node_modules_10c51o2qv6mbi._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0b8ndnuql44z1._.js.map → node_modules_10g1mmrf63d8a._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_13nsmuhey_ti_._.js.map → node_modules_152txg4c7ose3._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_12qn4bepdou5v._.js.map → node_modules_185fuy4cv7loa._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1iovyom0-y7o_._.js.map → node_modules_19vqq7rvf-_ar._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0hqa9oxz17hip._.js.map → node_modules_1hd7j-tdb8drq._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1rlnehdn16ay-._.js.map → node_modules_1hu83gui7_nj2._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_12qw2ucebdvol._.js.map → node_modules_1i3x2csuq_-jt._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0kovk4b6czb22._.js.map → node_modules_1j8hpoiygrs5e._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1ll-58c1c-qab._.js.map → node_modules_1l2g7btuh3e4s._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1zs2nou5uogby._.js.map → node_modules_1n4kig4mj6n4s._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0vn0kjn_84lmn._.js.map → node_modules_1nz3ckv6tu_66._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_05w7qhtrzvnk6._.js.map → node_modules_1qnypguag6s_l._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1v4lexrfhnlhs._.js.map → node_modules_1r6w-jlme6d10._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1f4geuvkvlxj1._.js.map → node_modules_1s8oxeqa5_i3k._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1216007ogd5om._.js.map → node_modules_1tskcaktx6ods._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_0m3ihe65818y_._.js.map → node_modules_1xz1dstlo5ri-._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{node_modules_1kv6lsckqs-m0._.js.map → node_modules_1zii7uk468cay._.js.map} +0 -0
- /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
- /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
- /package/.next/static/{sQnXeiP8rV6y2v0az_xw3 → dGN2VKPk5TJgezxS3JvRc}/_buildManifest.js +0 -0
- /package/.next/static/{sQnXeiP8rV6y2v0az_xw3 → dGN2VKPk5TJgezxS3JvRc}/_clientMiddlewareManifest.js +0 -0
- /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-
|
|
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-
|
|
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-
|
|
73
|
-
<span className="text-[10px] text-
|
|
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-
|
|
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-
|
|
85
|
-
: "text-
|
|
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-
|
|
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-
|
|
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-
|
|
129
|
-
<span className="text-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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({
|
|
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
|
|
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:
|
|
62
|
+
// Torso: 5 Distinct Staff Uniforms & Silhouettes
|
|
60
63
|
const body = new Graphics();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
body.
|
|
70
|
-
|
|
71
|
-
body.
|
|
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
|
|
112
|
-
|
|
113
|
-
|
|
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:
|
|
119
|
-
} else if (
|
|
120
|
-
|
|
121
|
-
head.
|
|
122
|
-
|
|
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
|
-
|
|
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 });
|
|
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
|
|