@elabs-ai/components-ai 4.1.0 → 5.0.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/README.md +3 -3
- package/dist/{_audio-player-media-chrome-T3XVXWRZ.js → _audio-player-media-chrome.js} +1 -1
- package/dist/_code-block-theme.js +12 -0
- package/dist/_code-block-theme.js.map +1 -0
- package/dist/{_flow-boundary-SHNWLQG5.js → _flow-boundary.js} +1 -1
- package/dist/_flow-boundary.js.map +1 -0
- package/dist/_flow-lazy.js +8 -0
- package/dist/_flow-lazy.js.map +1 -0
- package/dist/_lazy-boundary-conformance.js +2 -0
- package/dist/_lazy-boundary-conformance.js.map +1 -0
- package/dist/_lazy-cjk.js +13 -0
- package/dist/_lazy-cjk.js.map +1 -0
- package/dist/_lazy-engine-boundary.js +9 -0
- package/dist/_lazy-engine-boundary.js.map +1 -0
- package/dist/_lazy-math.js +13 -0
- package/dist/_lazy-math.js.map +1 -0
- package/dist/_lazy-mermaid.js +12 -0
- package/dist/_lazy-mermaid.js.map +1 -0
- package/dist/_mermaid-error-panel.js +9 -0
- package/dist/_mermaid-error-panel.js.map +1 -0
- package/dist/{_persona-rive-JEG44YHX.js → _persona-rive.js} +19 -24
- package/dist/_persona-rive.js.map +1 -0
- package/dist/_streamdown-i18n.js +21 -0
- package/dist/_streamdown-i18n.js.map +1 -0
- package/dist/_streamdown-safety.js +10 -0
- package/dist/_streamdown-safety.js.map +1 -0
- package/dist/_theme-scope-store.js +13 -0
- package/dist/_theme-scope-store.js.map +1 -0
- package/dist/a2ui/a2ui-surface.js +17 -0
- package/dist/a2ui/a2ui-surface.js.map +1 -0
- package/dist/a2ui/core/catalog.generated.js +10 -0
- package/dist/a2ui/core/catalog.generated.js.map +1 -0
- package/dist/a2ui/core/complete-json.js +10 -0
- package/dist/a2ui/core/complete-json.js.map +1 -0
- package/dist/a2ui/core/index.js +37 -0
- package/dist/a2ui/core/index.js.map +1 -0
- package/dist/a2ui/core/schema.js +11 -0
- package/dist/a2ui/core/schema.js.map +1 -0
- package/dist/a2ui/core/spec.js +10 -0
- package/dist/a2ui/core/spec.js.map +1 -0
- package/dist/a2ui/core/validate.js +16 -0
- package/dist/a2ui/core/validate.js.map +1 -0
- package/dist/a2ui/index.js +54 -0
- package/dist/a2ui/index.js.map +1 -0
- package/dist/a2ui/ui-catalog.js +18 -0
- package/dist/a2ui/ui-catalog.js.map +1 -0
- package/dist/agent-event.js +10 -0
- package/dist/agent-event.js.map +1 -0
- package/dist/agent-timeline.js +11 -0
- package/dist/agent-timeline.js.map +1 -0
- package/dist/agent.js +25 -0
- package/dist/agent.js.map +1 -0
- package/dist/artifact.js +23 -0
- package/dist/artifact.js.map +1 -0
- package/dist/asset-preview.js +22 -0
- package/dist/asset-preview.js.map +1 -0
- package/dist/attachments.js +33 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audio-player.js +30 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/audio-visualizer.js +9 -0
- package/dist/audio-visualizer.js.map +1 -0
- package/dist/canvas.js +10 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chain-of-thought.js +22 -0
- package/dist/chain-of-thought.js.map +1 -0
- package/dist/chat-greeting.js +8 -0
- package/dist/chat-greeting.js.map +1 -0
- package/dist/chat-shell.js +9 -0
- package/dist/chat-shell.js.map +1 -0
- package/dist/checkpoint.js +13 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/chunk-2XOIG5TG.js +72 -0
- package/dist/chunk-2XOIG5TG.js.map +1 -0
- package/dist/chunk-34CQ2VHM.js +172 -0
- package/dist/chunk-34CQ2VHM.js.map +1 -0
- package/dist/chunk-3TDHPXZZ.js +60 -0
- package/dist/chunk-3TDHPXZZ.js.map +1 -0
- package/dist/chunk-3XNPMDZY.js +478 -0
- package/dist/chunk-3XNPMDZY.js.map +1 -0
- package/dist/chunk-3YPT3V7L.js +56 -0
- package/dist/chunk-3YPT3V7L.js.map +1 -0
- package/dist/chunk-475UEU2Q.js +87 -0
- package/dist/chunk-475UEU2Q.js.map +1 -0
- package/dist/chunk-4CBTII2I.js +95 -0
- package/dist/chunk-4CBTII2I.js.map +1 -0
- package/dist/chunk-4XRNIZMG.js +22 -0
- package/dist/chunk-4XRNIZMG.js.map +1 -0
- package/dist/chunk-5NNWUKK5.js +2 -0
- package/dist/chunk-5NNWUKK5.js.map +1 -0
- package/dist/chunk-5UOMU27Z.js +164 -0
- package/dist/chunk-5UOMU27Z.js.map +1 -0
- package/dist/chunk-5VGYZ62I.js +264 -0
- package/dist/chunk-5VGYZ62I.js.map +1 -0
- package/dist/chunk-6IQPH2OS.js +21 -0
- package/dist/chunk-6IQPH2OS.js.map +1 -0
- package/dist/chunk-6PB2MXCI.js +482 -0
- package/dist/chunk-6PB2MXCI.js.map +1 -0
- package/dist/chunk-7FD5CZEU.js +215 -0
- package/dist/chunk-7FD5CZEU.js.map +1 -0
- package/dist/chunk-7RL6KZDF.js +266 -0
- package/dist/chunk-7RL6KZDF.js.map +1 -0
- package/dist/chunk-ADOYAGGK.js +482 -0
- package/dist/chunk-ADOYAGGK.js.map +1 -0
- package/dist/chunk-AXMNJMMH.js +984 -0
- package/dist/chunk-AXMNJMMH.js.map +1 -0
- package/dist/chunk-BA3AEMOU.js +21 -0
- package/dist/chunk-BA3AEMOU.js.map +1 -0
- package/dist/chunk-BBHFOYML.js +336 -0
- package/dist/chunk-BBHFOYML.js.map +1 -0
- package/dist/chunk-BHUVISIY.js +221 -0
- package/dist/chunk-BHUVISIY.js.map +1 -0
- package/dist/chunk-BM2LWERA.js +100 -0
- package/dist/chunk-BM2LWERA.js.map +1 -0
- package/dist/chunk-CMOYXNPJ.js +211 -0
- package/dist/chunk-CMOYXNPJ.js.map +1 -0
- package/dist/chunk-CZP3RHC4.js +74 -0
- package/dist/chunk-CZP3RHC4.js.map +1 -0
- package/dist/chunk-DDG5JHQT.js +112 -0
- package/dist/chunk-DDG5JHQT.js.map +1 -0
- package/dist/chunk-DJODXO7I.js +38 -0
- package/dist/chunk-DJODXO7I.js.map +1 -0
- package/dist/chunk-DWJDJLPU.js +192 -0
- package/dist/chunk-DWJDJLPU.js.map +1 -0
- package/dist/chunk-DZW6LM22.js +45 -0
- package/dist/chunk-DZW6LM22.js.map +1 -0
- package/dist/chunk-ELGCZLZF.js +305 -0
- package/dist/chunk-ELGCZLZF.js.map +1 -0
- package/dist/chunk-EV3U6ZZB.js +209 -0
- package/dist/chunk-EV3U6ZZB.js.map +1 -0
- package/dist/chunk-EY7LVUKQ.js +87 -0
- package/dist/chunk-EY7LVUKQ.js.map +1 -0
- package/dist/chunk-F4C74F7M.js +230 -0
- package/dist/chunk-F4C74F7M.js.map +1 -0
- package/dist/chunk-FERYDFZA.js +108 -0
- package/dist/chunk-FERYDFZA.js.map +1 -0
- package/dist/chunk-FUIYG2DS.js +166 -0
- package/dist/chunk-FUIYG2DS.js.map +1 -0
- package/dist/chunk-GKYZT3LA.js +136 -0
- package/dist/chunk-GKYZT3LA.js.map +1 -0
- package/dist/chunk-H42LRUQ4.js +102 -0
- package/dist/chunk-H42LRUQ4.js.map +1 -0
- package/dist/chunk-HVXM2G5F.js +222 -0
- package/dist/chunk-HVXM2G5F.js.map +1 -0
- package/dist/chunk-I4RDAGVY.js +53 -0
- package/dist/chunk-I4RDAGVY.js.map +1 -0
- package/dist/chunk-IQJG4IDX.js +208 -0
- package/dist/chunk-IQJG4IDX.js.map +1 -0
- package/dist/chunk-IX2PXG4Y.js +144 -0
- package/dist/chunk-IX2PXG4Y.js.map +1 -0
- package/dist/chunk-IXICSOOP.js +287 -0
- package/dist/chunk-IXICSOOP.js.map +1 -0
- package/dist/chunk-JY5HSN4D.js +84 -0
- package/dist/chunk-JY5HSN4D.js.map +1 -0
- package/dist/chunk-LMNSBOBG.js +19 -0
- package/dist/chunk-LMNSBOBG.js.map +1 -0
- package/dist/chunk-MBI7D6IL.js +42 -0
- package/dist/chunk-MBI7D6IL.js.map +1 -0
- package/dist/chunk-MLFMYCPH.js +192 -0
- package/dist/chunk-MLFMYCPH.js.map +1 -0
- package/dist/chunk-MOK66734.js +33 -0
- package/dist/chunk-MOK66734.js.map +1 -0
- package/dist/chunk-MXTAWPYM.js +196 -0
- package/dist/chunk-MXTAWPYM.js.map +1 -0
- package/dist/chunk-NCL2UZKI.js +96 -0
- package/dist/chunk-NCL2UZKI.js.map +1 -0
- package/dist/chunk-NET4YLRY.js +107 -0
- package/dist/chunk-NET4YLRY.js.map +1 -0
- package/dist/chunk-NIG67CFM.js +329 -0
- package/dist/chunk-NIG67CFM.js.map +1 -0
- package/dist/chunk-NMVNNO3L.js +10 -0
- package/dist/chunk-NMVNNO3L.js.map +1 -0
- package/dist/chunk-NP7VID4K.js +2 -0
- package/dist/chunk-NP7VID4K.js.map +1 -0
- package/dist/chunk-NPUCMAG2.js +382 -0
- package/dist/chunk-NPUCMAG2.js.map +1 -0
- package/dist/chunk-NSCLRHES.js +102 -0
- package/dist/chunk-NSCLRHES.js.map +1 -0
- package/dist/chunk-OEUXHVFW.js +86 -0
- package/dist/chunk-OEUXHVFW.js.map +1 -0
- package/dist/chunk-ONBHFIMZ.js +137 -0
- package/dist/chunk-ONBHFIMZ.js.map +1 -0
- package/dist/chunk-OPH2HHL2.js +228 -0
- package/dist/chunk-OPH2HHL2.js.map +1 -0
- package/dist/chunk-OQERHIQP.js +287 -0
- package/dist/chunk-OQERHIQP.js.map +1 -0
- package/dist/chunk-P6YJAZFG.js +103 -0
- package/dist/chunk-P6YJAZFG.js.map +1 -0
- package/dist/chunk-PG7ERDOD.js +65 -0
- package/dist/chunk-PG7ERDOD.js.map +1 -0
- package/dist/chunk-PIB5JBMA.js +90 -0
- package/dist/chunk-PIB5JBMA.js.map +1 -0
- package/dist/chunk-PQ5BPIWQ.js +22 -0
- package/dist/chunk-PQ5BPIWQ.js.map +1 -0
- package/dist/chunk-PQBF6ZIQ.js +55 -0
- package/dist/chunk-PQBF6ZIQ.js.map +1 -0
- package/dist/chunk-PTWCTSZL.js +173 -0
- package/dist/chunk-PTWCTSZL.js.map +1 -0
- package/dist/chunk-QJUNPVOS.js +101 -0
- package/dist/chunk-QJUNPVOS.js.map +1 -0
- package/dist/chunk-QUDZOTRV.js +43 -0
- package/dist/chunk-QUDZOTRV.js.map +1 -0
- package/dist/chunk-RB7G65JC.js +213 -0
- package/dist/chunk-RB7G65JC.js.map +1 -0
- package/dist/chunk-RPHAOR7P.js +45 -0
- package/dist/chunk-RPHAOR7P.js.map +1 -0
- package/dist/chunk-RTCSH2AN.js +331 -0
- package/dist/chunk-RTCSH2AN.js.map +1 -0
- package/dist/chunk-S3ISPW3A.js +28 -0
- package/dist/chunk-S3ISPW3A.js.map +1 -0
- package/dist/chunk-SEVN656N.js +36 -0
- package/dist/chunk-SEVN656N.js.map +1 -0
- package/dist/chunk-SKVG672B.js +64 -0
- package/dist/chunk-SKVG672B.js.map +1 -0
- package/dist/chunk-SPQIU7GA.js +236 -0
- package/dist/chunk-SPQIU7GA.js.map +1 -0
- package/dist/chunk-SSGJ6MEM.js +1076 -0
- package/dist/chunk-SSGJ6MEM.js.map +1 -0
- package/dist/chunk-SSKZ2AED.js +118 -0
- package/dist/chunk-SSKZ2AED.js.map +1 -0
- package/dist/chunk-T56JFGXI.js +561 -0
- package/dist/chunk-T56JFGXI.js.map +1 -0
- package/dist/chunk-TCB6O74A.js +137 -0
- package/dist/chunk-TCB6O74A.js.map +1 -0
- package/dist/chunk-TTEYPO6X.js +57 -0
- package/dist/chunk-TTEYPO6X.js.map +1 -0
- package/dist/chunk-TWPHAJNM.js +462 -0
- package/dist/chunk-TWPHAJNM.js.map +1 -0
- package/dist/chunk-TXJJLP4O.js +336 -0
- package/dist/chunk-TXJJLP4O.js.map +1 -0
- package/dist/chunk-TZ6TB7VM.js +245 -0
- package/dist/chunk-TZ6TB7VM.js.map +1 -0
- package/dist/chunk-UBLKJJ7K.js +278 -0
- package/dist/chunk-UBLKJJ7K.js.map +1 -0
- package/dist/chunk-UCJ6IUFS.js +52 -0
- package/dist/chunk-UCJ6IUFS.js.map +1 -0
- package/dist/chunk-UCKFJ3QF.js +197 -0
- package/dist/chunk-UCKFJ3QF.js.map +1 -0
- package/dist/chunk-UPXSLILN.js +20 -0
- package/dist/chunk-UPXSLILN.js.map +1 -0
- package/dist/chunk-V4DORGWJ.js +92 -0
- package/dist/chunk-V4DORGWJ.js.map +1 -0
- package/dist/chunk-V6E5DYZD.js +80 -0
- package/dist/chunk-V6E5DYZD.js.map +1 -0
- package/dist/chunk-VLLYKF3P.js +96 -0
- package/dist/chunk-VLLYKF3P.js.map +1 -0
- package/dist/chunk-VLNBTOH6.js +43 -0
- package/dist/chunk-VLNBTOH6.js.map +1 -0
- package/dist/chunk-WMDSMSPL.js +303 -0
- package/dist/chunk-WMDSMSPL.js.map +1 -0
- package/dist/chunk-WU5BLGTF.js +149 -0
- package/dist/chunk-WU5BLGTF.js.map +1 -0
- package/dist/chunk-WYTZVSGK.js +245 -0
- package/dist/chunk-WYTZVSGK.js.map +1 -0
- package/dist/chunk-X47QMM6K.js +187 -0
- package/dist/chunk-X47QMM6K.js.map +1 -0
- package/dist/chunk-XNBKO32Y.js +174 -0
- package/dist/chunk-XNBKO32Y.js.map +1 -0
- package/dist/chunk-YHUSW7D2.js +40 -0
- package/dist/chunk-YHUSW7D2.js.map +1 -0
- package/dist/chunk-YSKD7UXX.js +126 -0
- package/dist/chunk-YSKD7UXX.js.map +1 -0
- package/dist/chunk-YYRDGWOU.js +72 -0
- package/dist/chunk-YYRDGWOU.js.map +1 -0
- package/dist/chunk-ZIGZMWM7.js +19 -0
- package/dist/chunk-ZIGZMWM7.js.map +1 -0
- package/dist/chunk-ZKRTUKNP.js +40 -0
- package/dist/chunk-ZKRTUKNP.js.map +1 -0
- package/dist/code-block.js +38 -0
- package/dist/code-block.js.map +1 -0
- package/dist/commit.js +51 -0
- package/dist/commit.js.map +1 -0
- package/dist/composer.js +15 -0
- package/dist/composer.js.map +1 -0
- package/dist/confirmation.js +53 -0
- package/dist/confirmation.js.map +1 -0
- package/dist/connection.js +8 -0
- package/dist/connection.js.map +1 -0
- package/dist/context-panel.js +25 -0
- package/dist/context-panel.js.map +1 -0
- package/dist/controls.js +10 -0
- package/dist/controls.js.map +1 -0
- package/dist/conversation.js +19 -0
- package/dist/conversation.js.map +1 -0
- package/dist/diff-view.js +15 -0
- package/dist/diff-view.js.map +1 -0
- package/dist/edge.js +10 -0
- package/dist/edge.js.map +1 -0
- package/dist/environment-variables.js +29 -0
- package/dist/environment-variables.js.map +1 -0
- package/dist/file-tree.js +23 -0
- package/dist/file-tree.js.map +1 -0
- package/dist/gallery.js +9 -0
- package/dist/gallery.js.map +1 -0
- package/dist/grouped-parts.js +21 -0
- package/dist/grouped-parts.js.map +1 -0
- package/dist/image.js +9 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +403 -108
- package/dist/index.js +671 -13205
- package/dist/index.js.map +1 -1
- package/dist/inline-citation.js +39 -0
- package/dist/inline-citation.js.map +1 -0
- package/dist/jsx-preview.js +17 -0
- package/dist/jsx-preview.js.map +1 -0
- package/dist/markdown-view.js +17 -0
- package/dist/markdown-view.js.map +1 -0
- package/dist/message-compare.js +12 -0
- package/dist/message-compare.js.map +1 -0
- package/dist/message-edit.js +19 -0
- package/dist/message-edit.js.map +1 -0
- package/dist/message-feedback.js +11 -0
- package/dist/message-feedback.js.map +1 -0
- package/dist/message-form-spec.js +38 -0
- package/dist/message-form-spec.js.map +1 -0
- package/dist/message-form.js +26 -0
- package/dist/message-form.js.map +1 -0
- package/dist/message-table-spec.js +26 -0
- package/dist/message-table-spec.js.map +1 -0
- package/dist/message-table.js +12 -0
- package/dist/message-table.js.map +1 -0
- package/dist/message.js +51 -0
- package/dist/message.js.map +1 -0
- package/dist/mic-selector.js +27 -0
- package/dist/mic-selector.js.map +1 -0
- package/dist/model-provider-logo.js +13 -0
- package/dist/model-provider-logo.js.map +1 -0
- package/dist/motion-config.js +9 -0
- package/dist/motion-config.js.map +1 -0
- package/dist/node.js +22 -0
- package/dist/node.js.map +1 -0
- package/dist/open-in-chat.js +31 -0
- package/dist/open-in-chat.js.map +1 -0
- package/dist/package-info.js +25 -0
- package/dist/package-info.js.map +1 -0
- package/dist/panel.js +10 -0
- package/dist/panel.js.map +1 -0
- package/dist/part-groups.js +18 -0
- package/dist/part-groups.js.map +1 -0
- package/dist/permission-mode-select.js +9 -0
- package/dist/permission-mode-select.js.map +1 -0
- package/dist/persona-sources.js +8 -0
- package/dist/persona-sources.js.map +1 -0
- package/dist/persona.js +14 -0
- package/dist/persona.js.map +1 -0
- package/dist/plan.js +32 -0
- package/dist/plan.js.map +1 -0
- package/dist/prompt-input-effort.js +9 -0
- package/dist/prompt-input-effort.js.map +1 -0
- package/dist/prompt-input-mode.js +10 -0
- package/dist/prompt-input-mode.js.map +1 -0
- package/dist/prompt-input-slash.js +12 -0
- package/dist/prompt-input-slash.js.map +1 -0
- package/dist/prompt-input.js +89 -0
- package/dist/prompt-input.js.map +1 -0
- package/dist/queue.js +37 -0
- package/dist/queue.js.map +1 -0
- package/dist/reasoning.js +23 -0
- package/dist/reasoning.js.map +1 -0
- package/dist/sandbox.js +28 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema-display.js +33 -0
- package/dist/schema-display.js.map +1 -0
- package/dist/selection-toolbar.js +11 -0
- package/dist/selection-toolbar.js.map +1 -0
- package/dist/session-header.js +9 -0
- package/dist/session-header.js.map +1 -0
- package/dist/session-status-bar.js +9 -0
- package/dist/session-status-bar.js.map +1 -0
- package/dist/shimmer.js +9 -0
- package/dist/shimmer.js.map +1 -0
- package/dist/snippet.js +17 -0
- package/dist/snippet.js.map +1 -0
- package/dist/sources.js +17 -0
- package/dist/sources.js.map +1 -0
- package/dist/speech-input.js +9 -0
- package/dist/speech-input.js.map +1 -0
- package/dist/stack-trace.js +27 -0
- package/dist/stack-trace.js.map +1 -0
- package/dist/suggestion.js +16 -0
- package/dist/suggestion.js.map +1 -0
- package/dist/task.js +18 -0
- package/dist/task.js.map +1 -0
- package/dist/test-results.js +43 -0
- package/dist/test-results.js.map +1 -0
- package/dist/token-usage.js +27 -0
- package/dist/token-usage.js.map +1 -0
- package/dist/tool-result-card.js +9 -0
- package/dist/tool-result-card.js.map +1 -0
- package/dist/tool.js +27 -0
- package/dist/tool.js.map +1 -0
- package/dist/toolbar.js +10 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/transcription.js +11 -0
- package/dist/transcription.js.map +1 -0
- package/dist/turn-status.js +9 -0
- package/dist/turn-status.js.map +1 -0
- package/dist/use-audio-level.js +9 -0
- package/dist/use-audio-level.js.map +1 -0
- package/dist/voice-selector.js +47 -0
- package/dist/voice-selector.js.map +1 -0
- package/dist/web-preview.js +19 -0
- package/dist/web-preview.js.map +1 -0
- package/package.json +12 -10
- package/schemas/a2ui-surface.v1.schema.json +5425 -0
- package/src/__contract__/audio-visualizer.contract.test.tsx +49 -0
- package/src/__contract__/chat-shell.contract.test.tsx +49 -0
- package/src/__contract__/grouped-parts.contract.test.tsx +49 -0
- package/src/__contract__/image.contract.test.tsx +49 -0
- package/src/__contract__/markdown-view.contract.test.tsx +49 -0
- package/src/__contract__/message-feedback.contract.test.tsx +49 -0
- package/src/__contract__/message-form.contract.test.tsx +49 -0
- package/src/__contract__/message-table.contract.test.tsx +49 -0
- package/src/__contract__/model-provider-logo.contract.test.tsx +49 -0
- package/src/__contract__/persona.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-effort.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-mode.contract.test.tsx +49 -0
- package/src/_chat-shell-rail.tsx +4 -2
- package/src/_flow-boundary.tsx +2 -2
- package/src/_lazy-cjk.test.ts +43 -0
- package/src/_lazy-cjk.ts +73 -0
- package/src/_lazy-math.test.ts +63 -0
- package/src/_lazy-math.ts +90 -0
- package/src/_persona-rive.tsx +27 -41
- package/src/_streamdown-i18n.ts +73 -21
- package/src/_streamdown-safety.ts +2 -2
- package/src/_theme-scope-store.test.ts +83 -0
- package/src/_theme-scope-store.ts +103 -0
- package/src/a2ui/a2ui-surface.stories.tsx +662 -0
- package/src/a2ui/a2ui-surface.test.tsx +201 -0
- package/src/a2ui/a2ui-surface.tsx +354 -0
- package/src/a2ui/catalog.source.json +772 -0
- package/src/a2ui/core/a2ui-core.test.ts +178 -0
- package/src/a2ui/core/catalog.generated.ts +1006 -0
- package/src/a2ui/core/complete-json.ts +81 -0
- package/src/a2ui/core/index.ts +10 -0
- package/src/a2ui/core/schema.ts +111 -0
- package/src/a2ui/core/spec.ts +148 -0
- package/src/a2ui/core/validate.ts +266 -0
- package/src/a2ui/index.ts +7 -0
- package/src/a2ui/ui-catalog.tsx +225 -0
- package/src/a2ui/wave1-chart-spec.test.ts +87 -0
- package/src/a2ui/wave2-chart-spec.test.ts +108 -0
- package/src/agent-event.tsx +1 -1
- package/src/agent.tsx +18 -13
- package/src/agentic-workspace.stories.tsx +6 -45
- package/src/artifact.tsx +2 -2
- package/src/asset-preview.test.tsx +40 -0
- package/src/asset-preview.tsx +83 -12
- package/src/attachments.tsx +7 -4
- package/src/chat-shell.test.tsx +40 -10
- package/src/chat-shell.tsx +90 -44
- package/src/chat.stories.tsx +47 -42
- package/src/code-block.test.tsx +100 -1
- package/src/code-block.tsx +166 -103
- package/src/commit.tsx +30 -41
- package/src/composer.stories.tsx +4 -5
- package/src/composer.test.tsx +45 -0
- package/src/composer.tsx +104 -21
- package/src/confirmation.tsx +1 -1
- package/src/context-panel.stories.tsx +1 -1
- package/src/context-panel.tsx +2 -2
- package/src/conversation.stories.tsx +24 -0
- package/src/conversation.test.tsx +69 -0
- package/src/conversation.tsx +47 -8
- package/src/diff-view.stories.tsx +79 -0
- package/src/diff-view.test.tsx +52 -2
- package/src/diff-view.tsx +92 -36
- package/src/environment-variables.tsx +20 -37
- package/src/file-tree.test.tsx +21 -0
- package/src/file-tree.tsx +33 -16
- package/src/gallery.stories.tsx +42 -0
- package/src/gallery.tsx +2 -2
- package/src/grouped-parts.stories.tsx +23 -0
- package/src/grouped-parts.tsx +1 -1
- package/src/index.ts +1 -0
- package/src/inline-citation.tsx +5 -5
- package/src/jsx-preview.tsx +151 -43
- package/src/markdown-view.test.tsx +7 -3
- package/src/markdown-view.tsx +8 -1
- package/src/message-compare.stories.tsx +40 -0
- package/src/message-compare.tsx +2 -1
- package/src/message-form.stories.tsx +36 -3
- package/src/message-form.test.tsx +8 -2
- package/src/message-form.tsx +16 -8
- package/src/message-table.stories.tsx +26 -2
- package/src/message-table.test.tsx +7 -0
- package/src/message-table.tsx +9 -5
- package/src/message.test.tsx +73 -3
- package/src/message.tsx +36 -12
- package/src/model-provider-logo.test.tsx +57 -3
- package/src/model-provider-logo.tsx +45 -11
- package/src/open-in-chat.tsx +50 -29
- package/src/package-info.tsx +23 -20
- package/src/prompt-input-slash.stories.tsx +1 -1
- package/src/prompt-input.test.tsx +67 -1
- package/src/prompt-input.tsx +42 -4
- package/src/queue.tsx +4 -4
- package/src/reasoning.stories.tsx +27 -0
- package/src/reasoning.tsx +17 -6
- package/src/sandbox.tsx +3 -3
- package/src/schema-display.test.tsx +56 -0
- package/src/schema-display.tsx +68 -37
- package/src/session-header.stories.tsx +35 -1
- package/src/session-header.tsx +1 -1
- package/src/snippet.test.tsx +6 -2
- package/src/snippet.tsx +14 -34
- package/src/sources.stories.tsx +30 -0
- package/src/sources.tsx +4 -1
- package/src/speech-input.test.tsx +109 -0
- package/src/speech-input.tsx +31 -3
- package/src/stack-trace.tsx +20 -36
- package/src/streamdown-i18n.test.tsx +23 -0
- package/src/templates-ai-assistant.stories.tsx +151 -87
- package/src/test-results.tsx +47 -27
- package/src/token-usage.tsx +6 -6
- package/src/tool.stories.tsx +39 -0
- package/src/tool.test.tsx +65 -0
- package/src/tool.tsx +61 -25
- package/src/toolbar.tsx +0 -10
- package/src/transcription.tsx +1 -1
- package/src/voice-selector.tsx +5 -5
- package/src/web-preview.test.tsx +51 -1
- package/src/web-preview.tsx +39 -8
- package/dist/_flow-boundary-SHNWLQG5.js.map +0 -1
- package/dist/_persona-rive-JEG44YHX.js.map +0 -1
- /package/dist/{_audio-player-media-chrome-T3XVXWRZ.js.map → _audio-player-media-chrome.js.map} +0 -0
package/src/message-table.tsx
CHANGED
|
@@ -116,6 +116,8 @@ export interface MessageTableProps extends Omit<HTMLAttributes<HTMLDivElement>,
|
|
|
116
116
|
/** Per-cell render override for custom composition. */
|
|
117
117
|
renderCell?: RenderCell;
|
|
118
118
|
/** The spec is still streaming (columns-but-no-rows shows skeleton rows). */
|
|
119
|
+
isStreaming?: boolean;
|
|
120
|
+
/** @deprecated Use `isStreaming`. */
|
|
119
121
|
streaming?: boolean;
|
|
120
122
|
}
|
|
121
123
|
|
|
@@ -145,12 +147,14 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
145
147
|
onSortChange,
|
|
146
148
|
maxRows,
|
|
147
149
|
renderCell,
|
|
150
|
+
isStreaming,
|
|
148
151
|
streaming = false,
|
|
149
152
|
className,
|
|
150
153
|
...props
|
|
151
154
|
},
|
|
152
155
|
ref,
|
|
153
156
|
) {
|
|
157
|
+
const resolvedStreaming = isStreaming ?? streaming;
|
|
154
158
|
const { t } = useLocale();
|
|
155
159
|
const reactId = useId();
|
|
156
160
|
const isControlled = sortProp !== undefined;
|
|
@@ -181,7 +185,7 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
181
185
|
|
|
182
186
|
// No columns: skeleton while streaming, otherwise a fallback.
|
|
183
187
|
if (columns.length === 0) {
|
|
184
|
-
if (
|
|
188
|
+
if (resolvedStreaming) {
|
|
185
189
|
return (
|
|
186
190
|
<div ref={ref} className={cn("w-full", className)} {...props}>
|
|
187
191
|
<span className="sr-only" role="status" aria-live="polite">
|
|
@@ -256,7 +260,7 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
256
260
|
</p>
|
|
257
261
|
)}
|
|
258
262
|
<div className="overflow-hidden rounded-md border border-border">
|
|
259
|
-
{
|
|
263
|
+
{resolvedStreaming && cappedRows.length === 0 && (
|
|
260
264
|
<span className="sr-only" role="status" aria-live="polite">
|
|
261
265
|
Loading table…
|
|
262
266
|
</span>
|
|
@@ -264,7 +268,7 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
264
268
|
<Table
|
|
265
269
|
aria-labelledby={titleId}
|
|
266
270
|
aria-label={title ? undefined : t("ai.messageTable.label")}
|
|
267
|
-
aria-busy={(
|
|
271
|
+
aria-busy={(resolvedStreaming && cappedRows.length === 0) || undefined}
|
|
268
272
|
>
|
|
269
273
|
{truncatedCount > 0 && (
|
|
270
274
|
<TableCaption className="mb-2 mt-3 px-3 text-caption">
|
|
@@ -297,7 +301,7 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
297
301
|
onClick={() => toggleSort(column)}
|
|
298
302
|
className={cn(
|
|
299
303
|
"inline-flex items-center gap-1 rounded-sm font-medium",
|
|
300
|
-
"focus-
|
|
304
|
+
"focus-ring",
|
|
301
305
|
"hover:text-foreground transition-colors duration-fast ease-standard motion-reduce:transition-none",
|
|
302
306
|
numeric && "flex-row-reverse",
|
|
303
307
|
)}
|
|
@@ -322,7 +326,7 @@ export const MessageTable = forwardRef<HTMLDivElement, MessageTableProps>(functi
|
|
|
322
326
|
</TableRow>
|
|
323
327
|
</TableHeader>
|
|
324
328
|
<TableBody>
|
|
325
|
-
{cappedRows.length === 0 &&
|
|
329
|
+
{cappedRows.length === 0 && resolvedStreaming ? (
|
|
326
330
|
[0, 1, 2].map((i) => (
|
|
327
331
|
<TableRow key={`skeleton-${i}`} aria-hidden="true">
|
|
328
332
|
{columns.map((column) => (
|
package/src/message.test.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { math } from "@streamdown/math";
|
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import type { ComponentProps } from "react";
|
|
4
4
|
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { render, screen } from "@testing-library/react";
|
|
5
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
6
6
|
import userEvent from "@testing-library/user-event";
|
|
7
7
|
import {
|
|
8
8
|
AgentMessage,
|
|
@@ -185,8 +185,47 @@ describe("MessageResponse loading (#269, loading-states.md)", () => {
|
|
|
185
185
|
});
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
+
describe("MessageResponse memo comparator covers every prop, not a hand-picked field list (perf review §2)", () => {
|
|
189
|
+
it("re-renders when only `className` changes (children/isAnimating/loading held constant)", () => {
|
|
190
|
+
const { container, rerender } = render(
|
|
191
|
+
<MessageResponse className="response-a">{"same content"}</MessageResponse>,
|
|
192
|
+
);
|
|
193
|
+
// Streamdown's own root `<div>` does not forward `data-slot` (it spreads
|
|
194
|
+
// rest props onto the inner markdown renderer, not its wrapper div), so
|
|
195
|
+
// key off the fixed `size-full` class `MessageResponse` always applies.
|
|
196
|
+
const responseEl = () => container.querySelector(".size-full");
|
|
197
|
+
expect(responseEl()).toHaveClass("response-a");
|
|
198
|
+
|
|
199
|
+
rerender(<MessageResponse className="response-b">{"same content"}</MessageResponse>);
|
|
200
|
+
|
|
201
|
+
// Under the old comparator (children/isAnimating/loading only), this
|
|
202
|
+
// rerender was skipped entirely — the DOM kept `response-a` even though
|
|
203
|
+
// the caller supplied a new `className`.
|
|
204
|
+
expect(responseEl()).toHaveClass("response-b");
|
|
205
|
+
expect(responseEl()).not.toHaveClass("response-a");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("re-renders when only `mode` changes (children/isAnimating/loading held constant)", () => {
|
|
209
|
+
// `mode="streaming"` (the default) leniently auto-closes incomplete
|
|
210
|
+
// markdown syntax; `mode="static"` renders it literally. A real,
|
|
211
|
+
// discriminating, upstream-independent signal for this prop.
|
|
212
|
+
const { container, rerender } = render(
|
|
213
|
+
<MessageResponse mode="streaming">{"**bold"}</MessageResponse>,
|
|
214
|
+
);
|
|
215
|
+
expect(container.querySelector('[data-streamdown="strong"]')).toBeInTheDocument();
|
|
216
|
+
|
|
217
|
+
rerender(<MessageResponse mode="static">{"**bold"}</MessageResponse>);
|
|
218
|
+
|
|
219
|
+
// Under the old comparator (children/isAnimating/loading only), this
|
|
220
|
+
// rerender was skipped, so the switch to `mode="static"` never took
|
|
221
|
+
// effect and the incomplete markdown stayed auto-completed.
|
|
222
|
+
expect(container.querySelector('[data-streamdown="strong"]')).not.toBeInTheDocument();
|
|
223
|
+
expect(screen.getByText("**bold")).toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
188
227
|
describe("MessageResponse plugins/components overrides (#10 — merge-not-replace semantics)", () => {
|
|
189
|
-
it("merges a real `plugins.cjk` override in (append), keeps sanitisation on, and keeps the untouched `plugins.math` default alive (#10)", () => {
|
|
228
|
+
it("merges a real `plugins.cjk` override in (append), keeps sanitisation on, and keeps the untouched `plugins.math` default alive (#10)", async () => {
|
|
190
229
|
// A real, discriminating lock — NOT `plugins={{}}` (that exercises zero
|
|
191
230
|
// slots and passes identically under merge, replace, or a no-op; #10
|
|
192
231
|
// review I3). This test supplies a genuine `cjk` plugin (one of the two
|
|
@@ -228,7 +267,14 @@ $$x^2$$
|
|
|
228
267
|
expect(cjkRemarkSpy).toHaveBeenCalled();
|
|
229
268
|
// (2b) …and the internal `math` default the consumer did not set is
|
|
230
269
|
// still active — real KaTeX markup, not the literal `$$x^2$$` text.
|
|
231
|
-
|
|
270
|
+
// `math` is now lazy-loaded off the source text (#perf-5) — it starts
|
|
271
|
+
// `undefined` and arrives after a dynamic import, hence `waitFor`.
|
|
272
|
+
await waitFor(
|
|
273
|
+
() => {
|
|
274
|
+
expect(document.querySelector(".katex")).toBeInTheDocument();
|
|
275
|
+
},
|
|
276
|
+
{ timeout: 5000 },
|
|
277
|
+
);
|
|
232
278
|
});
|
|
233
279
|
});
|
|
234
280
|
|
|
@@ -381,6 +427,30 @@ function BranchHarness(props: { branch?: number; onBranchChange?: (n: number) =>
|
|
|
381
427
|
);
|
|
382
428
|
}
|
|
383
429
|
|
|
430
|
+
describe("MessageBranchContent — id uniqueness and content sync (perf review §2)", () => {
|
|
431
|
+
it("does not spread a caller-supplied id onto every branch (each branch keeps a unique id)", () => {
|
|
432
|
+
const { container } = render(
|
|
433
|
+
<MessageBranch>
|
|
434
|
+
<MessageBranchContent id="branch-content">
|
|
435
|
+
<div key="a">Branch A</div>
|
|
436
|
+
<div key="b">Branch B</div>
|
|
437
|
+
<div key="c">Branch C</div>
|
|
438
|
+
</MessageBranchContent>
|
|
439
|
+
</MessageBranch>,
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
const contentDivs = container.querySelectorAll('[data-slot="message-branch-content"]');
|
|
443
|
+
expect(contentDivs).toHaveLength(3);
|
|
444
|
+
const ids = Array.from(contentDivs).map((el) => el.getAttribute("id"));
|
|
445
|
+
// Under the bug, `{...props}` spread the literal `id="branch-content"`
|
|
446
|
+
// onto every mapped div — three duplicate DOM ids for one supplied id.
|
|
447
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
448
|
+
for (const id of ids) {
|
|
449
|
+
expect(id).toMatch(/^branch-content-\d+$/);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
384
454
|
describe("MessageBranch — controlled mode (#361)", () => {
|
|
385
455
|
it("is uncontrolled by default: defaultBranch + internal Next/Previous navigation are unaffected", async () => {
|
|
386
456
|
const onBranchChange = vi.fn();
|
package/src/message.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { useLocale } from "@elabs-ai/components-ui";
|
|
|
8
8
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
9
|
import { stripSanitizerOverrides, warnOnTrustedPluginSlots } from "./_streamdown-safety";
|
|
10
10
|
import {
|
|
11
|
+
getStreamdownPluginsKey,
|
|
11
12
|
useStreamdownMermaidOptions,
|
|
12
13
|
useStreamdownPlugins,
|
|
13
14
|
useStreamdownTranslations,
|
|
@@ -433,19 +434,20 @@ export const MessageBranch = ({
|
|
|
433
434
|
|
|
434
435
|
export type MessageBranchContentProps = HTMLAttributes<HTMLDivElement>;
|
|
435
436
|
|
|
436
|
-
export const MessageBranchContent = ({ children, ...props }: MessageBranchContentProps) => {
|
|
437
|
-
const { currentBranch, setBranches
|
|
437
|
+
export const MessageBranchContent = ({ children, id, ...props }: MessageBranchContentProps) => {
|
|
438
|
+
const { currentBranch, setBranches } = useMessageBranch();
|
|
438
439
|
const childrenArray = useMemo(
|
|
439
440
|
() => (Array.isArray(children) ? children : [children]),
|
|
440
441
|
[children],
|
|
441
442
|
);
|
|
442
443
|
|
|
443
|
-
//
|
|
444
|
+
// Always resync — an earlier length-gate (`branches.length !==
|
|
445
|
+
// childrenArray.length`) skipped this when a branch's CONTENT changed
|
|
446
|
+
// (e.g. an edited message) without the branch COUNT changing, leaving
|
|
447
|
+
// `context.branches` stale relative to what is actually rendered.
|
|
444
448
|
useEffect(() => {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
}, [childrenArray, branches, setBranches]);
|
|
449
|
+
setBranches(childrenArray);
|
|
450
|
+
}, [childrenArray, setBranches]);
|
|
449
451
|
|
|
450
452
|
return childrenArray.map((branch, index) => (
|
|
451
453
|
<div
|
|
@@ -454,6 +456,10 @@ export const MessageBranchContent = ({ children, ...props }: MessageBranchConten
|
|
|
454
456
|
index === currentBranch ? "block" : "hidden",
|
|
455
457
|
)}
|
|
456
458
|
data-slot="message-branch-content"
|
|
459
|
+
// Spreading a caller-supplied `id` onto every mapped branch produced
|
|
460
|
+
// duplicate DOM ids (one per branch); suffix it per index instead so
|
|
461
|
+
// each branch keeps a unique, stable id.
|
|
462
|
+
id={id ? `${id}-${index}` : undefined}
|
|
457
463
|
key={branch.key}
|
|
458
464
|
{...props}
|
|
459
465
|
>
|
|
@@ -624,7 +630,12 @@ export const MessageResponse = memo(
|
|
|
624
630
|
const { t } = useLocale();
|
|
625
631
|
// Brand-token-derived `code` plugin, not the package's static github-*
|
|
626
632
|
// default (#315 follow-up) — re-derives when the active theme changes.
|
|
627
|
-
|
|
633
|
+
// `math`/`cjk` are lazy-loaded off the raw markdown source (#perf-5, see
|
|
634
|
+
// `_streamdown-i18n.ts`) — pass `props.children` so they load only when
|
|
635
|
+
// this message actually needs them.
|
|
636
|
+
const internalPlugins = useStreamdownPlugins(
|
|
637
|
+
typeof props.children === "string" ? props.children : "",
|
|
638
|
+
);
|
|
628
639
|
// MERGED per key over the internal defaults — the same semantics as
|
|
629
640
|
// `MarkdownView`'s `plugins` prop (#10 fix round, M4): a consumer entry
|
|
630
641
|
// wins for its key; every key the consumer does not set keeps the
|
|
@@ -676,6 +687,9 @@ export const MessageResponse = memo(
|
|
|
676
687
|
"[&_p]:text-body [&_li]:text-body [&_code]:text-code",
|
|
677
688
|
className,
|
|
678
689
|
)}
|
|
690
|
+
// Forces a remount the first time the lazy math/cjk slot resolves —
|
|
691
|
+
// see `getStreamdownPluginsKey`'s doc in `_streamdown-i18n.ts` for why.
|
|
692
|
+
key={getStreamdownPluginsKey(internalPlugins)}
|
|
679
693
|
data-slot="message-response"
|
|
680
694
|
plugins={plugins}
|
|
681
695
|
translations={translations}
|
|
@@ -684,10 +698,20 @@ export const MessageResponse = memo(
|
|
|
684
698
|
/>
|
|
685
699
|
);
|
|
686
700
|
},
|
|
687
|
-
(
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
701
|
+
// A hand-picked field list (`children`/`isAnimating`/`loading`) silently
|
|
702
|
+
// went stale as `MessageResponseProps` grew: `className`, `components`,
|
|
703
|
+
// `plugins`, `mode` and every other `Streamdown` prop passed through
|
|
704
|
+
// `...props` were never compared, so a caller-driven change to any of them
|
|
705
|
+
// (e.g. toggling `mode="static"`/`"streaming"`, swapping `components`, or
|
|
706
|
+
// overriding a `plugins` slot) rendered stale content until an unrelated
|
|
707
|
+
// prop also changed. Compare every own key on BOTH sides with `Object.is`
|
|
708
|
+
// instead, so a newly added prop is covered for free.
|
|
709
|
+
(prevProps, nextProps) => {
|
|
710
|
+
const prevKeys = Object.keys(prevProps) as (keyof MessageResponseProps)[];
|
|
711
|
+
const nextKeys = Object.keys(nextProps) as (keyof MessageResponseProps)[];
|
|
712
|
+
if (prevKeys.length !== nextKeys.length) return false;
|
|
713
|
+
return prevKeys.every((key) => Object.is(prevProps[key], nextProps[key]));
|
|
714
|
+
},
|
|
691
715
|
);
|
|
692
716
|
|
|
693
717
|
MessageResponse.displayName = "MessageResponse";
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
MODEL_PROVIDER_LOGO_BASE_URL,
|
|
6
|
+
ModelProviderLogo,
|
|
7
|
+
ModelProviderLogoGroup,
|
|
8
|
+
} from "./model-provider-logo";
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
document.documentElement.removeAttribute("data-theme");
|
|
13
|
+
document.documentElement.style.colorScheme = "";
|
|
14
|
+
});
|
|
5
15
|
|
|
6
16
|
describe("ModelProviderLogo — remote origin escape hatches", () => {
|
|
7
17
|
it("defaults to the models.dev logo for the provider", () => {
|
|
@@ -53,3 +63,47 @@ describe("ModelProviderLogo — remote origin escape hatches", () => {
|
|
|
53
63
|
expect(screen.getByRole("img", { name: "openai logo" })).toBeInTheDocument();
|
|
54
64
|
});
|
|
55
65
|
});
|
|
66
|
+
|
|
67
|
+
// Was `dark:invert` — a bare Tailwind dark: variant, which per theming.md only
|
|
68
|
+
// covers the two SHIPPED themes and misses any consumer-registered dark theme
|
|
69
|
+
// (ADR 0029, open theme registry). Now driven by `resolveThemeIsDark`, the
|
|
70
|
+
// same signal `Toaster` uses, so it follows the ACTIVE theme's own
|
|
71
|
+
// `color-scheme`, not a hardcoded theme name (review #1).
|
|
72
|
+
describe("ModelProviderLogo — theme-driven invert (review #1)", () => {
|
|
73
|
+
it("does not invert under the light theme", () => {
|
|
74
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
75
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
76
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).not.toHaveClass("invert");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("inverts under the dark theme", () => {
|
|
80
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
81
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
82
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveClass("invert");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("also inverts under a consumer-registered dark theme, not only the shipped one", () => {
|
|
86
|
+
// A raw `dark:` utility only ever matches `data-theme="dark"` — this
|
|
87
|
+
// is exactly the gap `resolveThemeIsDark`'s `color-scheme` reading closes.
|
|
88
|
+
document.documentElement.setAttribute("data-theme", "midnight");
|
|
89
|
+
document.documentElement.style.colorScheme = "dark";
|
|
90
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
91
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveClass("invert");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("ModelProviderLogoGroup — theme-driven backdrop (review #1)", () => {
|
|
96
|
+
it("backs logos with --background in the light theme", () => {
|
|
97
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
98
|
+
const { container } = render(<ModelProviderLogoGroup />);
|
|
99
|
+
expect(container.firstElementChild?.className).toContain("[&>img]:bg-background");
|
|
100
|
+
expect(container.firstElementChild?.className).not.toContain("[&>img]:bg-foreground");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("backs logos with --foreground in the dark theme", () => {
|
|
104
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
105
|
+
const { container } = render(<ModelProviderLogoGroup />);
|
|
106
|
+
expect(container.firstElementChild?.className).toContain("[&>img]:bg-foreground");
|
|
107
|
+
expect(container.firstElementChild?.className).not.toContain("[&>img]:bg-background");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
4
|
+
import { resolveThemeIsDark } from "@elabs-ai/components-tokens";
|
|
2
5
|
import { BotIcon } from "lucide-react";
|
|
3
6
|
import type { ComponentProps, ReactNode } from "react";
|
|
4
|
-
import { useState } from "react";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks the ACTIVE theme's own `color-scheme` (`resolveThemeIsDark`), not the
|
|
11
|
+
* two shipped `light`/`dark` themes — a consumer-authored dark theme still
|
|
12
|
+
* gets an inverted (visible) logo with no registration step (theming.md:
|
|
13
|
+
* "Use semantic tokens, not `dark:` — `@custom-variant dark` covers only the
|
|
14
|
+
* themes shipped HERE"). Mirrors `Toaster`'s `useDocumentThemeMode`
|
|
15
|
+
* (`packages/ui/src/components/sonner/sonner.tsx`).
|
|
16
|
+
*/
|
|
17
|
+
function useIsDarkTheme(): boolean {
|
|
18
|
+
const [isDark, setIsDark] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (typeof document === "undefined") return;
|
|
21
|
+
const el = document.documentElement;
|
|
22
|
+
const read = () => setIsDark(resolveThemeIsDark(el));
|
|
23
|
+
read();
|
|
24
|
+
const obs = new MutationObserver(read);
|
|
25
|
+
obs.observe(el, { attributes: true, attributeFilter: ["data-theme"] });
|
|
26
|
+
return () => obs.disconnect();
|
|
27
|
+
}, []);
|
|
28
|
+
return isDark;
|
|
29
|
+
}
|
|
5
30
|
|
|
6
31
|
/**
|
|
7
32
|
* Where provider logos are fetched from by default.
|
|
@@ -106,6 +131,7 @@ export const ModelProviderLogo = ({
|
|
|
106
131
|
...props
|
|
107
132
|
}: ModelProviderLogoProps) => {
|
|
108
133
|
const [failed, setFailed] = useState(false);
|
|
134
|
+
const isDark = useIsDarkTheme();
|
|
109
135
|
|
|
110
136
|
// A blocked/missing logo must not leave a broken-image glyph in the row.
|
|
111
137
|
if (failed) {
|
|
@@ -122,7 +148,7 @@ export const ModelProviderLogo = ({
|
|
|
122
148
|
<img
|
|
123
149
|
{...props}
|
|
124
150
|
alt={`${provider} logo`}
|
|
125
|
-
className={cn("size-3
|
|
151
|
+
className={cn("size-3", isDark && "invert", className)}
|
|
126
152
|
height={12}
|
|
127
153
|
onError={(event) => {
|
|
128
154
|
setFailed(true);
|
|
@@ -138,12 +164,20 @@ export const ModelProviderLogo = ({
|
|
|
138
164
|
|
|
139
165
|
export type ModelProviderLogoGroupProps = ComponentProps<"div">;
|
|
140
166
|
|
|
141
|
-
export const ModelProviderLogoGroup = ({ className, ...props }: ModelProviderLogoGroupProps) =>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
export const ModelProviderLogoGroup = ({ className, ...props }: ModelProviderLogoGroupProps) => {
|
|
168
|
+
// Same active-theme signal as the single logo above (not the two shipped
|
|
169
|
+
// `light`/`dark` themes) — the backdrop swaps to `--foreground` so a
|
|
170
|
+
// transparent-background SVG still reads against a dark chip in ANY dark
|
|
171
|
+
// theme a consumer registers.
|
|
172
|
+
const isDark = useIsDarkTheme();
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
className={cn(
|
|
176
|
+
"flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:p-px [&>img]:ring-1",
|
|
177
|
+
isDark ? "[&>img]:bg-foreground" : "[&>img]:bg-background",
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...props}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
};
|
package/src/open-in-chat.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Button } from "@elabs-ai/components-ui";
|
|
3
|
+
import { Button, useLocale } from "@elabs-ai/components-ui";
|
|
4
4
|
import {
|
|
5
5
|
DropdownMenu,
|
|
6
6
|
DropdownMenuContent,
|
|
@@ -23,11 +23,11 @@ const providers = {
|
|
|
23
23
|
})}`,
|
|
24
24
|
icon: (
|
|
25
25
|
<svg fill="currentColor" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
26
|
-
<title>OpenAI</title>
|
|
26
|
+
<title>OpenAI</title> {/* i18n-exempt: brand name */}
|
|
27
27
|
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
|
|
28
28
|
</svg>
|
|
29
29
|
),
|
|
30
|
-
title: "
|
|
30
|
+
title: "ChatGPT", // i18n-exempt: brand name
|
|
31
31
|
},
|
|
32
32
|
claude: {
|
|
33
33
|
createUrl: (q: string) =>
|
|
@@ -36,7 +36,7 @@ const providers = {
|
|
|
36
36
|
})}`,
|
|
37
37
|
icon: (
|
|
38
38
|
<svg fill="currentColor" role="img" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
|
39
|
-
<title>Claude</title>
|
|
39
|
+
<title>Claude</title> {/* i18n-exempt: brand name */}
|
|
40
40
|
<path
|
|
41
41
|
clipRule="evenodd"
|
|
42
42
|
d="M2.3545 7.9775L4.7145 6.654L4.7545 6.539L4.7145 6.475H4.6L4.205 6.451L2.856 6.4145L1.6865 6.366L0.5535 6.305L0.268 6.2445L0 5.892L0.0275 5.716L0.2675 5.5555L0.6105 5.5855L1.3705 5.637L2.5095 5.716L3.3355 5.7645L4.56 5.892H4.7545L4.782 5.8135L4.715 5.7645L4.6635 5.716L3.4845 4.918L2.2085 4.074L1.5405 3.588L1.1785 3.3425L0.9965 3.1115L0.9175 2.6075L1.2455 2.2465L1.686 2.2765L1.7985 2.307L2.245 2.65L3.199 3.388L4.4445 4.3045L4.627 4.4565L4.6995 4.405L4.709 4.3685L4.627 4.2315L3.9495 3.0085L3.2265 1.7635L2.9045 1.2475L2.8195 0.938C2.78711 0.819128 2.76965 0.696687 2.7675 0.5735L3.1415 0.067L3.348 0L3.846 0.067L4.056 0.249L4.366 0.956L4.867 2.0705L5.6445 3.5855L5.8725 4.0345L5.994 4.4505L6.0395 4.578H6.1185V4.505L6.1825 3.652L6.301 2.6045L6.416 1.257L6.456 0.877L6.644 0.422L7.0175 0.176L7.3095 0.316L7.5495 0.6585L7.516 0.8805L7.373 1.806L7.0935 3.2575L6.9115 4.2285H7.0175L7.139 4.1075L7.6315 3.4545L8.4575 2.4225L8.8225 2.0125L9.2475 1.5605L9.521 1.345H10.0375L10.4175 1.9095L10.2475 2.4925L9.7155 3.166L9.275 3.737L8.643 4.587L8.248 5.267L8.2845 5.322L8.3785 5.312L9.8065 5.009L10.578 4.869L11.4985 4.7115L11.915 4.9055L11.9605 5.103L11.7965 5.5065L10.812 5.7495L9.6575 5.9805L7.938 6.387L7.917 6.402L7.9415 6.4325L8.716 6.5055L9.047 6.5235H9.858L11.368 6.636L11.763 6.897L12 7.216L11.9605 7.4585L11.353 7.7685L10.533 7.574L8.6185 7.119L7.9625 6.9545H7.8715V7.0095L8.418 7.5435L9.421 8.4485L10.6755 9.6135L10.739 9.9025L10.578 10.13L10.408 10.1055L9.3055 9.277L8.88 8.9035L7.917 8.0935H7.853V8.1785L8.075 8.503L9.2475 10.2635L9.3085 10.8035L9.2235 10.98L8.9195 11.0865L8.5855 11.0255L7.8985 10.063L7.191 8.9795L6.6195 8.008L6.5495 8.048L6.2125 11.675L6.0545 11.86L5.69 12L5.3865 11.7695L5.2255 11.396L5.3865 10.658L5.581 9.696L5.7385 8.931L5.8815 7.981L5.9665 7.665L5.9605 7.644L5.8905 7.653L5.1735 8.6365L4.0835 10.109L3.2205 11.0315L3.0135 11.1135L2.655 10.9285L2.6885 10.5975L2.889 10.303L4.083 8.785L4.803 7.844L5.268 7.301L5.265 7.222H5.2375L2.066 9.28L1.501 9.353L1.2575 9.125L1.288 8.752L1.4035 8.6305L2.3575 7.9745L2.3545 7.9775Z"
|
|
@@ -44,7 +44,7 @@ const providers = {
|
|
|
44
44
|
/>
|
|
45
45
|
</svg>
|
|
46
46
|
),
|
|
47
|
-
title: "
|
|
47
|
+
title: "Claude", // i18n-exempt: brand name
|
|
48
48
|
},
|
|
49
49
|
cursor: {
|
|
50
50
|
createUrl: (text: string) => {
|
|
@@ -54,24 +54,24 @@ const providers = {
|
|
|
54
54
|
},
|
|
55
55
|
icon: (
|
|
56
56
|
<svg version="1.1" viewBox="0 0 466.73 532.09" xmlns="http://www.w3.org/2000/svg">
|
|
57
|
-
<title>Cursor</title>
|
|
57
|
+
<title>Cursor</title> {/* i18n-exempt: brand name */}
|
|
58
58
|
<path
|
|
59
59
|
d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"
|
|
60
60
|
fill="currentColor"
|
|
61
61
|
/>
|
|
62
62
|
</svg>
|
|
63
63
|
),
|
|
64
|
-
title: "
|
|
64
|
+
title: "Cursor", // i18n-exempt: brand name
|
|
65
65
|
},
|
|
66
66
|
github: {
|
|
67
67
|
createUrl: (url: string) => url,
|
|
68
68
|
icon: (
|
|
69
69
|
<svg fill="currentColor" role="img" viewBox="0 0 24 24">
|
|
70
|
-
<title>GitHub</title>
|
|
70
|
+
<title>GitHub</title> {/* i18n-exempt: brand name */}
|
|
71
71
|
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
|
72
72
|
</svg>
|
|
73
73
|
),
|
|
74
|
-
title: "
|
|
74
|
+
title: "GitHub", // i18n-exempt: brand name
|
|
75
75
|
},
|
|
76
76
|
scira: {
|
|
77
77
|
createUrl: (q: string) =>
|
|
@@ -86,7 +86,7 @@ const providers = {
|
|
|
86
86
|
width="910"
|
|
87
87
|
xmlns="http://www.w3.org/2000/svg"
|
|
88
88
|
>
|
|
89
|
-
<title>Scira AI</title>
|
|
89
|
+
<title>Scira AI</title> {/* i18n-exempt: brand name */}
|
|
90
90
|
<path
|
|
91
91
|
d="M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z"
|
|
92
92
|
fill="currentColor"
|
|
@@ -136,7 +136,7 @@ const providers = {
|
|
|
136
136
|
/>
|
|
137
137
|
</svg>
|
|
138
138
|
),
|
|
139
|
-
title: "
|
|
139
|
+
title: "Scira", // i18n-exempt: brand name
|
|
140
140
|
},
|
|
141
141
|
t3: {
|
|
142
142
|
createUrl: (q: string) =>
|
|
@@ -144,7 +144,7 @@ const providers = {
|
|
|
144
144
|
q,
|
|
145
145
|
})}`,
|
|
146
146
|
icon: <MessageCircleIcon />,
|
|
147
|
-
title: "
|
|
147
|
+
title: "T3 Chat", // i18n-exempt: brand name
|
|
148
148
|
},
|
|
149
149
|
v0: {
|
|
150
150
|
createUrl: (q: string) =>
|
|
@@ -158,7 +158,7 @@ const providers = {
|
|
|
158
158
|
<path d="M147 56H133V23.9531L100.953 56H133V70H96.6875C85.8144 70 77 61.1856 77 50.3125V14H91V46.1562L123.156 14H91V0H127.312C138.186 0 147 8.81439 147 19.6875V56Z" />
|
|
159
159
|
</svg>
|
|
160
160
|
),
|
|
161
|
-
title: "
|
|
161
|
+
title: "v0", // i18n-exempt: brand name
|
|
162
162
|
},
|
|
163
163
|
};
|
|
164
164
|
|
|
@@ -208,21 +208,25 @@ export const OpenInSeparator = (props: OpenInSeparatorProps) => (
|
|
|
208
208
|
|
|
209
209
|
export type OpenInTriggerProps = ComponentProps<typeof DropdownMenuTrigger>;
|
|
210
210
|
|
|
211
|
-
export const OpenInTrigger = ({ children, ...props }: OpenInTriggerProps) =>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
211
|
+
export const OpenInTrigger = ({ children, ...props }: OpenInTriggerProps) => {
|
|
212
|
+
const { t } = useLocale();
|
|
213
|
+
return (
|
|
214
|
+
<DropdownMenuTrigger {...props} asChild>
|
|
215
|
+
{children ?? (
|
|
216
|
+
<Button type="button" variant="outline">
|
|
217
|
+
{t("ai.openInChat.trigger")}
|
|
218
|
+
<ChevronDownIcon className="size-4" />
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</DropdownMenuTrigger>
|
|
222
|
+
);
|
|
223
|
+
};
|
|
221
224
|
|
|
222
225
|
export type OpenInChatGPTProps = ComponentProps<typeof DropdownMenuItem>;
|
|
223
226
|
|
|
224
227
|
export const OpenInChatGPT = (props: OpenInChatGPTProps) => {
|
|
225
228
|
const { query } = useOpenInContext();
|
|
229
|
+
const { t } = useLocale();
|
|
226
230
|
return (
|
|
227
231
|
<DropdownMenuItem asChild {...props}>
|
|
228
232
|
<a
|
|
@@ -232,7 +236,9 @@ export const OpenInChatGPT = (props: OpenInChatGPTProps) => {
|
|
|
232
236
|
target="_blank"
|
|
233
237
|
>
|
|
234
238
|
<span className="shrink-0">{providers.chatgpt.icon}</span>
|
|
235
|
-
<span className="flex-1">
|
|
239
|
+
<span className="flex-1">
|
|
240
|
+
{t("ai.openInChat.openInProvider", { provider: providers.chatgpt.title })}
|
|
241
|
+
</span>
|
|
236
242
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
237
243
|
</a>
|
|
238
244
|
</DropdownMenuItem>
|
|
@@ -243,6 +249,7 @@ export type OpenInClaudeProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
243
249
|
|
|
244
250
|
export const OpenInClaude = (props: OpenInClaudeProps) => {
|
|
245
251
|
const { query } = useOpenInContext();
|
|
252
|
+
const { t } = useLocale();
|
|
246
253
|
return (
|
|
247
254
|
<DropdownMenuItem asChild {...props}>
|
|
248
255
|
<a
|
|
@@ -252,7 +259,9 @@ export const OpenInClaude = (props: OpenInClaudeProps) => {
|
|
|
252
259
|
target="_blank"
|
|
253
260
|
>
|
|
254
261
|
<span className="shrink-0">{providers.claude.icon}</span>
|
|
255
|
-
<span className="flex-1">
|
|
262
|
+
<span className="flex-1">
|
|
263
|
+
{t("ai.openInChat.openInProvider", { provider: providers.claude.title })}
|
|
264
|
+
</span>
|
|
256
265
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
257
266
|
</a>
|
|
258
267
|
</DropdownMenuItem>
|
|
@@ -263,6 +272,7 @@ export type OpenInT3Props = ComponentProps<typeof DropdownMenuItem>;
|
|
|
263
272
|
|
|
264
273
|
export const OpenInT3 = (props: OpenInT3Props) => {
|
|
265
274
|
const { query } = useOpenInContext();
|
|
275
|
+
const { t } = useLocale();
|
|
266
276
|
return (
|
|
267
277
|
<DropdownMenuItem asChild {...props}>
|
|
268
278
|
<a
|
|
@@ -272,7 +282,9 @@ export const OpenInT3 = (props: OpenInT3Props) => {
|
|
|
272
282
|
target="_blank"
|
|
273
283
|
>
|
|
274
284
|
<span className="shrink-0">{providers.t3.icon}</span>
|
|
275
|
-
<span className="flex-1">
|
|
285
|
+
<span className="flex-1">
|
|
286
|
+
{t("ai.openInChat.openInProvider", { provider: providers.t3.title })}
|
|
287
|
+
</span>
|
|
276
288
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
277
289
|
</a>
|
|
278
290
|
</DropdownMenuItem>
|
|
@@ -283,6 +295,7 @@ export type OpenInSciraProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
283
295
|
|
|
284
296
|
export const OpenInScira = (props: OpenInSciraProps) => {
|
|
285
297
|
const { query } = useOpenInContext();
|
|
298
|
+
const { t } = useLocale();
|
|
286
299
|
return (
|
|
287
300
|
<DropdownMenuItem asChild {...props}>
|
|
288
301
|
<a
|
|
@@ -292,7 +305,9 @@ export const OpenInScira = (props: OpenInSciraProps) => {
|
|
|
292
305
|
target="_blank"
|
|
293
306
|
>
|
|
294
307
|
<span className="shrink-0">{providers.scira.icon}</span>
|
|
295
|
-
<span className="flex-1">
|
|
308
|
+
<span className="flex-1">
|
|
309
|
+
{t("ai.openInChat.openInProvider", { provider: providers.scira.title })}
|
|
310
|
+
</span>
|
|
296
311
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
297
312
|
</a>
|
|
298
313
|
</DropdownMenuItem>
|
|
@@ -303,6 +318,7 @@ export type OpenInv0Props = ComponentProps<typeof DropdownMenuItem>;
|
|
|
303
318
|
|
|
304
319
|
export const OpenInv0 = (props: OpenInv0Props) => {
|
|
305
320
|
const { query } = useOpenInContext();
|
|
321
|
+
const { t } = useLocale();
|
|
306
322
|
return (
|
|
307
323
|
<DropdownMenuItem asChild {...props}>
|
|
308
324
|
<a
|
|
@@ -312,7 +328,9 @@ export const OpenInv0 = (props: OpenInv0Props) => {
|
|
|
312
328
|
target="_blank"
|
|
313
329
|
>
|
|
314
330
|
<span className="shrink-0">{providers.v0.icon}</span>
|
|
315
|
-
<span className="flex-1">
|
|
331
|
+
<span className="flex-1">
|
|
332
|
+
{t("ai.openInChat.openInProvider", { provider: providers.v0.title })}
|
|
333
|
+
</span>
|
|
316
334
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
317
335
|
</a>
|
|
318
336
|
</DropdownMenuItem>
|
|
@@ -323,6 +341,7 @@ export type OpenInCursorProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
323
341
|
|
|
324
342
|
export const OpenInCursor = (props: OpenInCursorProps) => {
|
|
325
343
|
const { query } = useOpenInContext();
|
|
344
|
+
const { t } = useLocale();
|
|
326
345
|
return (
|
|
327
346
|
<DropdownMenuItem asChild {...props}>
|
|
328
347
|
<a
|
|
@@ -332,7 +351,9 @@ export const OpenInCursor = (props: OpenInCursorProps) => {
|
|
|
332
351
|
target="_blank"
|
|
333
352
|
>
|
|
334
353
|
<span className="shrink-0">{providers.cursor.icon}</span>
|
|
335
|
-
<span className="flex-1">
|
|
354
|
+
<span className="flex-1">
|
|
355
|
+
{t("ai.openInChat.openInProvider", { provider: providers.cursor.title })}
|
|
356
|
+
</span>
|
|
336
357
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
337
358
|
</a>
|
|
338
359
|
</DropdownMenuItem>
|