@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/diff-view.tsx
CHANGED
|
@@ -59,6 +59,7 @@ import { cva } from "class-variance-authority";
|
|
|
59
59
|
import type { BundledLanguage, ThemedToken } from "shiki";
|
|
60
60
|
import { highlightCode } from "./code-block";
|
|
61
61
|
import { Shimmer } from "./shimmer";
|
|
62
|
+
import { getThemeScope } from "./_theme-scope-store";
|
|
62
63
|
|
|
63
64
|
// ─── Public types ───────────────────────────────────────────────────────────
|
|
64
65
|
//
|
|
@@ -139,41 +140,27 @@ const MARKER_TONE: Record<"add" | "del" | "context", string> = {
|
|
|
139
140
|
context: "text-muted-foreground",
|
|
140
141
|
};
|
|
141
142
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
el?.closest("[data-theme]") ??
|
|
146
|
-
(typeof document !== "undefined" ? document.documentElement : null);
|
|
143
|
+
// `getThemeScope` (nearest `[data-theme]` ancestor, defaulting to `<html>`)
|
|
144
|
+
// comes from the shared `_theme-scope-store` module — the exact same helper
|
|
145
|
+
// `code-block.tsx` uses, not a second copy of it.
|
|
147
146
|
|
|
148
147
|
// ─── Intra-line syntax highlighting ────────────────────────────────────────
|
|
149
148
|
|
|
150
149
|
type TokenizedResult = NonNullable<ReturnType<typeof highlightCode>>;
|
|
151
150
|
|
|
152
151
|
/**
|
|
153
|
-
* Highlights
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
152
|
+
* Highlights ONE side's combined document (see `useDiffTokens` below) and
|
|
153
|
+
* returns the raw Shiki result once it resolves. `isStreaming` is forwarded
|
|
154
|
+
* to `highlightCode`'s `skipCache` — a streaming diff re-tokenizes a growing
|
|
155
|
+
* document on every line, and every intermediate value is never seen again,
|
|
156
|
+
* so permanently caching it is pure waste (perf review 1.4a/§3.2).
|
|
158
157
|
*/
|
|
159
|
-
function
|
|
160
|
-
|
|
158
|
+
function useSideTokens(
|
|
159
|
+
combinedCode: string,
|
|
161
160
|
language: BundledLanguage | undefined,
|
|
162
161
|
scopeEl: Element | null,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
() =>
|
|
166
|
-
lines
|
|
167
|
-
.map((line, index) => ({ line, index }))
|
|
168
|
-
.filter(({ line }) => line.type !== "hunk" && line.type !== "meta")
|
|
169
|
-
.map(({ index }) => index),
|
|
170
|
-
[lines],
|
|
171
|
-
);
|
|
172
|
-
const combinedCode = useMemo(
|
|
173
|
-
() => codeIndices.map((index) => lines[index]?.text ?? "").join("\n"),
|
|
174
|
-
[codeIndices, lines],
|
|
175
|
-
);
|
|
176
|
-
|
|
162
|
+
isStreaming: boolean,
|
|
163
|
+
): TokenizedResult | null {
|
|
177
164
|
const [result, setResult] = useState<TokenizedResult | null>(null);
|
|
178
165
|
|
|
179
166
|
useEffect(() => {
|
|
@@ -189,22 +176,90 @@ function useDiffTokens(
|
|
|
189
176
|
if (!cancelled) setResult(r);
|
|
190
177
|
},
|
|
191
178
|
scopeEl,
|
|
179
|
+
isStreaming,
|
|
192
180
|
);
|
|
193
181
|
if (cached) setResult(cached);
|
|
194
182
|
return () => {
|
|
195
183
|
cancelled = true;
|
|
196
184
|
};
|
|
197
|
-
}, [combinedCode, language, scopeEl]);
|
|
185
|
+
}, [combinedCode, language, scopeEl, isStreaming]);
|
|
186
|
+
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Highlights the OLD file (`del` + `context` lines) and the NEW file (`add` +
|
|
192
|
+
* `context` lines) as two SEPARATE Shiki documents, then maps each back onto
|
|
193
|
+
* the original line indices. `hunk` / `meta` lines are diff headers, not
|
|
194
|
+
* code, and are excluded from both documents.
|
|
195
|
+
*
|
|
196
|
+
* The two sides used to be combined into ONE document (every real line,
|
|
197
|
+
* `del`/`add`/`context` alike, joined in original order). That let a
|
|
198
|
+
* still-open construct on one side leak its tokenizer STATE across the
|
|
199
|
+
* boundary onto the other — a deleted, unterminated `/*` opened a block
|
|
200
|
+
* comment that swallowed the immediately-following ADDED line as commented-out
|
|
201
|
+
* text, even though the two lines never coexist in either real version of the
|
|
202
|
+
* file. Tokenizing old/new as their own documents means Shiki only ever sees
|
|
203
|
+
* genuine same-version context around a construct.
|
|
204
|
+
*/
|
|
205
|
+
function useDiffTokens(
|
|
206
|
+
lines: DiffLine[],
|
|
207
|
+
language: BundledLanguage | undefined,
|
|
208
|
+
scopeEl: Element | null,
|
|
209
|
+
isStreaming: boolean,
|
|
210
|
+
): Map<number, ThemedToken[]> | null {
|
|
211
|
+
const oldIndices = useMemo(
|
|
212
|
+
() =>
|
|
213
|
+
lines
|
|
214
|
+
.map((line, index) => ({ line, index }))
|
|
215
|
+
.filter(({ line }) => line.type === "del" || line.type === "context")
|
|
216
|
+
.map(({ index }) => index),
|
|
217
|
+
[lines],
|
|
218
|
+
);
|
|
219
|
+
const newIndices = useMemo(
|
|
220
|
+
() =>
|
|
221
|
+
lines
|
|
222
|
+
.map((line, index) => ({ line, index }))
|
|
223
|
+
.filter(({ line }) => line.type === "add" || line.type === "context")
|
|
224
|
+
.map(({ index }) => index),
|
|
225
|
+
[lines],
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const oldCode = useMemo(
|
|
229
|
+
() => oldIndices.map((index) => lines[index]?.text ?? "").join("\n"),
|
|
230
|
+
[oldIndices, lines],
|
|
231
|
+
);
|
|
232
|
+
const newCode = useMemo(
|
|
233
|
+
() => newIndices.map((index) => lines[index]?.text ?? "").join("\n"),
|
|
234
|
+
[newIndices, lines],
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const oldResult = useSideTokens(oldCode, language, scopeEl, isStreaming);
|
|
238
|
+
const newResult = useSideTokens(newCode, language, scopeEl, isStreaming);
|
|
198
239
|
|
|
199
240
|
return useMemo(() => {
|
|
200
|
-
if (!language
|
|
241
|
+
if (!language) return null;
|
|
201
242
|
const map = new Map<number, ThemedToken[]>();
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
243
|
+
// `del` lines exist ONLY in the old-file document.
|
|
244
|
+
if (oldResult) {
|
|
245
|
+
oldIndices.forEach((originalIndex, i) => {
|
|
246
|
+
if (lines[originalIndex]?.type !== "del") return;
|
|
247
|
+
const tokenLine = oldResult.tokens[i];
|
|
248
|
+
if (tokenLine) map.set(originalIndex, tokenLine);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// `add` AND `context` lines come from the new-file document — a `context`
|
|
252
|
+
// line reads identically in both versions, and picking one consistently
|
|
253
|
+
// (rather than whichever side happened to resolve last) keeps its color
|
|
254
|
+
// stable across re-highlights.
|
|
255
|
+
if (newResult) {
|
|
256
|
+
newIndices.forEach((originalIndex, i) => {
|
|
257
|
+
const tokenLine = newResult.tokens[i];
|
|
258
|
+
if (tokenLine) map.set(originalIndex, tokenLine);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
206
261
|
return map;
|
|
207
|
-
}, [
|
|
262
|
+
}, [oldIndices, newIndices, oldResult, newResult, language, lines]);
|
|
208
263
|
}
|
|
209
264
|
|
|
210
265
|
/** Renders pre-highlighted tokens; colour only (a diff row doesn't need bold/italic/underline). */
|
|
@@ -241,7 +296,7 @@ function CollapsedRow({ hiddenCount, onExpand }: { hiddenCount: number; onExpand
|
|
|
241
296
|
<button
|
|
242
297
|
type="button"
|
|
243
298
|
onClick={onExpand}
|
|
244
|
-
className="col-start-4 w-fit rounded-sm px-1 text-info-text text-meta hover:underline focus-
|
|
299
|
+
className="col-start-4 w-fit rounded-sm px-1 text-info-text text-meta hover:underline focus-ring"
|
|
245
300
|
>
|
|
246
301
|
{t("ai.diffView.showMore", { count: hiddenCount })}
|
|
247
302
|
</button>
|
|
@@ -431,6 +486,7 @@ function DiffViewHeader({
|
|
|
431
486
|
}) {
|
|
432
487
|
const { t } = useLocale();
|
|
433
488
|
return (
|
|
489
|
+
// header-band-exempt: a file header inside the diff card, not shell chrome
|
|
434
490
|
<div
|
|
435
491
|
data-slot="diff-view-header"
|
|
436
492
|
className="flex items-center justify-between gap-3 border-b bg-muted/80 px-3 py-2 text-meta text-muted-foreground"
|
|
@@ -524,7 +580,7 @@ function DiffViewPager({
|
|
|
524
580
|
tabIndex={0}
|
|
525
581
|
onScroll={updatePercent}
|
|
526
582
|
onKeyDown={onKeyDown}
|
|
527
|
-
className="max-h-[32rem] overflow-y-auto focus-
|
|
583
|
+
className="max-h-[32rem] overflow-y-auto focus-ring-inset"
|
|
528
584
|
>
|
|
529
585
|
{children}
|
|
530
586
|
</div>
|
|
@@ -573,7 +629,7 @@ export const DiffView = forwardRef<HTMLDivElement, DiffViewProps>(function DiffV
|
|
|
573
629
|
() => (maxLines ? lines.slice(0, maxLines) : lines),
|
|
574
630
|
[lines, maxLines],
|
|
575
631
|
);
|
|
576
|
-
const tokens = useDiffTokens(clippedLines, language, scopeEl);
|
|
632
|
+
const tokens = useDiffTokens(clippedLines, language, scopeEl, isStreaming);
|
|
577
633
|
const { rows, expand } = useDiffRows(clippedLines, contextLines);
|
|
578
634
|
|
|
579
635
|
const RowComponent = variant === "split" ? SplitRow : InlineRow;
|
|
@@ -4,18 +4,10 @@ import { Badge } from "@elabs-ai/components-ui";
|
|
|
4
4
|
import { Button } from "@elabs-ai/components-ui";
|
|
5
5
|
import { Switch } from "@elabs-ai/components-ui";
|
|
6
6
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
7
|
-
import { useLocale } from "@elabs-ai/components-ui";
|
|
7
|
+
import { useCopyToClipboard, useLocale } from "@elabs-ai/components-ui";
|
|
8
8
|
import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "lucide-react";
|
|
9
9
|
import type { ComponentProps, HTMLAttributes } from "react";
|
|
10
|
-
import {
|
|
11
|
-
createContext,
|
|
12
|
-
useCallback,
|
|
13
|
-
useContext,
|
|
14
|
-
useEffect,
|
|
15
|
-
useMemo,
|
|
16
|
-
useRef,
|
|
17
|
-
useState,
|
|
18
|
-
} from "react";
|
|
10
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
19
11
|
|
|
20
12
|
interface EnvironmentVariablesContextType {
|
|
21
13
|
showValues: boolean;
|
|
@@ -86,7 +78,7 @@ export const EnvironmentVariablesTitle = ({
|
|
|
86
78
|
children,
|
|
87
79
|
...props
|
|
88
80
|
}: EnvironmentVariablesTitleProps) => (
|
|
89
|
-
<h3 className={cn("font-medium text-
|
|
81
|
+
<h3 className={cn("font-medium text-body", className)} {...props}>
|
|
90
82
|
{children ?? "Environment Variables"}
|
|
91
83
|
</h3>
|
|
92
84
|
);
|
|
@@ -102,7 +94,7 @@ export const EnvironmentVariablesToggle = ({
|
|
|
102
94
|
|
|
103
95
|
return (
|
|
104
96
|
<div className={cn("flex items-center gap-2", className)}>
|
|
105
|
-
<span className="text-muted-foreground text-
|
|
97
|
+
<span className="text-muted-foreground text-meta">
|
|
106
98
|
{showValues ? <EyeIcon size={14} /> : <EyeOffIcon size={14} />}
|
|
107
99
|
</span>
|
|
108
100
|
<Switch
|
|
@@ -159,7 +151,7 @@ export const EnvironmentVariableName = ({
|
|
|
159
151
|
const { name } = useContext(EnvironmentVariableContext);
|
|
160
152
|
|
|
161
153
|
return (
|
|
162
|
-
<span className={cn("font-mono text-
|
|
154
|
+
<span className={cn("font-mono text-body", className)} {...props}>
|
|
163
155
|
{children ?? name}
|
|
164
156
|
</span>
|
|
165
157
|
);
|
|
@@ -180,7 +172,7 @@ export const EnvironmentVariableValue = ({
|
|
|
180
172
|
return (
|
|
181
173
|
<span
|
|
182
174
|
className={cn(
|
|
183
|
-
"font-mono text-muted-foreground text-
|
|
175
|
+
"font-mono text-muted-foreground text-body",
|
|
184
176
|
!showValues && "select-none",
|
|
185
177
|
className,
|
|
186
178
|
)}
|
|
@@ -234,15 +226,19 @@ export type EnvironmentVariableCopyButtonProps = ComponentProps<typeof Button> &
|
|
|
234
226
|
export const EnvironmentVariableCopyButton = ({
|
|
235
227
|
onCopy,
|
|
236
228
|
onError,
|
|
237
|
-
timeout
|
|
229
|
+
timeout,
|
|
238
230
|
copyFormat = "value",
|
|
239
231
|
children,
|
|
240
232
|
className,
|
|
233
|
+
"aria-label": ariaLabel,
|
|
241
234
|
...props
|
|
242
235
|
}: EnvironmentVariableCopyButtonProps) => {
|
|
243
|
-
const
|
|
244
|
-
const timeoutRef = useRef<number>(0);
|
|
236
|
+
const { t } = useLocale();
|
|
245
237
|
const { name, value } = useContext(EnvironmentVariableContext);
|
|
238
|
+
// Shared implementation (`@elabs-ai/components-ui`) instead of a private
|
|
239
|
+
// copy of the same copy-to-clipboard state machine (issue-workflow.md
|
|
240
|
+
// dedupe finding) — see `CodeBlockCopyButton` for the reference usage.
|
|
241
|
+
const { copied: isCopied, copy } = useCopyToClipboard({ resetAfterMs: timeout ?? 2000 });
|
|
246
242
|
|
|
247
243
|
const getTextToCopy = useCallback((): string => {
|
|
248
244
|
const formatMap = {
|
|
@@ -254,32 +250,19 @@ export const EnvironmentVariableCopyButton = ({
|
|
|
254
250
|
}, [name, value, copyFormat]);
|
|
255
251
|
|
|
256
252
|
const copyToClipboard = useCallback(async () => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
await navigator.clipboard.writeText(getTextToCopy());
|
|
264
|
-
setIsCopied(true);
|
|
253
|
+
const ok = await copy(getTextToCopy());
|
|
254
|
+
if (ok) {
|
|
265
255
|
onCopy?.();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
onError?.(error as Error);
|
|
256
|
+
} else {
|
|
257
|
+
onError?.(new Error("Clipboard API not available"));
|
|
269
258
|
}
|
|
270
|
-
}, [getTextToCopy, onCopy, onError
|
|
271
|
-
|
|
272
|
-
useEffect(
|
|
273
|
-
() => () => {
|
|
274
|
-
window.clearTimeout(timeoutRef.current);
|
|
275
|
-
},
|
|
276
|
-
[],
|
|
277
|
-
);
|
|
259
|
+
}, [copy, getTextToCopy, onCopy, onError]);
|
|
278
260
|
|
|
279
261
|
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
280
262
|
|
|
281
263
|
return (
|
|
282
264
|
<Button
|
|
265
|
+
aria-label={ariaLabel ?? t("copy")}
|
|
283
266
|
className={cn("size-6 shrink-0", className)}
|
|
284
267
|
onClick={copyToClipboard}
|
|
285
268
|
size="icon"
|
|
@@ -298,7 +281,7 @@ export const EnvironmentVariableRequired = ({
|
|
|
298
281
|
children,
|
|
299
282
|
...props
|
|
300
283
|
}: EnvironmentVariableRequiredProps) => (
|
|
301
|
-
<Badge className={cn("text-
|
|
284
|
+
<Badge className={cn("text-meta", className)} variant="secondary" {...props}>
|
|
302
285
|
{children ?? "Required"}
|
|
303
286
|
</Badge>
|
|
304
287
|
);
|
package/src/file-tree.test.tsx
CHANGED
|
@@ -25,6 +25,27 @@ describe("FileTree folder icons", () => {
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
describe("FileTreeFolder expand/collapse control (accessibility.md — icon-only controls need aria-label)", () => {
|
|
29
|
+
it("names the chevron toggle, and flips the name with expanded state", async () => {
|
|
30
|
+
const user = userEvent.setup();
|
|
31
|
+
render(
|
|
32
|
+
<FileTree defaultExpanded={new Set()}>
|
|
33
|
+
<FileTreeFolder path="src" name="src">
|
|
34
|
+
<FileTreeFile path="src/index.ts" name="index.ts" />
|
|
35
|
+
</FileTreeFolder>
|
|
36
|
+
</FileTree>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const toggle = screen.getByRole("button", { name: "Expand src" });
|
|
40
|
+
expect(toggle).toBeInTheDocument();
|
|
41
|
+
|
|
42
|
+
await user.click(toggle);
|
|
43
|
+
|
|
44
|
+
expect(screen.getByRole("button", { name: "Collapse src" })).toBeInTheDocument();
|
|
45
|
+
expect(screen.queryByRole("button", { name: "Expand src" })).not.toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
28
49
|
describe("FileTree variant axis (#193, research 04 §4 ASSET-1)", () => {
|
|
29
50
|
it("keeps the IDE look by default (code variant — non-breaking)", () => {
|
|
30
51
|
const { container } = render(
|
package/src/file-tree.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CollapsibleContent,
|
|
6
6
|
CollapsibleTrigger,
|
|
7
7
|
fileIconFor,
|
|
8
|
+
useLocale,
|
|
8
9
|
} from "@elabs-ai/components-ui";
|
|
9
10
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
10
11
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
@@ -50,7 +51,7 @@ const FileTreeContext = createContext<FileTreeContextType>({
|
|
|
50
51
|
const fileTreeVariants = cva("", {
|
|
51
52
|
variants: {
|
|
52
53
|
variant: {
|
|
53
|
-
code: "rounded-lg border bg-background font-mono text-
|
|
54
|
+
code: "rounded-lg border bg-background font-mono text-body",
|
|
54
55
|
document: "text-body",
|
|
55
56
|
},
|
|
56
57
|
},
|
|
@@ -158,6 +159,7 @@ export const FileTreeFolder = ({
|
|
|
158
159
|
children,
|
|
159
160
|
...props
|
|
160
161
|
}: FileTreeFolderProps) => {
|
|
162
|
+
const { t } = useLocale();
|
|
161
163
|
const { expandedPaths, togglePath, selectedPath, onSelect } = useContext(FileTreeContext);
|
|
162
164
|
const isExpanded = expandedPaths.has(path);
|
|
163
165
|
const isSelected = selectedPath === path;
|
|
@@ -184,10 +186,15 @@ export const FileTreeFolder = ({
|
|
|
184
186
|
>
|
|
185
187
|
<CollapsibleTrigger asChild>
|
|
186
188
|
<button
|
|
189
|
+
aria-label={t(
|
|
190
|
+
isExpanded ? "ai.fileTree.collapseFolder" : "ai.fileTree.expandFolder",
|
|
191
|
+
{ name },
|
|
192
|
+
)}
|
|
187
193
|
className="flex shrink-0 cursor-pointer items-center border-none bg-transparent p-0"
|
|
188
194
|
type="button"
|
|
189
195
|
>
|
|
190
196
|
<ChevronRightIcon
|
|
197
|
+
aria-hidden="true"
|
|
191
198
|
className={cn(
|
|
192
199
|
"size-4 shrink-0 text-muted-foreground transition-transform",
|
|
193
200
|
isExpanded && "rotate-90",
|
|
@@ -353,6 +360,7 @@ export type ProducedAssetTreeProps = Omit<
|
|
|
353
360
|
*/
|
|
354
361
|
export const ProducedAssetTree = forwardRef<HTMLDivElement, ProducedAssetTreeProps>(
|
|
355
362
|
function ProducedAssetTree({ assets, selectedId, onSelect, ...props }, ref) {
|
|
363
|
+
const { t } = useLocale();
|
|
356
364
|
const assetsById = useMemo(() => new Map(assets.map((asset) => [asset.id, asset])), [assets]);
|
|
357
365
|
const handleSelect = useCallback(
|
|
358
366
|
(path: string) => {
|
|
@@ -361,6 +369,19 @@ export const ProducedAssetTree = forwardRef<HTMLDivElement, ProducedAssetTreePro
|
|
|
361
369
|
},
|
|
362
370
|
[assetsById, onSelect],
|
|
363
371
|
);
|
|
372
|
+
// Empty: a plain note, not an empty `role="tree"` — a tree with no
|
|
373
|
+
// `treeitem` fails `aria-required-children` and announces nothing useful.
|
|
374
|
+
if (assets.length === 0) {
|
|
375
|
+
return (
|
|
376
|
+
<div
|
|
377
|
+
ref={ref}
|
|
378
|
+
data-slot="file-tree-empty"
|
|
379
|
+
className={cn("px-2 py-1 text-body text-muted-foreground", props.className)}
|
|
380
|
+
>
|
|
381
|
+
{t("ai.fileTree.noAssetsProduced")}
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
364
385
|
return (
|
|
365
386
|
<FileTree
|
|
366
387
|
ref={ref}
|
|
@@ -369,21 +390,17 @@ export const ProducedAssetTree = forwardRef<HTMLDivElement, ProducedAssetTreePro
|
|
|
369
390
|
onSelect={handleSelect}
|
|
370
391
|
{...props}
|
|
371
392
|
>
|
|
372
|
-
{assets.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
</FileTreeFile>
|
|
384
|
-
);
|
|
385
|
-
})
|
|
386
|
-
)}
|
|
393
|
+
{assets.map((asset) => {
|
|
394
|
+
const Icon = assetIcon(asset);
|
|
395
|
+
return (
|
|
396
|
+
<FileTreeFile key={asset.id} path={asset.id} name={asset.name}>
|
|
397
|
+
<FileTreeIcon>
|
|
398
|
+
<Icon aria-hidden="true" className="size-4 text-muted-foreground" />
|
|
399
|
+
</FileTreeIcon>
|
|
400
|
+
<FileTreeName>{asset.name}</FileTreeName>
|
|
401
|
+
</FileTreeFile>
|
|
402
|
+
);
|
|
403
|
+
})}
|
|
387
404
|
</FileTree>
|
|
388
405
|
);
|
|
389
406
|
},
|
package/src/gallery.stories.tsx
CHANGED
|
@@ -180,3 +180,45 @@ export const PartialSet: Story = {
|
|
|
180
180
|
await expect(canvas.queryByText(/more/i)).not.toBeInTheDocument();
|
|
181
181
|
},
|
|
182
182
|
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Focus indicator on image thumbnails (#313): Tab to an image button and
|
|
186
|
+
* assert the compound indicator's OUTLINE layer — `outlineStyle: "solid"`
|
|
187
|
+
* sourced from `--ring-contour`. The pre-#67 `focus-visible:outline-none
|
|
188
|
+
* focus-visible:ring-2 focus-visible:ring-ring` pattern also paints a
|
|
189
|
+
* non-"none" `boxShadow` (Tailwind's `ring-2` box-shadow trick), so asserting
|
|
190
|
+
* boxShadow alone passes on the legacy class string too; the outline layer is
|
|
191
|
+
* the one thing only `focus-ring`/`focus-ring-inset` add.
|
|
192
|
+
*/
|
|
193
|
+
export const FocusIndicator: Story = {
|
|
194
|
+
name: "Keyboard focus",
|
|
195
|
+
args: { images: IMAGES.slice(0, 4) },
|
|
196
|
+
play: async ({ canvas, userEvent }) => {
|
|
197
|
+
const buttons = canvas.getAllByRole("button", { name: /fjord|desert/i });
|
|
198
|
+
await userEvent.tab();
|
|
199
|
+
await expect(buttons[0]).toHaveFocus();
|
|
200
|
+
const focused = getComputedStyle(buttons[0]);
|
|
201
|
+
await expect(focused.boxShadow).not.toBe("none");
|
|
202
|
+
await expect(focused.outlineStyle).toBe("solid");
|
|
203
|
+
await expect(parseFloat(focused.outlineWidth)).toBeGreaterThan(0);
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Focus indicator on the overflow ("+N more") tile (#313): a separate DOM
|
|
209
|
+
* node from the plain thumbnails, styled the same way — never covered by any
|
|
210
|
+
* prior test (`gallery.tsx:409`, `GalleryOverflowTile`).
|
|
211
|
+
*/
|
|
212
|
+
export const OverflowFocusIndicator: Story = {
|
|
213
|
+
name: "Overflow tile keyboard focus",
|
|
214
|
+
args: { images: IMAGES },
|
|
215
|
+
play: async ({ canvas, userEvent }) => {
|
|
216
|
+
const overflow = canvas.getByRole("button", { name: /show all 12 images/i });
|
|
217
|
+
overflow.focus();
|
|
218
|
+
await expect(overflow).toHaveFocus();
|
|
219
|
+
const focused = getComputedStyle(overflow);
|
|
220
|
+
await expect(focused.boxShadow).not.toBe("none");
|
|
221
|
+
await expect(focused.outlineStyle).toBe("solid");
|
|
222
|
+
await expect(parseFloat(focused.outlineWidth)).toBeGreaterThan(0);
|
|
223
|
+
},
|
|
224
|
+
};
|
package/src/gallery.tsx
CHANGED
|
@@ -372,7 +372,7 @@ function GalleryTile({ image, index }: { image: GalleryImage; index: number }) {
|
|
|
372
372
|
type="button"
|
|
373
373
|
onClick={() => actions.open(index)}
|
|
374
374
|
aria-label={label}
|
|
375
|
-
className="block w-full rounded-lg focus-
|
|
375
|
+
className="block w-full rounded-lg focus-ring"
|
|
376
376
|
>
|
|
377
377
|
<AspectRatio ratio={aspectRatio} className="overflow-hidden rounded-lg">
|
|
378
378
|
{/* Decorative: the button carries the accessible name. */}
|
|
@@ -406,7 +406,7 @@ function GalleryOverflowTile() {
|
|
|
406
406
|
type="button"
|
|
407
407
|
onClick={() => actions.open(maxVisible)}
|
|
408
408
|
aria-label={`Show all ${total} images`}
|
|
409
|
-
className="block w-full rounded-lg border border-border bg-muted focus-
|
|
409
|
+
className="block w-full rounded-lg border border-border bg-muted focus-ring"
|
|
410
410
|
>
|
|
411
411
|
<AspectRatio ratio={aspectRatio} className="overflow-hidden rounded-lg">
|
|
412
412
|
<span className="flex size-full items-center justify-center text-subtitle text-muted-foreground transition-colors duration-fast ease-standard group-hover:text-foreground motion-reduce:transition-none">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { expect, userEvent } from "storybook/test";
|
|
2
3
|
import { GroupedParts } from "./grouped-parts";
|
|
3
4
|
import { groupPartByType, type GroupablePart } from "./part-groups";
|
|
4
5
|
|
|
@@ -102,3 +103,25 @@ export const DarkTheme: Story = {
|
|
|
102
103
|
),
|
|
103
104
|
],
|
|
104
105
|
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Focus indicator on group triggers (#313): Tab to a collapsible trigger and
|
|
109
|
+
* assert the compound indicator's OUTLINE layer (`outlineStyle: "solid"`,
|
|
110
|
+
* `--ring-contour`) that `focus-ring-inset` adds. `boxShadow !== "none"`
|
|
111
|
+
* alone also passes on the legacy `focus-visible:outline-none
|
|
112
|
+
* focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset`
|
|
113
|
+
* pattern (Tailwind's `ring-2` still paints a box-shadow), so it does not
|
|
114
|
+
* lock the fix.
|
|
115
|
+
*/
|
|
116
|
+
export const FocusIndicator: Story = {
|
|
117
|
+
args: { parts },
|
|
118
|
+
play: async ({ canvas, userEvent }) => {
|
|
119
|
+
const triggers = canvas.getAllByRole("button");
|
|
120
|
+
await userEvent.tab();
|
|
121
|
+
await expect(triggers[0]).toHaveFocus();
|
|
122
|
+
const focused = getComputedStyle(triggers[0]);
|
|
123
|
+
await expect(focused.boxShadow).not.toBe("none");
|
|
124
|
+
await expect(focused.outlineStyle).toBe("solid");
|
|
125
|
+
await expect(parseFloat(focused.outlineWidth)).toBeGreaterThan(0);
|
|
126
|
+
},
|
|
127
|
+
};
|
package/src/grouped-parts.tsx
CHANGED
|
@@ -87,7 +87,7 @@ function DefaultGroup({
|
|
|
87
87
|
className={cn(
|
|
88
88
|
"group flex w-full items-center gap-2 p-3 text-body text-muted-foreground",
|
|
89
89
|
"transition-colors duration-fast ease-standard hover:text-foreground motion-reduce:transition-none",
|
|
90
|
-
"focus-
|
|
90
|
+
"focus-ring-inset",
|
|
91
91
|
)}
|
|
92
92
|
>
|
|
93
93
|
<BrainIcon aria-hidden="true" className="size-4 shrink-0" />
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ export * from "./gallery";
|
|
|
47
47
|
export * from "./grouped-parts";
|
|
48
48
|
export * from "./image";
|
|
49
49
|
export * from "./inline-citation";
|
|
50
|
+
export * from "./a2ui";
|
|
50
51
|
export * from "./jsx-preview";
|
|
51
52
|
// Mermaid loads on first diagram render, not in the entry chunk. `preloadMermaid`
|
|
52
53
|
// warms it early when a surface is known to render diagrams.
|
package/src/inline-citation.tsx
CHANGED
|
@@ -208,7 +208,7 @@ export const InlineCitationCarouselIndex = ({
|
|
|
208
208
|
return (
|
|
209
209
|
<div
|
|
210
210
|
className={cn(
|
|
211
|
-
"flex flex-1 items-center justify-end px-3 py-1 text-muted-foreground text-
|
|
211
|
+
"flex flex-1 items-center justify-end px-3 py-1 text-muted-foreground text-meta",
|
|
212
212
|
className,
|
|
213
213
|
)}
|
|
214
214
|
{...props}
|
|
@@ -289,10 +289,10 @@ export const InlineCitationSource = ({
|
|
|
289
289
|
...props
|
|
290
290
|
}: InlineCitationSourceProps) => (
|
|
291
291
|
<div className={cn("space-y-1", className)} {...props}>
|
|
292
|
-
{title && <h4 className="truncate font-medium text-
|
|
293
|
-
{url && <p className="truncate break-all text-muted-foreground text-
|
|
292
|
+
{title && <h4 className="truncate font-medium text-body leading-tight">{title}</h4>}
|
|
293
|
+
{url && <p className="truncate break-all text-muted-foreground text-meta">{url}</p>}
|
|
294
294
|
{description && (
|
|
295
|
-
<p className="line-clamp-3 text-muted-foreground text-
|
|
295
|
+
<p className="line-clamp-3 text-muted-foreground text-body leading-relaxed">{description}</p>
|
|
296
296
|
)}
|
|
297
297
|
{children}
|
|
298
298
|
</div>
|
|
@@ -306,7 +306,7 @@ export const InlineCitationQuote = ({
|
|
|
306
306
|
...props
|
|
307
307
|
}: InlineCitationQuoteProps) => (
|
|
308
308
|
<blockquote
|
|
309
|
-
className={cn("border-muted border-s-2 ps-3 text-muted-foreground text-
|
|
309
|
+
className={cn("border-muted border-s-2 ps-3 text-muted-foreground text-body italic", className)}
|
|
310
310
|
{...props}
|
|
311
311
|
>
|
|
312
312
|
{children}
|