@adecore/agents-react 0.0.1 → 0.17.0-beta.1
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/LICENSE +110 -0
- package/README.md +53 -0
- package/dist/agents/AccountDot.d.ts +4 -0
- package/dist/agents/AccountDot.js +8 -0
- package/dist/agents/AgentIcon.d.ts +6 -0
- package/dist/agents/AgentIcon.js +20 -0
- package/dist/agents/ModelOptionControl.d.ts +15 -0
- package/dist/agents/ModelOptionControl.js +33 -0
- package/dist/agents/ProviderLogo.d.ts +8 -0
- package/dist/agents/ProviderLogo.js +8 -0
- package/dist/agents/account-limits.d.ts +7 -0
- package/dist/agents/account-limits.js +40 -0
- package/dist/agents/accounts.d.ts +22 -0
- package/dist/agents/accounts.js +113 -0
- package/dist/agents/model-name.d.ts +12 -0
- package/dist/agents/model-name.js +56 -0
- package/dist/agents/model-options.d.ts +5 -0
- package/dist/agents/model-options.js +15 -0
- package/dist/agents/provider-paths.d.ts +2 -0
- package/dist/agents/provider-paths.js +8 -0
- package/dist/agents/status-look.d.ts +13 -0
- package/dist/agents/status-look.js +24 -0
- package/dist/chat/account-choice.d.ts +11 -0
- package/dist/chat/account-choice.js +30 -0
- package/dist/chat/actions.d.ts +6 -0
- package/dist/chat/actions.js +68 -0
- package/dist/chat/activity.d.ts +5 -0
- package/dist/chat/activity.js +6 -0
- package/dist/chat/attachments.d.ts +23 -0
- package/dist/chat/attachments.js +86 -0
- package/dist/chat/bookmarks.d.ts +14 -0
- package/dist/chat/bookmarks.js +73 -0
- package/dist/chat/chat-client.d.ts +55 -0
- package/dist/chat/chat-client.js +279 -0
- package/dist/chat/chat-references.d.ts +5 -0
- package/dist/chat/chat-references.js +5 -0
- package/dist/chat/drafts.d.ts +29 -0
- package/dist/chat/drafts.js +116 -0
- package/dist/chat/forks.d.ts +2 -0
- package/dist/chat/forks.js +28 -0
- package/dist/chat/guards.d.ts +12 -0
- package/dist/chat/guards.js +69 -0
- package/dist/chat/logic/ansi.d.ts +13 -0
- package/dist/chat/logic/ansi.js +185 -0
- package/dist/chat/logic/answers.d.ts +1 -0
- package/dist/chat/logic/answers.js +8 -0
- package/dist/chat/logic/bookmarks.d.ts +5 -0
- package/dist/chat/logic/bookmarks.js +33 -0
- package/dist/chat/logic/context-usage.d.ts +12 -0
- package/dist/chat/logic/context-usage.js +21 -0
- package/dist/chat/logic/fork.d.ts +45 -0
- package/dist/chat/logic/fork.js +148 -0
- package/dist/chat/logic/handback.d.ts +2 -0
- package/dist/chat/logic/handback.js +8 -0
- package/dist/chat/logic/json.d.ts +1 -0
- package/dist/chat/logic/json.js +4 -0
- package/dist/chat/logic/limit.d.ts +7 -0
- package/dist/chat/logic/limit.js +34 -0
- package/dist/chat/logic/resume-compaction.d.ts +14 -0
- package/dist/chat/logic/resume-compaction.js +47 -0
- package/dist/chat/logic/scrubber.d.ts +50 -0
- package/dist/chat/logic/scrubber.js +220 -0
- package/dist/chat/logic/thread-cards.d.ts +3 -0
- package/dist/chat/logic/thread-cards.js +59 -0
- package/dist/chat/logic/timeline-copy.d.ts +4 -0
- package/dist/chat/logic/timeline-copy.js +59 -0
- package/dist/chat/logic/timeline-target.d.ts +12 -0
- package/dist/chat/logic/timeline-target.js +34 -0
- package/dist/chat/logic/timeline.d.ts +115 -0
- package/dist/chat/logic/timeline.js +343 -0
- package/dist/chat/logic/tool-catalog.d.ts +10 -0
- package/dist/chat/logic/tool-catalog.js +44 -0
- package/dist/chat/logic/tools.d.ts +15 -0
- package/dist/chat/logic/tools.js +82 -0
- package/dist/chat/logic/welcome.d.ts +2 -0
- package/dist/chat/logic/welcome.js +10 -0
- package/dist/chat/logic/workflow.d.ts +14 -0
- package/dist/chat/logic/workflow.js +47 -0
- package/dist/chat/mentions.d.ts +65 -0
- package/dist/chat/mentions.js +184 -0
- package/dist/chat/persisted-json.d.ts +5 -0
- package/dist/chat/persisted-json.js +58 -0
- package/dist/chat/preferences.d.ts +27 -0
- package/dist/chat/preferences.js +139 -0
- package/dist/chat/quote.d.ts +2 -0
- package/dist/chat/quote.js +25 -0
- package/dist/chat/recent-messages.d.ts +17 -0
- package/dist/chat/recent-messages.js +36 -0
- package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
- package/dist/chat/resume-compaction-dismissals.js +14 -0
- package/dist/chat/runtime-modes.d.ts +4 -0
- package/dist/chat/runtime-modes.js +15 -0
- package/dist/chat/stash.d.ts +24 -0
- package/dist/chat/stash.js +91 -0
- package/dist/chat/subagent-conversation.d.ts +41 -0
- package/dist/chat/subagent-conversation.js +167 -0
- package/dist/chat/subagent-list.d.ts +19 -0
- package/dist/chat/subagent-list.js +106 -0
- package/dist/chat/subagent-support.d.ts +6 -0
- package/dist/chat/subagent-support.js +14 -0
- package/dist/chat/subagent-view.d.ts +35 -0
- package/dist/chat/subagent-view.js +96 -0
- package/dist/chat/thumbnails.d.ts +10 -0
- package/dist/chat/thumbnails.js +89 -0
- package/dist/chat/timeline-flash.d.ts +12 -0
- package/dist/chat/timeline-flash.js +16 -0
- package/dist/chat/timeline-scroll.d.ts +27 -0
- package/dist/chat/timeline-scroll.js +152 -0
- package/dist/chat/ui/AccountPill.d.ts +3 -0
- package/dist/chat/ui/AccountPill.js +23 -0
- package/dist/chat/ui/AnsiOutput.d.ts +5 -0
- package/dist/chat/ui/AnsiOutput.js +9 -0
- package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
- package/dist/chat/ui/BookmarkMarker.js +57 -0
- package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
- package/dist/chat/ui/BookmarkSubmenu.js +33 -0
- package/dist/chat/ui/ChatActivity.d.ts +7 -0
- package/dist/chat/ui/ChatActivity.js +84 -0
- package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
- package/dist/chat/ui/ChatReferenceChip.js +16 -0
- package/dist/chat/ui/CodeBlock.d.ts +4 -0
- package/dist/chat/ui/CodeBlock.js +132 -0
- package/dist/chat/ui/Composer.d.ts +15 -0
- package/dist/chat/ui/Composer.js +856 -0
- package/dist/chat/ui/ComposerInput.d.ts +29 -0
- package/dist/chat/ui/ComposerInput.js +127 -0
- package/dist/chat/ui/DiffPool.d.ts +5 -0
- package/dist/chat/ui/DiffPool.js +26 -0
- package/dist/chat/ui/EditDiff.d.ts +4 -0
- package/dist/chat/ui/EditDiff.js +21 -0
- package/dist/chat/ui/FadingWords.d.ts +3 -0
- package/dist/chat/ui/FadingWords.js +5 -0
- package/dist/chat/ui/ImageView.d.ts +6 -0
- package/dist/chat/ui/ImageView.js +81 -0
- package/dist/chat/ui/LimitState.d.ts +8 -0
- package/dist/chat/ui/LimitState.js +112 -0
- package/dist/chat/ui/Markdown.d.ts +18 -0
- package/dist/chat/ui/Markdown.js +159 -0
- package/dist/chat/ui/MessageActions.d.ts +9 -0
- package/dist/chat/ui/MessageActions.js +53 -0
- package/dist/chat/ui/Pickers.d.ts +21 -0
- package/dist/chat/ui/Pickers.js +111 -0
- package/dist/chat/ui/PromptComposer.d.ts +13 -0
- package/dist/chat/ui/PromptComposer.js +56 -0
- package/dist/chat/ui/QuoteButton.d.ts +7 -0
- package/dist/chat/ui/QuoteButton.js +74 -0
- package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
- package/dist/chat/ui/ResumeCompactionDock.js +14 -0
- package/dist/chat/ui/RunSettings.d.ts +28 -0
- package/dist/chat/ui/RunSettings.js +153 -0
- package/dist/chat/ui/Scrubber.d.ts +17 -0
- package/dist/chat/ui/Scrubber.js +118 -0
- package/dist/chat/ui/SubagentControls.d.ts +11 -0
- package/dist/chat/ui/SubagentControls.js +36 -0
- package/dist/chat/ui/SubagentInfo.d.ts +4 -0
- package/dist/chat/ui/SubagentInfo.js +34 -0
- package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
- package/dist/chat/ui/SubagentStopButton.js +34 -0
- package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
- package/dist/chat/ui/SubagentTimeline.js +107 -0
- package/dist/chat/ui/Timeline.d.ts +6 -0
- package/dist/chat/ui/Timeline.js +424 -0
- package/dist/chat/ui/TimelineMenu.d.ts +12 -0
- package/dist/chat/ui/TimelineMenu.js +54 -0
- package/dist/chat/ui/UnifiedDiff.d.ts +11 -0
- package/dist/chat/ui/UnifiedDiff.js +66 -0
- package/dist/chat/ui/UploadThumb.d.ts +4 -0
- package/dist/chat/ui/UploadThumb.js +11 -0
- package/dist/chat/ui/Welcome.d.ts +4 -0
- package/dist/chat/ui/Welcome.js +16 -0
- package/dist/chat/ui/chips.d.ts +4 -0
- package/dist/chat/ui/chips.js +11 -0
- package/dist/chat/ui/code-lines.d.ts +18 -0
- package/dist/chat/ui/code-lines.js +46 -0
- package/dist/chat/ui/code-streaming.d.ts +1 -0
- package/dist/chat/ui/code-streaming.js +3 -0
- package/dist/chat/ui/code-theme.d.ts +3 -0
- package/dist/chat/ui/code-theme.js +12 -0
- package/dist/chat/ui/composer/chips.d.ts +6 -0
- package/dist/chat/ui/composer/chips.js +31 -0
- package/dist/chat/ui/composer/editor.d.ts +11 -0
- package/dist/chat/ui/composer/editor.js +98 -0
- package/dist/chat/ui/composer/insert.d.ts +2 -0
- package/dist/chat/ui/composer/insert.js +19 -0
- package/dist/chat/ui/composer/keys.d.ts +30 -0
- package/dist/chat/ui/composer/keys.js +100 -0
- package/dist/chat/ui/diff-theme.d.ts +2 -0
- package/dist/chat/ui/diff-theme.js +20 -0
- package/dist/chat/ui/file-links.d.ts +5 -0
- package/dist/chat/ui/file-links.js +20 -0
- package/dist/chat/ui/find-reveal.d.ts +7 -0
- package/dist/chat/ui/find-reveal.js +13 -0
- package/dist/chat/ui/full-diff.d.ts +6 -0
- package/dist/chat/ui/full-diff.js +19 -0
- package/dist/chat/ui/icons.d.ts +4 -0
- package/dist/chat/ui/icons.js +20 -0
- package/dist/chat/ui/markdown-blocks.d.ts +6 -0
- package/dist/chat/ui/markdown-blocks.js +103 -0
- package/dist/chat/ui/quote-selection.d.ts +3 -0
- package/dist/chat/ui/quote-selection.js +46 -0
- package/dist/chat/ui/rehype-chips.d.ts +23 -0
- package/dist/chat/ui/rehype-chips.js +32 -0
- package/dist/chat/ui/rehype-fade.d.ts +23 -0
- package/dist/chat/ui/rehype-fade.js +36 -0
- package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
- package/dist/chat/ui/remark-html-as-text.js +26 -0
- package/dist/chat/ui/reply-context.d.ts +5 -0
- package/dist/chat/ui/reply-context.js +2 -0
- package/dist/chat/ui/reveal.d.ts +13 -0
- package/dist/chat/ui/reveal.js +114 -0
- package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
- package/dist/chat/ui/rows/ForksRow.js +38 -0
- package/dist/chat/ui/rows/MessageRows.d.ts +40 -0
- package/dist/chat/ui/rows/MessageRows.js +204 -0
- package/dist/chat/ui/rows/Rows.d.ts +12 -0
- package/dist/chat/ui/rows/Rows.js +61 -0
- package/dist/chat/ui/rows/SubagentRow.d.ts +17 -0
- package/dist/chat/ui/rows/SubagentRow.js +101 -0
- package/dist/chat/ui/rows/TaskRow.d.ts +8 -0
- package/dist/chat/ui/rows/TaskRow.js +63 -0
- package/dist/chat/ui/rows/WorkRows.d.ts +47 -0
- package/dist/chat/ui/rows/WorkRows.js +168 -0
- package/dist/chat/ui/rows/WorkflowRow.d.ts +8 -0
- package/dist/chat/ui/rows/WorkflowRow.js +48 -0
- package/dist/chat/ui/rows/row-rhythm.d.ts +6 -0
- package/dist/chat/ui/rows/row-rhythm.js +34 -0
- package/dist/chat/ui/useToggleSet.d.ts +6 -0
- package/dist/chat/ui/useToggleSet.js +25 -0
- package/dist/host.d.ts +195 -0
- package/dist/host.js +55 -0
- package/dist/lazy.d.ts +6 -0
- package/dist/lazy.js +57 -0
- package/dist/locales/en/agent-chat.json +446 -0
- package/dist/locales/en/agent-prompts.json +42 -0
- package/dist/locales/en/agent-providers.json +131 -0
- package/dist/locales/en/agent-usage.json +131 -0
- package/dist/locales/nl/agent-chat.json +446 -0
- package/dist/locales/nl/agent-prompts.json +42 -0
- package/dist/locales/nl/agent-providers.json +131 -0
- package/dist/locales/nl/agent-usage.json +131 -0
- package/dist/locales.d.ts +5 -0
- package/dist/locales.js +20 -0
- package/dist/mounted-registry.d.ts +7 -0
- package/dist/mounted-registry.js +39 -0
- package/dist/port-transport.d.ts +6 -0
- package/dist/port-transport.js +129 -0
- package/dist/prompts/logic/focus.d.ts +2 -0
- package/dist/prompts/logic/focus.js +23 -0
- package/dist/prompts/logic/keys.d.ts +42 -0
- package/dist/prompts/logic/keys.js +94 -0
- package/dist/prompts/logic/prompts.d.ts +34 -0
- package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
- package/dist/prompts/logic/prompts.fixtures.js +79 -0
- package/dist/prompts/logic/prompts.js +32 -0
- package/dist/prompts/logic/subjects.d.ts +50 -0
- package/dist/prompts/logic/subjects.js +63 -0
- package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
- package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
- package/dist/prompts/logic/usePromptSession.d.ts +24 -0
- package/dist/prompts/logic/usePromptSession.js +87 -0
- package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
- package/dist/prompts/ui/ApprovalBody.js +34 -0
- package/dist/prompts/ui/PromptActions.d.ts +27 -0
- package/dist/prompts/ui/PromptActions.js +18 -0
- package/dist/prompts/ui/PromptCard.d.ts +16 -0
- package/dist/prompts/ui/PromptCard.js +50 -0
- package/dist/prompts/ui/PromptView.d.ts +2 -0
- package/dist/prompts/ui/PromptView.js +47 -0
- package/dist/prompts/ui/QuestionBody.d.ts +9 -0
- package/dist/prompts/ui/QuestionBody.js +87 -0
- package/dist/providers/AccountColors.d.ts +5 -0
- package/dist/providers/AccountColors.js +8 -0
- package/dist/providers/AccountDetail.d.ts +12 -0
- package/dist/providers/AccountDetail.js +69 -0
- package/dist/providers/AccountVariables.d.ts +8 -0
- package/dist/providers/AccountVariables.js +43 -0
- package/dist/providers/AddAccountForm.d.ts +10 -0
- package/dist/providers/AddAccountForm.js +54 -0
- package/dist/providers/CliDetail.d.ts +12 -0
- package/dist/providers/CliDetail.js +54 -0
- package/dist/providers/ProvidersPane.d.ts +9 -0
- package/dist/providers/ProvidersPane.js +86 -0
- package/dist/providers/account-actions.d.ts +7 -0
- package/dist/providers/account-actions.js +57 -0
- package/dist/providers/parts.d.ts +9 -0
- package/dist/providers/parts.js +18 -0
- package/dist/providers/provider-abilities.d.ts +2 -0
- package/dist/providers/provider-abilities.js +7 -0
- package/dist/providers/variables.d.ts +13 -0
- package/dist/providers/variables.js +63 -0
- package/dist/scope.d.ts +11 -0
- package/dist/scope.js +10 -0
- package/dist/settings/sections.d.ts +13 -0
- package/dist/settings/sections.js +32 -0
- package/dist/state/chats.d.ts +44 -0
- package/dist/state/chats.js +199 -0
- package/dist/state/provider-accounts.d.ts +17 -0
- package/dist/state/provider-accounts.js +67 -0
- package/dist/state/providers.d.ts +17 -0
- package/dist/state/providers.js +33 -0
- package/dist/state/usage.d.ts +44 -0
- package/dist/state/usage.js +121 -0
- package/dist/storage.d.ts +20 -0
- package/dist/storage.js +89 -0
- package/dist/theme.css +196 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.js +18 -0
- package/dist/ui/PlainTextarea.d.ts +2 -0
- package/dist/ui/PlainTextarea.js +5 -0
- package/dist/usage/LimitsList.d.ts +18 -0
- package/dist/usage/LimitsList.js +73 -0
- package/dist/usage/UsageBreakdown.d.ts +9 -0
- package/dist/usage/UsageBreakdown.js +67 -0
- package/dist/usage/UsageChart.d.ts +10 -0
- package/dist/usage/UsageChart.js +47 -0
- package/dist/usage/UsageDialog.d.ts +6 -0
- package/dist/usage/UsageDialog.js +12 -0
- package/dist/usage/UsageLimits.d.ts +1 -0
- package/dist/usage/UsageLimits.js +57 -0
- package/dist/usage/UsageLimitsCard.d.ts +4 -0
- package/dist/usage/UsageLimitsCard.js +26 -0
- package/dist/usage/UsagePage.d.ts +7 -0
- package/dist/usage/UsagePage.js +154 -0
- package/dist/usage/UsagePane.d.ts +3 -0
- package/dist/usage/UsagePane.js +21 -0
- package/dist/usage/UsageSummary.d.ts +12 -0
- package/dist/usage/UsageSummary.js +15 -0
- package/dist/usage/UsageTiles.d.ts +7 -0
- package/dist/usage/UsageTiles.js +19 -0
- package/dist/usage/format.d.ts +13 -0
- package/dist/usage/format.js +55 -0
- package/dist/usage/limit-groups.d.ts +22 -0
- package/dist/usage/limit-groups.js +86 -0
- package/dist/usage/limits.d.ts +4 -0
- package/dist/usage/limits.js +58 -0
- package/dist/usage/money.d.ts +1 -0
- package/dist/usage/money.js +13 -0
- package/dist/usage/summary.d.ts +34 -0
- package/dist/usage/summary.js +94 -0
- package/package.json +895 -3
- package/src/agents/AccountDot.tsx +14 -0
- package/src/agents/AgentIcon.tsx +26 -0
- package/src/agents/ModelOptionControl.tsx +68 -0
- package/src/agents/ProviderLogo.tsx +20 -0
- package/src/agents/account-limits.ts +61 -0
- package/src/agents/accounts.ts +137 -0
- package/src/agents/model-name.ts +70 -0
- package/src/agents/model-options.ts +21 -0
- package/src/agents/provider-paths.ts +10 -0
- package/src/agents/status-look.ts +43 -0
- package/src/chat/account-choice.ts +44 -0
- package/src/chat/actions.ts +72 -0
- package/src/chat/activity.ts +8 -0
- package/src/chat/attachments.ts +118 -0
- package/src/chat/bookmarks.ts +87 -0
- package/src/chat/chat-client.ts +345 -0
- package/src/chat/chat-references.ts +11 -0
- package/src/chat/drafts.ts +165 -0
- package/src/chat/forks.ts +33 -0
- package/src/chat/guards.ts +84 -0
- package/src/chat/logic/ansi.ts +206 -0
- package/src/chat/logic/answers.ts +8 -0
- package/src/chat/logic/bookmarks.ts +42 -0
- package/src/chat/logic/context-usage.ts +36 -0
- package/src/chat/logic/fork.ts +200 -0
- package/src/chat/logic/handback.ts +10 -0
- package/src/chat/logic/json.ts +4 -0
- package/src/chat/logic/limit.ts +46 -0
- package/src/chat/logic/resume-compaction.ts +71 -0
- package/src/chat/logic/scrubber.ts +289 -0
- package/src/chat/logic/thread-cards.ts +64 -0
- package/src/chat/logic/timeline-copy.ts +65 -0
- package/src/chat/logic/timeline-target.ts +52 -0
- package/src/chat/logic/timeline.ts +419 -0
- package/src/chat/logic/tool-catalog.ts +62 -0
- package/src/chat/logic/tools.ts +102 -0
- package/src/chat/logic/welcome.ts +13 -0
- package/src/chat/logic/workflow.ts +66 -0
- package/src/chat/mentions.ts +232 -0
- package/src/chat/persisted-json.ts +67 -0
- package/src/chat/preferences.ts +179 -0
- package/src/chat/quote.ts +26 -0
- package/src/chat/recent-messages.ts +59 -0
- package/src/chat/resume-compaction-dismissals.ts +17 -0
- package/src/chat/runtime-modes.ts +18 -0
- package/src/chat/stash.ts +122 -0
- package/src/chat/subagent-conversation.ts +203 -0
- package/src/chat/subagent-list.ts +141 -0
- package/src/chat/subagent-support.ts +21 -0
- package/src/chat/subagent-view.ts +137 -0
- package/src/chat/thumbnails.ts +108 -0
- package/src/chat/timeline-flash.ts +33 -0
- package/src/chat/timeline-scroll.ts +199 -0
- package/src/chat/ui/AccountPill.tsx +31 -0
- package/src/chat/ui/AnsiOutput.tsx +18 -0
- package/src/chat/ui/BookmarkMarker.tsx +108 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +53 -0
- package/src/chat/ui/ChatActivity.tsx +198 -0
- package/src/chat/ui/ChatReferenceChip.tsx +28 -0
- package/src/chat/ui/CodeBlock.tsx +175 -0
- package/src/chat/ui/Composer.tsx +1326 -0
- package/src/chat/ui/ComposerInput.tsx +187 -0
- package/src/chat/ui/DiffPool.tsx +36 -0
- package/src/chat/ui/EditDiff.tsx +30 -0
- package/src/chat/ui/FadingWords.tsx +18 -0
- package/src/chat/ui/ImageView.tsx +159 -0
- package/src/chat/ui/LimitState.tsx +165 -0
- package/src/chat/ui/Markdown.tsx +258 -0
- package/src/chat/ui/MessageActions.tsx +88 -0
- package/src/chat/ui/Pickers.tsx +272 -0
- package/src/chat/ui/PromptComposer.tsx +119 -0
- package/src/chat/ui/QuoteButton.tsx +103 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +28 -0
- package/src/chat/ui/RunSettings.tsx +546 -0
- package/src/chat/ui/Scrubber.tsx +265 -0
- package/src/chat/ui/SubagentControls.tsx +66 -0
- package/src/chat/ui/SubagentInfo.tsx +67 -0
- package/src/chat/ui/SubagentStopButton.tsx +45 -0
- package/src/chat/ui/SubagentTimeline.tsx +170 -0
- package/src/chat/ui/Timeline.tsx +628 -0
- package/src/chat/ui/TimelineMenu.tsx +124 -0
- package/src/chat/ui/UnifiedDiff.tsx +99 -0
- package/src/chat/ui/UploadThumb.tsx +13 -0
- package/src/chat/ui/Welcome.tsx +22 -0
- package/src/chat/ui/chips.ts +15 -0
- package/src/chat/ui/code-lines.ts +60 -0
- package/src/chat/ui/code-streaming.ts +4 -0
- package/src/chat/ui/code-theme.ts +15 -0
- package/src/chat/ui/composer/chips.ts +43 -0
- package/src/chat/ui/composer/editor.ts +118 -0
- package/src/chat/ui/composer/insert.ts +22 -0
- package/src/chat/ui/composer/keys.ts +144 -0
- package/src/chat/ui/diff-theme.ts +23 -0
- package/src/chat/ui/file-links.ts +24 -0
- package/src/chat/ui/find-reveal.ts +23 -0
- package/src/chat/ui/full-diff.ts +25 -0
- package/src/chat/ui/icons.tsx +24 -0
- package/src/chat/ui/markdown-blocks.ts +123 -0
- package/src/chat/ui/quote-selection.ts +53 -0
- package/src/chat/ui/rehype-chips.ts +62 -0
- package/src/chat/ui/rehype-fade.ts +64 -0
- package/src/chat/ui/remark-html-as-text.ts +36 -0
- package/src/chat/ui/reply-context.ts +4 -0
- package/src/chat/ui/reveal.ts +135 -0
- package/src/chat/ui/rows/ForksRow.tsx +79 -0
- package/src/chat/ui/rows/MessageRows.tsx +415 -0
- package/src/chat/ui/rows/Rows.tsx +81 -0
- package/src/chat/ui/rows/SubagentRow.tsx +209 -0
- package/src/chat/ui/rows/TaskRow.tsx +91 -0
- package/src/chat/ui/rows/WorkRows.tsx +445 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +120 -0
- package/src/chat/ui/rows/row-rhythm.ts +37 -0
- package/src/chat/ui/useToggleSet.ts +39 -0
- package/src/host.ts +324 -0
- package/src/lazy.tsx +64 -0
- package/src/locales/en/agent-chat.json +446 -0
- package/src/locales/en/agent-prompts.json +42 -0
- package/src/locales/en/agent-providers.json +131 -0
- package/src/locales/en/agent-usage.json +131 -0
- package/src/locales/nl/agent-chat.json +446 -0
- package/src/locales/nl/agent-prompts.json +42 -0
- package/src/locales/nl/agent-providers.json +131 -0
- package/src/locales/nl/agent-usage.json +131 -0
- package/src/locales.ts +33 -0
- package/src/mounted-registry.ts +45 -0
- package/src/port-transport.ts +146 -0
- package/src/prompts/logic/focus.ts +25 -0
- package/src/prompts/logic/keys.ts +116 -0
- package/src/prompts/logic/prompts.fixtures.ts +84 -0
- package/src/prompts/logic/prompts.ts +58 -0
- package/src/prompts/logic/subjects.ts +122 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +38 -0
- package/src/prompts/logic/usePromptSession.ts +114 -0
- package/src/prompts/ui/ApprovalBody.tsx +109 -0
- package/src/prompts/ui/PromptActions.tsx +109 -0
- package/src/prompts/ui/PromptCard.tsx +101 -0
- package/src/prompts/ui/PromptView.tsx +111 -0
- package/src/prompts/ui/QuestionBody.tsx +174 -0
- package/src/providers/AccountColors.tsx +8 -0
- package/src/providers/AccountDetail.tsx +209 -0
- package/src/providers/AccountVariables.tsx +126 -0
- package/src/providers/AddAccountForm.tsx +121 -0
- package/src/providers/CliDetail.tsx +157 -0
- package/src/providers/ProvidersPane.tsx +236 -0
- package/src/providers/account-actions.ts +67 -0
- package/src/providers/parts.tsx +25 -0
- package/src/providers/provider-abilities.ts +9 -0
- package/src/providers/variables.ts +87 -0
- package/src/scope.ts +30 -0
- package/src/settings/sections.ts +47 -0
- package/src/state/chats.ts +279 -0
- package/src/state/provider-accounts.ts +87 -0
- package/src/state/providers.ts +50 -0
- package/src/state/usage.ts +196 -0
- package/src/storage.ts +113 -0
- package/src/theme.css +196 -0
- package/src/transport.ts +50 -0
- package/src/ui/PlainTextarea.tsx +6 -0
- package/src/usage/LimitsList.tsx +232 -0
- package/src/usage/UsageBreakdown.tsx +210 -0
- package/src/usage/UsageChart.tsx +113 -0
- package/src/usage/UsageDialog.tsx +20 -0
- package/src/usage/UsageLimits.tsx +145 -0
- package/src/usage/UsageLimitsCard.tsx +36 -0
- package/src/usage/UsagePage.tsx +276 -0
- package/src/usage/UsagePane.tsx +52 -0
- package/src/usage/UsageSummary.tsx +50 -0
- package/src/usage/UsageTiles.tsx +34 -0
- package/src/usage/format.ts +71 -0
- package/src/usage/limit-groups.ts +119 -0
- package/src/usage/limits.ts +71 -0
- package/src/usage/money.ts +14 -0
- package/src/usage/summary.ts +137 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/* How long a word takes to fade in. `.chat-fade` in styles.css carries the same number. */
|
|
4
|
+
export const REVEAL_FADE_MS = 300;
|
|
5
|
+
|
|
6
|
+
/* The time constant of the catch-up: after this long about two thirds of what was behind is on screen. */
|
|
7
|
+
export const REVEAL_TAU_MS = 250;
|
|
8
|
+
|
|
9
|
+
// Without a floor the last few characters of a gap crawl, since the exponential step shrinks with the gap.
|
|
10
|
+
export const REVEAL_MIN_CPS = 40;
|
|
11
|
+
|
|
12
|
+
// Once the item is done nothing more arrives to wait for, so the tail is closed faster.
|
|
13
|
+
export const REVEAL_FINISH_MIN_CPS = 120;
|
|
14
|
+
|
|
15
|
+
// A tab that comes back hands one frame the whole time it was away; it would show everything at once.
|
|
16
|
+
export const REVEAL_MAX_FRAME_MS = 100;
|
|
17
|
+
|
|
18
|
+
// A word this long (a URL, a line of CJK) would otherwise hold everything behind it until it ends.
|
|
19
|
+
export const REVEAL_MAX_HELD_WORD = 32;
|
|
20
|
+
|
|
21
|
+
// The span mounts a commit after the frame that grew the text, so its fade ends a little later than that frame plus the duration.
|
|
22
|
+
const FADE_SETTLE_MS = REVEAL_FADE_MS + 50;
|
|
23
|
+
|
|
24
|
+
function isSpace(text: string, index: number): boolean {
|
|
25
|
+
return index >= 0 && index < text.length && /\s/.test(text[index]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* Where the reveal stands `elapsedMs` after it stood at `position`, with `target` characters
|
|
30
|
+
* received. The exponential part composes over frames, so the pace does not depend on the refresh
|
|
31
|
+
* rate of the screen.
|
|
32
|
+
*/
|
|
33
|
+
export function advanceReveal(position: number, target: number, elapsedMs: number, finished: boolean): number {
|
|
34
|
+
if (position >= target) {
|
|
35
|
+
return target;
|
|
36
|
+
}
|
|
37
|
+
const elapsed = Math.min(Math.max(elapsedMs, 0), REVEAL_MAX_FRAME_MS);
|
|
38
|
+
const eased = (target - position) * (1 - Math.exp(-elapsed / REVEAL_TAU_MS));
|
|
39
|
+
const floor = ((finished ? REVEAL_FINISH_MIN_CPS : REVEAL_MIN_CPS) * elapsed) / 1000;
|
|
40
|
+
return Math.min(target, position + Math.max(eased, floor));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* How much of `text` to draw for a reveal at `position`: back to the last word boundary, because a
|
|
45
|
+
* span that starts with half a word shows the rest of its letters at whatever opacity it already
|
|
46
|
+
* reached. The last word of a text that is still arriving may still grow, so it waits for the
|
|
47
|
+
* whitespace after it.
|
|
48
|
+
*/
|
|
49
|
+
export function revealBoundary(text: string, position: number, complete: boolean): number {
|
|
50
|
+
const end = Math.min(text.length, Math.max(0, Math.floor(position)));
|
|
51
|
+
if (end === text.length && complete) {
|
|
52
|
+
return end;
|
|
53
|
+
}
|
|
54
|
+
let cut = end;
|
|
55
|
+
while (cut > 0 && !isSpace(text, cut) && !isSpace(text, cut - 1)) {
|
|
56
|
+
cut -= 1;
|
|
57
|
+
}
|
|
58
|
+
if (end - cut <= REVEAL_MAX_HELD_WORD) {
|
|
59
|
+
return cut;
|
|
60
|
+
}
|
|
61
|
+
// Half of a surrogate pair renders as a replacement box.
|
|
62
|
+
const last = text.charCodeAt(end - 1);
|
|
63
|
+
return last >= 0xd800 && last <= 0xdbff ? end - 1 : end;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface RevealedText {
|
|
67
|
+
text: string;
|
|
68
|
+
/* True until the reveal caught up with a finished text and its last fade is over; spans are needed until then. */
|
|
69
|
+
active: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* The part of a streaming text that is on screen, catching up with what arrived over time. It starts
|
|
74
|
+
* at whatever was there on mount, so reopening a thread mid-reply does not replay it, and a text that
|
|
75
|
+
* was already done on mount is shown whole at once.
|
|
76
|
+
*/
|
|
77
|
+
export function useRevealedText(text: string, streaming: boolean): RevealedText {
|
|
78
|
+
const [active, setActive] = useState(streaming);
|
|
79
|
+
const [shown, setShown] = useState(() => revealBoundary(text, text.length, !streaming));
|
|
80
|
+
const latest = useRef({ text, streaming });
|
|
81
|
+
const position = useRef(text.length);
|
|
82
|
+
const shownRef = useRef(shown);
|
|
83
|
+
const activeRef = useRef(active);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
latest.current = { text, streaming };
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Turning streaming on for a text that stood still (the setting flipped mid-reply) starts from what is there.
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!streaming || activeRef.current) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
position.current = latest.current.text.length;
|
|
95
|
+
shownRef.current = revealBoundary(latest.current.text, position.current, false);
|
|
96
|
+
activeRef.current = true;
|
|
97
|
+
setShown(shownRef.current);
|
|
98
|
+
setActive(true);
|
|
99
|
+
}, [streaming]);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!active) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
let frame = 0;
|
|
106
|
+
let previous: number | null = null;
|
|
107
|
+
let grewAt = -Infinity;
|
|
108
|
+
const tick = (now: number) => {
|
|
109
|
+
const elapsed = previous === null ? 0 : now - previous;
|
|
110
|
+
previous = now;
|
|
111
|
+
const current = latest.current;
|
|
112
|
+
const finished = !current.streaming;
|
|
113
|
+
position.current = advanceReveal(position.current, current.text.length, elapsed, finished);
|
|
114
|
+
const length = revealBoundary(current.text, position.current, finished);
|
|
115
|
+
if (length !== shownRef.current) {
|
|
116
|
+
shownRef.current = length;
|
|
117
|
+
grewAt = now;
|
|
118
|
+
setShown(length);
|
|
119
|
+
}
|
|
120
|
+
if (finished && length >= current.text.length && now - grewAt >= FADE_SETTLE_MS) {
|
|
121
|
+
activeRef.current = false;
|
|
122
|
+
setActive(false);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
frame = requestAnimationFrame(tick);
|
|
126
|
+
};
|
|
127
|
+
frame = requestAnimationFrame(tick);
|
|
128
|
+
return () => cancelAnimationFrame(frame);
|
|
129
|
+
}, [active]);
|
|
130
|
+
|
|
131
|
+
if (!active) {
|
|
132
|
+
return { text, active: false };
|
|
133
|
+
}
|
|
134
|
+
return { text: text.slice(0, Math.min(shown, text.length)), active: true };
|
|
135
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { ChevronRight, GitFork } from 'lucide-react';
|
|
3
|
+
import { useForkIdsAfter } from '../../forks';
|
|
4
|
+
import { ROW_GUTTER } from '../icons';
|
|
5
|
+
import { chatHost } from '../../../host';
|
|
6
|
+
import { Icon, Menu } from '@adecore/ui';
|
|
7
|
+
|
|
8
|
+
const LINE = '-mx-1 mb-0.5 flex h-7 items-center gap-2 rounded-md px-1 text-xs text-text-muted';
|
|
9
|
+
const LINK = `${LINE} hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-hover data-[popup-open]:text-text`;
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Under a turn that forks went on after, the way over to them: one fork opens at once, more open a
|
|
13
|
+
* menu of them by name.
|
|
14
|
+
*/
|
|
15
|
+
export function ForksRow({ chatId, turnId }: { chatId: string; turnId: string }) {
|
|
16
|
+
const { t } = useTranslation('agent-chat');
|
|
17
|
+
const forks = useForkIdsAfter(chatId, turnId);
|
|
18
|
+
const label = t('work.forksAfter', { count: forks.length });
|
|
19
|
+
if (forks.length === 0) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (forks.length === 1) {
|
|
23
|
+
return <OneFork forkId={forks[0]!} label={label} />;
|
|
24
|
+
}
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex">
|
|
27
|
+
<Menu.Root>
|
|
28
|
+
<Menu.Trigger className={LINK}>
|
|
29
|
+
<ForksLabel label={label} />
|
|
30
|
+
</Menu.Trigger>
|
|
31
|
+
<Menu.Popup className="min-w-48">
|
|
32
|
+
{forks.map((forkId) => (
|
|
33
|
+
<ForkItem key={forkId} forkId={forkId} />
|
|
34
|
+
))}
|
|
35
|
+
</Menu.Popup>
|
|
36
|
+
</Menu.Root>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ForksLabel({ label, link = true }: { label: string; link?: boolean }) {
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<span className={ROW_GUTTER}>
|
|
45
|
+
<Icon icon={GitFork} size={12} />
|
|
46
|
+
</span>
|
|
47
|
+
{label}
|
|
48
|
+
{link && <Icon icon={ChevronRight} size={12} />}
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// A fork no longer in this project has nowhere to lead, so it only says it is there.
|
|
54
|
+
function OneFork({ forkId, label }: { forkId: string; label: string }) {
|
|
55
|
+
const place = chatHost().useChatPlace(forkId);
|
|
56
|
+
return (
|
|
57
|
+
<div className="flex">
|
|
58
|
+
{place.title === null ? (
|
|
59
|
+
<span className={LINE}>
|
|
60
|
+
<ForksLabel label={label} link={false} />
|
|
61
|
+
</span>
|
|
62
|
+
) : (
|
|
63
|
+
<button className={LINK} onClick={place.go}>
|
|
64
|
+
<ForksLabel label={label} />
|
|
65
|
+
</button>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ForkItem({ forkId }: { forkId: string }) {
|
|
72
|
+
const { t } = useTranslation('agent-chat');
|
|
73
|
+
const place = chatHost().useChatPlace(forkId);
|
|
74
|
+
return (
|
|
75
|
+
<Menu.Item disabled={place.title === null} onClick={place.go}>
|
|
76
|
+
<Icon icon={GitFork} size={14} /> <span className="truncate">{place.title ?? t('fork.pill.label')}</span>
|
|
77
|
+
</Menu.Item>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { useContext, useState, type ReactNode } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Bot, Brain, Check, ChevronDown, CircleAlert, File, Info, MessageCircleQuestionMark, TriangleAlert, X } from 'lucide-react';
|
|
5
|
+
import type { ChatApprovalItem, ChatAttachment, ChatAssistantItem, ChatQuestionItem, ChatThinkingItem, ChatUserItem } from '@adecore/agent-contracts';
|
|
6
|
+
import { fileBadge, formatBytes, isImageAttachment } from '../../attachments';
|
|
7
|
+
import { useChatRow, useCurrentItem } from '../../../state/chats';
|
|
8
|
+
import { chatHost } from '../../../host';
|
|
9
|
+
import { useChatScope } from '../../../scope';
|
|
10
|
+
import { useProviders } from '../../../state/providers';
|
|
11
|
+
import { ChatReferenceChip } from '../ChatReferenceChip';
|
|
12
|
+
import { ImageThumb } from '../ImageView';
|
|
13
|
+
import { MessageMarkdown, ReplyMarkdown } from '../Markdown';
|
|
14
|
+
import { FadingWords } from '../FadingWords';
|
|
15
|
+
import { settledBlocksText } from '../markdown-blocks';
|
|
16
|
+
import { WHOLE_FADE_CLASS } from '../rehype-fade';
|
|
17
|
+
import { useRevealedText } from '../reveal';
|
|
18
|
+
import { formatAgo, formatElapsedShort, formatTokens } from '@adecore/ui/format';
|
|
19
|
+
import { Icon, Tooltip, useTickingText } from '@adecore/ui';
|
|
20
|
+
import { toolSummary } from '../../logic/tools';
|
|
21
|
+
import { ROW_GUTTER } from '../icons';
|
|
22
|
+
import { useOpenForFind } from '../find-reveal';
|
|
23
|
+
import { ReplyContext } from '../reply-context';
|
|
24
|
+
|
|
25
|
+
// A long prompt folds so the answer stays in view; the reader can open it.
|
|
26
|
+
const USER_FOLD_LINES = 8;
|
|
27
|
+
const USER_FOLD_CHARS = 600;
|
|
28
|
+
|
|
29
|
+
/* A folded user prompt fades out at the bottom instead of cutting a line in half. */
|
|
30
|
+
const FOLD = 'max-h-[10em] overflow-hidden [mask-image:linear-gradient(to_bottom,black_70%,transparent)]';
|
|
31
|
+
|
|
32
|
+
function useReplyAuthor(chatId: string) {
|
|
33
|
+
const { id } = useChatScope();
|
|
34
|
+
const context = useContext(ReplyContext);
|
|
35
|
+
const author = chatHost().useReplyAuthor(id, context?.chatId ?? chatId);
|
|
36
|
+
return context !== null && context.chatId === undefined ? null : author;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* A file attached to a message that is not a picture. `download` is what saves a blob URL: the shell
|
|
41
|
+
* hands a link that opens a window to the system browser, which cannot reach a blob of this page.
|
|
42
|
+
*/
|
|
43
|
+
function AttachmentLink({ chatId, attachment }: { chatId: string; attachment: ChatAttachment }) {
|
|
44
|
+
const { id } = useChatScope();
|
|
45
|
+
const { url } = chatHost().attachments.useUrl(id, chatId, attachment.id);
|
|
46
|
+
const badge = fileBadge(attachment.name);
|
|
47
|
+
return (
|
|
48
|
+
<Tooltip label={attachment.name}>
|
|
49
|
+
<a
|
|
50
|
+
href={url ?? undefined}
|
|
51
|
+
download={attachment.name}
|
|
52
|
+
target="_blank"
|
|
53
|
+
rel="noreferrer"
|
|
54
|
+
aria-disabled={url === null}
|
|
55
|
+
className="flex h-28 w-36 flex-col rounded-xl border border-border bg-surface-raised bg-clip-padding p-2.5 text-left hover:bg-surface-hover"
|
|
56
|
+
>
|
|
57
|
+
<span className="flex h-6 min-w-6 items-center justify-center self-start rounded-md border border-border px-1.5 text-xs font-medium text-text-muted">
|
|
58
|
+
{badge ?? <Icon icon={File} size={12} />}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="mt-auto line-clamp-2 text-xs break-all text-text">{attachment.name}</span>
|
|
61
|
+
<span className="text-xs text-text-faint">{formatBytes(attachment.size)}</span>
|
|
62
|
+
</a>
|
|
63
|
+
</Tooltip>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* A picture attached to a message, which opens large. */
|
|
68
|
+
function AttachmentThumb({ chatId, attachment }: { chatId: string; attachment: ChatAttachment }) {
|
|
69
|
+
const { id } = useChatScope();
|
|
70
|
+
const source = chatHost().attachments.useUrl(id, chatId, attachment.id);
|
|
71
|
+
return <ImageThumb source={source} alt={attachment.name} className="max-h-32 max-w-48 object-cover" />;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function UserRow({ chatId, item }: { chatId: string; item: ChatUserItem }) {
|
|
75
|
+
const { t } = useTranslation('agent-chat');
|
|
76
|
+
const [open, setOpen] = useState(false);
|
|
77
|
+
const long = item.text.split('\n').length > USER_FOLD_LINES || item.text.length > USER_FOLD_CHARS;
|
|
78
|
+
const attachments = item.attachments ?? [];
|
|
79
|
+
const chats = item.chats ?? [];
|
|
80
|
+
useOpenForFind(item.id, 'text', setOpen);
|
|
81
|
+
return (
|
|
82
|
+
<div data-find-item={item.id} className="flex flex-col items-end">
|
|
83
|
+
<MessageHeading>{t('rows.user.heading')}</MessageHeading>
|
|
84
|
+
{attachments.length > 0 && (
|
|
85
|
+
<div className="mb-1.5 flex max-w-[80%] flex-wrap justify-end gap-1.5">
|
|
86
|
+
{attachments.map((attachment) =>
|
|
87
|
+
isImageAttachment(attachment.mime) ? (
|
|
88
|
+
<AttachmentThumb key={attachment.id} chatId={chatId} attachment={attachment} />
|
|
89
|
+
) : (
|
|
90
|
+
<AttachmentLink key={attachment.id} chatId={chatId} attachment={attachment} />
|
|
91
|
+
)
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
{chats.length > 0 && (
|
|
96
|
+
<div className="mb-1.5 flex max-w-[80%] flex-wrap justify-end gap-1.5">
|
|
97
|
+
{chats.map((id) => (
|
|
98
|
+
<ChatReferenceChip key={id} chatId={id} />
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
{item.text !== '' && (
|
|
103
|
+
<div className="relative max-w-[80%] rounded-2xl bg-surface-active px-3.5 py-2.5 text-sm wrap-anywhere text-text select-text">
|
|
104
|
+
<div data-find-field="text" className={clsx(long && !open && FOLD)}>
|
|
105
|
+
<MessageMarkdown text={item.text} mentions={item.mentions} skills={item.skills} />
|
|
106
|
+
</div>
|
|
107
|
+
{long && (
|
|
108
|
+
<button className="mt-1 flex items-center gap-1 text-xs text-text-muted hover:text-text" onClick={() => setOpen((o) => !o)}>
|
|
109
|
+
<Icon icon={ChevronDown} size={12} className={clsx('transition-transform', open && 'rotate-180')} />{' '}
|
|
110
|
+
{open ? t('rows.user.showLess') : t('rows.user.showAll')}
|
|
111
|
+
</button>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
* Who wrote a message, for a screen reader only: one heading per message is what lets VoiceOver's
|
|
121
|
+
* rotor walk a thread from message to message. `select-none`, so a selection copied across two
|
|
122
|
+
* messages does not pick the names up.
|
|
123
|
+
*/
|
|
124
|
+
function MessageHeading({ children }: { children: ReactNode }) {
|
|
125
|
+
return <h3 className="sr-only select-none">{children}</h3>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* The report a subagent handed back, drawn as a reply under a label of its own. */
|
|
129
|
+
export function ReportRow({ id, text }: { id: string; text: string }) {
|
|
130
|
+
const { t } = useTranslation('agent-chat');
|
|
131
|
+
return (
|
|
132
|
+
<div data-find-item={id} className="-mx-1 px-1 pb-2">
|
|
133
|
+
<MessageHeading>{t('rows.report.heading')}</MessageHeading>
|
|
134
|
+
<div aria-hidden className="mb-1 text-xs font-medium text-text-faint select-none">
|
|
135
|
+
{t('rows.report.heading')}
|
|
136
|
+
</div>
|
|
137
|
+
<div data-find-field="text" data-quote-answer className="contents">
|
|
138
|
+
<ReplyMarkdown text={text} streaming={false} />
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* The heading of a reply, named after its author when the host names one and after the agent the chat runs otherwise. */
|
|
145
|
+
function ReplyHeading({ chatId }: { chatId: string }) {
|
|
146
|
+
const { t } = useTranslation('agent-chat');
|
|
147
|
+
const context = useContext(ReplyContext);
|
|
148
|
+
const author = useReplyAuthor(chatId);
|
|
149
|
+
const kind = useChatRow(chatId, (row) => row?.info.provider);
|
|
150
|
+
const name = useProviders((s) => s.providers.find((provider) => provider.kind === (context?.provider ?? kind))?.name);
|
|
151
|
+
return <MessageHeading>{author?.name ?? name ?? t('rows.reply.agent')}</MessageHeading>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* The line over a reply with who wrote it and how long ago, when the host names its author. A screen
|
|
156
|
+
* reader hears the name from the reply's own heading, so this line is for the eye only.
|
|
157
|
+
*/
|
|
158
|
+
export function ReplyHeader({ chatId, at }: { chatId: string; at: number | null }) {
|
|
159
|
+
const author = useReplyAuthor(chatId);
|
|
160
|
+
if (author === null) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return (
|
|
164
|
+
<div aria-hidden className="mb-1 flex h-6 items-center gap-2 text-sm font-medium text-text select-none">
|
|
165
|
+
{author.mark}
|
|
166
|
+
<span className="min-w-0 truncate">{author.name}</span>
|
|
167
|
+
{at !== null && <ReplyAge at={at} />}
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function ReplyAge({ at }: { at: number }) {
|
|
173
|
+
const ref = useTickingText(() => formatAgo(Date.now() - at), 30_000);
|
|
174
|
+
return <span ref={ref} className="shrink-0 font-normal text-text-faint" />;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* The line that stands where the next words will land; a component, so the word is read at render and not at import. */
|
|
178
|
+
function Writing() {
|
|
179
|
+
const { t } = useTranslation('agent-chat');
|
|
180
|
+
return <span className="chat-live-text text-sm">{t('rows.reply.writing')}</span>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* A reply. A word at a time, it follows the text as it arrives. A block at a time, the blocks that
|
|
185
|
+
* closed are drawn and fade in as they land, with "Writing…" where the next one will stand. Whole,
|
|
186
|
+
* it says it is being written and fades in once it is done. Only a row that saw the reply being
|
|
187
|
+
* written fades anything: an old reply scrolled back into view just stands there.
|
|
188
|
+
*/
|
|
189
|
+
export function AssistantRow({ chatId, item: derived }: { chatId: string; item: ChatAssistantItem }) {
|
|
190
|
+
const item = useCurrentItem(chatId, derived);
|
|
191
|
+
const mode = chatHost().useStreaming();
|
|
192
|
+
const [sawWriting] = useState(item.streaming);
|
|
193
|
+
const live = mode === 'words' && item.streaming;
|
|
194
|
+
const reveal = useRevealedText(item.text, live);
|
|
195
|
+
|
|
196
|
+
if (mode === 'blocks') {
|
|
197
|
+
const settled = item.streaming ? settledBlocksText(item.text) : item.text;
|
|
198
|
+
return (
|
|
199
|
+
<div data-find-item={item.id} className="-mx-1 px-1 pb-2">
|
|
200
|
+
<ReplyHeading chatId={chatId} />
|
|
201
|
+
{settled !== '' && (
|
|
202
|
+
<div data-find-field="text" data-quote-answer className="contents">
|
|
203
|
+
<ReplyMarkdown text={settled} streaming={false} arriving={sawWriting} />
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
{item.streaming && <Writing />}
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (mode === 'whole' && item.streaming) {
|
|
211
|
+
return (
|
|
212
|
+
<div className="-mx-1 px-1 pb-2">
|
|
213
|
+
<ReplyHeading chatId={chatId} />
|
|
214
|
+
<Writing />
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return (
|
|
219
|
+
<div data-find-item={item.id} className={clsx('-mx-1 px-1 pb-2', mode === 'whole' && sawWriting && WHOLE_FADE_CLASS)}>
|
|
220
|
+
<ReplyHeading chatId={chatId} />
|
|
221
|
+
<div data-find-field="text" data-quote-answer className="contents">
|
|
222
|
+
<ReplyMarkdown text={reveal.text} streaming={reveal.active} />
|
|
223
|
+
</div>
|
|
224
|
+
{live && item.text === '' && <span className="inline-block h-3.5 w-1.5 animate-pulse rounded-sm bg-text-faint align-middle" />}
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/*
|
|
230
|
+
* What the model thought before it answered. It shimmers while it streams and folds itself away
|
|
231
|
+
* once the answer starts, because the thought is worth a glance and rarely worth reading twice.
|
|
232
|
+
*/
|
|
233
|
+
export function ThinkingRow({ chatId, item: derived }: { chatId: string; item: ChatThinkingItem }) {
|
|
234
|
+
const { t } = useTranslation('agent-chat');
|
|
235
|
+
const item = useCurrentItem(chatId, derived);
|
|
236
|
+
const mode = chatHost().useStreaming();
|
|
237
|
+
const [open, setOpen] = useState(false);
|
|
238
|
+
const reveal = useRevealedText(item.text, mode === 'words' && item.streaming);
|
|
239
|
+
// A block at a time, the paragraphs of the thought that closed are shown as they land.
|
|
240
|
+
const settled = mode === 'blocks' && item.streaming ? settledBlocksText(item.text) : null;
|
|
241
|
+
// Whole, the thought stays behind "Thinking…" until it is done and then folds like any other.
|
|
242
|
+
const shown = open || reveal.active || (settled !== null && settled !== '');
|
|
243
|
+
const text = settled ?? reveal.text;
|
|
244
|
+
useOpenForFind(item.id, 'text', setOpen);
|
|
245
|
+
return (
|
|
246
|
+
<div data-find-item={item.id} className="-mx-1 px-1 pb-2">
|
|
247
|
+
<button className="flex items-center gap-2 text-xs text-text-muted hover:text-text" disabled={item.streaming} onClick={() => setOpen((o) => !o)}>
|
|
248
|
+
<span className={ROW_GUTTER}>
|
|
249
|
+
<Icon icon={Brain} size={12} />
|
|
250
|
+
</span>
|
|
251
|
+
{item.streaming ? (
|
|
252
|
+
<span className="chat-live-text">{t('rows.thinking.live')}</span>
|
|
253
|
+
) : (
|
|
254
|
+
<>
|
|
255
|
+
<span>{t('rows.thinking.thoughtFor', { duration: formatElapsedShort((item.endedAt ?? item.createdAt) - item.createdAt) })}</span>
|
|
256
|
+
<Icon icon={ChevronDown} size={12} className={clsx('transition-transform', open && 'rotate-180')} />
|
|
257
|
+
</>
|
|
258
|
+
)}
|
|
259
|
+
</button>
|
|
260
|
+
{shown && text !== '' && (
|
|
261
|
+
<div data-find-field="text" className="mt-1 border-l border-border pl-2.5 text-sm whitespace-pre-wrap text-text-faint select-text">
|
|
262
|
+
{reveal.active ? <FadingWords text={reveal.text} /> : text}
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const NOTE_ICON = {
|
|
270
|
+
info: <Icon icon={Info} size={12} />,
|
|
271
|
+
warning: <Icon icon={TriangleAlert} size={12} />,
|
|
272
|
+
error: <Icon icon={CircleAlert} size={12} />
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/*
|
|
276
|
+
* A line from the machine in the thread. A note of more than one line (a summary a fork sent back)
|
|
277
|
+
* shows its first line and folds the rest open as markdown; one that came from a fork leads to it.
|
|
278
|
+
*/
|
|
279
|
+
export function NoteRow({ id, level, text, from }: { id: string; level: 'info' | 'warning' | 'error'; text: string; from?: string }) {
|
|
280
|
+
const { t } = useTranslation('agent-chat');
|
|
281
|
+
const [open, setOpen] = useState(false);
|
|
282
|
+
const breakAt = text.indexOf('\n');
|
|
283
|
+
const head = breakAt === -1 ? text : text.slice(0, breakAt);
|
|
284
|
+
const rest = breakAt === -1 ? '' : text.slice(breakAt + 1).trim();
|
|
285
|
+
useOpenForFind(id, 'output', setOpen);
|
|
286
|
+
return (
|
|
287
|
+
<div
|
|
288
|
+
data-find-item={id}
|
|
289
|
+
className={clsx(
|
|
290
|
+
'-mx-1 mb-0.5 flex min-h-7 items-start gap-2 px-1 py-1 text-xs',
|
|
291
|
+
level === 'error' ? 'text-status-error' : level === 'warning' ? 'text-status-needs-you' : 'text-text-faint'
|
|
292
|
+
)}
|
|
293
|
+
>
|
|
294
|
+
<span className={ROW_GUTTER}>{NOTE_ICON[level]}</span>
|
|
295
|
+
<div className="min-w-0 flex-1">
|
|
296
|
+
<div className="flex flex-wrap items-center gap-x-2">
|
|
297
|
+
<span data-find-field="summary" className="select-text">
|
|
298
|
+
{head}
|
|
299
|
+
</span>
|
|
300
|
+
{rest !== '' && (
|
|
301
|
+
<button className="text-text-muted hover:text-text" onClick={() => setOpen(!open)}>
|
|
302
|
+
{open ? t('rows.note.hide') : t('rows.note.show')}
|
|
303
|
+
</button>
|
|
304
|
+
)}
|
|
305
|
+
{from !== undefined && <OpenChatButton chatId={from} />}
|
|
306
|
+
</div>
|
|
307
|
+
{open && rest !== '' && (
|
|
308
|
+
<div data-find-field="output" className="mt-1 text-text-muted select-text">
|
|
309
|
+
<MessageMarkdown text={rest} />
|
|
310
|
+
</div>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* "Open fork" on a note a fork sent, while that fork is still in the project. */
|
|
318
|
+
function OpenChatButton({ chatId }: { chatId: string }) {
|
|
319
|
+
const { t } = useTranslation('agent-chat');
|
|
320
|
+
const place = chatHost().useChatPlace(chatId);
|
|
321
|
+
if (place.title === null) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return (
|
|
325
|
+
<button className="text-text-muted hover:text-text" onClick={place.go}>
|
|
326
|
+
{t('rows.note.openFork')}
|
|
327
|
+
</button>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/*
|
|
332
|
+
* The header of a turn the agent started itself, in place of the message of the person that is
|
|
333
|
+
* missing. It is one line: what the sub-agent came back with lives on that agent's own row, behind
|
|
334
|
+
* "Show result". When the CLI named the sub-agent it woke up about, the header opens that row.
|
|
335
|
+
*/
|
|
336
|
+
export function AgentTurnRow({ label, onOpen }: { label: string; onOpen?: () => void }) {
|
|
337
|
+
const line = 'mb-0.5 flex h-7 w-full items-center gap-2 text-left text-xs text-text-muted';
|
|
338
|
+
// A header with nowhere to go is text to read and copy; one that opens a row is a button.
|
|
339
|
+
const content = (
|
|
340
|
+
<>
|
|
341
|
+
<span className={ROW_GUTTER}>
|
|
342
|
+
<Icon icon={Bot} size={12} />
|
|
343
|
+
</span>
|
|
344
|
+
<span className={clsx('min-w-0 truncate', onOpen === undefined && 'select-text')}>{label}</span>
|
|
345
|
+
</>
|
|
346
|
+
);
|
|
347
|
+
if (onOpen === undefined) {
|
|
348
|
+
return <div className={line}>{content}</div>;
|
|
349
|
+
}
|
|
350
|
+
return (
|
|
351
|
+
<button className={clsx(line, 'hover:text-text')} onClick={onOpen}>
|
|
352
|
+
{content}
|
|
353
|
+
</button>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* A dashed line, since what stands above it is still there for the reader but only a summary to the agent. */
|
|
358
|
+
export function CompactionRow({ preTokens }: { preTokens: number | null }) {
|
|
359
|
+
const { t } = useTranslation('agent-chat');
|
|
360
|
+
return (
|
|
361
|
+
<div className="flex items-center gap-3 pb-3 text-xs text-text-faint">
|
|
362
|
+
<span className="grow border-t border-dashed border-border" />
|
|
363
|
+
<Tooltip label={t('rows.compaction.summary')}>
|
|
364
|
+
<span>{preTokens ? t('rows.compaction.from', { tokens: formatTokens(preTokens) }) : t('rows.compaction.plain')}</span>
|
|
365
|
+
</Tooltip>
|
|
366
|
+
<span className="grow border-t border-dashed border-border" />
|
|
367
|
+
</div>
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const DECISION_ICON: Record<Exclude<ChatApprovalItem['decision'], 'pending'>, React.ReactNode> = {
|
|
372
|
+
allow: <Icon icon={Check} size={12} />,
|
|
373
|
+
'allow-always': <Icon icon={Check} size={12} />,
|
|
374
|
+
deny: <Icon icon={X} size={12} />,
|
|
375
|
+
cancelled: <Icon icon={X} size={12} />
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/* The outcome of a permission request, one quiet line; the request itself lived on the composer. */
|
|
379
|
+
export function ApprovalHistoryRow({ item }: { item: ChatApprovalItem }) {
|
|
380
|
+
const { t } = useTranslation('agent-chat');
|
|
381
|
+
if (item.decision === 'pending') {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
return (
|
|
385
|
+
<div className="-mx-1 mb-0.5 flex h-7 items-center gap-2 px-1 text-xs text-text-faint">
|
|
386
|
+
<span className={clsx(ROW_GUTTER, item.decision === 'deny' && 'text-status-error')}>{DECISION_ICON[item.decision]}</span>
|
|
387
|
+
<span className={clsx(item.decision === 'deny' && 'text-status-error')}>{t(`rows.approval.${item.decision}`)}</span>
|
|
388
|
+
<span className="text-text-muted">{item.toolName}</span>
|
|
389
|
+
<span className="min-w-0 truncate font-mono">{toolSummary(item.toolName, item.input)}</span>
|
|
390
|
+
</div>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export function QuestionHistoryRow({ item }: { item: ChatQuestionItem }) {
|
|
395
|
+
const { t } = useTranslation('agent-chat');
|
|
396
|
+
return (
|
|
397
|
+
<div>
|
|
398
|
+
{item.questions.map((question) => (
|
|
399
|
+
<div key={question.id} className="-mx-1 mb-0.5 flex min-h-7 items-start gap-2 px-1 py-1 text-xs text-text-faint">
|
|
400
|
+
<span className={ROW_GUTTER}>
|
|
401
|
+
<Icon icon={MessageCircleQuestionMark} size={12} />
|
|
402
|
+
</span>
|
|
403
|
+
<span className="min-w-0">
|
|
404
|
+
<span className="text-text-muted">{question.question}</span>
|
|
405
|
+
{item.state === 'answered' && item.answers?.[question.id] !== undefined && (
|
|
406
|
+
<span className="ml-1.5 text-text">{item.answers[question.id]}</span>
|
|
407
|
+
)}
|
|
408
|
+
{item.state === 'cancelled' && <span className="ml-1.5">{t('rows.question.notAnswered')}</span>}
|
|
409
|
+
{item.state === 'dismissed' && <span className="ml-1.5">{t('rows.question.dismissed')}</span>}
|
|
410
|
+
</span>
|
|
411
|
+
</div>
|
|
412
|
+
))}
|
|
413
|
+
</div>
|
|
414
|
+
);
|
|
415
|
+
}
|