thursday-agent 0.8.0 → 0.9.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/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- 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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/keep-working/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js +1 -1
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/probe/route.js.nft.json +1 -1
- package/.next/server/app/api/reach/route.js +11 -11
- package/.next/server/app/api/reach/route.js.nft.json +1 -1
- package/.next/server/app/api/routine/route.js +6 -6
- package/.next/server/app/api/routine/route.js.nft.json +1 -1
- package/.next/server/app/api/signins/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/text/route.js +11 -11
- package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +6 -6
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
- 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.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/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next/server/app/page/server-reference-manifest.json +56 -56
- package/.next/server/app/page.js +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__012s2o8._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__04r4h-2._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0nmhgeo._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1fy2g82._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1ndi14y._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1nmflqk._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1v7-zke._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1wnzmx9._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1xv-5cq._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
- package/.next/server/chunks/_05v8k21._.js +1 -1
- package/.next/server/chunks/_0awtznl._.js +4 -4
- package/.next/server/chunks/_0k6qjcf._.js +1 -1
- package/.next/server/chunks/_0na2fj1._.js +5 -5
- package/.next/server/chunks/_0x-ej3f._.js +21 -21
- package/.next/server/chunks/_1dc_d1q._.js +22 -20
- package/.next/server/chunks/_1jc5g4p._.js +2 -2
- package/.next/server/chunks/_1jn-aep._.js +50 -48
- package/.next/server/chunks/{_1u8ygbz._.js → _1m88lkh._.js} +7 -7
- package/.next/server/chunks/_1uuucy-._.js +4 -4
- package/.next/server/chunks/_1v9r2ke._.js +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__1h4bnrf._.js → [root-of-the-server]__0oo-wlv._.js} +2 -2
- package/.next/server/chunks/ssr/{[root-of-the-server]__1kyi568._.js → [root-of-the-server]__0tvavy9._.js} +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0tzekbl._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0zuns7e._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1se25x0._.js +1 -1
- package/.next/server/chunks/ssr/{_1i3ddn7._.js → _05pl_u8._.js} +1 -1
- package/.next/server/chunks/ssr/_070nv1_._.js +3 -0
- package/.next/server/chunks/ssr/_097-ljd._.js +1 -1
- package/.next/server/chunks/ssr/_0dshsh4._.js +3 -0
- package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
- package/.next/server/chunks/ssr/{_0z8jpch._.js → _0ital-2._.js} +2 -2
- package/.next/server/chunks/ssr/{_19g570d._.js → _0kjj5ts._.js} +3 -3
- package/.next/server/chunks/ssr/{_1nod5hh._.js → _0l_mcfz._.js} +2 -2
- package/.next/server/chunks/ssr/_0ld9_qq._.js +1 -1
- package/.next/server/chunks/ssr/_0ltz2q5._.js +3 -0
- package/.next/server/chunks/ssr/_0scf7xy._.js +3 -0
- package/.next/server/chunks/ssr/_0u5xv6g._.js +3 -0
- package/.next/server/chunks/ssr/{_0ewory0._.js → _0urj3mc._.js} +2 -2
- package/.next/server/chunks/ssr/{_0pm-2p-._.js → _1-zyvdb._.js} +2 -2
- package/.next/server/chunks/ssr/{_1_rqu8x._.js → _12mijol._.js} +7 -7
- package/.next/server/chunks/ssr/_15zqt-t._.js +1 -1
- package/.next/server/chunks/ssr/_184327j._.js +1 -1
- package/.next/server/chunks/ssr/{_1y-ea28._.js → _1a-nuxx._.js} +2 -2
- package/.next/server/chunks/ssr/_1alxym6._.js +3 -0
- package/.next/server/chunks/ssr/{_0hzz82u._.js → _1d423aj._.js} +25 -23
- package/.next/server/chunks/ssr/{_1u1mxu9._.js → _1flbs77._.js} +2 -2
- package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
- package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
- package/.next/server/chunks/ssr/{_1oijqjw._.js → _1tmu8z-._.js} +2 -2
- package/.next/server/chunks/ssr/{_10y6r1n._.js → _1uy0xom._.js} +3 -3
- package/.next/server/chunks/ssr/_1x-kojb._.js +20 -0
- package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
- package/.next/server/chunks/ssr/_1z74dmq._.js +164 -0
- package/.next/server/chunks/ssr/features_bot_1nr94ks._.js +3 -0
- package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +1 -1
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
- package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
- package/.next/server/chunks/ssr/{node_modules_111sh9o._.js → node_modules_03d0rfz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0lg75qx._.js → node_modules_04_sbbc._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_10gbm2y._.js → node_modules_05ybmta._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1v9tc1c._.js → node_modules_068ws5v._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_8mv4v._.js → node_modules_06bb4q8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_01f8_se._.js → node_modules_07rs_p2._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ii1l89._.js → node_modules_0dzm4um._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20xmwtu._.js → node_modules_0jljel9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ix-g49._.js → node_modules_0sgy-dc._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0woscox._.js → node_modules_0t_ez6-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_059vtf2._.js → node_modules_0th357h._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_06w645b._.js → node_modules_0ubdtk-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0b8df3s._.js → node_modules_0uyh_vb._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_07ih6wb._.js → node_modules_0wnlddw._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0gt-uw9._.js → node_modules_0x630an._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1x825hz._.js → node_modules_0yvz9e0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0t-44lj._.js → node_modules_12et8d5._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1m3gb16._.js → node_modules_13kwejm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1p9rdsy._.js → node_modules_1af3w91._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0sz5yg1._.js → node_modules_1bucjlk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1teyzfa._.js → node_modules_1lkix61._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lsyy7k._.js → node_modules_1ogczzz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1-7qawg._.js → node_modules_1s-gb-1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0yv-re6._.js → node_modules_1uwg-gz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_03ptrl9._.js → node_modules_1wbkzl9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_00jv9_3._.js → node_modules_1z1lrbk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@base-ui_react_0jne69c._.js → node_modules_@base-ui_react_0buz1-j._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_00q1oa3._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0r443fh._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1-2_5z7._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_10wnlbk._.js} +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/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +57 -57
- package/.next/static/chunks/06nt9tatr-dug.js +1 -0
- package/.next/static/chunks/0_awn8tucznxc.js +1 -0
- package/.next/static/chunks/{34s52v8f841zo.js → 0ge-hx0ed83a1.js} +1 -1
- package/.next/static/chunks/0pda_vf-abwvx.js +1 -0
- package/.next/static/chunks/{2u9yjkpkoij75.js → 0qmd8n52hp-6v.js} +1 -1
- package/.next/static/chunks/0rowp9rx3qbxu.js +18 -0
- package/.next/static/chunks/{1ocy4w0v20a3v.js → 13ixzheqw7o-w.js} +2 -2
- package/.next/static/chunks/1grkt5gr8pbg1.js +17 -0
- package/.next/static/chunks/{21gf2t_whhurc.js → 1yugpvvi8qrbu.js} +2 -2
- package/.next/static/chunks/{1e4yrzs5evq_r.js → 245hb85nsy6j4.js} +1 -1
- package/.next/static/chunks/27lkanxvtdn09.js +1 -0
- package/.next/static/chunks/{0oz7a68qanm99.js → 27tvv1i-smrck.js} +1 -1
- package/.next/static/chunks/{2zhfdbq50ee5i.js → 2c64drdevdla3.js} +1 -1
- package/.next/static/chunks/{0k5tkt_qi52kb.js → 2ndw4w4u2x51r.js} +1 -1
- package/.next/static/chunks/37j27b3e1gt7r.js +1 -0
- package/.next/static/chunks/{2om4lkg86feb4.js → 38301ji-85i30.js} +1 -1
- package/.next/static/chunks/{09c13vnqy7al3.js → 3bbzsitgiltnt.js} +2 -2
- package/.next/static/chunks/3fec-aeteddr-.js +1 -0
- package/.next/static/chunks/3go3hwp1z2_h2.css +1 -0
- package/.next/static/chunks/3h2bun3hcggg8.js +1 -0
- package/.next/static/chunks/{0g1p98s2pfx2w.js → 3h2e-xv1zxh_f.js} +2 -2
- package/.next/static/chunks/3j1y335o95mj7.js +1 -0
- package/.next/static/chunks/3obf9n3cxj99k.js +1 -0
- package/.next/static/chunks/{2upc9mkcc3nlq.js → 427qsc6kahxyo.js} +1 -1
- package/README.md +31 -14
- package/bin/port.mjs +11 -7
- package/bin/thursday.mjs +1 -1
- package/components/ui/radio-group.tsx +2 -2
- package/components/ui/shiny-text.tsx +3 -22
- package/docs/how-it-works.md +11 -3
- package/docs/images/first-run.png +0 -0
- package/docs/images/keeps-going.png +0 -0
- package/docs/images/two-minds.png +0 -0
- package/features/ai/components/model-browser.tsx +1 -1
- package/features/ai/components/model-picker.tsx +1 -1
- package/features/ai/guide.ts +9 -4
- package/features/ai/prompts/bot.prompt.ts +4 -1
- package/features/ai/tools/routine.tool.ts +28 -16
- package/features/bot/bot.runner.ts +3 -1
- package/features/bot/components/bot-mark.tsx +6 -1
- package/features/bot/components/bot-setting.tsx +2 -4
- package/features/bot/components/bot-tool.tsx +2 -0
- package/features/bot/components/room-list.tsx +12 -3
- package/features/bot/components/room-pill.tsx +26 -19
- package/features/config/components/config-setting.tsx +17 -5
- package/features/config/components/voice-key.tsx +38 -18
- package/features/connectors/mcp.const.ts +90 -59
- package/features/intro/components/intro.tsx +50 -27
- package/features/routine/components/routine-mark.tsx +4 -6
- package/features/routine/components/routine-setting.tsx +332 -143
- package/features/routine/routine.query.ts +24 -3
- package/features/routine/routine.schema.ts +55 -4
- package/features/settings/components/install-app.tsx +1 -4
- package/features/settings/components/setting-ui.tsx +1 -1
- package/features/skills/skills.discover.ts +2 -1
- package/features/skills/skills.query.ts +5 -1
- package/features/skills/skills.schema.ts +2 -0
- package/features/thursday/components/ascii-orb.tsx +39 -10
- package/features/thursday/components/connect-wave.tsx +284 -0
- package/features/thursday/components/side-captions.tsx +29 -17
- package/features/thursday/components/thursday-setting.tsx +2 -2
- package/features/thursday/components/thursday.tsx +130 -58
- package/features/thursday/components/voice-picker.tsx +1 -1
- package/features/thursday/components/write-line.tsx +2 -2
- package/features/thursday/open-work.ts +11 -3
- package/features/thursday/tool-line.ts +94 -12
- package/features/thursday/use-text-call.ts +3 -2
- package/features/thursday/use-thursday.ts +5 -2
- package/features/workspace/components/artifact-view.tsx +129 -7
- package/guide/bots.md +17 -5
- package/guide/calls.md +15 -7
- package/guide/setup.md +10 -2
- package/lib/desktop-notify.ts +3 -2
- package/package.json +1 -1
- package/scripts/bot-context.test.mts +1 -1
- package/skills/computer/SKILL.md +1 -0
- package/skills/interactive-page/SKILL.md +4 -0
- package/.next/server/chunks/[root-of-the-server]__0n-l_n_._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0wvo2of._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1e6aut3._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1ziojdq._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1srp8l5._.js +0 -3
- package/.next/server/chunks/ssr/_0mes4s0._.js +0 -3
- package/.next/server/chunks/ssr/_0ya0cb0._.js +0 -3
- package/.next/server/chunks/ssr/_0yto5s7._.js +0 -20
- package/.next/server/chunks/ssr/_10dw2nv._.js +0 -3
- package/.next/server/chunks/ssr/_1p0gxeo._.js +0 -164
- package/.next/server/chunks/ssr/_1r41yvh._.js +0 -3
- package/.next/server/chunks/ssr/_1r79ksv._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1ogjt52._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_@base-ui_react_0b3d4m9._.js +0 -3
- package/.next/static/chunks/03omwz_vi2ndg.js +0 -1
- package/.next/static/chunks/0_1bqgd6wt_og.js +0 -1
- package/.next/static/chunks/0abgkzikaznxc.js +0 -1
- package/.next/static/chunks/0bddkpash59-o.js +0 -17
- package/.next/static/chunks/0c2-bzhy3a0ib.js +0 -1
- package/.next/static/chunks/0iilfb1ocevnx.js +0 -1
- package/.next/static/chunks/0imbhx-j830a_.css +0 -1
- package/.next/static/chunks/24h1y9dhy3m_2.js +0 -1
- package/.next/static/chunks/2be08eqxsfc1y.js +0 -1
- package/.next/static/chunks/2d3yhjtafgmtw.js +0 -1
- package/.next/static/chunks/2f_5saocsko_b.js +0 -1
- package/.next/static/chunks/2lwbr5vx0ixaj.js +0 -18
- package/.next/static/chunks/44kjmi5h_p8px.js +0 -1
- /package/.next/static/{UiOMPYpQ4fqGyu5PzS-7j → u8kJqShUAa49vY7BxAWsw}/_buildManifest.js +0 -0
- /package/.next/static/{UiOMPYpQ4fqGyu5PzS-7j → u8kJqShUAa49vY7BxAWsw}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{UiOMPYpQ4fqGyu5PzS-7j → u8kJqShUAa49vY7BxAWsw}/_ssgManifest.js +0 -0
|
@@ -300,14 +300,8 @@ export function happenedIn(
|
|
|
300
300
|
text: `stopped · ${label}`,
|
|
301
301
|
});
|
|
302
302
|
} else if (thread.status === "done") {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
at: own.name,
|
|
306
|
-
from: own,
|
|
307
|
-
to: [],
|
|
308
|
-
sign: "done",
|
|
309
|
-
text: `done · ${label}`,
|
|
310
|
-
});
|
|
303
|
+
// A job's own ending is the card in the screen's left corner (artifact-view); the pill
|
|
304
|
+
// keeps its bubbles for what is still going on
|
|
311
305
|
} else if (thread.status === "working") {
|
|
312
306
|
// Words from the user already have their bubble.
|
|
313
307
|
if (!fresh.some((line) => line.kind === "user")) {
|
|
@@ -525,24 +519,33 @@ export function Chip({
|
|
|
525
519
|
pending > 0
|
|
526
520
|
? `${pending} ${pending === 1 ? "needs" : "need"} a reply`
|
|
527
521
|
: "",
|
|
528
|
-
unread > 0
|
|
529
|
-
? `${unread} new ${unread === 1 ? "result" : "results"}`
|
|
530
|
-
: "",
|
|
531
522
|
]
|
|
532
523
|
.filter(Boolean)
|
|
533
524
|
.join(" · ")}
|
|
525
|
+
{unread > 0 && (
|
|
526
|
+
// What just arrived is what the card most needs you to see
|
|
527
|
+
<span className="text-brand">
|
|
528
|
+
{pending > 0 && " · "}
|
|
529
|
+
{unread} new {unread === 1 ? "result" : "results"}
|
|
530
|
+
</span>
|
|
531
|
+
)}
|
|
534
532
|
</span>
|
|
535
533
|
</p>
|
|
536
534
|
{/* px-1: a row keeps its own 8px, so its mark lands on the rail while
|
|
537
535
|
the shape it lights up on hover stays inside the card's corners */}
|
|
538
536
|
<div className="max-h-[45vh] overflow-y-auto px-1.5 pb-2">
|
|
539
|
-
{rows
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
537
|
+
{/* Keyed on growing, so the rows rise in after the card each time it opens */}
|
|
538
|
+
<div key={String(grown)}>
|
|
539
|
+
{rows.map((thread, index) => (
|
|
540
|
+
<div
|
|
541
|
+
key={thread.id}
|
|
542
|
+
style={{ animationDelay: `${120 + index * 60}ms` }}
|
|
543
|
+
className="animate-in duration-300 fill-mode-backwards fade-in slide-in-from-bottom-1"
|
|
544
|
+
>
|
|
545
|
+
<ThreadRow thread={thread} onPick={() => onPick(thread.id)} />
|
|
546
|
+
</div>
|
|
547
|
+
))}
|
|
548
|
+
</div>
|
|
546
549
|
</div>
|
|
547
550
|
<span className="block h-1.5" />
|
|
548
551
|
</div>
|
|
@@ -697,6 +700,9 @@ function Crew({
|
|
|
697
700
|
/** The hand-off up, drawn over the face it points at. */
|
|
698
701
|
bubble: Handoff | null;
|
|
699
702
|
}) {
|
|
703
|
+
// While anyone is moving or waiting, the rest step back so it reads at a glance who is
|
|
704
|
+
const lit = (face: CrewFace) => face.awake || face.waiting || face.word;
|
|
705
|
+
const astir = crew.some(lit);
|
|
700
706
|
return (
|
|
701
707
|
<span className="flex min-w-0 shrink items-center">
|
|
702
708
|
{crew.map((face, index) => {
|
|
@@ -707,7 +713,8 @@ function Crew({
|
|
|
707
713
|
// these are not circles. Earlier faces sit on top, so the dot on a
|
|
708
714
|
// waiting face is never covered by its neighbour.
|
|
709
715
|
className={cn(
|
|
710
|
-
"relative shrink-0 transition-[margin] duration-500 ease-out",
|
|
716
|
+
"relative shrink-0 transition-[margin,opacity] duration-500 ease-out",
|
|
717
|
+
astir && !lit(face) && "opacity-40",
|
|
711
718
|
// A word to the left has already broken the shingle.
|
|
712
719
|
index > 0 && !crew[index - 1].word && "-ml-2",
|
|
713
720
|
face.standIn && "opacity-35",
|
|
@@ -208,7 +208,12 @@ function ConfigScreen({ screen }: { screen: keyof typeof SCREENS }) {
|
|
|
208
208
|
* bots think with. The same cards, marks and dialogs as the screen above, so a key set
|
|
209
209
|
* on the way in is the key Settings shows.
|
|
210
210
|
*/
|
|
211
|
-
export function AccountsSetup(
|
|
211
|
+
export function AccountsSetup({
|
|
212
|
+
/** Leave the voice key out: the screen asked for it already. */
|
|
213
|
+
withoutVoice = false,
|
|
214
|
+
}: {
|
|
215
|
+
withoutVoice?: boolean;
|
|
216
|
+
}) {
|
|
212
217
|
const { data } = useServerRoute<ConfigStatus[]>(queryKey.config);
|
|
213
218
|
const isSet = (key: string) => isConfigSet(data, key);
|
|
214
219
|
const entries = (id: ConfigGroup["id"]) =>
|
|
@@ -217,11 +222,18 @@ export function AccountsSetup() {
|
|
|
217
222
|
// already set. The rest are one press away, over the row rather than below it, so the
|
|
218
223
|
// screen around it does not move
|
|
219
224
|
const [more, setMore] = useState(false);
|
|
220
|
-
const
|
|
225
|
+
const voice = entries("voice");
|
|
226
|
+
const marks = [...voice, ...entries("text")];
|
|
227
|
+
// Without the voice key the row is one shorter, not refilled from the rest
|
|
221
228
|
const first = marks.filter(
|
|
222
|
-
(entry, at) =>
|
|
229
|
+
(entry, at) =>
|
|
230
|
+
(at < ACCOUNTS_FIRST || isSet(entry.key)) &&
|
|
231
|
+
!(withoutVoice && voice.includes(entry)),
|
|
232
|
+
);
|
|
233
|
+
const rest = marks.filter(
|
|
234
|
+
(entry) =>
|
|
235
|
+
!first.includes(entry) && !(withoutVoice && voice.includes(entry)),
|
|
223
236
|
);
|
|
224
|
-
const rest = marks.filter((entry) => !first.includes(entry));
|
|
225
237
|
|
|
226
238
|
return (
|
|
227
239
|
<div className="flex flex-col gap-4">
|
|
@@ -395,7 +407,7 @@ function KeyRow({
|
|
|
395
407
|
<span className="flex items-center gap-2 truncate text-sm font-medium">
|
|
396
408
|
{entry.label}
|
|
397
409
|
{entry.recommended && (
|
|
398
|
-
<span className="rounded-full px-1.5 font-mono text-[9.5px] leading-4 font-normal text-
|
|
410
|
+
<span className="rounded-full px-1.5 font-mono text-[9.5px] leading-4 font-normal text-muted-foreground ring-1 ring-border ring-inset">
|
|
399
411
|
recommended
|
|
400
412
|
</span>
|
|
401
413
|
)}
|
|
@@ -33,6 +33,8 @@ export function VoiceKeys({
|
|
|
33
33
|
autoFocus,
|
|
34
34
|
/** Compact layout for the call screen. */
|
|
35
35
|
dense = false,
|
|
36
|
+
/** The field alone, the provider's mark inside it: for a spot whose heading already names the key. */
|
|
37
|
+
plain = false,
|
|
36
38
|
onCancel,
|
|
37
39
|
onSaved,
|
|
38
40
|
className,
|
|
@@ -40,6 +42,7 @@ export function VoiceKeys({
|
|
|
40
42
|
ref?: Ref<VoiceKeysHandle>;
|
|
41
43
|
autoFocus?: boolean;
|
|
42
44
|
dense?: boolean;
|
|
45
|
+
plain?: boolean;
|
|
43
46
|
onCancel?: () => void;
|
|
44
47
|
onSaved?: (provider: "openai") => void;
|
|
45
48
|
className?: string;
|
|
@@ -90,7 +93,7 @@ export function VoiceKeys({
|
|
|
90
93
|
|
|
91
94
|
return (
|
|
92
95
|
<div className={cn("w-full", dense ? "space-y-2" : "space-y-4", className)}>
|
|
93
|
-
{dense && (
|
|
96
|
+
{dense && !plain && (
|
|
94
97
|
<div className="flex h-6 items-center justify-between gap-2">
|
|
95
98
|
<span className="px-0.5 font-mono text-[11px] text-muted-foreground">
|
|
96
99
|
Voice key
|
|
@@ -115,6 +118,7 @@ export function VoiceKeys({
|
|
|
115
118
|
saved={isConfigSet(config, LIVE_PROVIDER.apiKeyName)}
|
|
116
119
|
autoFocus={autoFocus}
|
|
117
120
|
dense={dense}
|
|
121
|
+
plain={plain}
|
|
118
122
|
value={drafts[LIVE_PROVIDER.apiKeyName] ?? ""}
|
|
119
123
|
ready={ready(LIVE_PROVIDER.apiKeyName)}
|
|
120
124
|
saving={saving === LIVE_PROVIDER.apiKeyName}
|
|
@@ -137,16 +141,18 @@ export function VoiceKeys({
|
|
|
137
141
|
</p>
|
|
138
142
|
)}
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
{!plain && (
|
|
145
|
+
<p
|
|
146
|
+
className={cn(
|
|
147
|
+
"px-0.5 font-mono text-muted-foreground",
|
|
148
|
+
dense ? "text-[11px]" : "pt-1 text-center text-[12.5px]",
|
|
149
|
+
)}
|
|
150
|
+
>
|
|
151
|
+
{dense
|
|
152
|
+
? "Live voice and reasoning share this OpenAI API key."
|
|
153
|
+
: "Live uses an OpenAI API key, billed separately from ChatGPT. Stored on this machine, in this app's database."}
|
|
154
|
+
</p>
|
|
155
|
+
)}
|
|
150
156
|
</div>
|
|
151
157
|
);
|
|
152
158
|
}
|
|
@@ -156,6 +162,7 @@ function KeyField({
|
|
|
156
162
|
saved,
|
|
157
163
|
autoFocus,
|
|
158
164
|
dense,
|
|
165
|
+
plain,
|
|
159
166
|
value,
|
|
160
167
|
ready,
|
|
161
168
|
saving,
|
|
@@ -166,6 +173,7 @@ function KeyField({
|
|
|
166
173
|
saved: boolean;
|
|
167
174
|
autoFocus?: boolean;
|
|
168
175
|
dense?: boolean;
|
|
176
|
+
plain?: boolean;
|
|
169
177
|
value: string;
|
|
170
178
|
ready: boolean;
|
|
171
179
|
saving: boolean;
|
|
@@ -174,20 +182,23 @@ function KeyField({
|
|
|
174
182
|
}) {
|
|
175
183
|
return (
|
|
176
184
|
<div className="space-y-2">
|
|
177
|
-
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
{!plain && (
|
|
186
|
+
<span className="flex items-center gap-2 px-0.5 text-muted-foreground">
|
|
187
|
+
<ProviderIcon provider={provider.id} className="size-4 shrink-0" />
|
|
188
|
+
<span
|
|
189
|
+
className={cn("font-mono", dense ? "text-[11px]" : "text-[12.5px]")}
|
|
190
|
+
>
|
|
191
|
+
{provider.label}
|
|
192
|
+
</span>
|
|
183
193
|
</span>
|
|
184
|
-
|
|
194
|
+
)}
|
|
185
195
|
|
|
186
196
|
<KeyInput
|
|
187
197
|
provider={provider}
|
|
188
198
|
saved={saved}
|
|
189
199
|
autoFocus={autoFocus}
|
|
190
200
|
dense={dense}
|
|
201
|
+
mark={plain}
|
|
191
202
|
value={value}
|
|
192
203
|
ready={ready}
|
|
193
204
|
saving={saving}
|
|
@@ -204,6 +215,7 @@ export function KeyInput({
|
|
|
204
215
|
saved,
|
|
205
216
|
autoFocus,
|
|
206
217
|
dense,
|
|
218
|
+
mark,
|
|
207
219
|
value,
|
|
208
220
|
ready,
|
|
209
221
|
saving,
|
|
@@ -214,6 +226,8 @@ export function KeyInput({
|
|
|
214
226
|
saved: boolean;
|
|
215
227
|
autoFocus?: boolean;
|
|
216
228
|
dense?: boolean;
|
|
229
|
+
/** The provider's mark at the start of the field. */
|
|
230
|
+
mark?: boolean;
|
|
217
231
|
value: string;
|
|
218
232
|
ready: boolean;
|
|
219
233
|
saving: boolean;
|
|
@@ -236,6 +250,12 @@ export function KeyInput({
|
|
|
236
250
|
saved || ready ? "border-foreground bg-transparent" : "border-border",
|
|
237
251
|
)}
|
|
238
252
|
>
|
|
253
|
+
{mark && (
|
|
254
|
+
<ProviderIcon
|
|
255
|
+
provider={provider.id}
|
|
256
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
239
259
|
<Input
|
|
240
260
|
autoFocus={autoFocus}
|
|
241
261
|
type="password"
|
|
@@ -32,60 +32,64 @@ export type MCPPreset = MCPServerForm & {
|
|
|
32
32
|
/**
|
|
33
33
|
* First-party servers only, each one reachable at the URL below. Most sign in
|
|
34
34
|
* with OAuth and ask nothing up front; a few want a token pasted into the
|
|
35
|
-
* header (GitHub, whose server skips OAuth discovery
|
|
36
|
-
*
|
|
35
|
+
* header (GitHub, whose server skips OAuth discovery; Home Assistant, whose
|
|
36
|
+
* URL is also the user's own), and flight search and the docs servers need
|
|
37
|
+
* no account at all. Everyday services come first, developer tools after;
|
|
38
|
+
* two stdio entries at the end drive a browser.
|
|
37
39
|
*/
|
|
38
40
|
export const MCP_PRESETS: MCPPreset[] = [
|
|
39
41
|
{
|
|
40
|
-
name: "
|
|
41
|
-
icon: "
|
|
42
|
-
description: "
|
|
43
|
-
config: { url: "https://
|
|
42
|
+
name: "todoist",
|
|
43
|
+
icon: "todoist.com",
|
|
44
|
+
description: "Tasks, projects, and due dates",
|
|
45
|
+
config: { url: "https://ai.todoist.net/mcp" },
|
|
44
46
|
},
|
|
45
47
|
{
|
|
46
|
-
name: "
|
|
47
|
-
icon: "
|
|
48
|
-
description:
|
|
48
|
+
name: "home-assistant",
|
|
49
|
+
icon: "home-assistant.io",
|
|
50
|
+
description:
|
|
51
|
+
"Lights, switches, and the rest of the house — your address and a long-lived token",
|
|
49
52
|
config: {
|
|
50
|
-
url: "
|
|
51
|
-
headers: { Authorization: "Bearer <your-
|
|
53
|
+
url: "http://homeassistant.local:8123/api/mcp",
|
|
54
|
+
headers: { Authorization: "Bearer <your-home-assistant-token>" },
|
|
52
55
|
},
|
|
53
56
|
},
|
|
54
57
|
{
|
|
55
|
-
name: "
|
|
56
|
-
icon: "
|
|
57
|
-
description: "
|
|
58
|
-
config: { url: "https://mcp.
|
|
58
|
+
name: "kiwi",
|
|
59
|
+
icon: "kiwi.com",
|
|
60
|
+
description: "Flight search — no account",
|
|
61
|
+
config: { url: "https://mcp.kiwi.com" },
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
|
-
name: "
|
|
62
|
-
icon: "
|
|
63
|
-
description:
|
|
64
|
-
|
|
64
|
+
name: "zapier",
|
|
65
|
+
icon: "zapier.com",
|
|
66
|
+
description:
|
|
67
|
+
"Gmail, Google Calendar, and thousands of apps you connect there",
|
|
68
|
+
config: { url: "https://mcp.zapier.com/api/v1/connect" },
|
|
65
69
|
},
|
|
66
70
|
{
|
|
67
|
-
name: "
|
|
68
|
-
icon: "
|
|
69
|
-
description: "
|
|
70
|
-
config: { url: "https://mcp.
|
|
71
|
+
name: "notion",
|
|
72
|
+
icon: "notion.so",
|
|
73
|
+
description: "Pages, databases, and search",
|
|
74
|
+
config: { url: "https://mcp.notion.com/mcp" },
|
|
71
75
|
},
|
|
72
76
|
{
|
|
73
|
-
name: "
|
|
74
|
-
icon: "
|
|
75
|
-
description: "
|
|
76
|
-
config: { url: "https://mcp.
|
|
77
|
+
name: "canva",
|
|
78
|
+
icon: "canva.com",
|
|
79
|
+
description: "Create and manage designs",
|
|
80
|
+
config: { url: "https://mcp.canva.com/mcp" },
|
|
77
81
|
},
|
|
78
82
|
{
|
|
79
|
-
name: "
|
|
80
|
-
icon: "
|
|
81
|
-
description: "
|
|
82
|
-
config: { url: "https://mcp.
|
|
83
|
+
name: "slack",
|
|
84
|
+
icon: "slack.com",
|
|
85
|
+
description: "Channels, messages, and search",
|
|
86
|
+
config: { url: "https://mcp.slack.com/mcp" },
|
|
83
87
|
},
|
|
84
88
|
{
|
|
85
|
-
name: "
|
|
86
|
-
icon: "
|
|
87
|
-
description: "
|
|
88
|
-
config: { url: "https://mcp.
|
|
89
|
+
name: "box",
|
|
90
|
+
icon: "box.com",
|
|
91
|
+
description: "Files, folders, and content search",
|
|
92
|
+
config: { url: "https://mcp.box.com/mcp" },
|
|
89
93
|
},
|
|
90
94
|
{
|
|
91
95
|
name: "airtable",
|
|
@@ -94,10 +98,10 @@ export const MCP_PRESETS: MCPPreset[] = [
|
|
|
94
98
|
config: { url: "https://mcp.airtable.com/mcp" },
|
|
95
99
|
},
|
|
96
100
|
{
|
|
97
|
-
name: "
|
|
98
|
-
icon: "
|
|
99
|
-
description: "
|
|
100
|
-
config: { url: "https://mcp.
|
|
101
|
+
name: "fireflies",
|
|
102
|
+
icon: "fireflies.ai",
|
|
103
|
+
description: "Meeting transcripts and summaries",
|
|
104
|
+
config: { url: "https://mcp.fireflies.ai/mcp" },
|
|
101
105
|
},
|
|
102
106
|
{
|
|
103
107
|
name: "figma",
|
|
@@ -106,28 +110,34 @@ export const MCP_PRESETS: MCPPreset[] = [
|
|
|
106
110
|
config: { url: "https://mcp.figma.com/mcp" },
|
|
107
111
|
},
|
|
108
112
|
{
|
|
109
|
-
name: "
|
|
110
|
-
icon: "
|
|
111
|
-
description: "
|
|
112
|
-
config: { url: "https://mcp.
|
|
113
|
+
name: "asana",
|
|
114
|
+
icon: "asana.com",
|
|
115
|
+
description: "Tasks, projects, and portfolios",
|
|
116
|
+
config: { url: "https://mcp.asana.com/mcp" },
|
|
113
117
|
},
|
|
114
118
|
{
|
|
115
|
-
name: "
|
|
116
|
-
icon: "
|
|
117
|
-
description: "
|
|
118
|
-
config: { url: "https://mcp.
|
|
119
|
+
name: "monday",
|
|
120
|
+
icon: "monday.com",
|
|
121
|
+
description: "Boards, items, and updates",
|
|
122
|
+
config: { url: "https://mcp.monday.com/mcp" },
|
|
119
123
|
},
|
|
120
124
|
{
|
|
121
|
-
name: "
|
|
122
|
-
icon: "
|
|
123
|
-
description: "
|
|
124
|
-
config: { url: "https://mcp.
|
|
125
|
+
name: "clickup",
|
|
126
|
+
icon: "clickup.com",
|
|
127
|
+
description: "Tasks, docs, and spaces",
|
|
128
|
+
config: { url: "https://mcp.clickup.com/mcp" },
|
|
125
129
|
},
|
|
126
130
|
{
|
|
127
|
-
name: "
|
|
128
|
-
icon: "
|
|
129
|
-
description: "
|
|
130
|
-
config: { url: "https://mcp.
|
|
131
|
+
name: "linear",
|
|
132
|
+
icon: "linear.app",
|
|
133
|
+
description: "Issues, projects, and cycles",
|
|
134
|
+
config: { url: "https://mcp.linear.app/mcp" },
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "atlassian",
|
|
138
|
+
icon: "atlassian.com",
|
|
139
|
+
description: "Jira and Confluence",
|
|
140
|
+
config: { url: "https://mcp.atlassian.com/v1/mcp" },
|
|
131
141
|
},
|
|
132
142
|
{
|
|
133
143
|
name: "hubspot",
|
|
@@ -148,10 +158,22 @@ export const MCP_PRESETS: MCPPreset[] = [
|
|
|
148
158
|
config: { url: "https://mcp.intercom.com/mcp" },
|
|
149
159
|
},
|
|
150
160
|
{
|
|
151
|
-
name: "
|
|
152
|
-
icon: "
|
|
153
|
-
description: "
|
|
154
|
-
config: { url: "https://mcp.
|
|
161
|
+
name: "wix",
|
|
162
|
+
icon: "wix.com",
|
|
163
|
+
description: "Sites, stores, and bookings",
|
|
164
|
+
config: { url: "https://mcp.wix.com/mcp" },
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "webflow",
|
|
168
|
+
icon: "webflow.com",
|
|
169
|
+
description: "Sites, pages, and CMS collections",
|
|
170
|
+
config: { url: "https://mcp.webflow.com/mcp" },
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "sanity",
|
|
174
|
+
icon: "sanity.io",
|
|
175
|
+
description: "Structured content and datasets",
|
|
176
|
+
config: { url: "https://mcp.sanity.io/mcp" },
|
|
155
177
|
},
|
|
156
178
|
{
|
|
157
179
|
name: "stripe",
|
|
@@ -177,6 +199,15 @@ export const MCP_PRESETS: MCPPreset[] = [
|
|
|
177
199
|
description: "Spend, cards, and transactions",
|
|
178
200
|
config: { url: "https://mcp.ramp.com/mcp" },
|
|
179
201
|
},
|
|
202
|
+
{
|
|
203
|
+
name: "github",
|
|
204
|
+
icon: "github.com",
|
|
205
|
+
description: "Repos, issues, PRs — paste a personal access token",
|
|
206
|
+
config: {
|
|
207
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
208
|
+
headers: { Authorization: "Bearer <your-github-pat>" },
|
|
209
|
+
},
|
|
210
|
+
},
|
|
180
211
|
{
|
|
181
212
|
name: "vercel",
|
|
182
213
|
icon: "vercel.com",
|
|
@@ -15,6 +15,12 @@ import { queryKey } from "@/app/api/query-key";
|
|
|
15
15
|
import { Button } from "@/components/ui/button";
|
|
16
16
|
import { ShinyText } from "@/components/ui/shiny-text";
|
|
17
17
|
import { Switch } from "@/components/ui/switch";
|
|
18
|
+
import {
|
|
19
|
+
Tooltip,
|
|
20
|
+
TooltipContent,
|
|
21
|
+
TooltipProvider,
|
|
22
|
+
TooltipTrigger,
|
|
23
|
+
} from "@/components/ui/tooltip";
|
|
18
24
|
import { ModelPicker } from "@/features/ai/components/model-picker";
|
|
19
25
|
import {
|
|
20
26
|
parseTextModel,
|
|
@@ -188,23 +194,33 @@ export function Intro({
|
|
|
188
194
|
)}
|
|
189
195
|
>
|
|
190
196
|
{/* Her recorded voice, and the way to switch it off */}
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
<TooltipProvider>
|
|
198
|
+
<Tooltip>
|
|
199
|
+
<TooltipTrigger
|
|
200
|
+
render={
|
|
201
|
+
<Button
|
|
202
|
+
size="icon"
|
|
203
|
+
variant="ghost"
|
|
204
|
+
onClick={voice.toggle}
|
|
205
|
+
aria-label={voice.muted ? "Let her speak" : "Mute her"}
|
|
206
|
+
aria-pressed={voice.muted}
|
|
207
|
+
className="absolute top-5 right-5 z-10 text-muted-foreground hover:text-foreground"
|
|
208
|
+
/>
|
|
209
|
+
}
|
|
210
|
+
>
|
|
211
|
+
{voice.muted ? <VolumeX /> : <Volume2 />}
|
|
212
|
+
</TooltipTrigger>
|
|
213
|
+
<TooltipContent side="bottom">
|
|
214
|
+
{voice.muted ? "Let her speak" : "Mute her voice"}
|
|
215
|
+
</TooltipContent>
|
|
216
|
+
</Tooltip>
|
|
217
|
+
</TooltipProvider>
|
|
204
218
|
|
|
205
219
|
{/* The call screen's own column, so nothing moves when the intro lifts */}
|
|
206
220
|
<div className="flex h-full flex-col items-center justify-center gap-5 pt-[7vh]">
|
|
207
|
-
|
|
221
|
+
{/* The call screen's face box and `--face-bleed`: the canvas draws past the box, and
|
|
222
|
+
what stands beside her stands past the canvas */}
|
|
223
|
+
<div className="relative w-[min(20rem,52vw,37.5vh)] [--face-bleed:19.5%]">
|
|
208
224
|
<button
|
|
209
225
|
type="button"
|
|
210
226
|
disabled={!last || !keyed}
|
|
@@ -222,7 +238,7 @@ export function Intro({
|
|
|
222
238
|
failed={false}
|
|
223
239
|
word={word}
|
|
224
240
|
getSpectrum={step === "hello" ? demo.voice : voice.spectrum}
|
|
225
|
-
className="w-
|
|
241
|
+
className="-m-(--face-bleed) w-[calc(100%+2*var(--face-bleed))] max-w-none"
|
|
226
242
|
/>
|
|
227
243
|
</button>
|
|
228
244
|
|
|
@@ -237,7 +253,7 @@ export function Intro({
|
|
|
237
253
|
// Where the caller's words go on a call: the caller's turn
|
|
238
254
|
<div
|
|
239
255
|
key={step}
|
|
240
|
-
className="absolute top-1/2 left-full ml-
|
|
256
|
+
className="absolute top-1/2 left-full ml-[calc(var(--face-bleed)+0.375rem)] flex w-[min(22rem,26vw)] -translate-y-1/2 animate-in flex-col gap-4 text-left fade-in slide-in-from-bottom-1 duration-300"
|
|
241
257
|
>
|
|
242
258
|
{step === "key" && (
|
|
243
259
|
<KeyTurn
|
|
@@ -268,7 +284,7 @@ export function Intro({
|
|
|
268
284
|
under the button keep theirs: her face and the button stand still from step to step */}
|
|
269
285
|
<div className="flex h-14 items-center gap-2 text-[13px] text-muted-foreground">
|
|
270
286
|
{step === "hello" ? (
|
|
271
|
-
<p className="max-w-130 text-[20px] leading-[1.5] text-balance text-foreground">
|
|
287
|
+
<p className="max-w-130 animate-in text-[20px] leading-[1.5] text-balance text-foreground duration-700 fill-mode-backwards fade-in slide-in-from-bottom-2">
|
|
272
288
|
Just talk to her. She gets it done on this computer, and tells
|
|
273
289
|
you when it is ready.
|
|
274
290
|
</p>
|
|
@@ -294,7 +310,12 @@ export function Intro({
|
|
|
294
310
|
} else if (last) leave(keyed);
|
|
295
311
|
else setStep(STEPS[at + 1]);
|
|
296
312
|
}}
|
|
297
|
-
|
|
313
|
+
// on the first screen it follows her line up, once
|
|
314
|
+
className={cn(
|
|
315
|
+
"h-12 px-7 pl-8 text-[15px]",
|
|
316
|
+
step === "hello" &&
|
|
317
|
+
"animate-in delay-300 duration-700 fill-mode-backwards fade-in slide-in-from-bottom-2",
|
|
318
|
+
)}
|
|
298
319
|
>
|
|
299
320
|
{step === "hello"
|
|
300
321
|
? "Start"
|
|
@@ -341,7 +362,7 @@ export function Intro({
|
|
|
341
362
|
key={name}
|
|
342
363
|
className={cn(
|
|
343
364
|
"h-1.75 rounded-full transition-all duration-300",
|
|
344
|
-
index === at ? "w-5.5 bg-
|
|
365
|
+
index === at ? "w-5.5 bg-foreground" : "w-1.75",
|
|
345
366
|
index < at && "bg-foreground/40",
|
|
346
367
|
index > at && "bg-border",
|
|
347
368
|
)}
|
|
@@ -416,10 +437,12 @@ const Fine = ({ children }: { children: React.ReactNode }) => (
|
|
|
416
437
|
function Done({ children, tail }: { children: string; tail?: string }) {
|
|
417
438
|
return (
|
|
418
439
|
<p className="flex items-center gap-2.5 text-sm">
|
|
419
|
-
<span className="grid size-5 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground">
|
|
440
|
+
<span className="grid size-5 shrink-0 animate-in place-items-center rounded-full bg-primary text-primary-foreground duration-300 zoom-in-50">
|
|
420
441
|
<Check className="size-3" />
|
|
421
442
|
</span>
|
|
422
|
-
|
|
443
|
+
<span className="animate-in delay-150 duration-300 fill-mode-backwards fade-in slide-in-from-left-1">
|
|
444
|
+
{children}
|
|
445
|
+
</span>
|
|
423
446
|
{tail && (
|
|
424
447
|
<span className="truncate font-mono text-[11px] text-muted-foreground/70">
|
|
425
448
|
{tail}
|
|
@@ -440,14 +463,14 @@ function KeyTurn({ keyed, onSaved }: { keyed: boolean; onSaved: () => void }) {
|
|
|
440
463
|
return (
|
|
441
464
|
<>
|
|
442
465
|
<Mine>Paste your OpenAI API key</Mine>
|
|
443
|
-
<VoiceKeys dense autoFocus onSaved={onSaved} />
|
|
466
|
+
<VoiceKeys dense plain autoFocus onSaved={onSaved} />
|
|
444
467
|
<a
|
|
445
468
|
href="https://platform.openai.com/api-keys"
|
|
446
469
|
target="_blank"
|
|
447
470
|
rel="noreferrer"
|
|
448
471
|
className="flex items-center gap-2.5 text-[13px] font-medium text-foreground no-underline"
|
|
449
472
|
>
|
|
450
|
-
<span className="flex h-7.5 items-center gap-1.5 rounded-full bg-muted px-3.5 transition-colors hover:bg-accent">
|
|
473
|
+
<span className="flex h-7.5 shrink-0 items-center gap-1.5 rounded-full bg-muted px-3.5 whitespace-nowrap transition-colors hover:bg-accent">
|
|
451
474
|
Get a key
|
|
452
475
|
<ArrowUpRight className="size-3.5" />
|
|
453
476
|
</span>
|
|
@@ -591,7 +614,7 @@ function MicTurn({ mic }: { mic: MicState }) {
|
|
|
591
614
|
</span>
|
|
592
615
|
) : (
|
|
593
616
|
<span className="mt-1.5 flex items-center gap-2 text-[13px]">
|
|
594
|
-
<Mic className="size-4 shrink-0
|
|
617
|
+
<Mic className="size-4 shrink-0" />
|
|
595
618
|
<ShinyText
|
|
596
619
|
text={`Try it now: say "${wake.phrase}"`}
|
|
597
620
|
speed={2.2}
|
|
@@ -659,8 +682,8 @@ function BotsTurn({
|
|
|
659
682
|
})}
|
|
660
683
|
</div>
|
|
661
684
|
<Fine>
|
|
662
|
-
|
|
663
|
-
make your own.
|
|
685
|
+
Every one comes along. Switch off any you will not use: Settings › Bots
|
|
686
|
+
has them all, and you can make your own.
|
|
664
687
|
</Fine>
|
|
665
688
|
</>
|
|
666
689
|
);
|
|
@@ -686,7 +709,7 @@ function ModelsTurn() {
|
|
|
686
709
|
return (
|
|
687
710
|
<>
|
|
688
711
|
<Mine>Pick what they think with</Mine>
|
|
689
|
-
<AccountsSetup />
|
|
712
|
+
<AccountsSetup withoutVoice />
|
|
690
713
|
<div className="flex items-center gap-2.5">
|
|
691
714
|
<span className="shrink-0 text-[13px] text-muted-foreground">
|
|
692
715
|
Bots think with
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Repeat } from "lucide-react";
|
|
1
|
+
import { type LucideIcon, Repeat } from "lucide-react";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The glyph of something that starts by itself: the settings nav, a routine's row,
|
|
5
|
-
*
|
|
4
|
+
* The glyph of something that starts by itself: the settings nav, a routine's row, every
|
|
5
|
+
* job a routine opened, in Threads and in the room, and the call's line that sets one.
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
8
|
-
return <Repeat className={className} aria-hidden="true" />;
|
|
9
|
-
}
|
|
7
|
+
export const RoutineMark: LucideIcon = Repeat;
|