thursday-agent 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +3 -2
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.json +8 -1
- package/.next/routes-manifest.json +18 -6
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- 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 +13 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/artifact/route.js +10 -6
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js +5 -4
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js +8 -8
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js +8 -8
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/thread/[id]/route.js +15 -0
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/thread/[id]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/thread/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/thread/route.js +15 -0
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/thread/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/config/route.js +7 -7
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js +3 -3
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js +4 -4
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js +8 -8
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route.js +8 -8
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js +8 -8
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route.js +8 -8
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js +6 -6
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js +4 -4
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js +6 -6
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js +6 -6
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js +15 -15
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js +7 -6
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js +6 -6
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +6 -6
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js +10 -11
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js +9 -10
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +15 -15
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/missing/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/workspace/missing/route/build-manifest.json +12 -0
- package/.next/server/app/api/workspace/missing/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/workspace/missing/route.js +12 -0
- package/.next/server/app/api/workspace/missing/route.js.map +5 -0
- package/.next/server/app/api/workspace/missing/route.js.nft.json +1 -0
- package/.next/server/app/api/workspace/missing/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/workspace/route.js +5 -5
- package/.next/server/app/api/workspace/route.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
- package/.next/server/app/artifact/[...path]/page.js +4 -3
- package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/page/server-reference-manifest.json +67 -45
- package/.next/server/app/page.js +11 -9
- 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-paths-manifest.json +3 -2
- package/.next/server/chunks/[root-of-the-server]__0-06t3f._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__02846fb._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__05luceg._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__06--3q3._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__06l05n_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__07bnah_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__08eae20._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0btqrct._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +5 -0
- package/.next/server/chunks/{[root-of-the-server]__0pa2n68._.js → [root-of-the-server]__0kxrvxf._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__0q0vvt2._.js → [root-of-the-server]__0ov3iir._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__0qwtlmx._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0zp35ul._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__144u9d_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__18-2tsq._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1e2365e._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__1fix932._.js +8 -0
- package/.next/server/chunks/{[root-of-the-server]__0ce8nf8._.js → [root-of-the-server]__1g3t7vn._.js} +3 -3
- package/.next/server/chunks/[root-of-the-server]__1kp-83u._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1kv8p3e._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +2 -3
- package/.next/server/chunks/_02szwk8._.js +5 -0
- package/.next/server/chunks/_05h2cii._.js +1 -1
- package/.next/server/chunks/_06c9kie._.js +216 -0
- package/.next/server/chunks/{_1x6kd1r._.js → _07y2gj-._.js} +1 -1
- package/.next/server/chunks/_0a43a-0._.js +3 -0
- package/.next/server/chunks/_0bujjey._.js +5 -0
- package/.next/server/chunks/_0c9j6rb._.js +1 -1
- package/.next/server/chunks/_0dkd549._.js +3 -0
- package/.next/server/chunks/_0ej1kvv._.js +3 -0
- package/.next/server/chunks/_0f6ts7z._.js +3 -0
- package/.next/server/chunks/_0gm9hyv._.js +55 -0
- package/.next/server/chunks/_0i55en5._.js +25 -0
- package/.next/server/chunks/_0k50n27._.js +18 -0
- package/.next/server/chunks/_0khihjk._.js +3 -3
- package/.next/server/chunks/_0n74zsf._.js +5 -5
- package/.next/server/chunks/_0nvm9ef._.js +3 -0
- package/.next/server/chunks/_0p56fn0._.js +3 -0
- package/.next/server/chunks/_0recufk._.js +1 -1
- package/.next/server/chunks/_0xbkz10._.js +3 -0
- package/.next/server/chunks/_10tkr26._.js +3 -0
- package/.next/server/chunks/{_0ioz3nd._.js → _11u2kp7._.js} +1 -1
- package/.next/server/chunks/_11ujpya._.js +3 -0
- package/.next/server/chunks/_127nx_p._.js +5 -0
- package/.next/server/chunks/_17l5ygt._.js +1 -1
- package/.next/server/chunks/_19uauj6._.js +3 -0
- package/.next/server/chunks/_1_5mnq4._.js +5 -5
- package/.next/server/chunks/_1abl6t9._.js +55 -0
- package/.next/server/chunks/_1d0j961._.js +3 -3
- package/.next/server/chunks/_1dbixxz._.js +3 -0
- package/.next/server/chunks/_1dc_d1q._.js +98 -0
- package/.next/server/chunks/_1drgfx4._.js +5 -5
- package/.next/server/chunks/_1e48czp._.js +3 -3
- package/.next/server/chunks/_1f31u5x._.js +5 -0
- package/.next/server/chunks/_1gnjfli._.js +3 -0
- package/.next/server/chunks/_1kg70wc._.js +3 -3
- package/.next/server/chunks/_1klbcft._.js +5 -0
- package/.next/server/chunks/_1krd9rf._.js +4 -1
- package/.next/server/chunks/_1kwmzab._.js +3 -0
- package/.next/server/chunks/{_1pf4qe-._.js → _1oxj9fa._.js} +1 -1
- package/.next/server/chunks/{_1f46eie._.js → _1pcmdbb._.js} +1 -1
- package/.next/server/chunks/_1qgix-j._.js +3 -0
- package/.next/server/chunks/{lib_logger_ts_1exwjut._.js → _1rpchta._.js} +5 -5
- package/.next/server/chunks/_1vsej-s._.js +5 -0
- package/.next/server/chunks/_1yx0p_s._.js +5 -0
- package/.next/server/chunks/{_0rnf-6n._.js → _20fsr_z._.js} +6 -6
- package/.next/server/chunks/_2128hkj._.js +5 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_thread_[id]_route_actions_1fstjn2.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_thread_route_actions_003ccso.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_workspace_missing_route_actions_1fidi6p.js +3 -0
- package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
- package/.next/server/chunks/features_0-cnt0l._.js +4 -0
- package/.next/server/chunks/features_0hdaci0._.js +4 -0
- package/.next/server/chunks/features_1e39j7-._.js +8 -0
- package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
- package/.next/server/chunks/features_memory_1uqvksn._.js +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js → node_modules_consola_dist_chunks_prompt_mjs_0a7-pbw._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_17k8irj._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js → node_modules_consola_dist_chunks_prompt_mjs_107pxfn._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js → node_modules_consola_dist_chunks_prompt_mjs_15hbel8._.js} +1 -1
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_18i-23e._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1bewgf3._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1nlww1m._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1q77nfx._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xqdzx_._.js +3 -0
- package/.next/server/chunks/{node_modules_zod_v4_1801ax7._.js → node_modules_zod_v4_0lkux5-._.js} +2 -2
- package/.next/server/chunks/{node_modules_zod_v4_1cha_bw._.js → node_modules_zod_v4_1jmr9cq._.js} +2 -2
- package/.next/server/chunks/{node_modules_zod_v4_0ubdflj._.js → node_modules_zod_v4_1yrxmek._.js} +2 -2
- package/.next/server/chunks/node_modules_zod_v4_classic_errors_0tp0y-q.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0-e011f._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0pd8yr7._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__18dthbp._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__18e68w8._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +5 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +8 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1vdmfbe._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +85 -0
- package/.next/server/chunks/ssr/_0-2qsxo._.js +3 -0
- package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
- package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -97
- package/.next/server/chunks/ssr/_05yghx6._.js +3 -0
- package/.next/server/chunks/ssr/_07nbwap._.js +3 -0
- package/.next/server/chunks/ssr/_08c4z-9._.js +3 -0
- package/.next/server/chunks/ssr/_0_qfdlw._.js +4 -0
- package/.next/server/chunks/ssr/{_0m2lsd1._.js → _0b6_jv-._.js} +2 -2
- package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
- package/.next/server/chunks/ssr/_0geg212._.js +3 -0
- package/.next/server/chunks/ssr/{_1jdgjv9._.js → _0hxt2ul._.js} +2 -2
- package/.next/server/chunks/ssr/_0i_6q1w._.js +2 -2
- package/.next/server/chunks/ssr/_0lu1n_x._.js +1 -1
- package/.next/server/chunks/ssr/_0no7i1o._.js +3 -0
- package/.next/server/chunks/ssr/_0tzg--b._.js +3 -0
- package/.next/server/chunks/ssr/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
- package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
- package/.next/server/chunks/ssr/_114ylv7._.js +7 -0
- package/.next/server/chunks/ssr/_123y-ah._.js +3 -0
- package/.next/server/chunks/ssr/_12c-iwh._.js +3 -0
- package/.next/server/chunks/ssr/_12w42gi._.js +3 -0
- package/.next/server/chunks/ssr/_161kqav._.js +3 -0
- package/.next/server/chunks/ssr/_167hchk._.js +3 -0
- package/.next/server/chunks/ssr/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
- package/.next/server/chunks/ssr/_18opah7._.js +3 -0
- package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
- package/.next/server/chunks/ssr/{_0rzucul._.js → _1_x5spz._.js} +3 -3
- package/.next/server/chunks/ssr/_1a61fc9._.js +3 -0
- package/.next/server/chunks/ssr/_1ey7qn-._.js +70 -0
- package/.next/server/chunks/ssr/_1g9vs7q._.js +3 -0
- package/.next/server/chunks/ssr/_1jmz33w._.js +3 -0
- package/.next/server/chunks/ssr/_1rgtxhr._.js +5 -0
- package/.next/server/chunks/ssr/_1slp25y._.js +3 -0
- package/.next/server/chunks/ssr/_1w_t7h7._.js +101 -0
- package/.next/server/chunks/ssr/{_0s99bgb._.js → _1wyfaq_._.js} +9 -3
- package/.next/server/chunks/ssr/_200gt-m._.js +102 -0
- package/.next/server/chunks/ssr/components_ui_tabs_tsx_0ej40b4._.js +3 -0
- package/.next/server/chunks/ssr/features_bot_thread_query_ts_023eklt._.js +4 -0
- package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +8 -0
- package/.next/server/chunks/ssr/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
- package/.next/server/chunks/ssr/{node_modules_05-n6dz._.js → node_modules_02ai7br._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1chvq5f._.js → node_modules_02deexj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0m383r2._.js → node_modules_07za262._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_07dsgkx._.js → node_modules_0_s9get._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1a0kmjl._.js → node_modules_0n3c7h0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0zeg3x_._.js → node_modules_0od6jwr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1y79i83._.js → node_modules_0rdt7pk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_17bwo43._.js → node_modules_0tusx4d._.js} +3 -3
- package/.next/server/chunks/ssr/{node_modules_04sksn9._.js → node_modules_0w1z7kz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1uyk91w._.js → node_modules_0wdupfz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ifp389._.js → node_modules_1720w0a._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0u9da1h._.js → node_modules_18lsg7d._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k6n600._.js → node_modules_1b94fxf._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20c28js._.js → node_modules_1ecdo1s._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0fkeku3._.js → node_modules_1fa9ag7._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1j8j4pt._.js → node_modules_1fv-_si._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0-dr_o7._.js → node_modules_1hf461g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lz0oxc._.js → node_modules_1m71ta-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1t77e4n._.js → node_modules_1n93n-g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_16mkxuo._.js → node_modules_1n9lqu9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k7z2j6._.js → node_modules_1npnsvd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_056qgf-._.js → node_modules_1rn2r0n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0x3-ply._.js → node_modules_1seuurl._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0bvopy2._.js → node_modules_1vq-mfk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0iur59s._.js → node_modules_1vzyw1t._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k8j8n0._.js → node_modules_1w359w4._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1x0ztf4._.js → node_modules_1yajbl6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1-y7nni._.js → node_modules_20eqhy8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0bkwd0u._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_019sbis._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0p9a9su._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0s6piq3._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_1o553h-._.js → node_modules_@vercel_oidc_dist_05itbb1._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_trash_mjs_0px2gzr._.js +3 -0
- 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/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +68 -46
- package/.next/static/chunks/07vqbk_9t4em4.js +2 -0
- package/.next/static/chunks/08krja1zx7nqg.js +1 -0
- package/.next/static/chunks/0g4b348l0pve7.js +1 -0
- package/.next/static/chunks/0qcuwdk132jjt.js +1 -0
- package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
- package/.next/static/chunks/{11r2yp_yh9_qn.js → 0z69gvysmave7.js} +1 -1
- package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
- package/.next/static/chunks/12fvz1w5ows75.js +1 -0
- package/.next/static/chunks/14t03ned286iu.js +1 -0
- package/.next/static/chunks/156xxbkrz5lzu.js +18 -0
- package/.next/static/chunks/1fz14reod4lft.js +1 -0
- package/.next/static/chunks/1g23dbabl-vkr.js +1 -0
- package/.next/static/chunks/1ohslk4568tm3.js +1 -0
- package/.next/static/chunks/1qy_rc3oxfxok.js +1 -0
- package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
- package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
- package/.next/static/chunks/1zea7qdl-7365.js +1 -0
- package/.next/static/chunks/1zvqumkg7kmo0.css +1 -0
- package/.next/static/chunks/26hpqt3y6bogl.js +12 -0
- package/.next/static/chunks/2_j5mqm6d-z18.js +99 -0
- package/.next/static/chunks/2dt6tu7a22svs.js +1 -0
- package/.next/static/chunks/2h5ls3d86kya6.js +1 -0
- package/.next/static/chunks/2wc6y1ht6dfuy.js +1 -0
- package/.next/static/chunks/{435atsnhs7ksu.js → 2xr0zpll7thyi.js} +2 -2
- package/.next/static/chunks/31jztc-yq30m7.js +1 -0
- package/.next/static/chunks/391af0i8eljzk.js +1 -0
- package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
- package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
- package/.next/static/chunks/3p2s2cjokx1aa.js +5 -0
- package/.next/static/chunks/{408ueu4hfvcg7.js → 41s3fml5oivsa.js} +1 -1
- package/README.md +45 -193
- package/app/api/events/app-event.ts +6 -4
- package/app/api/query-key.ts +25 -16
- package/bin/database.mjs +37 -0
- package/bin/thursday.mjs +44 -28
- package/components/ui/custom-icon.tsx +0 -60
- package/components/ui/radio-group.tsx +37 -0
- package/components/ui/shiny-text.tsx +33 -6
- package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/migration.sql +80 -24
- package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/snapshot.json +689 -110
- package/database/migrations/20260916160713_thread_cancelled/migration.sql +2 -0
- package/database/migrations/{20260911030320_opposite_jack_power → 20260916160713_thread_cancelled}/snapshot.json +706 -84
- package/docs/how-it-works.md +40 -35
- package/docs/images/keeps-going.png +0 -0
- package/docs/images/two-minds.png +0 -0
- package/docs/live-calls.md +236 -0
- package/docs/thread-rooms.md +71 -0
- package/features/ai/chatgpt.ts +1 -2
- package/features/ai/live.schema.ts +128 -0
- package/features/ai/load-tools.ts +152 -79
- package/features/ai/model.schema.ts +2 -120
- package/features/ai/model.ts +52 -88
- package/features/ai/prompts/bot.prompt.ts +91 -214
- package/features/ai/prompts/live.prompt.ts +169 -0
- package/features/ai/prompts/prompt-helper.ts +92 -32
- package/features/ai/prompts/thursday.prompt.ts +164 -190
- package/features/ai/tools/bot.tool.ts +38 -145
- package/features/ai/tools/call.tool.ts +33 -5
- package/features/ai/tools/connected.ts +5 -3
- package/features/ai/tools/mcp.tool.ts +7 -4
- package/features/ai/tools/memory.tool.ts +35 -31
- package/features/ai/tools/skills.tool.ts +11 -6
- package/features/ai/tools/studio.tool.ts +26 -21
- package/features/ai/tools/tool-name.ts +3 -5
- package/features/ai/tools/workspace.tool.ts +33 -10
- package/features/artifact/artifact.action.ts +2 -2
- package/features/artifact/artifact.query.ts +112 -61
- package/features/artifact/artifact.schema.ts +8 -6
- package/features/artifact/components/artifact-setting.tsx +83 -33
- package/features/bot/bot.action.ts +43 -36
- package/features/bot/bot.memory.ts +137 -20
- package/features/bot/bot.query.ts +28 -11
- package/features/bot/bot.run.ts +296 -744
- package/features/bot/bot.runner.ts +463 -864
- package/features/bot/bot.schema.ts +93 -82
- package/features/bot/bot.seed.ts +141 -64
- package/features/bot/components/bot-mark.tsx +324 -232
- package/features/bot/components/bot-room.tsx +1613 -697
- package/features/bot/components/bot-roster.tsx +8 -13
- package/features/bot/components/bot-setting.tsx +58 -42
- package/features/bot/components/bot-tool.tsx +55 -87
- package/features/bot/components/mark-palette.tsx +35 -32
- package/features/bot/components/path-chips.tsx +33 -3
- package/features/bot/components/thread-badge.tsx +34 -0
- package/features/bot/components/thread-reply.tsx +639 -0
- package/features/bot/components/thread-setting.tsx +674 -0
- package/features/bot/mark.const.ts +79 -31
- package/features/bot/room.query.ts +889 -0
- package/features/bot/room.schema.ts +64 -0
- package/features/bot/seed-bots.ts +2 -1
- package/features/bot/thread.query.ts +971 -0
- package/features/bot/thread.store.ts +587 -0
- package/features/config/components/voice-key.tsx +113 -88
- package/features/config/config.const.ts +5 -9
- package/features/connectors/mcp.schema.ts +0 -2
- package/features/intro/components/intro.tsx +169 -139
- package/features/memory/components/memory-setting.tsx +2 -7
- package/features/memory/memory.schema.ts +14 -23
- package/features/settings/components/setting-ui.tsx +0 -44
- package/features/settings/components/settings.tsx +12 -12
- package/features/settings/settings.alert.ts +3 -3
- package/features/settings/settings.store.ts +1 -1
- package/features/skills/skills.schema.ts +0 -2
- package/features/thursday/ascii.const.ts +74 -0
- package/features/thursday/components/ascii-field.tsx +24 -44
- package/features/thursday/components/ascii-orb.tsx +525 -381
- package/features/thursday/components/call-log.tsx +46 -44
- package/features/thursday/components/face.tsx +40 -10
- package/features/thursday/components/side-captions.tsx +273 -0
- package/features/thursday/components/tab-state.tsx +14 -6
- package/features/thursday/components/thursday-mark.tsx +1 -0
- package/features/thursday/components/thursday-setting.tsx +633 -333
- package/features/thursday/components/thursday.tsx +315 -274
- package/features/thursday/components/voice-picker.tsx +255 -0
- package/features/thursday/face.store.ts +5 -0
- package/features/thursday/thursday.action.ts +96 -72
- package/features/thursday/thursday.query.ts +49 -30
- package/features/thursday/thursday.schema.ts +37 -35
- package/features/thursday/thursday.store.ts +14 -7
- package/features/thursday/tool-call.ts +3 -3
- package/features/thursday/tool-line.ts +5 -3
- package/features/thursday/use-thursday.ts +801 -457
- package/features/workspace/file-kind.ts +9 -0
- package/features/workspace/workspace.query.ts +17 -0
- package/features/workspace/workspace.ts +82 -36
- package/hooks/use-hotkey.ts +3 -2
- package/lib/live/live.schema.ts +26 -0
- package/lib/live/live.server.ts +153 -0
- package/lib/live/live.session.ts +574 -0
- package/lib/{realtime/realtime.tap.ts → live/live.tap.ts} +41 -12
- package/lib/live/live.transport.ts +152 -0
- package/lib/protocol/use-server-pages.ts +9 -5
- package/lib/utils.ts +21 -20
- package/package.json +2 -2
- package/public/voices/alloy.ogg +0 -0
- package/public/voices/ash.ogg +0 -0
- package/public/voices/ballad.ogg +0 -0
- package/public/voices/beacon.ogg +0 -0
- package/public/voices/bossa.ogg +0 -0
- package/public/voices/cedar.ogg +0 -0
- package/public/voices/cinder.ogg +0 -0
- package/public/voices/coral.ogg +0 -0
- package/public/voices/delta.ogg +0 -0
- package/public/voices/echo.ogg +0 -0
- package/public/voices/gleam.ogg +0 -0
- package/public/voices/marin.ogg +0 -0
- package/public/voices/meridian.ogg +0 -0
- package/public/voices/quartz.ogg +0 -0
- package/public/voices/ripple.ogg +0 -0
- package/public/voices/sage.ogg +0 -0
- package/public/voices/shimmer.ogg +0 -0
- package/public/voices/stone.ogg +0 -0
- package/public/voices/tempo.ogg +0 -0
- package/public/voices/verse.ogg +0 -0
- package/public/voices/vesper.ogg +0 -0
- package/public/voices/willow.ogg +0 -0
- package/scripts/artifact-paths.test.mts +38 -0
- package/scripts/bot-context.test.mts +1408 -0
- package/scripts/live-history.test.mts +115 -0
- package/scripts/live-session.test.mts +938 -0
- package/server.js +1 -1
- package/skills/browser/SKILL.md +11 -1
- package/skills/computer/SKILL.md +1 -1
- package/skills/find-skills/SKILL.md +3 -8
- package/skills/interactive-page/SKILL.md +12 -7
- package/skills/interactive-page/references/diagram.md +89 -0
- package/skills/interactive-page/scripts/archify/LICENSE +22 -0
- package/skills/interactive-page/scripts/archify/README.md +25 -0
- package/skills/interactive-page/scripts/archify/assets/JetBrainsMono-OFL.txt +93 -0
- package/skills/interactive-page/scripts/archify/assets/template.html +14934 -0
- package/skills/interactive-page/scripts/archify/bin/archify.mjs +2126 -0
- package/skills/interactive-page/scripts/archify/bin/open-artifact.mjs +86 -0
- package/skills/interactive-page/scripts/archify/bin/preview.mjs +653 -0
- package/skills/interactive-page/scripts/archify/bin/visual-check.mjs +829 -0
- package/skills/interactive-page/scripts/archify/delta/architecture-delta.mjs +1223 -0
- package/skills/interactive-page/scripts/archify/examples/agent-run.lifecycle.json +60 -0
- package/skills/interactive-page/scripts/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/interactive-page/scripts/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/interactive-page/scripts/archify/examples/cache-miss-request.sequence.json +82 -0
- package/skills/interactive-page/scripts/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/interactive-page/scripts/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/interactive-page/scripts/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/interactive-page/scripts/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/interactive-page/scripts/archify/examples/incident-response.workflow.json +64 -0
- package/skills/interactive-page/scripts/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/interactive-page/scripts/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/interactive-page/scripts/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/interactive-page/scripts/archify/examples/web-app.architecture.json +46 -0
- package/skills/interactive-page/scripts/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/interactive-page/scripts/archify/recipes/scenarios.mjs +284 -0
- package/skills/interactive-page/scripts/archify/references/authoring-contract.md +243 -0
- package/skills/interactive-page/scripts/archify/references/delivery-contract.md +120 -0
- package/skills/interactive-page/scripts/archify/references/viewer-runtime.md +45 -0
- package/skills/interactive-page/scripts/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/interactive-page/scripts/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/interactive-page/scripts/archify/renderers/dataflow/README.md +104 -0
- package/skills/interactive-page/scripts/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/interactive-page/scripts/archify/renderers/lifecycle/README.md +115 -0
- package/skills/interactive-page/scripts/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/interactive-page/scripts/archify/renderers/sequence/README.md +114 -0
- package/skills/interactive-page/scripts/archify/renderers/sequence/render-sequence.mjs +464 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/cli.mjs +218 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/generated-brand-marks.mjs +3 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/i18n.mjs +595 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/legend.mjs +217 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/output-path.mjs +340 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/repository-evidence.mjs +238 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/repository-location.mjs +58 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/utils.mjs +232 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/validator.mjs +86 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/README.md +223 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/interactive-page/scripts/archify/schemas/README.md +211 -0
- package/skills/interactive-page/scripts/archify/schemas/architecture.schema.json +178 -0
- package/skills/interactive-page/scripts/archify/schemas/common.schema.json +115 -0
- package/skills/interactive-page/scripts/archify/schemas/dataflow.schema.json +243 -0
- package/skills/interactive-page/scripts/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/interactive-page/scripts/archify/schemas/sequence.schema.json +223 -0
- package/skills/interactive-page/scripts/archify/schemas/workflow.schema.json +428 -0
- package/skills/interactive-page/scripts/archify/scripts/check-render-output.mjs +916 -0
- package/skills/interactive-page/scripts/archify/scripts/render-examples.mjs +26 -0
- package/skills/interactive-page/scripts/bundle-artifact.sh +9 -8
- package/skills/skill-creator/SKILL.md +4 -5
- package/.next/server/app/api/bot/task/[id]/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/bot/task/[id]/route.js +0 -16
- package/.next/server/app/api/bot/task/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/bot/task/[id]/route_client-reference-manifest.js +0 -3
- package/.next/server/app/api/bot/task/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/bot/task/route.js +0 -16
- package/.next/server/app/api/bot/task/route.js.nft.json +0 -1
- package/.next/server/app/api/bot/task/route_client-reference-manifest.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__026u77e._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__050zri4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0bg9710._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0in-d5s._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0pa9k-f._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0qi_m0e._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0tq0m_k._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0xep6j_._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__11n256_._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__144l7qt._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1jg8t86._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1lldzy9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1peqvsi._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1w8gm5h._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__1xt39nn._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1zb8mfo._.js +0 -8
- package/.next/server/chunks/_0-73of8._.js +0 -55
- package/.next/server/chunks/_0-fjql4._.js +0 -31
- package/.next/server/chunks/_00xh_q0._.js +0 -25
- package/.next/server/chunks/_051qh8u._.js +0 -3
- package/.next/server/chunks/_05sovvj._.js +0 -3
- package/.next/server/chunks/_091bi8d._.js +0 -3
- package/.next/server/chunks/_0fzqt2y._.js +0 -142
- package/.next/server/chunks/_0lr92vd._.js +0 -3
- package/.next/server/chunks/_0mecv0f._.js +0 -31
- package/.next/server/chunks/_0mh8j4m._.js +0 -3
- package/.next/server/chunks/_0sb7tlz._.js +0 -3
- package/.next/server/chunks/_0si7xzn._.js +0 -3
- package/.next/server/chunks/_0u10wmw._.js +0 -55
- package/.next/server/chunks/_0wp4le3._.js +0 -3
- package/.next/server/chunks/_0y5fg-x._.js +0 -3
- package/.next/server/chunks/_1230wbx._.js +0 -10
- package/.next/server/chunks/_12nheja._.js +0 -3
- package/.next/server/chunks/_13510xh._.js +0 -3
- package/.next/server/chunks/_15hq89o._.js +0 -3
- package/.next/server/chunks/_18-1wcc._.js +0 -55
- package/.next/server/chunks/_18d7-5p._.js +0 -3
- package/.next/server/chunks/_1c78vw5._.js +0 -237
- package/.next/server/chunks/_1h5_8sk._.js +0 -3
- package/.next/server/chunks/_1hdnxb7._.js +0 -3
- package/.next/server/chunks/_1j8k_j5._.js +0 -3
- package/.next/server/chunks/_1nula3v._.js +0 -3
- package/.next/server/chunks/_1php4eq._.js +0 -3
- package/.next/server/chunks/_1yuv4vp._.js +0 -3
- package/.next/server/chunks/_1zkfbaa._.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_bot_task_[id]_route_actions_1_k_8u3.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_bot_task_route_actions_0xln6ra.js +0 -3
- package/.next/server/chunks/features_0rpfxbk._.js +0 -3
- package/.next/server/chunks/features_1qf3cei._.js +0 -8
- package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1jidylq._.js +0 -3
- package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0-nob52._.js +0 -4
- package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0mz33a7._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0s4wb56._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sd3izg._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1-13tcn._.js +0 -43
- package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
- package/.next/server/chunks/ssr/[root-of-the-server]__1zidpfl._.js +0 -3
- package/.next/server/chunks/ssr/_001_ms1._.js +0 -33
- package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
- package/.next/server/chunks/ssr/_04z2c7b._.js +0 -3
- package/.next/server/chunks/ssr/_06tssji._.js +0 -3
- package/.next/server/chunks/ssr/_0fk4c7d._.js +0 -3
- package/.next/server/chunks/ssr/_0h42fze._.js +0 -3
- package/.next/server/chunks/ssr/_0h7zdni._.js +0 -3
- package/.next/server/chunks/ssr/_0mggveb._.js +0 -3
- package/.next/server/chunks/ssr/_0nbpc_8._.js +0 -3
- package/.next/server/chunks/ssr/_0q0zan6._.js +0 -70
- package/.next/server/chunks/ssr/_0zcjrbg._.js +0 -3
- package/.next/server/chunks/ssr/_0zhoc3r._.js +0 -3
- package/.next/server/chunks/ssr/_11j92tu._.js +0 -3
- package/.next/server/chunks/ssr/_13fke0x._.js +0 -3
- package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
- package/.next/server/chunks/ssr/_1ebxla1._.js +0 -3
- package/.next/server/chunks/ssr/_1nj4niv._.js +0 -42
- package/.next/server/chunks/ssr/_1ov37if._.js +0 -3
- package/.next/server/chunks/ssr/_1ovmc9s._.js +0 -3
- package/.next/server/chunks/ssr/_1sji22n._.js +0 -3
- package/.next/server/chunks/ssr/_1vi_uo7._.js +0 -3
- package/.next/server/chunks/ssr/_1vyd1ji._.js +0 -3
- package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
- package/.next/server/chunks/ssr/components_ui_1y1l7yj._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
- package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +0 -27
- package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +0 -27
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0npjf8-._.js +0 -3
- package/.next/static/chunks/0n3jle9kbqfr2.js +0 -40
- package/.next/static/chunks/0r5qcctujhxli.js +0 -1
- package/.next/static/chunks/10teww7wt-f6h.js +0 -1
- package/.next/static/chunks/126fj4pga_72k.js +0 -1
- package/.next/static/chunks/159udiexf6dom.js +0 -2
- package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
- package/.next/static/chunks/18gi7o2ibrgh8.js +0 -3
- package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
- package/.next/static/chunks/1q9z1tohil7xn.js +0 -1
- package/.next/static/chunks/1sjgfets224re.js +0 -1
- package/.next/static/chunks/1wszsr83t01gh.js +0 -1
- package/.next/static/chunks/22_zkas21rpna.js +0 -1
- package/.next/static/chunks/2fh4-3z_q8b_w.js +0 -1
- package/.next/static/chunks/2h0sd2vumhb15.js +0 -1
- package/.next/static/chunks/2szintyibu0s5.js +0 -1
- package/.next/static/chunks/2t4vhi3m8ahya.js +0 -1
- package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
- package/.next/static/chunks/3-b6b7entqazs.js +0 -1
- package/.next/static/chunks/3786h5vayt80-.js +0 -28
- package/.next/static/chunks/3cq5g494yuke8.css +0 -1
- package/.next/static/chunks/3dmbhsv2u_8kj.js +0 -1
- package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
- package/.next/static/chunks/3i0a5px-oufgc.js +0 -1
- package/.next/static/chunks/3wexk9qo8zuj3.js +0 -18
- package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
- package/.next/static/chunks/3xre8bu_pk1p_.js +0 -1
- package/.next/static/chunks/3zv6t3i77xhr_.js +0 -1
- package/database/migrations/20260911030320_opposite_jack_power/migration.sql +0 -1
- package/docs/images/task-thread.png +0 -0
- package/features/bot/components/task-badge.tsx +0 -42
- package/features/bot/components/task-reply.tsx +0 -328
- package/features/bot/components/task-setting.tsx +0 -524
- package/features/bot/task.query.ts +0 -1079
- package/features/bot/task.store.ts +0 -487
- package/lib/realtime/client-secret.ts +0 -86
- package/lib/realtime/open-session.ts +0 -31
- package/lib/realtime/openai.ts +0 -35
- package/lib/realtime/realtime.audio.ts +0 -196
- package/lib/realtime/realtime.driver.ts +0 -489
- package/lib/realtime/realtime.protocol.ts +0 -393
- package/lib/realtime/realtime.schema.ts +0 -37
- package/lib/realtime/realtime.session.ts +0 -106
- package/lib/realtime/realtime.transport.ts +0 -384
- package/lib/realtime/xai.ts +0 -39
- /package/.next/server/app/api/bot/{task → thread}/[id]/route/build-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/[id]/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/[id]/route.js.map +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route/build-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route.js.map +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_buildManifest.js +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_ssgManifest.js +0 -0
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef } from "react";
|
|
4
4
|
import { ASCII_FACE } from "@/config";
|
|
5
|
-
import {
|
|
6
|
-
createVoiceFollower,
|
|
7
|
-
SPECTRUM_BANDS,
|
|
8
|
-
} from "@/lib/realtime/realtime.tap";
|
|
5
|
+
import { createVoiceFollower, SPECTRUM_BANDS } from "@/lib/live/live.tap";
|
|
9
6
|
import {
|
|
10
7
|
ALPHA_TOP,
|
|
11
8
|
CHAR_RATE,
|
|
@@ -14,32 +11,30 @@ import {
|
|
|
14
11
|
EMOJI_POOL,
|
|
15
12
|
EMOJI_RATIO,
|
|
16
13
|
hash,
|
|
14
|
+
LETTERS,
|
|
17
15
|
RAMP,
|
|
18
16
|
smoothstep,
|
|
19
17
|
} from "../ascii.const";
|
|
20
18
|
import type { AsciiCharset } from "../face.const";
|
|
19
|
+
import type { FaceWord } from "../thursday.schema";
|
|
21
20
|
|
|
22
21
|
export type AsciiOrbMode =
|
|
23
22
|
| "idle"
|
|
24
23
|
| "connecting"
|
|
24
|
+
/** The user's turn: the resting body, thinned out, retyping with the microphone */
|
|
25
|
+
| "listening"
|
|
26
|
+
/** Hanging up: the body draws in and goes out, leaving the field empty */
|
|
27
|
+
| "ending"
|
|
25
28
|
| "speaking"
|
|
26
29
|
/** Running a tool; alive without speaking */
|
|
27
30
|
| "working"
|
|
28
31
|
| "error";
|
|
29
32
|
|
|
30
|
-
/** The list lives in face.const so the settings schema can build the enum without importing this component. */
|
|
31
|
-
export type AsciiOrbCharset = AsciiCharset;
|
|
32
|
-
|
|
33
|
-
/** Expressions the orb can wear while speaking. */
|
|
34
|
-
export type AsciiOrbEmotion = "happy" | "sad" | "love";
|
|
35
|
-
|
|
36
33
|
export type AsciiOrbProps = {
|
|
37
34
|
className?: string;
|
|
38
35
|
mode?: AsciiOrbMode;
|
|
39
36
|
/** "emoji" sprinkles emoji in; "emojiOnly" is all emoji */
|
|
40
|
-
charset?:
|
|
41
|
-
/** Expression while speaking; null for none */
|
|
42
|
-
emotion?: AsciiOrbEmotion | null;
|
|
37
|
+
charset?: AsciiCharset;
|
|
43
38
|
/** Glyph size (px) */
|
|
44
39
|
fontSize?: number;
|
|
45
40
|
/** Cell density; above 1 packs tighter */
|
|
@@ -49,6 +44,16 @@ export type AsciiOrbProps = {
|
|
|
49
44
|
* speaking; without it a synthetic waveform keeps the orb alive (previews).
|
|
50
45
|
*/
|
|
51
46
|
getSpectrum?: () => ArrayLike<number>;
|
|
47
|
+
/**
|
|
48
|
+
* Microphone bands, read the same way. While listening they set how fast the
|
|
49
|
+
* glyphs retype; without them listening is the resting body alone.
|
|
50
|
+
*/
|
|
51
|
+
getMicSpectrum?: () => ArrayLike<number>;
|
|
52
|
+
/**
|
|
53
|
+
* A word to show (emote): the body steps aside, the letters light in one by
|
|
54
|
+
* one, hold, and go out, and the mode takes the face back. A new `at` shows it again.
|
|
55
|
+
*/
|
|
56
|
+
word?: FaceWord | null;
|
|
52
57
|
/** Side length (px). Everything scales with it; cell count scales with area. */
|
|
53
58
|
size?: number;
|
|
54
59
|
/**
|
|
@@ -64,99 +69,55 @@ const DEFAULT_COLOR: [number, number, number] = [235, 235, 235];
|
|
|
64
69
|
/** Color easing per frame (0.05 is about 0.5s) */
|
|
65
70
|
const COLOR_EASE = 0.05;
|
|
66
71
|
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
".....................",
|
|
83
|
-
".....................",
|
|
84
|
-
"....███.......███....",
|
|
85
|
-
"...█████.....█████...",
|
|
86
|
-
"...█████.....█████...",
|
|
87
|
-
"....███.......███....",
|
|
88
|
-
".....................",
|
|
89
|
-
".....................",
|
|
90
|
-
"█...................█",
|
|
91
|
-
".██...............██.",
|
|
92
|
-
"...███.........███...",
|
|
93
|
-
"......█████████......",
|
|
94
|
-
".....................",
|
|
95
|
-
],
|
|
96
|
-
sad: [
|
|
97
|
-
".....................",
|
|
98
|
-
".....................",
|
|
99
|
-
"....███.......███....",
|
|
100
|
-
"...█████.....█████...",
|
|
101
|
-
"...█████.....█████...",
|
|
102
|
-
"....███.......███....",
|
|
103
|
-
".....................",
|
|
104
|
-
".....................",
|
|
105
|
-
"......█████████......",
|
|
106
|
-
"...███.........███...",
|
|
107
|
-
".██...............██.",
|
|
108
|
-
"█...................█",
|
|
109
|
-
".....................",
|
|
110
|
-
],
|
|
111
|
-
love: [
|
|
112
|
-
".....................",
|
|
113
|
-
"...██.██.....██.██...",
|
|
114
|
-
"..███████...███████..",
|
|
115
|
-
"..███████...███████..",
|
|
116
|
-
"...█████.....█████...",
|
|
117
|
-
"....███.......███....",
|
|
118
|
-
".....█.........█.....",
|
|
119
|
-
".....................",
|
|
120
|
-
"█...................█",
|
|
121
|
-
".██...............██.",
|
|
122
|
-
"...███.........███...",
|
|
123
|
-
"......█████████......",
|
|
124
|
-
".....................",
|
|
125
|
-
],
|
|
126
|
-
};
|
|
72
|
+
/** A word as five rows of letter indices, one empty column between letters; -1 is no ink. */
|
|
73
|
+
function spell(text: string) {
|
|
74
|
+
const rows: number[][] = [[], [], [], [], []];
|
|
75
|
+
let letter = 0;
|
|
76
|
+
for (const char of Array.from(text.toUpperCase())) {
|
|
77
|
+
const glyph = LETTERS[char];
|
|
78
|
+
if (!glyph) continue;
|
|
79
|
+
if (rows[0].length) for (const row of rows) row.push(-1);
|
|
80
|
+
glyph.forEach((line, r) => {
|
|
81
|
+
for (const px of line) rows[r].push(px === "#" ? letter : -1);
|
|
82
|
+
});
|
|
83
|
+
if (char !== " ") letter++;
|
|
84
|
+
}
|
|
85
|
+
return rows;
|
|
86
|
+
}
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
const FACE_OUT = 0.36;
|
|
130
|
-
const FACE_BLANK = 0.14;
|
|
131
|
-
const FACE_IN = 0.95;
|
|
132
|
-
/** Glyph swap rate for face cells (per second), slower than the background */
|
|
133
|
-
const FACE_CHAR_RATE = 0.75;
|
|
88
|
+
const ERROR_ROWS = spell("ERROR");
|
|
134
89
|
|
|
135
90
|
/** Reference size the tuning constants assume; coordinates are normalized to it. */
|
|
136
91
|
const DESIGN = 680;
|
|
137
92
|
/** Radius (reference units) within which cells exist. Must stay under DESIGN/2 or the canvas clips it. */
|
|
138
93
|
const FIELD_R = 328;
|
|
139
94
|
|
|
140
|
-
/**
|
|
141
|
-
|
|
142
|
-
const SPEAK_BASE = 184;
|
|
95
|
+
/** Where the rim sits while the voice is at the bottom of its range: the resting body's radius (IDLE_R), so speaking starts her own size */
|
|
96
|
+
const SPEAK_BASE = 170;
|
|
143
97
|
/** Swell across a phrase at the top of the voice's range */
|
|
144
98
|
const SPEAK_SWELL = 40;
|
|
145
|
-
/** Kick per syllable */
|
|
146
|
-
const SPEAK_KICK =
|
|
99
|
+
/** Kick per syllable. Held down on purpose: a syllable should push the rim, not throw it */
|
|
100
|
+
const SPEAK_KICK = 85;
|
|
147
101
|
/** Rotating lobes, harmonics 2 and up (the first harmonic shifts the whole circle) */
|
|
148
102
|
const SPEAK_LOBES = 4;
|
|
149
103
|
/** Max depth of one lobe */
|
|
150
|
-
const SPEAK_LOBE_R =
|
|
104
|
+
const SPEAK_LOBE_R = 28;
|
|
105
|
+
/**
|
|
106
|
+
* How fast the whole lobe pattern turns (rad/s), and how much a loud phrase adds.
|
|
107
|
+
* The lobes also drift against each other; this is the turn you actually see,
|
|
108
|
+
* since counter-drifting alone reads as shimmer rather than rotation.
|
|
109
|
+
*/
|
|
110
|
+
const SPEAK_SPIN = 1.1;
|
|
111
|
+
const SPEAK_SPIN_VOICE = 0.9;
|
|
151
112
|
/**
|
|
152
113
|
* Rim range. Past SPEAK_KNEE the rim slows into SPEAK_MAX rather than stopping
|
|
153
114
|
* at it, so a loud syllable still reads as a push instead of a flat edge. Max
|
|
154
115
|
* stays inside FIELD_R or thrown crumbs die at the edge; min keeps a deep
|
|
155
116
|
* breath from collapsing to a dot.
|
|
156
117
|
*/
|
|
157
|
-
const SPEAK_MIN =
|
|
158
|
-
const SPEAK_KNEE =
|
|
159
|
-
const SPEAK_MAX =
|
|
118
|
+
const SPEAK_MIN = 110;
|
|
119
|
+
const SPEAK_KNEE = 205;
|
|
120
|
+
const SPEAK_MAX = 235;
|
|
160
121
|
/** How far a rim cell sits in or out of the rim: the edge is crumbly, not drawn with a compass */
|
|
161
122
|
const SPEAK_ROUGH = 20;
|
|
162
123
|
/** Share of cells a syllable throws outward; the rest stay, so what leaves is crumbs, not a ring */
|
|
@@ -169,36 +130,116 @@ const MAX_RINGS = 5;
|
|
|
169
130
|
|
|
170
131
|
/** Per-frame values derived from the voice; every cell reads the same ones. */
|
|
171
132
|
type Voice = {
|
|
172
|
-
/** The voice inside its own range over about half a second (
|
|
133
|
+
/** The voice inside its own range over about half a second (live.tap createVoiceFollower) */
|
|
173
134
|
phrase: number;
|
|
135
|
+
/** The user's voice, the same measure, from the microphone (listening) */
|
|
136
|
+
micPhrase: number;
|
|
137
|
+
/** The same voice syllable by syllable, so a word shows and not only a sentence */
|
|
138
|
+
micLevel: number;
|
|
174
139
|
/** Spring kicked by each syllable */
|
|
175
140
|
bob: number;
|
|
176
141
|
bobVel: number;
|
|
177
142
|
/** Lobe strength and angle */
|
|
178
143
|
amp: number[];
|
|
179
144
|
phase: number[];
|
|
145
|
+
/** The whole pattern's turn (rad), one way */
|
|
146
|
+
spin: number;
|
|
180
147
|
/** One throw of crumbs per syllable */
|
|
181
148
|
rings: { born: number; power: number }[];
|
|
182
149
|
};
|
|
183
150
|
|
|
184
|
-
/**
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Seconds each channel of the field takes to arrive, and to leave. Leaving is
|
|
153
|
+
* slower everywhere: a behaviour that stops winds down — crumbs already on their
|
|
154
|
+
* way keep coming and fade out — rather than vanishing on the frame the mode
|
|
155
|
+
* changes.
|
|
156
|
+
*/
|
|
157
|
+
const RISE = {
|
|
158
|
+
scale: 0.45,
|
|
159
|
+
lift: 0.22,
|
|
160
|
+
gather: 0.3,
|
|
161
|
+
comet: 0.45,
|
|
162
|
+
speech: 0.5,
|
|
163
|
+
err: 0.5,
|
|
164
|
+
word: 0.35,
|
|
165
|
+
listen: 0.3,
|
|
166
|
+
};
|
|
167
|
+
const FALL = {
|
|
168
|
+
scale: 0.7,
|
|
169
|
+
lift: 0.4,
|
|
170
|
+
gather: 0.95,
|
|
171
|
+
comet: 0.9,
|
|
172
|
+
speech: 0.75,
|
|
173
|
+
err: 0.6,
|
|
174
|
+
word: 0.5,
|
|
175
|
+
listen: 0.6,
|
|
176
|
+
};
|
|
177
|
+
/** Hanging up pulls the body in faster than anything else grows back. */
|
|
178
|
+
const END_FALL = 0.4;
|
|
179
|
+
|
|
180
|
+
/** Moves one channel toward its target; arriving and leaving have their own times. */
|
|
181
|
+
function toward(
|
|
182
|
+
from: number,
|
|
183
|
+
to: number,
|
|
184
|
+
rise: number,
|
|
185
|
+
fall: number,
|
|
186
|
+
dt: number,
|
|
187
|
+
) {
|
|
188
|
+
return from + (to - from) * Math.min(1, dt / (to > from ? rise : fall));
|
|
189
|
+
}
|
|
188
190
|
|
|
189
191
|
/**
|
|
190
|
-
*
|
|
191
|
-
* for SPEAK_MAX and the
|
|
192
|
-
* the
|
|
192
|
+
* The body every mode rests at. The max drawable radius is DESIGN/2, so this
|
|
193
|
+
* sets the budget for SPEAK_MAX and the crumbs thrown past it: leave room, or a
|
|
194
|
+
* loud voice fills the box and its swell has nowhere left to read. The visible
|
|
195
|
+
* body is about half, since the idle wave fades from 0.55 x IDLE_R.
|
|
196
|
+
*/
|
|
197
|
+
const IDLE_R = 170;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Connecting: crumbs travel in from the edge of the field to the resting body.
|
|
201
|
+
* Each ray carries its own crumb at its own phase, so nothing lines up into a
|
|
202
|
+
* ring.
|
|
203
|
+
*/
|
|
204
|
+
const GATHER_R = 320;
|
|
205
|
+
/** Equal sectors around the circle; each is a few cells wide, so its crumb reads as a crumb and not an arc */
|
|
206
|
+
const GATHER_RAYS = 56;
|
|
207
|
+
/** One crumb's trip inward, in trips per second */
|
|
208
|
+
const GATHER_RATE = 0.7;
|
|
209
|
+
/**
|
|
210
|
+
* Connecting draws the body in to `DIP` of its size and fills back out over
|
|
211
|
+
* `GROW` as the crumbs land in it, so the gathering has somewhere to go instead
|
|
212
|
+
* of piling onto a body that is already full.
|
|
193
213
|
*/
|
|
194
|
-
const
|
|
214
|
+
const GATHER_DIP = 0.45;
|
|
215
|
+
const GATHER_GROW = 1.3;
|
|
195
216
|
|
|
196
|
-
/**
|
|
197
|
-
|
|
198
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Listening: the resting body, its glyphs retyping as fast as the user speaks.
|
|
219
|
+
* Quiet is slower than idle, and a loud voice is PACE_VOICE times that; the
|
|
220
|
+
* shape never moves, which is the point.
|
|
221
|
+
*/
|
|
222
|
+
const LISTEN_PACE_QUIET = 0.5;
|
|
223
|
+
const LISTEN_PACE_VOICE = 8;
|
|
224
|
+
/**
|
|
225
|
+
* Brightness the voice adds: the field fills in as the user talks and thins out
|
|
226
|
+
* when they stop. Most of it follows the phrase so it holds through a sentence,
|
|
227
|
+
* the rest the syllable, so the words show without the body moving.
|
|
228
|
+
*/
|
|
229
|
+
const LISTEN_LIFT = 0.24;
|
|
230
|
+
/** Seconds the retype rate takes to follow the voice (toward) */
|
|
231
|
+
const PACE_TIME = 0.14;
|
|
232
|
+
/**
|
|
233
|
+
* Listening also thins the body out, so the voice has gaps to show in: every
|
|
234
|
+
* cell's gate rises by THIN, and POP_RATE times a second each cell re-rolls up
|
|
235
|
+
* to POP of it, as far as the voice is loud — glyphs pop in and out with the words.
|
|
236
|
+
*/
|
|
237
|
+
const LISTEN_THIN = 0.16;
|
|
238
|
+
const LISTEN_POP = 0.6;
|
|
239
|
+
const LISTEN_POP_RATE = 9;
|
|
199
240
|
|
|
200
|
-
/** Working: a comet orbiting just outside the
|
|
201
|
-
const WORK_R =
|
|
241
|
+
/** Working: a comet orbiting just outside the resting body */
|
|
242
|
+
const WORK_R = 210;
|
|
202
243
|
/** Orbit speed (rad/s); 3.0 is about one lap per 2s */
|
|
203
244
|
const WORK_SPIN = 3;
|
|
204
245
|
/** Orbit band width; larger blurs more */
|
|
@@ -215,6 +256,15 @@ const ERR_STEP_OUT = 0.5;
|
|
|
215
256
|
const ERR_FADE_OUT = 0.7;
|
|
216
257
|
const ERR_CYCLE = 9.2;
|
|
217
258
|
|
|
259
|
+
/** A word (emote), once: letters light in one by one, hold, and go out the same way (s) */
|
|
260
|
+
const WORD_STEP_IN = 0.11;
|
|
261
|
+
const WORD_FADE_IN = 0.3;
|
|
262
|
+
const WORD_HOLD = 3.4;
|
|
263
|
+
const WORD_STEP_OUT = 0.08;
|
|
264
|
+
const WORD_FADE_OUT = 0.35;
|
|
265
|
+
/** The widest a word is drawn, as a share of the box: about her body */
|
|
266
|
+
const WORD_WIDTH = 0.62;
|
|
267
|
+
|
|
218
268
|
type Cell = {
|
|
219
269
|
/** Draw position on the canvas */
|
|
220
270
|
x: number;
|
|
@@ -230,60 +280,41 @@ type Cell = {
|
|
|
230
280
|
grain: number;
|
|
231
281
|
/** Below this brightness the cell is empty; random gaps */
|
|
232
282
|
gap: number;
|
|
233
|
-
/** Order this cell lights up when an expression appears (0..1) */
|
|
234
|
-
birth: number;
|
|
235
|
-
/** Order this cell goes out when an expression leaves; differs from birth */
|
|
236
|
-
death: number;
|
|
237
283
|
/** Emoji slot in emoji mode */
|
|
238
284
|
emoji: boolean;
|
|
239
285
|
/** Below SPECK_SHARE, a crumb a syllable throws outward */
|
|
240
286
|
speck: number;
|
|
241
287
|
/** Index of the ERROR letter this cell belongs to, or -1 */
|
|
242
288
|
letter: number;
|
|
289
|
+
/** Index of the shown word's letter this cell belongs to, or -1; laid again for each word */
|
|
290
|
+
word: number;
|
|
243
291
|
};
|
|
244
292
|
|
|
245
|
-
/** Index of the
|
|
246
|
-
function
|
|
293
|
+
/** Index of the letter at (dx,dy) of a word centred on the box, each of its cells `s` grid cells wide; -1 when none */
|
|
294
|
+
function letterAt(
|
|
295
|
+
rows: number[][],
|
|
247
296
|
dx: number,
|
|
248
297
|
dy: number,
|
|
249
298
|
cw: number,
|
|
250
299
|
ch: number,
|
|
251
300
|
s: number,
|
|
252
301
|
) {
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (by < 0 || by >= rows) return -1;
|
|
259
|
-
const line = ERROR_BITMAP[by];
|
|
260
|
-
if (bx < 0 || bx >= line.length) return -1;
|
|
261
|
-
if (line[bx] !== "█") return -1;
|
|
262
|
-
return Math.floor(bx / 5);
|
|
302
|
+
const bx = Math.floor(dx / (cw * s) + rows[0].length / 2);
|
|
303
|
+
const by = Math.floor(dy / (ch * s) + rows.length / 2);
|
|
304
|
+
if (by < 0 || by >= rows.length) return -1;
|
|
305
|
+
return rows[by][bx] ?? -1;
|
|
263
306
|
}
|
|
264
307
|
|
|
265
|
-
/**
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
s: number,
|
|
273
|
-
) {
|
|
274
|
-
const bx = Math.floor(dx / (cw * s) + FACE_COLS / 2);
|
|
275
|
-
const by = Math.floor(dy / (ch * s) + FACE_ROWS / 2);
|
|
276
|
-
if (by < 0 || by >= FACE_ROWS || bx < 0 || bx >= FACE_COLS) return 0;
|
|
277
|
-
if (FACES[em][by][bx] !== "█") return 0;
|
|
278
|
-
return by < EYE_ROWS ? 1 : 2;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/** Idle: a soft breathing wave inside a small circle */
|
|
282
|
-
function idleValue(cell: Cell, t: number) {
|
|
283
|
-
const body = 1 - smoothstep(IDLE_R * 0.55, IDLE_R, cell.dist);
|
|
308
|
+
/**
|
|
309
|
+
* Idle: a soft breathing wave inside a small circle. `lift` brightens it in
|
|
310
|
+
* place; `scale` is the body's share of IDLE_R, which is how it opens and closes.
|
|
311
|
+
*/
|
|
312
|
+
function idleValue(cell: Cell, t: number, lift = 0, scale = 1) {
|
|
313
|
+
const r = IDLE_R * Math.max(0.02, scale);
|
|
314
|
+
const body = 1 - smoothstep(r * 0.55, r, cell.dist);
|
|
284
315
|
const w = Math.sin(cell.dist * 0.05 - t * 0.4);
|
|
285
316
|
const breath = Math.sin(t * 0.35) * 0.07;
|
|
286
|
-
return (0.58 + w * 0.2 + breath) * body;
|
|
317
|
+
return (0.58 + lift + w * 0.2 + breath) * body;
|
|
287
318
|
}
|
|
288
319
|
|
|
289
320
|
/** The rim's radius for a raw push: as pushed up to SPEAK_KNEE, then easing into SPEAK_MAX. */
|
|
@@ -294,142 +325,250 @@ function rimAt(raw: number) {
|
|
|
294
325
|
}
|
|
295
326
|
|
|
296
327
|
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
328
|
+
* What the field is doing right now. A mode does not draw a picture of its own:
|
|
329
|
+
* it names targets for these channels, and each eases toward its target on its
|
|
330
|
+
* own clock, so a behaviour arrives and — when the mode changes — winds down.
|
|
299
331
|
*/
|
|
300
|
-
|
|
332
|
+
type Field = {
|
|
333
|
+
/** The body's radius as a share of IDLE_R; 0 is an empty field */
|
|
334
|
+
scale: number;
|
|
335
|
+
/** Brightness added to the body in place */
|
|
336
|
+
lift: number;
|
|
337
|
+
/** Crumbs streaming in from the edge of the field (connecting) */
|
|
338
|
+
gather: number;
|
|
339
|
+
/** A comet orbiting outside the body (working) */
|
|
340
|
+
comet: number;
|
|
341
|
+
/** Her voice: the rim it pushes and the crumbs a syllable throws (speaking) */
|
|
342
|
+
speech: number;
|
|
343
|
+
/** The ERROR letters */
|
|
344
|
+
err: number;
|
|
345
|
+
/** A word's letters (emote) */
|
|
346
|
+
word: number;
|
|
347
|
+
/** Listening's thinning out */
|
|
348
|
+
listen: number;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/** Where a mode wants the field. `e` is seconds since the mode was asked for. */
|
|
352
|
+
function targetFor(m: AsciiOrbMode, e: number, v: Voice): Field {
|
|
353
|
+
const rest: Field = {
|
|
354
|
+
scale: 1,
|
|
355
|
+
lift: 0,
|
|
356
|
+
gather: 0,
|
|
357
|
+
comet: 0,
|
|
358
|
+
speech: 0,
|
|
359
|
+
err: 0,
|
|
360
|
+
word: 0,
|
|
361
|
+
listen: 0,
|
|
362
|
+
};
|
|
301
363
|
switch (m) {
|
|
302
364
|
case "idle":
|
|
303
|
-
return
|
|
365
|
+
return rest;
|
|
304
366
|
|
|
305
|
-
//
|
|
367
|
+
// draws in first, then fills back out as the crumbs land in it
|
|
306
368
|
case "connecting": {
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
0.2 +
|
|
315
|
-
hash(Math.floor(cell.dist / 9), Math.floor(cell.angle * 11)) * 0.85 +
|
|
316
|
-
hash(cell.seed * 137, 7) * 0.5;
|
|
317
|
-
const ring = band * grain * k;
|
|
318
|
-
return ring + idleValue(cell, t) * (1 - k);
|
|
369
|
+
const filled = 1 - Math.exp(-e / GATHER_GROW);
|
|
370
|
+
return {
|
|
371
|
+
...rest,
|
|
372
|
+
scale: GATHER_DIP + (1 - GATHER_DIP) * filled,
|
|
373
|
+
lift: 0.06 * filled,
|
|
374
|
+
gather: 1,
|
|
375
|
+
};
|
|
319
376
|
}
|
|
320
377
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
case "speaking": {
|
|
329
|
-
const grow = smoothstep(0, 1, Math.min(1, e / 0.8));
|
|
330
|
-
|
|
331
|
-
let raw =
|
|
332
|
-
IDLE_R +
|
|
333
|
-
(SPEAK_BASE - IDLE_R + SPEAK_SWELL * v.phrase + SPEAK_KICK * v.bob) *
|
|
334
|
-
grow;
|
|
335
|
-
for (let i = 0; i < SPEAK_LOBES; i++) {
|
|
336
|
-
raw +=
|
|
337
|
-
SPEAK_LOBE_R *
|
|
338
|
-
v.amp[i] *
|
|
339
|
-
Math.sin((i + 2) * cell.angle + v.phase[i]) *
|
|
340
|
-
grow;
|
|
341
|
-
}
|
|
342
|
-
const edge = rimAt(raw);
|
|
343
|
-
|
|
344
|
-
// each cell sits a little in or out of the rim, and the offset drifts
|
|
345
|
-
const rough =
|
|
346
|
-
(cell.grain - 0.5) * SPEAK_ROUGH +
|
|
347
|
-
Math.sin(cell.angle * 5 + t * 0.9 + cell.seed * 6.283) *
|
|
348
|
-
SPEAK_ROUGH *
|
|
349
|
-
0.3;
|
|
350
|
-
const d = cell.dist - (edge + rough);
|
|
351
|
-
const rim = Math.exp(-(d * d) / 800);
|
|
352
|
-
const core = Math.exp(-(cell.dist * cell.dist) / 6000) * 0.5;
|
|
353
|
-
// a soft fill toward the center, with the idle wave still moving through it
|
|
354
|
-
const inside =
|
|
355
|
-
cell.dist < edge
|
|
356
|
-
? 0.3 *
|
|
357
|
-
(1 - cell.dist / edge) ** 0.6 *
|
|
358
|
-
(0.75 + 0.25 * Math.sin(cell.dist * 0.05 - t * 1.2))
|
|
359
|
-
: 0;
|
|
360
|
-
|
|
361
|
-
// crumbs: a sparse share of cells, each thrown past the rim at its own speed
|
|
362
|
-
let out = 0;
|
|
363
|
-
if (cell.speck < SPECK_SHARE) {
|
|
364
|
-
const speed = RING_SPEED * (0.6 + cell.grain * 0.9);
|
|
365
|
-
for (let i = 0; i < v.rings.length; i++) {
|
|
366
|
-
const ring = v.rings[i];
|
|
367
|
-
const age = t - ring.born;
|
|
368
|
-
const dd = cell.dist - (edge + 6 + age * speed);
|
|
369
|
-
out +=
|
|
370
|
-
Math.exp(-(dd * dd) / 500) *
|
|
371
|
-
ring.power *
|
|
372
|
-
Math.max(0, 1 - age / RING_LIFE);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
378
|
+
// the body holds still; the user's voice shows as ink, in its gaps and as the retype rate
|
|
379
|
+
case "listening":
|
|
380
|
+
return {
|
|
381
|
+
...rest,
|
|
382
|
+
lift: LISTEN_LIFT * (0.7 * v.micPhrase + 0.3 * v.micLevel),
|
|
383
|
+
listen: 1,
|
|
384
|
+
};
|
|
375
385
|
|
|
376
|
-
|
|
377
|
-
|
|
386
|
+
case "speaking":
|
|
387
|
+
return { ...rest, speech: 1 };
|
|
378
388
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
* the voice and never ends.
|
|
382
|
-
*/
|
|
383
|
-
case "working": {
|
|
384
|
-
const k = smoothstep(0, 1, Math.min(1, e / 0.7));
|
|
389
|
+
case "working":
|
|
390
|
+
return { ...rest, comet: 1 };
|
|
385
391
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const band = Math.exp(-(d * d) / WORK_BAND);
|
|
392
|
+
case "ending":
|
|
393
|
+
return { ...rest, scale: 0 };
|
|
389
394
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
395
|
+
case "error":
|
|
396
|
+
return { ...rest, scale: 0, err: 1 };
|
|
397
|
+
}
|
|
398
|
+
}
|
|
394
399
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
400
|
+
/** Crumbs traveling in from the edge of the field to the body's rim, dimming as they land. */
|
|
401
|
+
function gatherValue(cell: Cell, t: number, scale: number) {
|
|
402
|
+
const rim = IDLE_R * scale;
|
|
403
|
+
if (cell.speck >= 0.55 || cell.dist < rim * 0.8) return 0;
|
|
404
|
+
// atan2 reaches +PI, which would open a ray past the last one
|
|
405
|
+
const sector = Math.min(
|
|
406
|
+
GATHER_RAYS - 1,
|
|
407
|
+
Math.floor(((cell.angle + Math.PI) / (2 * Math.PI)) * GATHER_RAYS),
|
|
408
|
+
);
|
|
409
|
+
const ray = hash(sector, 5.3);
|
|
410
|
+
const p = (t * GATHER_RATE + ray) % 1;
|
|
411
|
+
const d =
|
|
412
|
+
cell.dist -
|
|
413
|
+
(rim + (GATHER_R - rim) * (1 - p) ** 1.25) -
|
|
414
|
+
(cell.grain - 0.5) * 16;
|
|
415
|
+
// dims as it lands, so a crumb is taken into the body rather than piling on it
|
|
416
|
+
return Math.exp(-(d * d) / 260) * smoothstep(0, 0.2, p) * (1 - p) * 1.1;
|
|
417
|
+
}
|
|
398
418
|
|
|
399
|
-
|
|
400
|
-
|
|
419
|
+
/** A comet on a thin orbit outside the body: sharp head, long tail. */
|
|
420
|
+
function cometValue(cell: Cell, t: number) {
|
|
421
|
+
const d = cell.dist - WORK_R;
|
|
422
|
+
const band = Math.exp(-(d * d) / WORK_BAND);
|
|
423
|
+
const raw = cell.angle - t * WORK_SPIN;
|
|
424
|
+
const da = Math.atan2(Math.sin(raw), Math.cos(raw));
|
|
425
|
+
return band * Math.exp(-(da * da) / (da < 0 ? WORK_TAIL : WORK_HEAD));
|
|
426
|
+
}
|
|
401
427
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
428
|
+
/**
|
|
429
|
+
* Her voice. Frequency is not mapped to angle: a voice's spectrum barely moves
|
|
430
|
+
* within a sentence, so that freezes into a fixed star. The voice gives the rim
|
|
431
|
+
* its size, its syllables and its lobes, and the whole pattern turns with time.
|
|
432
|
+
* No line in it is clean: the rim is crumbly, and a syllable throws crumbs
|
|
433
|
+
* rather than a ring.
|
|
434
|
+
*/
|
|
435
|
+
function speechValue(cell: Cell, t: number, v: Voice, amount: number) {
|
|
436
|
+
let raw = SPEAK_BASE + SPEAK_SWELL * v.phrase + SPEAK_KICK * v.bob;
|
|
437
|
+
for (let i = 0; i < SPEAK_LOBES; i++) {
|
|
438
|
+
raw +=
|
|
439
|
+
SPEAK_LOBE_R *
|
|
440
|
+
v.amp[i] *
|
|
441
|
+
Math.sin((i + 2) * (cell.angle + v.spin) + v.phase[i]);
|
|
442
|
+
}
|
|
443
|
+
// the rim grows out of the resting body as she starts and settles back into it
|
|
444
|
+
// when she stops; without this it appears at whatever the first syllable asks
|
|
445
|
+
// for, which reads as the circle jumping bigger
|
|
446
|
+
const edge = IDLE_R + (rimAt(raw) - IDLE_R) * amount;
|
|
447
|
+
|
|
448
|
+
// each cell sits a little in or out of the rim, and the offset drifts
|
|
449
|
+
const rough =
|
|
450
|
+
(cell.grain - 0.5) * SPEAK_ROUGH +
|
|
451
|
+
Math.sin(cell.angle * 5 + t * 0.9 + cell.seed * 6.283) * SPEAK_ROUGH * 0.3;
|
|
452
|
+
const d = cell.dist - (edge + rough);
|
|
453
|
+
let value =
|
|
454
|
+
Math.exp(-(d * d) / 800) + Math.exp(-(cell.dist * cell.dist) / 6000) * 0.5;
|
|
455
|
+
|
|
456
|
+
// a soft fill out to the rim, with the idle wave still moving through it
|
|
457
|
+
if (cell.dist < edge) {
|
|
458
|
+
value +=
|
|
459
|
+
0.22 *
|
|
460
|
+
(1 - cell.dist / edge) ** 0.6 *
|
|
461
|
+
(0.75 + 0.25 * Math.sin(cell.dist * 0.05 - t * 1.2));
|
|
462
|
+
}
|
|
405
463
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const flick = 0.86 + Math.sin(t * 3.5 + cell.seed * 6) * 0.14;
|
|
418
|
-
return on * flick;
|
|
464
|
+
// crumbs: a sparse share of cells, each thrown past the rim at its own speed
|
|
465
|
+
if (cell.speck < SPECK_SHARE) {
|
|
466
|
+
const speed = RING_SPEED * (0.6 + cell.grain * 0.9);
|
|
467
|
+
for (let i = 0; i < v.rings.length; i++) {
|
|
468
|
+
const ring = v.rings[i];
|
|
469
|
+
const age = t - ring.born;
|
|
470
|
+
const dd = cell.dist - (edge + 6 + age * speed);
|
|
471
|
+
value +=
|
|
472
|
+
Math.exp(-(dd * dd) / 500) *
|
|
473
|
+
ring.power *
|
|
474
|
+
Math.max(0, 1 - age / RING_LIFE);
|
|
419
475
|
}
|
|
420
476
|
}
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* ERROR, letter by letter: each lights up in turn and goes out the same way.
|
|
482
|
+
* `age` is ERROR's own clock, not the mode's, so leaving the mode fades the
|
|
483
|
+
* letters where they stand instead of restarting their cycle.
|
|
484
|
+
*/
|
|
485
|
+
function errorValue(cell: Cell, t: number, age: number) {
|
|
486
|
+
if (cell.letter < 0) return 0;
|
|
487
|
+
const pe = age % ERR_CYCLE;
|
|
488
|
+
const jitter = cell.seed * 0.12;
|
|
489
|
+
const appear = smoothstep(
|
|
490
|
+
0,
|
|
491
|
+
1,
|
|
492
|
+
(pe - cell.letter * ERR_STEP_IN - jitter) / ERR_FADE_IN,
|
|
493
|
+
);
|
|
494
|
+
const vanish = smoothstep(
|
|
495
|
+
0,
|
|
496
|
+
1,
|
|
497
|
+
(pe - ERR_HOLD_UNTIL - cell.letter * ERR_STEP_OUT - jitter) / ERR_FADE_OUT,
|
|
498
|
+
);
|
|
499
|
+
const flick = 0.86 + Math.sin(t * 3.5 + cell.seed * 6) * 0.14;
|
|
500
|
+
return Math.max(0, appear - vanish) * flick;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** Marks which cells ink each letter of `text`, fitted to about her body's width; returns the letter count. */
|
|
504
|
+
function layWord(cells: Cell[], cw: number, ch: number, text: string) {
|
|
505
|
+
const rows = spell(text);
|
|
506
|
+
const s = Math.max(
|
|
507
|
+
1,
|
|
508
|
+
Math.min(3, Math.floor((DESIGN * WORD_WIDTH) / (rows[0].length * cw))),
|
|
509
|
+
);
|
|
510
|
+
let letters = 0;
|
|
511
|
+
for (const cell of cells) {
|
|
512
|
+
cell.word = letterAt(rows, cell.dx, cell.dy, cw, ch, s);
|
|
513
|
+
letters = Math.max(letters, cell.word + 1);
|
|
514
|
+
}
|
|
515
|
+
return letters;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** A word's letters `age` seconds into its showing: each lights in turn, holds, and goes out in turn. */
|
|
519
|
+
function wordValue(cell: Cell, t: number, age: number) {
|
|
520
|
+
if (cell.word < 0) return 0;
|
|
521
|
+
const jitter = cell.seed * 0.08;
|
|
522
|
+
const appear = smoothstep(
|
|
523
|
+
0,
|
|
524
|
+
1,
|
|
525
|
+
(age - cell.word * WORD_STEP_IN - jitter) / WORD_FADE_IN,
|
|
526
|
+
);
|
|
527
|
+
const vanish = smoothstep(
|
|
528
|
+
0,
|
|
529
|
+
1,
|
|
530
|
+
(age - WORD_HOLD - cell.word * WORD_STEP_OUT - jitter) / WORD_FADE_OUT,
|
|
531
|
+
);
|
|
532
|
+
const flick = 0.86 + Math.sin(t * 3.5 + cell.seed * 6) * 0.14;
|
|
533
|
+
return Math.max(0, appear - vanish) * flick;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* One cell's brightness: the body, plus whichever behaviours are up. Her voice
|
|
538
|
+
* hollows the body rather than replacing it, so there is nothing to dissolve
|
|
539
|
+
* between when she starts or stops.
|
|
540
|
+
*/
|
|
541
|
+
function fieldValue(
|
|
542
|
+
cell: Cell,
|
|
543
|
+
t: number,
|
|
544
|
+
errAge: number,
|
|
545
|
+
wordAge: number,
|
|
546
|
+
f: Field,
|
|
547
|
+
v: Voice,
|
|
548
|
+
) {
|
|
549
|
+
let value =
|
|
550
|
+
f.scale > 0.02
|
|
551
|
+
? idleValue(cell, t, f.lift, f.scale) * (1 - 0.6 * f.speech)
|
|
552
|
+
: 0;
|
|
553
|
+
if (f.gather > 0.01) value += gatherValue(cell, t, f.scale) * f.gather;
|
|
554
|
+
if (f.comet > 0.01) value += cometValue(cell, t) * f.comet;
|
|
555
|
+
if (f.speech > 0.01) value += speechValue(cell, t, v, f.speech) * f.speech;
|
|
556
|
+
if (f.err > 0.01) value += errorValue(cell, t, errAge) * f.err;
|
|
557
|
+
if (f.word > 0.01) value += wordValue(cell, t, wordAge) * f.word;
|
|
558
|
+
return value;
|
|
421
559
|
}
|
|
422
560
|
|
|
423
561
|
export function AsciiOrb({
|
|
424
562
|
className,
|
|
425
563
|
mode = "idle",
|
|
426
564
|
charset = ASCII_FACE.charset,
|
|
427
|
-
emotion = null,
|
|
428
565
|
fontSize = ASCII_FACE.fontSize.default,
|
|
429
566
|
density = ASCII_FACE.density.default,
|
|
430
567
|
size = DESIGN,
|
|
431
568
|
color = DEFAULT_COLOR,
|
|
432
569
|
getSpectrum,
|
|
570
|
+
getMicSpectrum,
|
|
571
|
+
word = null,
|
|
433
572
|
}: AsciiOrbProps) {
|
|
434
573
|
const hostRef = useRef<HTMLCanvasElement>(null);
|
|
435
574
|
const ctxRef = useRef<CanvasRenderingContext2D | null>(null);
|
|
@@ -448,38 +587,53 @@ export function AsciiOrb({
|
|
|
448
587
|
cur: [...color] as [number, number, number],
|
|
449
588
|
target: color,
|
|
450
589
|
});
|
|
451
|
-
/**
|
|
452
|
-
const
|
|
453
|
-
cw: 1,
|
|
454
|
-
ch: 1,
|
|
455
|
-
errScale: 3,
|
|
456
|
-
faceScale: 3,
|
|
457
|
-
size: 0,
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
const emoRef = useRef<{
|
|
461
|
-
cur: AsciiOrbEmotion | null;
|
|
462
|
-
prev: AsciiOrbEmotion | null;
|
|
463
|
-
start: number;
|
|
464
|
-
}>({ cur: emotion, prev: null, start: -Infinity });
|
|
590
|
+
/** Side (px) the grid was built for; the loop clears this much */
|
|
591
|
+
const boxRef = useRef(0);
|
|
465
592
|
|
|
466
593
|
// the loop mounts once with no deps, so the latest getter comes through a ref
|
|
467
594
|
const specRef = useRef(getSpectrum);
|
|
468
595
|
specRef.current = getSpectrum;
|
|
596
|
+
const micRef = useRef(getMicSpectrum);
|
|
597
|
+
micRef.current = getMicSpectrum;
|
|
469
598
|
|
|
470
599
|
const voiceRef = useRef<Voice>({
|
|
471
600
|
phrase: 0,
|
|
601
|
+
micPhrase: 0,
|
|
602
|
+
micLevel: 0,
|
|
472
603
|
bob: 0,
|
|
473
604
|
bobVel: 0,
|
|
474
605
|
amp: new Array<number>(SPEAK_LOBES).fill(0),
|
|
475
606
|
// distinct start phases, or the lobes overlap into one lump at first
|
|
476
607
|
phase: Array.from({ length: SPEAK_LOBES }, (_, i) => i * 2.1),
|
|
608
|
+
spin: 0,
|
|
477
609
|
rings: [],
|
|
478
610
|
});
|
|
479
611
|
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
|
|
612
|
+
/** The mode being asked for, and when it was asked for. */
|
|
613
|
+
const modeRef = useRef({ mode, start: 0 });
|
|
614
|
+
/**
|
|
615
|
+
* The field on screen. It only ever eases toward the mode's targets, so it
|
|
616
|
+
* starts drawn in and opens on the first frames like any other arrival.
|
|
617
|
+
*/
|
|
618
|
+
const fieldRef = useRef<Field>({
|
|
619
|
+
scale: 0.3,
|
|
620
|
+
lift: 0,
|
|
621
|
+
gather: 0,
|
|
622
|
+
comet: 0,
|
|
623
|
+
speech: 0,
|
|
624
|
+
err: 0,
|
|
625
|
+
word: 0,
|
|
626
|
+
listen: 0,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
/** The word being shown, when it started, and how many letters it has; its cells carry the rest */
|
|
630
|
+
const wordRef = useRef<{
|
|
631
|
+
text: string;
|
|
632
|
+
start: number;
|
|
633
|
+
letters: number;
|
|
634
|
+
} | null>(null);
|
|
635
|
+
/** Grid pitch in reference units, for laying a word onto cells that already exist */
|
|
636
|
+
const pitchRef = useRef({ cw: 1, ch: 1 });
|
|
483
637
|
|
|
484
638
|
// grid is rebuilt only when size or density changes
|
|
485
639
|
useEffect(() => {
|
|
@@ -493,16 +647,13 @@ export function AsciiOrb({
|
|
|
493
647
|
const norm = DESIGN / size;
|
|
494
648
|
const cwN = cw * norm;
|
|
495
649
|
const chN = ch * norm;
|
|
496
|
-
// scale ERROR
|
|
650
|
+
// scale ERROR so it stays inside the box at any glyph size
|
|
497
651
|
const errScale = Math.max(
|
|
498
652
|
1,
|
|
499
|
-
Math.floor((DESIGN * 0.92) / (
|
|
500
|
-
);
|
|
501
|
-
const faceScale = Math.max(
|
|
502
|
-
1,
|
|
503
|
-
Math.floor((DESIGN * 0.82) / (FACE_COLS * cwN)),
|
|
653
|
+
Math.floor((DESIGN * 0.92) / (ERROR_ROWS[0].length * cwN)),
|
|
504
654
|
);
|
|
505
|
-
|
|
655
|
+
boxRef.current = size;
|
|
656
|
+
pitchRef.current = { cw: cwN, ch: chN };
|
|
506
657
|
|
|
507
658
|
const cells: Cell[] = [];
|
|
508
659
|
const cols = Math.ceil(size / cw);
|
|
@@ -517,7 +668,7 @@ export function AsciiOrb({
|
|
|
517
668
|
const dy = (y - size / 2) * norm;
|
|
518
669
|
const dist = Math.hypot(dx, dy);
|
|
519
670
|
|
|
520
|
-
const letter =
|
|
671
|
+
const letter = letterAt(ERROR_ROWS, dx, dy, cwN, chN, errScale);
|
|
521
672
|
if (letter < 0 && dist > FIELD_R) continue;
|
|
522
673
|
|
|
523
674
|
cells.push({
|
|
@@ -531,17 +682,17 @@ export function AsciiOrb({
|
|
|
531
682
|
seed: hash(c, r),
|
|
532
683
|
grain: hash(c * 5.7 + 19, r * 2.3 + 53),
|
|
533
684
|
gap: 0.05 + hash(c * 7.3 + 11, r * 3.1 + 5) * 0.3,
|
|
534
|
-
// independent order per cell, so cells appear one at a time rather than in clumps
|
|
535
|
-
birth: hash(c * 3.1 + 7, r * 5.7 + 2),
|
|
536
|
-
death: hash(c * 9.4 + 3, r * 2.6 + 8),
|
|
537
685
|
emoji: hash(c * 2.7 + 31, r * 5.9 + 17) < EMOJI_RATIO,
|
|
538
686
|
speck: hash(c * 4.1 + 23, r * 6.3 + 41),
|
|
539
687
|
letter,
|
|
688
|
+
word: -1,
|
|
540
689
|
});
|
|
541
690
|
}
|
|
542
691
|
}
|
|
543
692
|
|
|
544
693
|
cellsRef.current = cells;
|
|
694
|
+
// a word showing while the grid changes is laid onto the new cells
|
|
695
|
+
if (wordRef.current) layWord(cells, cwN, chN, wordRef.current.text);
|
|
545
696
|
|
|
546
697
|
// bucketing by level keeps fillStyle changes to one per level
|
|
547
698
|
bucketsRef.current = {
|
|
@@ -581,17 +732,6 @@ export function AsciiOrb({
|
|
|
581
732
|
};
|
|
582
733
|
}, [fontSize, density, size]);
|
|
583
734
|
|
|
584
|
-
// expression change: old one leaves, brief blank, new one appears
|
|
585
|
-
useEffect(() => {
|
|
586
|
-
const e = emoRef.current;
|
|
587
|
-
if (e.cur === emotion) return;
|
|
588
|
-
emoRef.current = {
|
|
589
|
-
cur: emotion,
|
|
590
|
-
prev: e.cur,
|
|
591
|
-
start: performance.now() * 0.001,
|
|
592
|
-
};
|
|
593
|
-
}, [emotion]);
|
|
594
|
-
|
|
595
735
|
// every frame redraws everything, so the charset only needs a ref update
|
|
596
736
|
useEffect(() => {
|
|
597
737
|
charsetRef.current = charset;
|
|
@@ -602,25 +742,37 @@ export function AsciiOrb({
|
|
|
602
742
|
colorRef.current.target = color;
|
|
603
743
|
}, [color]);
|
|
604
744
|
|
|
605
|
-
// mode change
|
|
745
|
+
// a mode change only moves the targets; the field eases the rest of the way
|
|
606
746
|
useEffect(() => {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
prevRef.current = { ...curRef.current };
|
|
610
|
-
curRef.current = { mode, start: now };
|
|
611
|
-
xfadeStartRef.current = now;
|
|
612
|
-
} else {
|
|
613
|
-
curRef.current = { mode, start: now };
|
|
614
|
-
}
|
|
747
|
+
if (modeRef.current.mode === mode) return;
|
|
748
|
+
modeRef.current = { mode, start: performance.now() * 0.001 };
|
|
615
749
|
}, [mode]);
|
|
616
750
|
|
|
751
|
+
// a new word starts from its first letter, in place of one still showing
|
|
752
|
+
useEffect(() => {
|
|
753
|
+
if (!word) return;
|
|
754
|
+
const { cw, ch } = pitchRef.current;
|
|
755
|
+
wordRef.current = {
|
|
756
|
+
text: word.text,
|
|
757
|
+
start: performance.now() * 0.001,
|
|
758
|
+
letters: layWord(cellsRef.current, cw, ch, word.text),
|
|
759
|
+
};
|
|
760
|
+
}, [word]);
|
|
761
|
+
|
|
617
762
|
// animation loop
|
|
618
763
|
useEffect(() => {
|
|
619
764
|
let raf = 0;
|
|
620
765
|
const follower = createVoiceFollower();
|
|
766
|
+
const micFollower = createVoiceFollower();
|
|
621
767
|
const murmur = new Array<number>(SPECTRUM_BANDS).fill(0);
|
|
768
|
+
const silence = new Array<number>(SPECTRUM_BANDS).fill(0);
|
|
622
769
|
|
|
623
770
|
let lastT = performance.now() * 0.001;
|
|
771
|
+
/** Glyph clock: it runs at the retype pace, so changing the pace never jumps a glyph */
|
|
772
|
+
let clock = 0;
|
|
773
|
+
let pace = 1;
|
|
774
|
+
/** Seconds ERROR has been showing (errorValue) */
|
|
775
|
+
let errAge = 0;
|
|
624
776
|
|
|
625
777
|
const draw = (nowMs: number) => {
|
|
626
778
|
const t = nowMs * 0.001;
|
|
@@ -634,7 +786,7 @@ export function AsciiOrb({
|
|
|
634
786
|
return;
|
|
635
787
|
}
|
|
636
788
|
const cs = charsetRef.current;
|
|
637
|
-
const
|
|
789
|
+
const box = boxRef.current;
|
|
638
790
|
bk.asciiN.fill(0);
|
|
639
791
|
bk.emojiN.fill(0);
|
|
640
792
|
|
|
@@ -665,6 +817,19 @@ export function AsciiOrb({
|
|
|
665
817
|
voice.bobVel += -voice.bob * 0.012 - voice.bobVel * 0.09;
|
|
666
818
|
voice.bob += voice.bobVel;
|
|
667
819
|
|
|
820
|
+
// the microphone is read every frame but answers only while listening: it
|
|
821
|
+
// sets how fast the glyphs retype, and lifts the body across a phrase
|
|
822
|
+
const mic = micRef.current?.();
|
|
823
|
+
const micHeard = micFollower.read(mic ?? silence, dt);
|
|
824
|
+
voice.micPhrase = mic ? micHeard.phrase : 0;
|
|
825
|
+
voice.micLevel = mic ? micHeard.level : 0;
|
|
826
|
+
const wantPace =
|
|
827
|
+
mic && modeRef.current.mode === "listening"
|
|
828
|
+
? LISTEN_PACE_QUIET + LISTEN_PACE_VOICE * micHeard.level
|
|
829
|
+
: 1;
|
|
830
|
+
pace = toward(pace, wantPace, PACE_TIME, PACE_TIME, dt);
|
|
831
|
+
clock += dt * pace;
|
|
832
|
+
|
|
668
833
|
for (let i = 0; i < SPEAK_LOBES; i++) {
|
|
669
834
|
// neighbouring bands per lobe, low lobes from low bands
|
|
670
835
|
const from = Math.floor((i * heard.bands.length) / SPEAK_LOBES);
|
|
@@ -680,74 +845,70 @@ export function AsciiOrb({
|
|
|
680
845
|
// different time constants per harmonic, or the star only scales
|
|
681
846
|
voice.amp[i] += (energy * wander - voice.amp[i]) * (0.05 + i * 0.02);
|
|
682
847
|
// own speed, alternating direction, so no standing wave forms
|
|
683
|
-
voice.phase[i] += dt * (0.
|
|
848
|
+
voice.phase[i] += dt * (0.9 + i * 0.5) * (i % 2 ? -1 : 1);
|
|
684
849
|
}
|
|
685
850
|
|
|
851
|
+
// the pattern turns one way, faster while she is mid-phrase
|
|
852
|
+
voice.spin += dt * (SPEAK_SPIN + SPEAK_SPIN_VOICE * voice.phrase);
|
|
853
|
+
|
|
686
854
|
while (voice.rings.length > 0 && t - voice.rings[0].born > RING_LIFE) {
|
|
687
855
|
voice.rings.shift();
|
|
688
856
|
}
|
|
689
857
|
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
858
|
+
// the field eases toward what this mode wants, each channel on its own clock
|
|
859
|
+
const cur = modeRef.current;
|
|
860
|
+
// a word takes the face from the mode until its last letter is out; an error keeps it
|
|
861
|
+
const shown = wordRef.current;
|
|
862
|
+
const wordAge = shown ? t - shown.start : 0;
|
|
863
|
+
const wording =
|
|
864
|
+
shown !== null &&
|
|
865
|
+
cur.mode !== "error" &&
|
|
866
|
+
wordAge < WORD_HOLD + shown.letters * WORD_STEP_OUT + WORD_FADE_OUT;
|
|
867
|
+
const want: Field = wording
|
|
868
|
+
? {
|
|
869
|
+
scale: 0,
|
|
870
|
+
lift: 0,
|
|
871
|
+
gather: 0,
|
|
872
|
+
comet: 0,
|
|
873
|
+
speech: 0,
|
|
874
|
+
err: 0,
|
|
875
|
+
word: 1,
|
|
876
|
+
listen: 0,
|
|
877
|
+
}
|
|
878
|
+
: targetFor(cur.mode, t - cur.start, voice);
|
|
879
|
+
const f = fieldRef.current;
|
|
880
|
+
f.scale = toward(
|
|
881
|
+
f.scale,
|
|
882
|
+
want.scale,
|
|
883
|
+
RISE.scale,
|
|
884
|
+
cur.mode === "ending" ? END_FALL : FALL.scale,
|
|
885
|
+
dt,
|
|
709
886
|
);
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
return (s === 1 ? 1 : 0.88) * shade * holes;
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
/** A few specks of dust around the face, like idle */
|
|
730
|
-
const faceDust = (cell: Cell) => {
|
|
731
|
-
if (cell.seed < 0.976) return 0;
|
|
732
|
-
// each speck floats at its own distance so they do not line up in a band
|
|
733
|
-
const r = 195 + hash(cell.seed * 311, 9) * 160;
|
|
734
|
-
const fall = Math.exp(-((cell.dist - r) ** 2) / 7000);
|
|
735
|
-
// own blink rate and phase
|
|
736
|
-
const sp = 0.5 + hash(cell.seed * 47, 21) * 1.5;
|
|
737
|
-
return fall * (0.5 + 0.5 * Math.sin(t * sp + cell.seed * 300)) * 0.36;
|
|
738
|
-
};
|
|
887
|
+
f.lift = toward(f.lift, want.lift, RISE.lift, FALL.lift, dt);
|
|
888
|
+
f.gather = toward(f.gather, want.gather, RISE.gather, FALL.gather, dt);
|
|
889
|
+
f.comet = toward(f.comet, want.comet, RISE.comet, FALL.comet, dt);
|
|
890
|
+
f.speech = toward(f.speech, want.speech, RISE.speech, FALL.speech, dt);
|
|
891
|
+
// ERROR's clock runs only in its mode and holds while the letters fade
|
|
892
|
+
// out; an error that finds them already gone starts from the first letter
|
|
893
|
+
if (cur.mode === "error") {
|
|
894
|
+
if (f.err <= 0.01) errAge = 0;
|
|
895
|
+
errAge += dt;
|
|
896
|
+
}
|
|
897
|
+
f.err = toward(f.err, want.err, RISE.err, FALL.err, dt);
|
|
898
|
+
f.word = toward(f.word, want.word, RISE.word, FALL.word, dt);
|
|
899
|
+
f.listen = toward(f.listen, want.listen, RISE.listen, FALL.listen, dt);
|
|
900
|
+
const solidError = f.err > 0.5;
|
|
901
|
+
const solidWord = f.word > 0.5;
|
|
902
|
+
const rate = cs === "emojiOnly" ? EMOJI_CHAR_RATE : CHAR_RATE;
|
|
739
903
|
|
|
740
904
|
const all = cellsRef.current;
|
|
741
905
|
for (let ci = 0; ci < all.length; ci++) {
|
|
742
906
|
const cell = all[ci];
|
|
743
|
-
let v =
|
|
744
|
-
|
|
745
|
-
if (blending && prev) {
|
|
746
|
-
const pv = valueFor(prev.mode, cell, t, t - prev.start, voice);
|
|
747
|
-
v = pv + (v - pv) * k;
|
|
748
|
-
}
|
|
907
|
+
let v = fieldValue(cell, t, errAge, wordAge, f, voice);
|
|
749
908
|
|
|
750
|
-
if (
|
|
909
|
+
if (
|
|
910
|
+
!((solidError && cell.letter >= 0) || (solidWord && cell.word >= 0))
|
|
911
|
+
) {
|
|
751
912
|
// per-cell brightness response breaks concentric rings; multiplicative, so empty (0) stays empty
|
|
752
913
|
v *= 0.66 + cell.grain * 0.72;
|
|
753
914
|
// slowly drifting noise on top
|
|
@@ -766,42 +927,27 @@ export function AsciiOrb({
|
|
|
766
927
|
v = Math.max(v, 0.9);
|
|
767
928
|
}
|
|
768
929
|
|
|
769
|
-
const gate =
|
|
930
|
+
const gate =
|
|
931
|
+
cell.gap +
|
|
932
|
+
Math.sin(t * 0.28 + cell.seed * 6.283) * 0.05 +
|
|
933
|
+
f.listen *
|
|
934
|
+
(LISTEN_THIN +
|
|
935
|
+
(hash(
|
|
936
|
+
Math.floor(t * LISTEN_POP_RATE + cell.seed * 3),
|
|
937
|
+
cell.seed * 97,
|
|
938
|
+
) -
|
|
939
|
+
0.5) *
|
|
940
|
+
LISTEN_POP *
|
|
941
|
+
voice.micLevel);
|
|
770
942
|
if (v < gate) v = 0;
|
|
771
943
|
}
|
|
772
944
|
|
|
773
|
-
// With an expression set, the whole field switches to it once the
|
|
774
|
-
// previous content (blob or old face) is fully gone
|
|
775
|
-
let solid = false;
|
|
776
|
-
if (faceBusy) {
|
|
777
|
-
// per-cell order with a narrow window: cells snap on and off rather than fade
|
|
778
|
-
const gone = smoothstep(0, 1, (rawOut * 1.1 - cell.death) / 0.08);
|
|
779
|
-
const born = smoothstep(0, 1, (rawIn * 1.1 - cell.birth) / 0.08);
|
|
780
|
-
|
|
781
|
-
// sample the bitmap once per expression
|
|
782
|
-
const kCur = emo.cur ? faceKind(emo.cur, cell) : 0;
|
|
783
|
-
const kPrev = emo.prev ? faceKind(emo.prev, cell) : 0;
|
|
784
|
-
|
|
785
|
-
const dust = faceDust(cell);
|
|
786
|
-
const oldV = emo.prev ? faceValue(kPrev, cell) + dust : v;
|
|
787
|
-
const newV = emo.cur ? faceValue(kCur, cell) + dust : v;
|
|
788
|
-
|
|
789
|
-
v = oldV * (1 - gone) + newV * born;
|
|
790
|
-
solid = (kCur > 0 && born > 0.05) || (kPrev > 0 && gone < 0.95);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
945
|
v = v < 0 ? 0 : v > 1 ? 1 : v;
|
|
794
946
|
|
|
795
947
|
const level = (v * (RAMP.length - 1)) | 0;
|
|
796
948
|
if (level === 0) continue;
|
|
797
949
|
|
|
798
|
-
|
|
799
|
-
const rate = solid
|
|
800
|
-
? FACE_CHAR_RATE
|
|
801
|
-
: cs === "emojiOnly"
|
|
802
|
-
? EMOJI_CHAR_RATE
|
|
803
|
-
: CHAR_RATE;
|
|
804
|
-
const slot = (t * rate + cell.seed * 7) | 0;
|
|
950
|
+
const slot = (clock * rate + cell.seed * 7) | 0;
|
|
805
951
|
|
|
806
952
|
const showEmoji =
|
|
807
953
|
cs === "emojiOnly" ||
|
|
@@ -859,8 +1005,6 @@ export function AsciiOrb({
|
|
|
859
1005
|
}
|
|
860
1006
|
ctx.globalAlpha = 1;
|
|
861
1007
|
|
|
862
|
-
if (!blending && prev) prevRef.current = null;
|
|
863
|
-
|
|
864
1008
|
raf = requestAnimationFrame(draw);
|
|
865
1009
|
};
|
|
866
1010
|
|