@adecore/agents-react 0.39.0 → 0.40.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/dist/agents/AgentModelLabel.js +2 -1
- package/dist/agents/model-name.js +2 -1
- package/dist/agents/provider-paths.d.ts +2 -1
- package/dist/agents/provider-paths.js +7 -3
- package/dist/chat/account-choice.js +57 -28
- package/dist/chat/bookmarks.js +2 -1
- package/dist/chat/drafts.d.ts +0 -1
- package/dist/chat/drafts.js +9 -28
- package/dist/chat/logic/picker-entries.js +2 -8
- package/dist/chat/logic/resume-compaction.js +2 -5
- package/dist/chat/logic/timeline-copy.js +2 -8
- package/dist/chat/logic/timeline.js +9 -10
- package/dist/chat/logic/workflow.js +2 -4
- package/dist/chat/subagent-conversation.js +2 -1
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
- package/dist/chat/ui/composer/use-notice.js +2 -1
- package/dist/chat/ui/composer/use-prompt-history.js +1 -1
- package/dist/chat/ui/composer/use-prompt-menus.js +216 -253
- package/dist/chat/ui/composer/use-take-back.js +2 -2
- package/dist/chat/ui/diff/ApprovalReview.js +1 -1
- package/dist/chat/ui/highlighter.d.ts +0 -6
- package/dist/chat/ui/highlighter.js +0 -12
- package/dist/chat/ui/icons.d.ts +0 -1
- package/dist/chat/ui/icons.js +0 -3
- package/dist/chat/ui/rows/MessageRows.js +2 -5
- package/dist/chat/ui/rows/row-rhythm.js +2 -5
- package/dist/chat/ui/theme-watch.js +3 -7
- package/dist/prompts/logic/usePromptSession.js +2 -1
- package/dist/providers/AddAccountForm.js +2 -1
- package/dist/providers/CliDetail.js +104 -130
- package/dist/providers/account-actions.js +6 -9
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +6 -11
- package/dist/state/provider-accounts.d.ts +1 -1
- package/dist/state/provider-accounts.js +2 -8
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +14 -0
- package/dist/usage/ChartLogo.d.ts +5 -0
- package/dist/usage/ChartLogo.js +48 -0
- package/dist/usage/LimitsList.js +6 -16
- package/dist/usage/UsageBreakdown.js +17 -40
- package/dist/usage/UsageLimits.js +183 -211
- package/dist/usage/UsagePage.js +3 -17
- package/dist/usage/UsageSummary.js +4 -9
- package/dist/usage/limits.js +16 -31
- package/dist/usage/summary.d.ts +1 -1
- package/dist/usage/summary.js +3 -5
- package/package.json +27 -27
- package/src/agents/AccountDot.tsx +1 -0
- package/src/agents/AgentIcon.tsx +2 -0
- package/src/agents/AgentModelLabel.tsx +4 -6
- package/src/agents/ModelOptionControl.tsx +8 -0
- package/src/agents/account-limits.ts +3 -0
- package/src/agents/accounts.ts +12 -0
- package/src/agents/model-name.ts +13 -1
- package/src/agents/model-options.ts +2 -0
- package/src/agents/model-requirement.ts +4 -0
- package/src/agents/option-words.ts +2 -0
- package/src/agents/provider-paths.ts +8 -3
- package/src/agents/status-look.ts +1 -0
- package/src/chat/account-choice.ts +25 -19
- package/src/chat/actions.ts +5 -0
- package/src/chat/attachments.ts +8 -0
- package/src/chat/bookmarks.ts +13 -1
- package/src/chat/chat-client.ts +63 -0
- package/src/chat/chat-references.ts +3 -0
- package/src/chat/composer-effort.ts +5 -0
- package/src/chat/composer-send.ts +4 -0
- package/src/chat/drafts.ts +21 -17
- package/src/chat/forks.ts +4 -0
- package/src/chat/guards.ts +2 -0
- package/src/chat/index.ts +7 -0
- package/src/chat/logic/ansi.ts +47 -0
- package/src/chat/logic/bookmarks.ts +7 -0
- package/src/chat/logic/command-looks.ts +29 -0
- package/src/chat/logic/context-usage.ts +3 -0
- package/src/chat/logic/continue-after-stop.ts +5 -0
- package/src/chat/logic/effort.ts +4 -0
- package/src/chat/logic/embedded-code.ts +63 -0
- package/src/chat/logic/fork.ts +22 -0
- package/src/chat/logic/generated-image.ts +7 -0
- package/src/chat/logic/goal.ts +7 -0
- package/src/chat/logic/handback.ts +2 -0
- package/src/chat/logic/limit.ts +8 -0
- package/src/chat/logic/picker-entries.ts +16 -4
- package/src/chat/logic/resume-compaction.ts +22 -5
- package/src/chat/logic/scrubber.ts +38 -5
- package/src/chat/logic/thought.ts +5 -0
- package/src/chat/logic/thread-cards.ts +6 -0
- package/src/chat/logic/timeline-copy.ts +4 -9
- package/src/chat/logic/timeline-target.ts +15 -0
- package/src/chat/logic/timeline.ts +113 -10
- package/src/chat/logic/tools.ts +30 -0
- package/src/chat/logic/visual-height.ts +5 -0
- package/src/chat/logic/visual-viewport.ts +1 -0
- package/src/chat/logic/welcome.ts +3 -0
- package/src/chat/logic/workflow.ts +26 -7
- package/src/chat/mentions.ts +56 -0
- package/src/chat/pending-prompts.ts +16 -0
- package/src/chat/persisted-json.ts +15 -0
- package/src/chat/preferences.ts +16 -0
- package/src/chat/prompt-hold.ts +2 -0
- package/src/chat/quote.ts +7 -0
- package/src/chat/reading-width.ts +1 -0
- package/src/chat/recent-messages.ts +5 -0
- package/src/chat/resume-compaction-dismissals.ts +1 -0
- package/src/chat/stash.ts +12 -0
- package/src/chat/subagent-conversation.ts +24 -4
- package/src/chat/subagent-list.ts +14 -0
- package/src/chat/subagent-support.ts +1 -0
- package/src/chat/subagent-view.ts +9 -0
- package/src/chat/thumbnails.ts +14 -0
- package/src/chat/timeline-flash.ts +2 -0
- package/src/chat/timeline-scroll.ts +23 -0
- package/src/chat/ui/AccountPill.tsx +4 -0
- package/src/chat/ui/AnsiOutput.tsx +1 -0
- package/src/chat/ui/BookmarkMarker.tsx +5 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
- package/src/chat/ui/ChatActivity.tsx +13 -0
- package/src/chat/ui/CodeBlock.tsx +8 -0
- package/src/chat/ui/Composer.tsx +15 -0
- package/src/chat/ui/ComposerInput.tsx +22 -0
- package/src/chat/ui/DiffPool.tsx +3 -0
- package/src/chat/ui/EditDiff.tsx +5 -0
- package/src/chat/ui/GoalDock.tsx +13 -1
- package/src/chat/ui/ImageView.tsx +11 -0
- package/src/chat/ui/LimitState.tsx +23 -6
- package/src/chat/ui/Markdown.tsx +41 -0
- package/src/chat/ui/MermaidBlock.tsx +16 -0
- package/src/chat/ui/MessageActions.tsx +8 -0
- package/src/chat/ui/ModelMarks.tsx +1 -0
- package/src/chat/ui/Pickers.tsx +26 -0
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +6 -0
- package/src/chat/ui/QuoteButton.tsx +12 -0
- package/src/chat/ui/ReferenceChip.tsx +3 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
- package/src/chat/ui/RowTime.tsx +1 -0
- package/src/chat/ui/RunSettings.tsx +38 -0
- package/src/chat/ui/Scrubber.tsx +14 -0
- package/src/chat/ui/SessionLine.tsx +1 -0
- package/src/chat/ui/ShellCommand.tsx +32 -0
- package/src/chat/ui/SubagentControls.tsx +7 -0
- package/src/chat/ui/SubagentInfo.tsx +1 -0
- package/src/chat/ui/SubagentStopButton.tsx +6 -0
- package/src/chat/ui/SubagentTimeline.tsx +10 -0
- package/src/chat/ui/Timeline.tsx +119 -0
- package/src/chat/ui/TimelineMenu.tsx +5 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -0
- package/src/chat/ui/UploadThumb.tsx +2 -0
- package/src/chat/ui/VisualDialogs.tsx +3 -0
- package/src/chat/ui/VisualFrame.tsx +15 -0
- package/src/chat/ui/Welcome.tsx +1 -0
- package/src/chat/ui/chip-clipboard.ts +20 -0
- package/src/chat/ui/code-lines.ts +14 -0
- package/src/chat/ui/code-theme.ts +1 -0
- package/src/chat/ui/composer/ComposerControls.tsx +5 -0
- package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
- package/src/chat/ui/composer/DraftParts.tsx +2 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
- package/src/chat/ui/composer/chip-paste.ts +16 -0
- package/src/chat/ui/composer/chips.ts +6 -0
- package/src/chat/ui/composer/editor.ts +9 -0
- package/src/chat/ui/composer/insert.ts +2 -0
- package/src/chat/ui/composer/key-handler.ts +39 -0
- package/src/chat/ui/composer/keys.ts +23 -0
- package/src/chat/ui/composer/placeholder.ts +6 -0
- package/src/chat/ui/composer/prompt-picks.ts +1 -0
- package/src/chat/ui/composer/surface.ts +5 -0
- package/src/chat/ui/composer/use-chat-controls.ts +13 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
- package/src/chat/ui/composer/use-composer-menus.ts +2 -0
- package/src/chat/ui/composer/use-composer-submit.ts +27 -0
- package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
- package/src/chat/ui/composer/use-draft-files.ts +24 -0
- package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
- package/src/chat/ui/composer/use-notice.ts +4 -1
- package/src/chat/ui/composer/use-pending-requests.ts +3 -0
- package/src/chat/ui/composer/use-prompt-history.ts +20 -11
- package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
- package/src/chat/ui/composer/use-prompt-menus.ts +34 -14
- package/src/chat/ui/composer/use-take-back.ts +20 -2
- package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
- package/src/chat/ui/diff/DiffStack.tsx +2 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
- package/src/chat/ui/diff/RawDiff.tsx +1 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
- package/src/chat/ui/diff/diff-source-budget.ts +8 -0
- package/src/chat/ui/diff/editor-edit-model.ts +4 -0
- package/src/chat/ui/diff/review-plan.ts +9 -0
- package/src/chat/ui/diff/review-settle.ts +1 -0
- package/src/chat/ui/diff/syntax-runtime.ts +52 -0
- package/src/chat/ui/diff/use-review-stack.ts +9 -0
- package/src/chat/ui/diff-syntax.ts +12 -0
- package/src/chat/ui/diff-theme.ts +2 -0
- package/src/chat/ui/file-links.ts +2 -0
- package/src/chat/ui/find-reveal.ts +1 -0
- package/src/chat/ui/full-diff.ts +1 -0
- package/src/chat/ui/highlighter.ts +29 -15
- package/src/chat/ui/icons.tsx +2 -4
- package/src/chat/ui/index.ts +4 -0
- package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
- package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
- package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
- package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
- package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
- package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
- package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
- package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
- package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
- package/src/chat/ui/markdown-blocks.ts +64 -0
- package/src/chat/ui/mermaid.ts +4 -0
- package/src/chat/ui/quote-selection.ts +10 -0
- package/src/chat/ui/rehype-chips.ts +5 -0
- package/src/chat/ui/rehype-fade.ts +2 -0
- package/src/chat/ui/remark-html-as-text.ts +5 -0
- package/src/chat/ui/reveal.ts +18 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
- package/src/chat/ui/rows/MessageRows.tsx +29 -5
- package/src/chat/ui/rows/RowContainer.tsx +1 -0
- package/src/chat/ui/rows/Rows.tsx +2 -0
- package/src/chat/ui/rows/SubagentRow.tsx +13 -0
- package/src/chat/ui/rows/TaskRow.tsx +7 -0
- package/src/chat/ui/rows/VisualRow.tsx +4 -0
- package/src/chat/ui/rows/WorkRows.tsx +46 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
- package/src/chat/ui/rows/row-rhythm.ts +7 -5
- package/src/chat/ui/theme-watch.ts +8 -7
- package/src/chat/ui/thread-references.ts +7 -0
- package/src/chat/ui/use-diff-source-tokens.ts +8 -0
- package/src/chat/ui/useToggleSet.ts +3 -0
- package/src/chat/ui/visual-theme.ts +30 -0
- package/src/chat/visual-bridge.ts +23 -0
- package/src/chat/visual-viewport.ts +12 -0
- package/src/host-hooks.ts +6 -4
- package/src/host.ts +2 -0
- package/src/index.ts +3 -0
- package/src/lazy.tsx +9 -0
- package/src/mounted-registry.ts +4 -0
- package/src/port-transport.ts +32 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/logic/focus.ts +5 -0
- package/src/prompts/logic/keys.ts +12 -0
- package/src/prompts/logic/prompts.ts +8 -0
- package/src/prompts/logic/subjects.ts +8 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
- package/src/prompts/logic/usePromptSession.ts +13 -1
- package/src/prompts/ui/ApprovalBody.tsx +2 -0
- package/src/prompts/ui/PromptActions.tsx +2 -0
- package/src/prompts/ui/PromptCard.tsx +6 -0
- package/src/prompts/ui/PromptView.tsx +6 -0
- package/src/prompts/ui/QuestionBody.tsx +11 -0
- package/src/providers/AccountColors.tsx +1 -0
- package/src/providers/AccountDetail.tsx +7 -0
- package/src/providers/AccountVariables.tsx +2 -0
- package/src/providers/AddAccountForm.tsx +5 -1
- package/src/providers/CliDetail.tsx +6 -5
- package/src/providers/InstallPage.tsx +11 -0
- package/src/providers/ProviderList.tsx +3 -0
- package/src/providers/ProvidersPane.tsx +18 -0
- package/src/providers/account-actions.ts +10 -8
- package/src/providers/install-steps.ts +3 -0
- package/src/providers/pane-target.ts +5 -0
- package/src/providers/provider-abilities.ts +1 -0
- package/src/providers/provider-groups.ts +6 -0
- package/src/providers/variables.ts +12 -0
- package/src/scope.ts +2 -0
- package/src/state/chats.ts +61 -14
- package/src/state/index.ts +2 -0
- package/src/state/provider-accounts.ts +7 -9
- package/src/state/providers.ts +1 -0
- package/src/state/usage.ts +6 -0
- package/src/storage.ts +18 -0
- package/src/testing/prompts.ts +5 -0
- package/src/transport.ts +18 -0
- package/src/usage/ChartLogo.tsx +12 -0
- package/src/usage/LimitsList.tsx +7 -8
- package/src/usage/UsageBreakdown.tsx +11 -4
- package/src/usage/UsageChart.tsx +5 -0
- package/src/usage/UsageLimits.tsx +10 -7
- package/src/usage/UsageLimitsCard.tsx +2 -0
- package/src/usage/UsagePage.tsx +16 -19
- package/src/usage/UsageSummary.tsx +4 -5
- package/src/usage/UsageTiles.tsx +2 -0
- package/src/usage/format.ts +7 -0
- package/src/usage/limit-groups.ts +14 -0
- package/src/usage/limits.ts +11 -13
- package/src/usage/money.ts +1 -0
- package/src/usage/summary.ts +15 -7
|
@@ -92,9 +92,11 @@ interface AccountPick {
|
|
|
92
92
|
|
|
93
93
|
function optionSummary(option: ModelOptionDescriptor, selection: ModelSelection): string | null {
|
|
94
94
|
const value = selection.options[option.id];
|
|
95
|
+
|
|
95
96
|
if (option.type === 'select') {
|
|
96
97
|
return option.choices.find((choice) => choice.id === (value ?? option.defaultChoice))?.label ?? null;
|
|
97
98
|
}
|
|
99
|
+
|
|
98
100
|
return (value ?? option.defaultValue) === true ? option.label : null;
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -148,16 +150,21 @@ export function RunSettings({
|
|
|
148
150
|
const current = modelName(selection.model, owner?.models);
|
|
149
151
|
const short = shortModelName(current, owner?.models);
|
|
150
152
|
const options = orderOptions((model?.options ?? []).map(localizedOption));
|
|
153
|
+
|
|
151
154
|
const summary = options.flatMap((option) => {
|
|
152
155
|
const label = optionSummary(option, selection);
|
|
156
|
+
|
|
153
157
|
return label === null ? [] : [{ id: option.id, label, ring: option.id === CONTEXT_OPTION }];
|
|
154
158
|
});
|
|
159
|
+
|
|
155
160
|
// A CLI without a context choice still fills one, so the ring stands on its own next to the size.
|
|
156
161
|
const hasContextOption = options.some((option) => option.id === CONTEXT_OPTION);
|
|
157
162
|
const standaloneContext = !hasContextOption && Boolean(usage.contextWindow);
|
|
163
|
+
|
|
158
164
|
if (standaloneContext) {
|
|
159
165
|
summary.push({ id: CONTEXT_OPTION, label: formatTokens(usage.contextWindow ?? 0), ring: true });
|
|
160
166
|
}
|
|
167
|
+
|
|
161
168
|
const grouped = providers.length > 1;
|
|
162
169
|
const live = modelGroupsOf(providers, false);
|
|
163
170
|
const legacy = modelGroupsOf(providers, true);
|
|
@@ -176,7 +183,9 @@ export function RunSettings({
|
|
|
176
183
|
|
|
177
184
|
const searchable = wantsModelSearch(providers);
|
|
178
185
|
const favorites = useChatPreferences((preferences) => preferences.favoriteModels);
|
|
186
|
+
|
|
179
187
|
const favoritesOf = (kind: AgentKind): ReadonlySet<string> => favoriteModelsOf(favorites, kind);
|
|
188
|
+
|
|
180
189
|
const found = query.trim() === '' ? null : searchModels(inPickerOrder(providers), query, favoritesOf);
|
|
181
190
|
|
|
182
191
|
// The menu moves the focus to its popup as it opens, so the search field takes it a frame later.
|
|
@@ -184,7 +193,9 @@ export function RunSettings({
|
|
|
184
193
|
if (!open || !searchable) {
|
|
185
194
|
return;
|
|
186
195
|
}
|
|
196
|
+
|
|
187
197
|
const frame = requestAnimationFrame(() => searchRef.current?.focus());
|
|
198
|
+
|
|
188
199
|
return () => cancelAnimationFrame(frame);
|
|
189
200
|
}, [open, searchable]);
|
|
190
201
|
|
|
@@ -192,6 +203,7 @@ export function RunSettings({
|
|
|
192
203
|
if (!next) {
|
|
193
204
|
setQuery('');
|
|
194
205
|
}
|
|
206
|
+
|
|
195
207
|
onOpenChange(next);
|
|
196
208
|
};
|
|
197
209
|
|
|
@@ -200,10 +212,13 @@ export function RunSettings({
|
|
|
200
212
|
if (!searchable || event.target === searchRef.current || event.metaKey || event.ctrlKey || event.altKey) {
|
|
201
213
|
return;
|
|
202
214
|
}
|
|
215
|
+
|
|
203
216
|
const typed = event.key.length === 1 && event.key !== ' ';
|
|
217
|
+
|
|
204
218
|
if (!typed && event.key !== 'Backspace') {
|
|
205
219
|
return;
|
|
206
220
|
}
|
|
221
|
+
|
|
207
222
|
event.preventDefault();
|
|
208
223
|
event.stopPropagation();
|
|
209
224
|
setQuery((now) => (typed ? now + event.key : now.slice(0, -1)));
|
|
@@ -216,17 +231,22 @@ export function RunSettings({
|
|
|
216
231
|
event.preventDefault();
|
|
217
232
|
event.stopPropagation();
|
|
218
233
|
setQuery('');
|
|
234
|
+
|
|
219
235
|
return;
|
|
220
236
|
}
|
|
237
|
+
|
|
221
238
|
if (event.key === 'Enter') {
|
|
222
239
|
event.preventDefault();
|
|
223
240
|
const first = found?.[0];
|
|
241
|
+
|
|
224
242
|
if (first !== undefined) {
|
|
225
243
|
onModel(first.entry.kind, first.models[0]!.slug);
|
|
226
244
|
changeOpen(false);
|
|
227
245
|
}
|
|
246
|
+
|
|
228
247
|
return;
|
|
229
248
|
}
|
|
249
|
+
|
|
230
250
|
if (event.key !== 'Escape' && event.key !== 'ArrowDown' && event.key !== 'ArrowUp' && event.key !== 'Tab') {
|
|
231
251
|
event.stopPropagation();
|
|
232
252
|
}
|
|
@@ -235,6 +255,7 @@ export function RunSettings({
|
|
|
235
255
|
// The name wraps rather than truncates, so a long name a provider gives is read whole.
|
|
236
256
|
const modelRow = (entry: ProviderInfo, row: ModelInfo) => {
|
|
237
257
|
const starred = favoritesOf(entry.kind).has(row.slug);
|
|
258
|
+
|
|
238
259
|
return (
|
|
239
260
|
<Menu.Row key={row.slug} aria-label={row.name}>
|
|
240
261
|
<Menu.RadioItem value={modelValue(entry.kind, row.slug)} closeOnClick>
|
|
@@ -256,10 +277,13 @@ export function RunSettings({
|
|
|
256
277
|
// Every model of one provider, the starred ones first and the rest under a label per maker where the provider names them.
|
|
257
278
|
const byMaker = (entry: ProviderInfo, models: ModelInfo[]) => {
|
|
258
279
|
const starred = favoritesOf(entry.kind);
|
|
280
|
+
|
|
259
281
|
return favoritesFirst(models, starred).map((row, i, sorted) => {
|
|
260
282
|
const previous = sorted[i - 1];
|
|
283
|
+
|
|
261
284
|
const labeled =
|
|
262
285
|
row.vendor !== undefined && !starred.has(row.slug) && (previous === undefined || starred.has(previous.slug) || previous.vendor !== row.vendor);
|
|
286
|
+
|
|
263
287
|
return (
|
|
264
288
|
<Fragment key={row.slug}>
|
|
265
289
|
{labeled && <Menu.GroupLabel>{row.vendor}</Menu.GroupLabel>}
|
|
@@ -273,9 +297,11 @@ export function RunSettings({
|
|
|
273
297
|
const providerModels = (entry: ProviderInfo, models: ModelInfo[]) => {
|
|
274
298
|
const starred = favoritesOf(entry.kind);
|
|
275
299
|
const shortlist = shortlistOf(entry.kind, models, { provider, model: selection.model }, starred);
|
|
300
|
+
|
|
276
301
|
if (shortlist === null) {
|
|
277
302
|
return [...models.filter((row) => starred.has(row.slug)), ...models.filter((row) => !starred.has(row.slug))].map((row) => modelRow(entry, row));
|
|
278
303
|
}
|
|
304
|
+
|
|
279
305
|
return (
|
|
280
306
|
<>
|
|
281
307
|
{shortlist.map((row) => modelRow(entry, row))}
|
|
@@ -468,6 +494,7 @@ function AccountSubmenu({ provider, account }: { provider: AgentKind; account: A
|
|
|
468
494
|
const host = useChatHost();
|
|
469
495
|
const { t } = useTranslation('agent-chat');
|
|
470
496
|
const { choice, started, onPick } = account;
|
|
497
|
+
|
|
471
498
|
return (
|
|
472
499
|
<Menu.SubmenuRoot>
|
|
473
500
|
<Menu.SubmenuTrigger className="text-text-muted">
|
|
@@ -502,9 +529,11 @@ function AccountRows({ choice, locked }: { choice: AccountChoice; locked(id: str
|
|
|
502
529
|
const { t } = useTranslation('agent-chat');
|
|
503
530
|
const limits = useUsageLimits();
|
|
504
531
|
const now = useNow(MINUTE_MS);
|
|
532
|
+
|
|
505
533
|
return choice.offered.map((entry) => {
|
|
506
534
|
const session = sessionWindow(limitsOfAccount(limits, entry.id));
|
|
507
535
|
const disabled = locked(entry.id);
|
|
536
|
+
|
|
508
537
|
const row = (
|
|
509
538
|
<Menu.RadioItem key={entry.id} value={entry.id} disabled={disabled} closeOnClick className="items-start data-disabled:opacity-50">
|
|
510
539
|
<span className="flex min-w-0 grow flex-col">
|
|
@@ -516,6 +545,7 @@ function AccountRows({ choice, locked }: { choice: AccountChoice; locked(id: str
|
|
|
516
545
|
</span>
|
|
517
546
|
</Menu.RadioItem>
|
|
518
547
|
);
|
|
548
|
+
|
|
519
549
|
return disabled ? (
|
|
520
550
|
<Tooltip key={entry.id} label={t('pickers.account.forkToSwitch', { account: choice.nameOf(entry) })} side="right" sideOffset={12}>
|
|
521
551
|
{row}
|
|
@@ -529,6 +559,7 @@ function AccountRows({ choice, locked }: { choice: AccountChoice; locked(id: str
|
|
|
529
559
|
/* How full the context is, drawn small enough to sit in the pill beside the window's size. */
|
|
530
560
|
function ContextRing({ usage }: { usage: ChatUsage }) {
|
|
531
561
|
const fraction = contextFraction(usage);
|
|
562
|
+
|
|
532
563
|
return (
|
|
533
564
|
<svg width="14" height="14" viewBox="0 0 14 14" className={clsx('shrink-0', contextTone(fraction))} aria-hidden="true">
|
|
534
565
|
<circle cx="7" cy="7" r={RING_RADIUS} fill="none" strokeWidth="2" className="stroke-border-strong" />
|
|
@@ -554,6 +585,7 @@ function ContextUsage({ usage, disabled, onCompact }: { usage: ChatUsage; disabl
|
|
|
554
585
|
const percent = Math.round(fraction * 100);
|
|
555
586
|
const segments = contextSegments(usage);
|
|
556
587
|
const used = formatTokens(usage.contextTokens);
|
|
588
|
+
|
|
557
589
|
return (
|
|
558
590
|
<Menu.Group className="flex flex-col gap-2 px-2.5 py-1.5">
|
|
559
591
|
<div className="flex items-center gap-2">
|
|
@@ -623,14 +655,17 @@ function ContextUsage({ usage, disabled, onCompact }: { usage: ChatUsage; disabl
|
|
|
623
655
|
*/
|
|
624
656
|
function OptionRow({ option, selection, onChange }: { option: ModelOptionDescriptor; selection: ModelSelection; onChange(value: string | boolean): void }) {
|
|
625
657
|
const icon = <Icon icon={optionIcon(option)} size={14} className="shrink-0 text-text-faint" />;
|
|
658
|
+
|
|
626
659
|
if (option.type === 'boolean') {
|
|
627
660
|
const checked = (selection.options[option.id] ?? option.defaultValue) === true;
|
|
661
|
+
|
|
628
662
|
const item = (
|
|
629
663
|
<Menu.CheckboxItem indicator="end" className="text-text-muted" checked={checked} onCheckedChange={onChange} closeOnClick={false}>
|
|
630
664
|
{icon}
|
|
631
665
|
<span className="min-w-0 grow truncate">{option.label}</span>
|
|
632
666
|
</Menu.CheckboxItem>
|
|
633
667
|
);
|
|
668
|
+
|
|
634
669
|
return option.description ? (
|
|
635
670
|
<Tooltip label={option.description} side="right" sideOffset={12}>
|
|
636
671
|
{item}
|
|
@@ -639,7 +674,9 @@ function OptionRow({ option, selection, onChange }: { option: ModelOptionDescrip
|
|
|
639
674
|
item
|
|
640
675
|
);
|
|
641
676
|
}
|
|
677
|
+
|
|
642
678
|
const value = String(selection.options[option.id] ?? option.defaultChoice);
|
|
679
|
+
|
|
643
680
|
if (option.choices.length > INLINE_CHOICES) {
|
|
644
681
|
return (
|
|
645
682
|
<Menu.SubmenuRoot>
|
|
@@ -663,6 +700,7 @@ function OptionRow({ option, selection, onChange }: { option: ModelOptionDescrip
|
|
|
663
700
|
</Menu.SubmenuRoot>
|
|
664
701
|
);
|
|
665
702
|
}
|
|
703
|
+
|
|
666
704
|
return (
|
|
667
705
|
<div className="flex items-center gap-2.5 px-2.5 py-1.5 text-sm text-text-muted">
|
|
668
706
|
{icon}
|
package/src/chat/ui/Scrubber.tsx
CHANGED
|
@@ -99,8 +99,10 @@ export const Scrubber = memo(function Scrubber({
|
|
|
99
99
|
if (strip === null || typeof ResizeObserver === 'undefined') {
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
+
|
|
102
103
|
const observer = new ResizeObserver(() => setHeight(strip.offsetHeight));
|
|
103
104
|
observer.observe(strip);
|
|
105
|
+
|
|
104
106
|
return () => observer.disconnect();
|
|
105
107
|
}, [strip]);
|
|
106
108
|
|
|
@@ -115,13 +117,16 @@ export const Scrubber = memo(function Scrubber({
|
|
|
115
117
|
|
|
116
118
|
const anchor = useMemo(() => {
|
|
117
119
|
const element = strip;
|
|
120
|
+
|
|
118
121
|
if (anchorY === null || element === null) {
|
|
119
122
|
return undefined;
|
|
120
123
|
}
|
|
124
|
+
|
|
121
125
|
return {
|
|
122
126
|
contextElement: element,
|
|
123
127
|
getBoundingClientRect: () => {
|
|
124
128
|
const rect = element.getBoundingClientRect();
|
|
129
|
+
|
|
125
130
|
return DOMRect.fromRect({ x: rect.left, y: rect.top + anchorY + TICK_HEIGHT_PX / 2, width: rect.width, height: 0 });
|
|
126
131
|
}
|
|
127
132
|
};
|
|
@@ -131,10 +136,13 @@ export const Scrubber = memo(function Scrubber({
|
|
|
131
136
|
|
|
132
137
|
const actionsFor = (tick: ScrubberTick | null): CardAction[] =>
|
|
133
138
|
tick === null ? [] : Object.values(CARD_ACTIONS).filter((action) => action.offers(tick, chat));
|
|
139
|
+
|
|
134
140
|
const actions = actionsFor(hoveredTick);
|
|
141
|
+
|
|
135
142
|
/* The message a bookmark goes on: the one it is on already, or the person's own. A wake is the machine's and holds none. */
|
|
136
143
|
const markableOf = (tick: ScrubberTick | null): string | null =>
|
|
137
144
|
tick === null || chat === null ? null : (tick.bookmark?.itemId ?? (tick.kind === 'person' ? tick.id : null));
|
|
145
|
+
|
|
138
146
|
const menuTick = menuIndex === null ? null : (ticks[menuIndex] ?? null);
|
|
139
147
|
const menuMarkable = markableOf(menuTick);
|
|
140
148
|
|
|
@@ -142,10 +150,13 @@ export const Scrubber = memo(function Scrubber({
|
|
|
142
150
|
// The hover card is a child here as well, and a click in it is about no tick.
|
|
143
151
|
const index = strip === null || cameThroughPortal(e) ? null : messageAt(layout, e.clientY - strip.getBoundingClientRect().top);
|
|
144
152
|
const tick = index === null ? null : (ticks[index] ?? null);
|
|
153
|
+
|
|
145
154
|
if (tick === null || (actionsFor(tick).length === 0 && markableOf(tick) === null)) {
|
|
146
155
|
e.preventBaseUIHandler();
|
|
156
|
+
|
|
147
157
|
return;
|
|
148
158
|
}
|
|
159
|
+
|
|
149
160
|
setMenuIndex(index);
|
|
150
161
|
};
|
|
151
162
|
|
|
@@ -165,6 +176,7 @@ export const Scrubber = memo(function Scrubber({
|
|
|
165
176
|
onPointerMove={(e) => {
|
|
166
177
|
setPointing(true);
|
|
167
178
|
const index = pointAt(e);
|
|
179
|
+
|
|
168
180
|
if (index !== hovered) {
|
|
169
181
|
setHovered(index);
|
|
170
182
|
}
|
|
@@ -172,6 +184,7 @@ export const Scrubber = memo(function Scrubber({
|
|
|
172
184
|
onPointerLeave={() => setPointing(false)}
|
|
173
185
|
onClick={(e) => {
|
|
174
186
|
const index = pointAt(e);
|
|
187
|
+
|
|
175
188
|
if (index !== null) {
|
|
176
189
|
onPick(index);
|
|
177
190
|
}
|
|
@@ -183,6 +196,7 @@ export const Scrubber = memo(function Scrubber({
|
|
|
183
196
|
const distance = pointing && hoveredSlot !== null ? Math.abs(index - hoveredSlot) : null;
|
|
184
197
|
// A bookmark is a place to find again from anywhere, so it never dims with the rest.
|
|
185
198
|
const bright = slot.marked || slotInView(slot, inView) || distance === 0;
|
|
199
|
+
|
|
186
200
|
return (
|
|
187
201
|
<Fragment key={slot.first}>
|
|
188
202
|
<span
|
|
@@ -20,6 +20,7 @@ export function SessionLine({ window, now }: { window: UsageWindow; now: number
|
|
|
20
20
|
const tone = sessionTone(window.used);
|
|
21
21
|
const mark = evenPace(window, now)?.percent ?? null;
|
|
22
22
|
const resetsAt = window.resetsAt;
|
|
23
|
+
|
|
23
24
|
return (
|
|
24
25
|
<span className="flex items-center gap-2 text-xs text-text-faint tabular-nums">
|
|
25
26
|
<span aria-hidden className="relative h-1 w-14 shrink-0 rounded-full bg-surface-sunken">
|
|
@@ -32,6 +32,7 @@ const cache = new Map<string, Run[]>();
|
|
|
32
32
|
function runsOf(highlighter: Highlighter, code: string, language: string, theme: string, offset: number): Run[] {
|
|
33
33
|
const fg = highlighter.getTheme(theme).fg.toLowerCase();
|
|
34
34
|
const { tokens } = highlighter.codeToTokens(code, { lang: language as BundledLanguage, theme });
|
|
35
|
+
|
|
35
36
|
return tokens.flat().map((token) => ({
|
|
36
37
|
start: offset + token.offset,
|
|
37
38
|
end: offset + token.offset + token.content.length,
|
|
@@ -44,18 +45,23 @@ function runsOf(highlighter: Highlighter, code: string, language: string, theme:
|
|
|
44
45
|
/* `base` with `[start, end)` cut out of it and `inner` in its place. */
|
|
45
46
|
function overlay(base: readonly Run[], start: number, end: number, inner: readonly Run[]): Run[] {
|
|
46
47
|
const kept: Run[] = [];
|
|
48
|
+
|
|
47
49
|
for (const run of base) {
|
|
48
50
|
if (run.end <= start || run.start >= end) {
|
|
49
51
|
kept.push(run);
|
|
52
|
+
|
|
50
53
|
continue;
|
|
51
54
|
}
|
|
55
|
+
|
|
52
56
|
if (run.start < start) {
|
|
53
57
|
kept.push({ ...run, end: start, content: run.content.slice(0, start - run.start) });
|
|
54
58
|
}
|
|
59
|
+
|
|
55
60
|
if (run.end > end) {
|
|
56
61
|
kept.push({ ...run, start: end, content: run.content.slice(end - run.start) });
|
|
57
62
|
}
|
|
58
63
|
}
|
|
64
|
+
|
|
59
65
|
return [...kept, ...inner].sort((left, right) => left.start - right.start);
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -74,25 +80,34 @@ function languagesOf(command: string, extra: readonly EmbeddedCode[]): Set<strin
|
|
|
74
80
|
function highlight(command: string, extra: readonly EmbeddedCode[], theme: string, code: CodeHighlighter): Run[] | null {
|
|
75
81
|
const key = `${theme}\n${extra.map(({ start, end, language }) => `${start}:${end}:${language}`).join(',')}\n${command}`;
|
|
76
82
|
const known = cache.get(key);
|
|
83
|
+
|
|
77
84
|
if (known !== undefined) {
|
|
78
85
|
// Touched entries move to the end, so the oldest falls out first.
|
|
79
86
|
cache.delete(key);
|
|
80
87
|
cache.set(key, known);
|
|
88
|
+
|
|
81
89
|
return known;
|
|
82
90
|
}
|
|
91
|
+
|
|
83
92
|
const embedded = codeIn(command, extra);
|
|
84
93
|
const highlighter = code.ready([SHELL_LANGUAGE, ...embedded.map(({ language }) => language)], theme);
|
|
94
|
+
|
|
85
95
|
if (highlighter === null) {
|
|
86
96
|
return null;
|
|
87
97
|
}
|
|
98
|
+
|
|
88
99
|
let runs = runsOf(highlighter, command, code.language(SHELL_LANGUAGE), theme, 0);
|
|
100
|
+
|
|
89
101
|
for (const part of embedded) {
|
|
90
102
|
runs = overlay(runs, part.start, part.end, runsOf(highlighter, command.slice(part.start, part.end), code.language(part.language), theme, part.start));
|
|
91
103
|
}
|
|
104
|
+
|
|
92
105
|
cache.set(key, runs);
|
|
106
|
+
|
|
93
107
|
if (cache.size > CACHE_LIMIT) {
|
|
94
108
|
cache.delete(cache.keys().next().value!);
|
|
95
109
|
}
|
|
110
|
+
|
|
96
111
|
return runs;
|
|
97
112
|
}
|
|
98
113
|
|
|
@@ -101,20 +116,26 @@ function draw(command: string, runs: readonly Run[] | null, from: number, to: nu
|
|
|
101
116
|
if (runs === null) {
|
|
102
117
|
return [command.slice(from, to)];
|
|
103
118
|
}
|
|
119
|
+
|
|
104
120
|
const parts: ReactNode[] = [];
|
|
105
121
|
let at = from;
|
|
122
|
+
|
|
106
123
|
for (const run of runs) {
|
|
107
124
|
if (run.end <= from || run.start >= to) {
|
|
108
125
|
continue;
|
|
109
126
|
}
|
|
127
|
+
|
|
110
128
|
const start = Math.max(run.start, from);
|
|
111
129
|
const end = Math.min(run.end, to);
|
|
130
|
+
|
|
112
131
|
// Shiki leaves the line breaks out of its tokens; they come from the command itself.
|
|
113
132
|
if (start > at) {
|
|
114
133
|
parts.push(command.slice(at, start));
|
|
115
134
|
}
|
|
135
|
+
|
|
116
136
|
const content = run.content.slice(start - run.start, end - run.start);
|
|
117
137
|
const style = tokenStyle(run);
|
|
138
|
+
|
|
118
139
|
parts.push(
|
|
119
140
|
style === undefined ? (
|
|
120
141
|
content
|
|
@@ -124,11 +145,14 @@ function draw(command: string, runs: readonly Run[] | null, from: number, to: nu
|
|
|
124
145
|
</span>
|
|
125
146
|
)
|
|
126
147
|
);
|
|
148
|
+
|
|
127
149
|
at = end;
|
|
128
150
|
}
|
|
151
|
+
|
|
129
152
|
if (at < to) {
|
|
130
153
|
parts.push(command.slice(at, to));
|
|
131
154
|
}
|
|
155
|
+
|
|
132
156
|
return parts;
|
|
133
157
|
}
|
|
134
158
|
|
|
@@ -148,14 +172,18 @@ function Name({ named }: { named: NamedWord }) {
|
|
|
148
172
|
function drawNamed(command: string, runs: readonly Run[] | null, names: readonly NamedWord[]): ReactNode[] {
|
|
149
173
|
const parts: ReactNode[] = [];
|
|
150
174
|
let at = 0;
|
|
175
|
+
|
|
151
176
|
for (const named of names) {
|
|
152
177
|
if (named.start < at) {
|
|
153
178
|
continue;
|
|
154
179
|
}
|
|
180
|
+
|
|
155
181
|
parts.push(...draw(command, runs, at, named.start), <Name key={`name-${named.start}`} named={named} />);
|
|
156
182
|
at = named.end;
|
|
157
183
|
}
|
|
184
|
+
|
|
158
185
|
parts.push(...draw(command, runs, at, command.length));
|
|
186
|
+
|
|
159
187
|
return parts;
|
|
160
188
|
}
|
|
161
189
|
|
|
@@ -180,10 +208,13 @@ export function ShellCommand({ command, code = NO_CODE, names = NO_NAMES }: { co
|
|
|
180
208
|
if (runs !== null || tooLong) {
|
|
181
209
|
return;
|
|
182
210
|
}
|
|
211
|
+
|
|
183
212
|
let cancelled = false;
|
|
213
|
+
|
|
184
214
|
Promise.all([highlighter.loadTheme(theme, custom), ...[...languagesOf(command, code)].map((lang) => highlighter.loadLanguage(lang))])
|
|
185
215
|
.then(() => !cancelled && setLoads((count) => count + 1))
|
|
186
216
|
.catch(() => undefined);
|
|
217
|
+
|
|
187
218
|
return () => {
|
|
188
219
|
cancelled = true;
|
|
189
220
|
};
|
|
@@ -192,5 +223,6 @@ export function ShellCommand({ command, code = NO_CODE, names = NO_NAMES }: { co
|
|
|
192
223
|
if (names.length > 0) {
|
|
193
224
|
return <>{drawNamed(command, runs, names)}</>;
|
|
194
225
|
}
|
|
226
|
+
|
|
195
227
|
return <>{runs === null ? command : draw(command, runs, 0, command.length)}</>;
|
|
196
228
|
}
|
|
@@ -11,9 +11,11 @@ const CRUMB_LINK = 'truncate text-text-muted hover:text-text';
|
|
|
11
11
|
/* The chat's own title in the bar; while a sub-agent stands in the chat's place, pressing it goes back to the main agent. */
|
|
12
12
|
export function SubagentTitleCrumb({ chatId, className, children }: { chatId: string; className?: string; children: ReactNode }) {
|
|
13
13
|
const { trail, show } = useSubagentTrail(chatId);
|
|
14
|
+
|
|
14
15
|
if (trail.length === 0) {
|
|
15
16
|
return <>{children}</>;
|
|
16
17
|
}
|
|
18
|
+
|
|
17
19
|
return (
|
|
18
20
|
<button type="button" className={clsx('flex min-w-0 items-center', className)} onClick={() => show(MAIN_AGENT)}>
|
|
19
21
|
{children}
|
|
@@ -25,9 +27,11 @@ export function SubagentTitleCrumb({ chatId, className, children }: { chatId: st
|
|
|
25
27
|
export function SubagentBreadcrumb({ chatId, className }: { chatId: string; className?: string }) {
|
|
26
28
|
const { t } = useTranslation('agent-chat');
|
|
27
29
|
const { trail, show } = useSubagentTrail(chatId);
|
|
30
|
+
|
|
28
31
|
if (trail.length === 0) {
|
|
29
32
|
return null;
|
|
30
33
|
}
|
|
34
|
+
|
|
31
35
|
return (
|
|
32
36
|
<nav aria-label={t('subagents.title')} className={clsx('flex min-w-0 items-center gap-1', className)}>
|
|
33
37
|
{breadcrumbOf(trail).map((step) => (
|
|
@@ -53,11 +57,14 @@ export function SubagentActions({ chatId }: { chatId: string }) {
|
|
|
53
57
|
const { t } = useTranslation('agent-chat');
|
|
54
58
|
const { trail, show } = useSubagentTrail(chatId);
|
|
55
59
|
const subagents = useOpenableSubagents(chatId);
|
|
60
|
+
|
|
56
61
|
if (trail.length === 0) {
|
|
57
62
|
return null;
|
|
58
63
|
}
|
|
64
|
+
|
|
59
65
|
// Only a row of the chat's own thread can be stopped; a grandchild belongs to the sub-agent that opened it.
|
|
60
66
|
const shown = trail.length === 1 ? subagents.find((item) => item.toolUseId === trail[0]!.toolUseId) : undefined;
|
|
67
|
+
|
|
61
68
|
return (
|
|
62
69
|
<div className="flex shrink-0 items-center gap-1">
|
|
63
70
|
{shown !== undefined && <SubagentStopButton chatId={chatId} item={shown} size="sm" />}
|
|
@@ -24,6 +24,7 @@ export function SubagentInfo({ chatId, toolUseId }: { chatId: string; toolUseId:
|
|
|
24
24
|
const now = useNow(1000, item?.status === 'running');
|
|
25
25
|
const word = item === null ? null : statusWordOf(item, task);
|
|
26
26
|
const facts = item === null ? [] : subagentFacts(item, item.model === undefined ? null : modelName);
|
|
27
|
+
|
|
27
28
|
return (
|
|
28
29
|
<section aria-label={t('subagents.info.label')} className="shrink-0 border-b border-border py-1.5 pr-1.5 pl-4">
|
|
29
30
|
<div className="flex min-w-0 items-center gap-2 text-xs text-text-faint">
|
|
@@ -26,9 +26,11 @@ export function SubagentStopButton({
|
|
|
26
26
|
const actions = useChatActions();
|
|
27
27
|
const turnRunning = useChatRow(chatId, (row) => (row?.info.activeTurnId ?? null) !== null);
|
|
28
28
|
const stop = stopOf(item, turnRunning);
|
|
29
|
+
|
|
29
30
|
if (stop === null) {
|
|
30
31
|
return null;
|
|
31
32
|
}
|
|
33
|
+
|
|
32
34
|
const send = (): void => {
|
|
33
35
|
void actions.stopSubagent(chatId, item.toolUseId).catch((e: unknown) => {
|
|
34
36
|
const stale = e instanceof Error && 'code' in e && e.code === 'subagent-stale';
|
|
@@ -36,12 +38,16 @@ export function SubagentStopButton({
|
|
|
36
38
|
host.notify({ kind: 'error', title: t('subagents.stopFailed'), description });
|
|
37
39
|
});
|
|
38
40
|
};
|
|
41
|
+
|
|
39
42
|
const run = (): void => {
|
|
40
43
|
if (stop === 'task' && item.childId !== undefined) {
|
|
41
44
|
host.confirm.stopTask(id, item.childId, subagentTitle(item), send);
|
|
45
|
+
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
48
|
+
|
|
44
49
|
send();
|
|
45
50
|
};
|
|
51
|
+
|
|
46
52
|
return <IconButton icon={Square} size={size} label={stopLabel(stop)} className={className} onClick={run} />;
|
|
47
53
|
}
|
|
@@ -38,9 +38,11 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
38
38
|
const threadRef = useRef<HTMLDivElement>(null);
|
|
39
39
|
const [target, setTarget] = useState<TimelineTarget>(EMPTY_TARGET);
|
|
40
40
|
const followRef = useRef(true);
|
|
41
|
+
|
|
41
42
|
const stopFollowing = () => {
|
|
42
43
|
followRef.current = false;
|
|
43
44
|
};
|
|
45
|
+
|
|
44
46
|
const groups = useToggleSet(stopFollowing);
|
|
45
47
|
const subagents = useToggleSet(stopFollowing);
|
|
46
48
|
// The row being read when an earlier page was asked for, so it stays where it was once that page is in.
|
|
@@ -52,6 +54,7 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
52
54
|
const conversation = new SubagentConversation(scope.transport, chatId, toolUseId, setState);
|
|
53
55
|
controller.current = conversation;
|
|
54
56
|
void conversation.start();
|
|
57
|
+
|
|
55
58
|
return () => {
|
|
56
59
|
conversation.dispose();
|
|
57
60
|
controller.current = null;
|
|
@@ -74,17 +77,21 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
74
77
|
|
|
75
78
|
useLayoutEffect(() => {
|
|
76
79
|
const element = scrollRef.current;
|
|
80
|
+
|
|
77
81
|
if (element === null) {
|
|
78
82
|
return;
|
|
79
83
|
}
|
|
84
|
+
|
|
80
85
|
const head = state.items[0]?.id;
|
|
81
86
|
const moved = head !== headRef.current;
|
|
82
87
|
headRef.current = head;
|
|
88
|
+
|
|
83
89
|
if (moved && anchorRef.current !== null && !followRef.current) {
|
|
84
90
|
restoreAnchor(element, anchorRef.current);
|
|
85
91
|
} else if (followRef.current) {
|
|
86
92
|
element.scrollTop = element.scrollHeight;
|
|
87
93
|
}
|
|
94
|
+
|
|
88
95
|
if (moved) {
|
|
89
96
|
anchorRef.current = null;
|
|
90
97
|
}
|
|
@@ -92,9 +99,11 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
92
99
|
|
|
93
100
|
const loadEarlier = useCallback(() => {
|
|
94
101
|
const element = scrollRef.current;
|
|
102
|
+
|
|
95
103
|
if (element === null || state.cursor === null || !wantsEarlier(element)) {
|
|
96
104
|
return;
|
|
97
105
|
}
|
|
106
|
+
|
|
98
107
|
// Taken on every call, since the reader may scroll on while a page is on its way.
|
|
99
108
|
anchorRef.current = firstRowInView(element);
|
|
100
109
|
void controller.current?.loadEarlier();
|
|
@@ -153,6 +162,7 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
153
162
|
{rows.length === 0 && !state.live && <EmptyState icon={Bot}>{t('rows.subagent.nothingYet')}</EmptyState>}
|
|
154
163
|
{rows.map((row, index) => {
|
|
155
164
|
const previous = index > 0 ? rows[index - 1]! : null;
|
|
165
|
+
|
|
156
166
|
return (
|
|
157
167
|
<div key={row.id} data-item-id={row.id} className={rowRhythm(row, previous)}>
|
|
158
168
|
<Row
|