@adecore/agents-react 0.0.1 → 0.17.0-beta.2
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 +61 -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 +212 -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 +262 -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 +212 -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,124 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { BookmarkPlus, BookmarkX, Braces, Copy, Eye, FileText, GitFork, MessageSquare, Pencil, Scan } from 'lucide-react';
|
|
4
|
+
import { placeBookmark, removeBookmark, useBookmarkNaming } from '../bookmarks';
|
|
5
|
+
import { forkRefusal, turnIdOfRow } from '../logic/fork';
|
|
6
|
+
import { markdownOf, messageTextOf } from '../logic/timeline-copy';
|
|
7
|
+
import type { TimelineTarget } from '../logic/timeline-target';
|
|
8
|
+
import { chatHost } from '../../host';
|
|
9
|
+
import { useChatScope } from '../../scope';
|
|
10
|
+
import { useChatRow } from '../../state/chats';
|
|
11
|
+
import { openFileLink, useFileLinkCwd } from './file-links';
|
|
12
|
+
import { ContextMenu, copyText, DisabledReason, EDIT_SHORTCUTS, Icon, Kbd, selectAllWithin } from '@adecore/ui';
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The menu behind a right-click in a thread. Copy is the reason it exists. Everything in a thread
|
|
16
|
+
* is text a person may want out of it, and a whole message, a code block or the markdown an answer
|
|
17
|
+
* was written in are each a different amount of that.
|
|
18
|
+
*/
|
|
19
|
+
export function TimelineMenuPopup({
|
|
20
|
+
target,
|
|
21
|
+
thread,
|
|
22
|
+
chatId = null
|
|
23
|
+
}: {
|
|
24
|
+
target: TimelineTarget;
|
|
25
|
+
thread: RefObject<HTMLDivElement | null>;
|
|
26
|
+
/* The chat whose own thread this is; a thread in its place (a sub-agent's) has nothing to fork. */
|
|
27
|
+
chatId?: string | null;
|
|
28
|
+
}) {
|
|
29
|
+
const { t } = useTranslation('agent-chat');
|
|
30
|
+
const message = target.row === null ? null : messageTextOf(target.row);
|
|
31
|
+
const turnId = chatId === null || target.row === null ? null : turnIdOfRow(target.row);
|
|
32
|
+
const forkBlocked = useChatRow(chatId ?? '', (row) => (turnId === null ? null : forkRefusal(row?.info ?? null, row?.structure[turnId])));
|
|
33
|
+
const markdown = target.row === null ? null : markdownOf(target.row);
|
|
34
|
+
const markable = chatId !== null && (target.row?.kind === 'user' || target.row?.kind === 'assistant') ? target.row.id : null;
|
|
35
|
+
// A thread outside a chat (a sub-agent's transcript) has no cwd of its own; the app answers for it then.
|
|
36
|
+
const cwd = useFileLinkCwd();
|
|
37
|
+
const { fork, fileLinks } = chatHost();
|
|
38
|
+
const openInPreview = (): void => {
|
|
39
|
+
if (target.path !== null) {
|
|
40
|
+
openFileLink(cwd, { path: target.path, ...(target.line === null ? {} : { line: target.line }), directory: false });
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return (
|
|
44
|
+
<ContextMenu.Popup>
|
|
45
|
+
<ContextMenu.Item disabled={target.selection === ''} onClick={() => copyText(target.selection)}>
|
|
46
|
+
<Icon icon={Copy} size={14} /> {t('common.action.copy')} <Kbd shortcut={EDIT_SHORTCUTS.copy} />
|
|
47
|
+
</ContextMenu.Item>
|
|
48
|
+
{message !== null && (
|
|
49
|
+
<ContextMenu.Item onClick={() => copyText(message)}>
|
|
50
|
+
<Icon icon={MessageSquare} size={14} /> {t('timeline.menu.copyMessage')}
|
|
51
|
+
</ContextMenu.Item>
|
|
52
|
+
)}
|
|
53
|
+
{target.code !== null && (
|
|
54
|
+
<ContextMenu.Item onClick={() => copyText(target.code ?? '')}>
|
|
55
|
+
<Icon icon={Braces} size={14} /> {t('timeline.menu.copyCode')}
|
|
56
|
+
</ContextMenu.Item>
|
|
57
|
+
)}
|
|
58
|
+
{markdown !== null && (
|
|
59
|
+
<ContextMenu.Item onClick={() => copyText(markdown)}>
|
|
60
|
+
<Icon icon={FileText} size={14} /> {t('timeline.menu.copyMarkdown')}
|
|
61
|
+
</ContextMenu.Item>
|
|
62
|
+
)}
|
|
63
|
+
{chatId !== null && turnId !== null && fork !== null && (
|
|
64
|
+
<DisabledReason reason={forkBlocked}>
|
|
65
|
+
<ContextMenu.Item disabled={forkBlocked !== null} onClick={() => fork(chatId, turnId)}>
|
|
66
|
+
<Icon icon={GitFork} size={14} /> {t('timeline.menu.forkFromHere')}
|
|
67
|
+
</ContextMenu.Item>
|
|
68
|
+
</DisabledReason>
|
|
69
|
+
)}
|
|
70
|
+
{chatId !== null && markable !== null && <BookmarkMenuItems chatId={chatId} itemId={markable} />}
|
|
71
|
+
<ContextMenu.Separator />
|
|
72
|
+
<ContextMenu.Item onClick={() => selectAllWithin(thread.current)}>
|
|
73
|
+
<Icon icon={Scan} size={14} /> {t('common.action.selectAll')}
|
|
74
|
+
</ContextMenu.Item>
|
|
75
|
+
{target.path !== null && fileLinks !== null && (
|
|
76
|
+
<>
|
|
77
|
+
<ContextMenu.Separator />
|
|
78
|
+
<ContextMenu.Item onClick={openInPreview}>
|
|
79
|
+
<Icon icon={Eye} size={14} /> {t('timeline.menu.openInPreview')}
|
|
80
|
+
</ContextMenu.Item>
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
83
|
+
</ContextMenu.Popup>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Place a bookmark on a message, or rename and remove the one it has: the same rows from the thread
|
|
89
|
+
* and from the strip beside it. `onName` runs when a row opens the name field, which sits on the
|
|
90
|
+
* bookmark's line over the message and so has to be on screen; the strip scrolls there, the thread
|
|
91
|
+
* was clicked on it.
|
|
92
|
+
*/
|
|
93
|
+
export function BookmarkMenuItems({ chatId, itemId, onName }: { chatId: string; itemId: string; onName?: () => void }) {
|
|
94
|
+
const { t } = useTranslation('agent-chat');
|
|
95
|
+
const scope = useChatScope();
|
|
96
|
+
const bookmark = useChatRow(chatId, (row) => row?.bookmarks?.find((candidate) => candidate.itemId === itemId) ?? null);
|
|
97
|
+
if (bookmark === null) {
|
|
98
|
+
return (
|
|
99
|
+
<ContextMenu.Item
|
|
100
|
+
onClick={() => {
|
|
101
|
+
void placeBookmark(scope, chatId, itemId);
|
|
102
|
+
onName?.();
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<Icon icon={BookmarkPlus} size={14} /> {t('bookmarks.add')}
|
|
106
|
+
</ContextMenu.Item>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
<ContextMenu.Item
|
|
112
|
+
onClick={() => {
|
|
113
|
+
useBookmarkNaming.getState().open(scope.keyOf(chatId), bookmark.itemId);
|
|
114
|
+
onName?.();
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<Icon icon={Pencil} size={14} /> {t('bookmarks.rename')}
|
|
118
|
+
</ContextMenu.Item>
|
|
119
|
+
<ContextMenu.Item onClick={() => void removeBookmark(scope, chatId, bookmark)}>
|
|
120
|
+
<Icon icon={BookmarkX} size={14} /> {t('bookmarks.remove')}
|
|
121
|
+
</ContextMenu.Item>
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { FileDiff, PatchDiff } from '@pierre/diffs/react';
|
|
4
|
+
import type { ChatFileChange } from '@adecore/agent-contracts';
|
|
5
|
+
import { chatHost } from '../../host';
|
|
6
|
+
import { useDiffTheme } from './diff-theme';
|
|
7
|
+
import { fullFileDiff, type DiffContents } from './full-diff';
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* A patch the CLI reported itself (Codex writes unified diffs). The renderer needs a file header
|
|
11
|
+
* to know what it is looking at, and a CLI that only sends hunks gets one from the path we know.
|
|
12
|
+
* A diff without hunks at all is shown as its own lines, so nothing is ever swallowed.
|
|
13
|
+
*/
|
|
14
|
+
function asPatch(change: ChatFileChange): string | null {
|
|
15
|
+
if (!/^@@/m.test(change.diff)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (/^\+\+\+ /m.test(change.diff)) {
|
|
19
|
+
return change.diff;
|
|
20
|
+
}
|
|
21
|
+
const from = change.kind === 'add' ? '/dev/null' : `a/${change.path}`;
|
|
22
|
+
const to = change.kind === 'delete' ? '/dev/null' : `b/${change.path}`;
|
|
23
|
+
return `--- ${from}\n+++ ${to}\n${change.diff}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Lets a diff with a view of its own run to the bottom of it, so the scrollbar of a short one sits
|
|
27
|
+
there instead of under its last line; the lines themselves keep their height. */
|
|
28
|
+
const FILL_CSS = `
|
|
29
|
+
:host { display: flex; flex-direction: column; flex-grow: 1; }
|
|
30
|
+
pre { flex-grow: 1; align-content: start; }
|
|
31
|
+
pre[data-diff-type="single"] { display: flex; flex-direction: column; }
|
|
32
|
+
pre[data-diff-type="split"][data-overflow="scroll"]:not([data-dehydrated]) { align-content: stretch; }
|
|
33
|
+
pre > [data-code] { flex-grow: 1; align-self: stretch; align-content: start; }
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
function lineClass(line: string): string {
|
|
37
|
+
if (line.startsWith('+')) {
|
|
38
|
+
return 'text-chat-added';
|
|
39
|
+
}
|
|
40
|
+
if (line.startsWith('-')) {
|
|
41
|
+
return 'text-chat-removed';
|
|
42
|
+
}
|
|
43
|
+
return 'text-text-muted';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface UnifiedDiffProps {
|
|
47
|
+
change: ChatFileChange;
|
|
48
|
+
overflow?: 'wrap' | 'scroll';
|
|
49
|
+
/* `split` puts the old and the new side by side; the chat always stacks. */
|
|
50
|
+
diffStyle?: 'unified' | 'split';
|
|
51
|
+
/* Grow to the height of a flex column around it. */
|
|
52
|
+
fill?: boolean;
|
|
53
|
+
/* Both whole texts the patch was made between, with which a person can unfold the lines between hunks. */
|
|
54
|
+
contents?: DiffContents;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default function UnifiedDiff({ change, overflow = 'wrap', diffStyle = 'unified', fill = false, contents }: UnifiedDiffProps) {
|
|
58
|
+
const resolved = chatHost().code.useMode();
|
|
59
|
+
const theme = useDiffTheme();
|
|
60
|
+
const patch = useMemo(() => asPatch(change), [change]);
|
|
61
|
+
const oldText = contents?.old;
|
|
62
|
+
const newText = contents?.new;
|
|
63
|
+
const full = useMemo(
|
|
64
|
+
() => (patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText })),
|
|
65
|
+
[patch, change.path, oldText, newText]
|
|
66
|
+
);
|
|
67
|
+
const unfolds = full !== null;
|
|
68
|
+
const options = useMemo(
|
|
69
|
+
() => ({
|
|
70
|
+
theme,
|
|
71
|
+
themeType: resolved,
|
|
72
|
+
disableFileHeader: true,
|
|
73
|
+
diffStyle,
|
|
74
|
+
overflow,
|
|
75
|
+
hunkSeparators: unfolds ? ('line-info' as const) : ('simple' as const),
|
|
76
|
+
unsafeCSS: fill ? FILL_CSS : undefined
|
|
77
|
+
}),
|
|
78
|
+
[diffStyle, overflow, theme, resolved, fill, unfolds]
|
|
79
|
+
);
|
|
80
|
+
if (patch === null) {
|
|
81
|
+
return (
|
|
82
|
+
<pre className="max-h-64 overflow-auto px-3 py-2 font-mono text-code select-text">
|
|
83
|
+
{change.diff
|
|
84
|
+
.replace(/\n$/, '')
|
|
85
|
+
.split('\n')
|
|
86
|
+
.map((line, index) => (
|
|
87
|
+
<div key={index} className={clsx(lineClass(line), 'whitespace-pre-wrap')}>
|
|
88
|
+
{line}
|
|
89
|
+
</div>
|
|
90
|
+
))}
|
|
91
|
+
</pre>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return (
|
|
95
|
+
<div className={clsx('chat-diff select-text', fill && 'flex grow flex-col')}>
|
|
96
|
+
{full === null ? <PatchDiff patch={patch} options={options} /> : <FileDiff fileDiff={full} options={options} />}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChatAttachmentUpload } from '@adecore/agent-contracts';
|
|
2
|
+
import { useUploadThumbnail } from '../thumbnails';
|
|
3
|
+
|
|
4
|
+
const BOX = 'h-14 w-14 rounded-lg border border-border';
|
|
5
|
+
|
|
6
|
+
/* An image the composer holds, drawn from its thumbnail; an empty box of the same size stands in while that is made. */
|
|
7
|
+
export function UploadThumb({ upload }: { upload: ChatAttachmentUpload }) {
|
|
8
|
+
const url = useUploadThumbnail(upload);
|
|
9
|
+
if (url === null) {
|
|
10
|
+
return <span role="img" aria-label={upload.name} className={`block bg-surface-sunken ${BOX}`} />;
|
|
11
|
+
}
|
|
12
|
+
return <img src={url} alt={upload.name} className={`object-cover ${BOX}`} />;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { AgentIcon } from '../../agents/AgentIcon';
|
|
4
|
+
import { useChatRow } from '../../state/chats';
|
|
5
|
+
import { dayPartOf } from '../logic/welcome';
|
|
6
|
+
|
|
7
|
+
function localClock(): Date {
|
|
8
|
+
return new Date();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* What stands over the composer of a chat nobody wrote in yet. The part of the day is read once, when the chat opens. */
|
|
12
|
+
export function WelcomeGreeting({ chatId, now = localClock }: { chatId: string; now?: () => Date }) {
|
|
13
|
+
const { t } = useTranslation('agent-chat');
|
|
14
|
+
const provider = useChatRow(chatId, (row) => row?.info.provider ?? null);
|
|
15
|
+
const [part] = useState(() => dayPartOf(now()));
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex items-center justify-center gap-3 px-3 pb-2 text-text">
|
|
18
|
+
{provider && <AgentIcon kind={provider} size={32} className="shrink-0" />}
|
|
19
|
+
<h2 className="text-4xl font-semibold tracking-tight text-balance">{t(`timeline.welcome.${part}`)}</h2>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* An `@file` or `$skill` token drawn as a pill: the tone says which of the two it is, the shape
|
|
2
|
+
says where it is drawn. */
|
|
3
|
+
|
|
4
|
+
export const MENTION_TONE = 'bg-accent-soft text-accent shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--accent)_25%,transparent)]';
|
|
5
|
+
|
|
6
|
+
/* A skill is the same token shape as a file mention, in its own hue. */
|
|
7
|
+
export const SKILL_TONE = 'bg-skill-soft text-skill shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--skill)_25%,transparent)]';
|
|
8
|
+
|
|
9
|
+
/* The pill in the composer's editor, around the token's own characters: the editor places the caret
|
|
10
|
+
from what it drew, so padding is safe, and a chip that wraps keeps it on every fragment. */
|
|
11
|
+
export const CHIP_IN_EDITOR = 'box-decoration-clone rounded-md px-1 py-0.5';
|
|
12
|
+
|
|
13
|
+
/* The same pill in the transcript: a real inline box a shade smaller than the text around it, with
|
|
14
|
+
the icon in place of the sigil. */
|
|
15
|
+
export const CHIP_IN_MESSAGE = 'inline-flex h-5 max-w-full items-center gap-1 rounded-md px-1.5 align-[-3px] text-xs/none font-medium';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* One colored run of a line, the part of a highlighter's token the code block draws. */
|
|
2
|
+
export interface CodeToken {
|
|
3
|
+
content: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
/* The highlighter's bit set: 1 italic, 2 bold, 4 underline, 8 strikethrough. */
|
|
6
|
+
fontStyle?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Tokenizes `code` as lines, carrying on from `state` when there is one, and answers the state after it. */
|
|
10
|
+
export type Tokenize<State> = (code: string, state: State | undefined) => { lines: CodeToken[][]; state: State | undefined };
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The lines of a code block that grows at the end. A line that ended keeps the tokens it got, and
|
|
14
|
+
* the grammar state after it is where the next line starts, so a delta tokenizes the lines that are
|
|
15
|
+
* new plus the one still being written, however long the block already is. The last line is always
|
|
16
|
+
* tokenized again, since the next delta may turn it into anything (the start of a comment, a string).
|
|
17
|
+
*/
|
|
18
|
+
export class IncrementalLines<State> {
|
|
19
|
+
private readonly tokenize: Tokenize<State>;
|
|
20
|
+
// The text of the lines that are kept: empty, or ending in a newline while the block grows.
|
|
21
|
+
private source = '';
|
|
22
|
+
private lines: CodeToken[][] = [];
|
|
23
|
+
private state: State | undefined = undefined;
|
|
24
|
+
|
|
25
|
+
constructor(tokenize: Tokenize<State>) {
|
|
26
|
+
this.tokenize = tokenize;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The tokens of every line of `code`; `complete` says nothing more arrives, so the last line is kept too. */
|
|
30
|
+
update(code: string, complete: boolean): CodeToken[][] {
|
|
31
|
+
// A carriage return splits lines in ways a cut at newlines does not see, so that code starts over every time.
|
|
32
|
+
if (code.includes('\r')) {
|
|
33
|
+
this.reset();
|
|
34
|
+
return this.tokenize(code, undefined).lines;
|
|
35
|
+
}
|
|
36
|
+
const end = complete ? code.length : code.lastIndexOf('\n') + 1;
|
|
37
|
+
const stable = code.slice(0, end);
|
|
38
|
+
const continues = (this.source === '' || this.source.endsWith('\n')) && stable.startsWith(this.source);
|
|
39
|
+
if (!continues) {
|
|
40
|
+
this.reset();
|
|
41
|
+
}
|
|
42
|
+
const added = stable.slice(this.source.length);
|
|
43
|
+
if (added !== '') {
|
|
44
|
+
const result = this.tokenize(added.endsWith('\n') ? added.slice(0, -1) : added, this.state);
|
|
45
|
+
this.lines = [...this.lines, ...result.lines];
|
|
46
|
+
this.state = result.state;
|
|
47
|
+
this.source = stable;
|
|
48
|
+
}
|
|
49
|
+
if (complete) {
|
|
50
|
+
return this.lines;
|
|
51
|
+
}
|
|
52
|
+
return [...this.lines, ...this.tokenize(code.slice(end), this.state).lines];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private reset(): void {
|
|
56
|
+
this.source = '';
|
|
57
|
+
this.lines = [];
|
|
58
|
+
this.state = undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ThemeRegistration } from 'shiki';
|
|
2
|
+
import { chatHost } from '../../host';
|
|
3
|
+
|
|
4
|
+
/* The Shiki theme code is drawn in under the app's current light or dark. */
|
|
5
|
+
export function useCodeTheme(): string {
|
|
6
|
+
const { code } = chatHost();
|
|
7
|
+
const mode = code.useMode();
|
|
8
|
+
const themes = code.useThemes();
|
|
9
|
+
return mode === 'dark' ? themes.dark : themes.light;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* What to hand Shiki for a theme id: a theme the app ships itself, or the id of a bundled one for Shiki to load. */
|
|
13
|
+
export function shikiThemeOf(id: string): string | ThemeRegistration {
|
|
14
|
+
return chatHost().code.custom.find((theme) => theme.name === id) ?? id;
|
|
15
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
import { Facet, RangeSetBuilder, StateField, type EditorState, type Extension } from '@codemirror/state';
|
|
3
|
+
import { Decoration, EditorView, type DecorationSet } from '@codemirror/view';
|
|
4
|
+
import { chipRanges } from '../../mentions';
|
|
5
|
+
import { CHIP_IN_EDITOR, MENTION_TONE, SKILL_TONE } from '../chips';
|
|
6
|
+
import { codeRanges } from './editor';
|
|
7
|
+
|
|
8
|
+
export interface ChipChoices {
|
|
9
|
+
mentions: string[];
|
|
10
|
+
skills: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const NO_CHOICES: ChipChoices = { mentions: [], skills: [] };
|
|
14
|
+
|
|
15
|
+
const choices = Facet.define<ChipChoices, ChipChoices>({ combine: (values) => values[0] ?? NO_CHOICES });
|
|
16
|
+
|
|
17
|
+
const mentionMark = Decoration.mark({ class: `${MENTION_TONE} ${CHIP_IN_EDITOR}` });
|
|
18
|
+
const skillMark = Decoration.mark({ class: `${SKILL_TONE} ${CHIP_IN_EDITOR}` });
|
|
19
|
+
|
|
20
|
+
function chipMarks(state: EditorState): DecorationSet {
|
|
21
|
+
const { mentions, skills } = state.facet(choices);
|
|
22
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
23
|
+
// `@a.ts` in backticks is code, not a mention.
|
|
24
|
+
for (const range of chipRanges(state.doc.toString(), mentions, skills, codeRanges(state))) {
|
|
25
|
+
builder.add(range.from, range.to, range.kind === 'skill' ? skillMark : mentionMark);
|
|
26
|
+
}
|
|
27
|
+
return builder.finish();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const chipField = StateField.define<DecorationSet>({
|
|
31
|
+
create: chipMarks,
|
|
32
|
+
// The parser finishes a long text after the edit that asked for it, and a code span it finds then unmakes a chip.
|
|
33
|
+
update: (marks, tr) =>
|
|
34
|
+
tr.docChanged || tr.startState.facet(choices) !== tr.state.facet(choices) || syntaxTree(tr.startState) !== syntaxTree(tr.state)
|
|
35
|
+
? chipMarks(tr.state)
|
|
36
|
+
: marks,
|
|
37
|
+
provide: (field) => EditorView.decorations.from(field)
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/* Draws the chosen `@path` and `$name` tokens as chips; a new `ChipChoices` object is what redraws them. */
|
|
41
|
+
export function chipDecorations(value: ChipChoices): Extension {
|
|
42
|
+
return [choices.of(value), chipField];
|
|
43
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { history, historyKeymap, standardKeymap } from '@codemirror/commands';
|
|
2
|
+
import { Language, defineLanguageFacet, indentUnit, syntaxHighlighting, syntaxTree } from '@codemirror/language';
|
|
3
|
+
import { Annotation, type EditorState, type Extension, type Range } from '@codemirror/state';
|
|
4
|
+
import { Decoration, EditorView, ViewPlugin, keymap, type DecorationSet, type ViewUpdate } from '@codemirror/view';
|
|
5
|
+
import { tagHighlighter, tags } from '@lezer/highlight';
|
|
6
|
+
import { Strikethrough, Table, parser } from '@lezer/markdown';
|
|
7
|
+
|
|
8
|
+
/* Marks a transaction that brings the editor in line with a value set from outside, so it is not reported back. */
|
|
9
|
+
export const externalChange = Annotation.define<boolean>();
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The markdown grammar in a Language of our own. `@codemirror/lang-markdown` builds the same thing
|
|
13
|
+
* but creates the HTML language and autocomplete at module level, which tree shaking cannot drop.
|
|
14
|
+
*/
|
|
15
|
+
export const markdownLanguage = new Language(defineLanguageFacet(), parser.configure([Strikethrough, Table]), [], 'markdown');
|
|
16
|
+
|
|
17
|
+
/* Classes rather than generated styles, so the look lives in `styles.css` on the theme's tokens. */
|
|
18
|
+
const markdownClasses = tagHighlighter([
|
|
19
|
+
{ tag: tags.strong, class: 'cm-md-strong' },
|
|
20
|
+
{ tag: tags.emphasis, class: 'cm-md-emphasis' },
|
|
21
|
+
{ tag: tags.strikethrough, class: 'cm-md-strike' },
|
|
22
|
+
{ tag: tags.heading, class: 'cm-md-heading' },
|
|
23
|
+
{ tag: [tags.link, tags.url], class: 'cm-md-link' },
|
|
24
|
+
{ tag: tags.processingInstruction, class: 'cm-md-mark' }
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export interface CodeRange {
|
|
28
|
+
from: number;
|
|
29
|
+
to: number;
|
|
30
|
+
kind: 'inline' | 'fence';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* The code spans and fenced blocks between `from` and `to`, as far as the parser has got. */
|
|
34
|
+
export function codeRanges(state: EditorState, from = 0, to = state.doc.length): CodeRange[] {
|
|
35
|
+
const found: CodeRange[] = [];
|
|
36
|
+
syntaxTree(state).iterate({
|
|
37
|
+
from,
|
|
38
|
+
to,
|
|
39
|
+
enter: (node) => {
|
|
40
|
+
if (node.name === 'InlineCode' || node.name === 'FencedCode') {
|
|
41
|
+
found.push({ from: node.from, to: node.to, kind: node.name === 'InlineCode' ? 'inline' : 'fence' });
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const inlineCodeMark = Decoration.mark({ class: 'cm-md-code' });
|
|
51
|
+
|
|
52
|
+
function fenceLine(first: boolean, last: boolean): Decoration {
|
|
53
|
+
return Decoration.line({ class: ['cm-md-fence', first ? 'cm-md-fence-start' : '', last ? 'cm-md-fence-end' : ''].join(' ').trim() });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* Code is decorated from the tree rather than highlighted per token: the highlighter styles the
|
|
58
|
+
* backticks apart from what they wrap, and a block's background belongs to its whole lines.
|
|
59
|
+
*/
|
|
60
|
+
function codeDecorations(view: EditorView): DecorationSet {
|
|
61
|
+
const { state } = view;
|
|
62
|
+
const decorations: Array<Range<Decoration>> = [];
|
|
63
|
+
const fenceLines = new Set<number>();
|
|
64
|
+
for (const visible of view.visibleRanges) {
|
|
65
|
+
for (const range of codeRanges(state, visible.from, visible.to)) {
|
|
66
|
+
if (range.kind === 'inline') {
|
|
67
|
+
decorations.push(inlineCodeMark.range(range.from, range.to));
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const first = state.doc.lineAt(range.from).number;
|
|
71
|
+
const last = state.doc.lineAt(range.to).number;
|
|
72
|
+
const start = state.doc.lineAt(Math.max(range.from, visible.from)).number;
|
|
73
|
+
const end = state.doc.lineAt(Math.min(range.to, visible.to)).number;
|
|
74
|
+
for (let number = start; number <= end; number++) {
|
|
75
|
+
if (!fenceLines.has(number)) {
|
|
76
|
+
fenceLines.add(number);
|
|
77
|
+
decorations.push(fenceLine(number === first, number === last).range(state.doc.line(number).from));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return Decoration.set(decorations, true);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const codeDecorationPlugin = ViewPlugin.fromClass(
|
|
86
|
+
class {
|
|
87
|
+
decorations: DecorationSet;
|
|
88
|
+
|
|
89
|
+
constructor(view: EditorView) {
|
|
90
|
+
this.decorations = codeDecorations(view);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
update(update: ViewUpdate): void {
|
|
94
|
+
if (update.docChanged || update.viewportChanged || syntaxTree(update.startState) !== syntaxTree(update.state)) {
|
|
95
|
+
this.decorations = codeDecorations(update.view);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{ decorations: (plugin) => plugin.decorations }
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* What every composer editor carries. The standard keymap rather than the default one: the default
|
|
104
|
+
* adds code editor commands (move a line, indent, select the syntax parent) on keys a prompt box
|
|
105
|
+
* has no business taking.
|
|
106
|
+
*/
|
|
107
|
+
export function composerEditorExtensions(): Extension {
|
|
108
|
+
return [
|
|
109
|
+
history(),
|
|
110
|
+
keymap.of([...standardKeymap, ...historyKeymap]),
|
|
111
|
+
EditorView.lineWrapping,
|
|
112
|
+
// What Tab and Shift+Tab in a fence add and take away, so a selection indents in the same steps as a caret.
|
|
113
|
+
indentUnit.of(' '),
|
|
114
|
+
markdownLanguage,
|
|
115
|
+
syntaxHighlighting(markdownClasses),
|
|
116
|
+
codeDecorationPlugin
|
|
117
|
+
];
|
|
118
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EditorState, TransactionSpec } from '@codemirror/state';
|
|
2
|
+
|
|
3
|
+
function isSpace(text: string): boolean {
|
|
4
|
+
return /^\s/.test(text);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Text an app puts in the draft, over the selection, as if it were typed there. A space keeps it apart
|
|
9
|
+
* from a word it would touch, and one after it at the end of the text lets the person type on.
|
|
10
|
+
*/
|
|
11
|
+
export function insertAtSelection(state: EditorState, text: string): TransactionSpec {
|
|
12
|
+
if (text === '') {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
const { from, to } = state.selection.main;
|
|
16
|
+
const before = state.doc.sliceString(Math.max(0, from - 1), from);
|
|
17
|
+
const after = state.doc.sliceString(to, to + 1);
|
|
18
|
+
const lead = before !== '' && !isSpace(before) && !isSpace(text) ? ' ' : '';
|
|
19
|
+
const trail = (after === '' || !isSpace(after)) && !/\s$/.test(text) ? ' ' : '';
|
|
20
|
+
const insert = `${lead}${text}${trail}`;
|
|
21
|
+
return { changes: { from, to, insert }, selection: { anchor: from + insert.length }, scrollIntoView: true, userEvent: 'input' };
|
|
22
|
+
}
|