@adecore/agents-react 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +26 -26
- 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
|
@@ -39,6 +39,7 @@ export function BookmarkMarker({ chatId, itemId, bookmark }: BookmarkMarkerProps
|
|
|
39
39
|
initial={bookmark?.name ?? ''}
|
|
40
40
|
onDone={(name) => {
|
|
41
41
|
useBookmarkNaming.getState().close();
|
|
42
|
+
|
|
42
43
|
if (name !== null) {
|
|
43
44
|
void nameBookmark(scope, host, chatId, itemId, name, bookmark?.name);
|
|
44
45
|
}
|
|
@@ -73,6 +74,7 @@ function NameField({ initial, onDone }: { initial: string; onDone: (name: string
|
|
|
73
74
|
useEffect(() => {
|
|
74
75
|
ref.current?.focus();
|
|
75
76
|
const frame = requestAnimationFrame(() => ref.current?.focus());
|
|
77
|
+
|
|
76
78
|
return () => cancelAnimationFrame(frame);
|
|
77
79
|
}, []);
|
|
78
80
|
|
|
@@ -80,6 +82,7 @@ function NameField({ initial, onDone }: { initial: string; onDone: (name: string
|
|
|
80
82
|
if (settled.current) {
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
85
|
+
|
|
83
86
|
settled.current = true;
|
|
84
87
|
onDone(name);
|
|
85
88
|
};
|
|
@@ -98,9 +101,11 @@ function NameField({ initial, onDone }: { initial: string; onDone: (name: string
|
|
|
98
101
|
onBlur={(e) => settle(e.currentTarget.value)}
|
|
99
102
|
onKeyDown={(e) => {
|
|
100
103
|
e.stopPropagation();
|
|
104
|
+
|
|
101
105
|
if (e.key === 'Enter') {
|
|
102
106
|
settle(e.currentTarget.value);
|
|
103
107
|
}
|
|
108
|
+
|
|
104
109
|
if (e.key === 'Escape') {
|
|
105
110
|
settle(null);
|
|
106
111
|
}
|
|
@@ -18,6 +18,7 @@ export function BookmarkSubmenu({ chatId }: { chatId: string }) {
|
|
|
18
18
|
const host = useChatHost();
|
|
19
19
|
const place = useHostChatPlace(chatId);
|
|
20
20
|
const sorted = bookmarksInThreadOrder(bookmarks ?? [], order ?? []);
|
|
21
|
+
|
|
21
22
|
if (sorted.length === 0) {
|
|
22
23
|
return null;
|
|
23
24
|
}
|
|
@@ -36,28 +36,37 @@ export function ChatActivity({ chatId }: { chatId: string }) {
|
|
|
36
36
|
const tasks = useHostTasks(id);
|
|
37
37
|
// The infos of these sub-agents' own chats, so the info of any other chat changing does not draw this again.
|
|
38
38
|
const children = useChats(useShallow((s) => subagents.map((item) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info))));
|
|
39
|
+
|
|
39
40
|
const progress = progressOf(
|
|
40
41
|
subagents.map((item, index) => {
|
|
41
42
|
const taskId = taskIdOf(item);
|
|
43
|
+
|
|
42
44
|
return taskRowStateOf(item, taskId === null ? null : (tasks?.[taskId] ?? null), children[index]);
|
|
43
45
|
})
|
|
44
46
|
);
|
|
47
|
+
|
|
45
48
|
const words = subagents.map((item) => {
|
|
46
49
|
const taskId = taskIdOf(item);
|
|
50
|
+
|
|
47
51
|
return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
|
|
48
52
|
});
|
|
53
|
+
|
|
49
54
|
const background = useChatRow(chatId, (row) => row?.info.background) ?? NO_TASKS;
|
|
55
|
+
|
|
50
56
|
if (subagents.length === 0 && background.length === 0) {
|
|
51
57
|
return null;
|
|
52
58
|
}
|
|
59
|
+
|
|
53
60
|
const { shells, monitors } = backgroundCounts(background);
|
|
54
61
|
const Slot = host.SubagentSlot;
|
|
55
62
|
const counting = progress.active > 0 && progress.total > 1;
|
|
63
|
+
|
|
56
64
|
const label = counting
|
|
57
65
|
? [t('activity.progress', { done: progress.done, total: progress.total }), progress.waiting > 0 && t('activity.needsYou', { count: progress.waiting })]
|
|
58
66
|
.filter(Boolean)
|
|
59
67
|
.join(', ')
|
|
60
68
|
: t('activity.subagents', { count: badgeCountOf(words) });
|
|
69
|
+
|
|
61
70
|
return (
|
|
62
71
|
<div className="mb-3 flex flex-wrap items-center gap-2">
|
|
63
72
|
{subagents.length > 0 &&
|
|
@@ -94,6 +103,7 @@ export function ChatActivity({ chatId }: { chatId: string }) {
|
|
|
94
103
|
|
|
95
104
|
export function StatusIcon({ word }: { word: StatusWord }) {
|
|
96
105
|
const look = statusLookOf(word);
|
|
106
|
+
|
|
97
107
|
return <Icon icon={look.icon} size={14} className={clsx('shrink-0', look.tone, look.spins && 'animate-spin')} />;
|
|
98
108
|
}
|
|
99
109
|
|
|
@@ -142,6 +152,7 @@ function ActivityRow({
|
|
|
142
152
|
{time !== '' && <span className="shrink-0 text-xs text-text-faint tabular-nums">{time}</span>}
|
|
143
153
|
</>
|
|
144
154
|
);
|
|
155
|
+
|
|
145
156
|
// The stop is a button of its own beside the line, since a button cannot hold another.
|
|
146
157
|
return (
|
|
147
158
|
<div className={clsx('flex items-center gap-1 rounded-md', onOpen && 'hover:bg-surface-hover')}>
|
|
@@ -163,6 +174,7 @@ function SubagentRows({ chatId, items }: { chatId: string; items: readonly ChatS
|
|
|
163
174
|
1000,
|
|
164
175
|
items.some((item) => item.status === 'running')
|
|
165
176
|
);
|
|
177
|
+
|
|
166
178
|
return items.map((item) => <SubagentEntry key={item.id} chatId={chatId} item={item} now={now} />);
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -175,6 +187,7 @@ function SubagentEntry({ chatId, item, now }: { chatId: string; item: ChatSubage
|
|
|
175
187
|
const child = useChats((s) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info));
|
|
176
188
|
const word = statusWordOf(item, task);
|
|
177
189
|
const state = taskRowStateOf(item, task, child);
|
|
190
|
+
|
|
178
191
|
return (
|
|
179
192
|
<ActivityRow
|
|
180
193
|
icon={<StatusIcon word={word} />}
|
|
@@ -51,7 +51,9 @@ export function CodeBlock({ code, lang, actions, view }: { code: string; lang: s
|
|
|
51
51
|
if (copyState !== 'copied') {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
+
|
|
54
55
|
const timer = setTimeout(() => setCopyState('idle'), COPIED_MS);
|
|
56
|
+
|
|
55
57
|
return () => clearTimeout(timer);
|
|
56
58
|
}, [copyState]);
|
|
57
59
|
|
|
@@ -59,8 +61,10 @@ export function CodeBlock({ code, lang, actions, view }: { code: string; lang: s
|
|
|
59
61
|
if (copying.current) {
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
64
|
+
|
|
62
65
|
copying.current = true;
|
|
63
66
|
setCopyState('copying');
|
|
67
|
+
|
|
64
68
|
try {
|
|
65
69
|
// The best-effort clipboard helper cannot distinguish a refusal from success.
|
|
66
70
|
await navigator.clipboard.writeText(code);
|
|
@@ -76,16 +80,20 @@ export function CodeBlock({ code, lang, actions, view }: { code: string; lang: s
|
|
|
76
80
|
if (tokenizer !== null) {
|
|
77
81
|
return;
|
|
78
82
|
}
|
|
83
|
+
|
|
79
84
|
let cancelled = false;
|
|
85
|
+
|
|
80
86
|
Promise.all([highlighter.loadLanguage(lang), highlighter.loadTheme(theme, custom)])
|
|
81
87
|
.then(() => !cancelled && setLoaded(`${lang}:${theme}`))
|
|
82
88
|
.catch(() => !cancelled && setLoaded('failed'));
|
|
89
|
+
|
|
83
90
|
return () => {
|
|
84
91
|
cancelled = true;
|
|
85
92
|
};
|
|
86
93
|
}, [highlighter, lang, theme, custom, tokenizer]);
|
|
87
94
|
|
|
88
95
|
const pending = (tokenizer === null || tokens === null) && loaded !== 'failed';
|
|
96
|
+
|
|
89
97
|
return (
|
|
90
98
|
<div className="chat-code" data-copy-state={copyState}>
|
|
91
99
|
{view ?? (
|
package/src/chat/ui/Composer.tsx
CHANGED
|
@@ -70,9 +70,11 @@ function sendLabelKey(busy: boolean, steerable: boolean, delivery: 'queue' | 'st
|
|
|
70
70
|
if (!busy) {
|
|
71
71
|
return 'composer.send';
|
|
72
72
|
}
|
|
73
|
+
|
|
73
74
|
if (!steerable) {
|
|
74
75
|
return 'composer.queueButton';
|
|
75
76
|
}
|
|
77
|
+
|
|
76
78
|
return delivery === 'steer' ? 'composer.steerButton' : 'composer.queueWithSteerButton';
|
|
77
79
|
}
|
|
78
80
|
|
|
@@ -96,6 +98,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
96
98
|
const notice = useNotice();
|
|
97
99
|
const history = usePromptHistory(chatId, box.setText);
|
|
98
100
|
const menus = useComposerMenus({ chatId, info, capabilities, providerName: provider?.name, draft: box, inputRef });
|
|
101
|
+
|
|
99
102
|
const files = useDraftFiles({
|
|
100
103
|
capabilities,
|
|
101
104
|
providerName: provider?.name,
|
|
@@ -107,7 +110,9 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
107
110
|
referables: menus.referables.chatsOnly ? menus.referables.items.filter((item) => item.kind === 'chat') : menus.referables.items,
|
|
108
111
|
referenceChips: menus.referenceChips
|
|
109
112
|
});
|
|
113
|
+
|
|
110
114
|
const controls = useChatControls({ chatId, info, notice, onRetarget });
|
|
115
|
+
|
|
111
116
|
useEffortStepper({
|
|
112
117
|
chatId,
|
|
113
118
|
info,
|
|
@@ -116,12 +121,14 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
116
121
|
controls,
|
|
117
122
|
setNotice: notice.setNotice
|
|
118
123
|
});
|
|
124
|
+
|
|
119
125
|
const guard = promptGuard(withQuote(draft.quote, draft.text));
|
|
120
126
|
const continuable = useContinuableTurn(chatId, info);
|
|
121
127
|
// The stopped turn something went out for, since the chat takes a moment to answer and Continue must not stand there meanwhile.
|
|
122
128
|
const [sentFor, setSentFor] = useState<string | null>(null);
|
|
123
129
|
const extraPrompts = useHostExtraPrompts(scope.id, chatId);
|
|
124
130
|
const sendActions = useHostComposerSendActions(scope.id, chatId);
|
|
131
|
+
|
|
125
132
|
const { submit, continueTurn, restoreStashed, toggleStash } = useComposerSubmit({
|
|
126
133
|
chatId,
|
|
127
134
|
writable,
|
|
@@ -138,6 +145,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
138
145
|
onSend: (text, extras) => {
|
|
139
146
|
setSentFor(continuable);
|
|
140
147
|
const sent = onSend(text, extras);
|
|
148
|
+
|
|
141
149
|
// A send the host did not take leaves Continue where it was; the original promise still reaches its caller.
|
|
142
150
|
void Promise.resolve(sent).then(
|
|
143
151
|
(taken) => {
|
|
@@ -147,12 +155,15 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
147
155
|
},
|
|
148
156
|
(error: unknown) => {
|
|
149
157
|
setSentFor(null);
|
|
158
|
+
|
|
150
159
|
throw error;
|
|
151
160
|
}
|
|
152
161
|
);
|
|
162
|
+
|
|
153
163
|
return sent;
|
|
154
164
|
}
|
|
155
165
|
});
|
|
166
|
+
|
|
156
167
|
const { takingBack, takeBack } = useTakeBack({
|
|
157
168
|
chatId,
|
|
158
169
|
draft: box,
|
|
@@ -162,16 +173,19 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
162
173
|
inputRef.current?.focus();
|
|
163
174
|
}
|
|
164
175
|
});
|
|
176
|
+
|
|
165
177
|
const pending = usePendingRequests(chatId);
|
|
166
178
|
const compactionOffer = useCompactionOffer(chatId, info, capabilities?.compaction);
|
|
167
179
|
const placeholder = useHintedPlaceholder({ chatId, disabled, readOnly, mentionable: capabilities?.mentions !== false, hasSkills: menus.hasSkills });
|
|
168
180
|
const editorExtensions = [chipDecorations({ mentions: draft.mentions, skills: draft.skills, references: menus.referenceChips }), LEAVE_DROPS_TO_THE_CARD];
|
|
181
|
+
|
|
169
182
|
/* The timeline owns the scroller; this is the one bit of it the composer needs to know. */
|
|
170
183
|
const atEnd = useSyncExternalStore(
|
|
171
184
|
subscribeTimelineEnd,
|
|
172
185
|
() => timelineAtEnd(chatId),
|
|
173
186
|
() => true
|
|
174
187
|
);
|
|
188
|
+
|
|
175
189
|
const queue = info.queue ?? [];
|
|
176
190
|
const empty = isEmptyDraft(draft);
|
|
177
191
|
|
|
@@ -201,6 +215,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
201
215
|
if (!writable || event.button !== 0 || event.defaultPrevented || surfacePartOf(event.target, event.currentTarget) !== 'surface') {
|
|
202
216
|
return;
|
|
203
217
|
}
|
|
218
|
+
|
|
204
219
|
// Nothing around the field selects, so the default would only blur it on the way, closing its picker and lifting a held prompt card.
|
|
205
220
|
event.preventDefault();
|
|
206
221
|
inputRef.current?.setCaret(Infinity);
|
|
@@ -69,12 +69,14 @@ export function ComposerInput({
|
|
|
69
69
|
const hostRef = useRef<HTMLDivElement>(null);
|
|
70
70
|
const targetRef = useRef<HTMLDivElement>(null);
|
|
71
71
|
const viewRef = useRef<EditorView | null>(null);
|
|
72
|
+
|
|
72
73
|
const [compartments] = useState(() => ({
|
|
73
74
|
attributes: new Compartment(),
|
|
74
75
|
editable: new Compartment(),
|
|
75
76
|
placeholder: new Compartment(),
|
|
76
77
|
extensions: new Compartment()
|
|
77
78
|
}));
|
|
79
|
+
|
|
78
80
|
const latest = useRef({ value, handlers });
|
|
79
81
|
// What the editor last reported. The parent handing that back is catching up, not setting a value of its own.
|
|
80
82
|
const reported = useRef(value);
|
|
@@ -85,10 +87,13 @@ export function ComposerInput({
|
|
|
85
87
|
|
|
86
88
|
useLayoutEffect(() => {
|
|
87
89
|
const host = hostRef.current;
|
|
90
|
+
|
|
88
91
|
if (!host) {
|
|
89
92
|
return;
|
|
90
93
|
}
|
|
94
|
+
|
|
91
95
|
const initial = latest.current.value;
|
|
96
|
+
|
|
92
97
|
const view = new EditorView({
|
|
93
98
|
parent: host,
|
|
94
99
|
state: EditorState.create({
|
|
@@ -109,24 +114,30 @@ export function ComposerInput({
|
|
|
109
114
|
cut: (event, target) => latest.current.handlers.onCopy?.(event, target) ?? false,
|
|
110
115
|
compositionstart: () => {
|
|
111
116
|
latest.current.handlers.onComposing?.(true);
|
|
117
|
+
|
|
112
118
|
return false;
|
|
113
119
|
},
|
|
114
120
|
compositionend: () => {
|
|
115
121
|
latest.current.handlers.onComposing?.(false);
|
|
122
|
+
|
|
116
123
|
return false;
|
|
117
124
|
}
|
|
118
125
|
})
|
|
119
126
|
),
|
|
120
127
|
EditorView.updateListener.of((update) => {
|
|
121
128
|
const current = latest.current.handlers;
|
|
129
|
+
|
|
122
130
|
if (update.focusChanged && !update.view.hasFocus) {
|
|
123
131
|
current.onBlur?.();
|
|
124
132
|
}
|
|
133
|
+
|
|
125
134
|
if ((!update.docChanged && !update.selectionSet) || update.transactions.some((tr) => tr.annotation(externalChange))) {
|
|
126
135
|
return;
|
|
127
136
|
}
|
|
137
|
+
|
|
128
138
|
const text = update.state.doc.toString();
|
|
129
139
|
const { from, to } = update.state.selection.main;
|
|
140
|
+
|
|
130
141
|
if (update.docChanged) {
|
|
131
142
|
reported.current = text;
|
|
132
143
|
current.onChange(text, { from, to }, update.state);
|
|
@@ -137,7 +148,9 @@ export function ComposerInput({
|
|
|
137
148
|
]
|
|
138
149
|
})
|
|
139
150
|
});
|
|
151
|
+
|
|
140
152
|
viewRef.current = view;
|
|
153
|
+
|
|
141
154
|
return () => {
|
|
142
155
|
view.destroy();
|
|
143
156
|
viewRef.current = null;
|
|
@@ -158,14 +171,18 @@ export function ComposerInput({
|
|
|
158
171
|
|
|
159
172
|
useLayoutEffect(() => {
|
|
160
173
|
const view = viewRef.current;
|
|
174
|
+
|
|
161
175
|
if (!view || value === reported.current) {
|
|
162
176
|
return;
|
|
163
177
|
}
|
|
178
|
+
|
|
164
179
|
reported.current = value;
|
|
165
180
|
const doc = view.state.doc.toString();
|
|
181
|
+
|
|
166
182
|
if (value === doc) {
|
|
167
183
|
return;
|
|
168
184
|
}
|
|
185
|
+
|
|
169
186
|
view.dispatch({
|
|
170
187
|
changes: { from: 0, to: doc.length, insert: value },
|
|
171
188
|
selection: { anchor: value.length },
|
|
@@ -179,17 +196,21 @@ export function ComposerInput({
|
|
|
179
196
|
focus: () => viewRef.current?.focus(),
|
|
180
197
|
setCaret: (pos: number) => {
|
|
181
198
|
const view = viewRef.current;
|
|
199
|
+
|
|
182
200
|
if (!view) {
|
|
183
201
|
return;
|
|
184
202
|
}
|
|
203
|
+
|
|
185
204
|
view.dispatch({ selection: { anchor: Math.min(pos, view.state.doc.length) }, scrollIntoView: true });
|
|
186
205
|
view.focus();
|
|
187
206
|
},
|
|
188
207
|
insert: (text: string) => {
|
|
189
208
|
const view = viewRef.current;
|
|
209
|
+
|
|
190
210
|
if (!view) {
|
|
191
211
|
return;
|
|
192
212
|
}
|
|
213
|
+
|
|
193
214
|
view.dispatch(insertAtSelection(view.state, text));
|
|
194
215
|
view.focus();
|
|
195
216
|
}
|
|
@@ -198,6 +219,7 @@ export function ComposerInput({
|
|
|
198
219
|
);
|
|
199
220
|
|
|
200
221
|
const Dictation = dictation.composer?.Control;
|
|
222
|
+
|
|
201
223
|
return (
|
|
202
224
|
<div ref={targetRef} className="min-w-0">
|
|
203
225
|
<div ref={hostRef} className={className} />
|
package/src/chat/ui/DiffPool.tsx
CHANGED
|
@@ -9,9 +9,11 @@ const POOL_SIZE = 2;
|
|
|
9
9
|
function ThemeSync() {
|
|
10
10
|
const pool = useWorkerPool();
|
|
11
11
|
const theme = useDiffTheme();
|
|
12
|
+
|
|
12
13
|
useEffect(() => {
|
|
13
14
|
void pool?.setRenderOptions({ theme });
|
|
14
15
|
}, [pool, theme]);
|
|
16
|
+
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -21,6 +23,7 @@ function ThemeSync() {
|
|
|
21
23
|
*/
|
|
22
24
|
export default function DiffPool({ workerFactory, children }: { workerFactory: () => Worker; children: ReactNode }) {
|
|
23
25
|
const theme = useDiffTheme();
|
|
26
|
+
|
|
24
27
|
return (
|
|
25
28
|
<WorkerPoolContextProvider
|
|
26
29
|
poolOptions={{
|
package/src/chat/ui/EditDiff.tsx
CHANGED
|
@@ -20,6 +20,7 @@ export default function EditDiff({ change, allowEditor = true, maxHeight }: { ch
|
|
|
20
20
|
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
21
21
|
const legacyAllowed = legacyMode && legacyDiffBudget(change.before, change.after);
|
|
22
22
|
const omitted = <DiffOmitted path={change.path} />;
|
|
23
|
+
|
|
23
24
|
const fallback = legacyAllowed ? (
|
|
24
25
|
<DiffFallbackBoundary fallback={omitted} resetKeys={[change.path, change.before, change.after]}>
|
|
25
26
|
<LazyBoundary fallback={<div className="px-3 py-2 text-xs text-text-faint">{t('work.loadingDiff')}</div>}>
|
|
@@ -29,13 +30,17 @@ export default function EditDiff({ change, allowEditor = true, maxHeight }: { ch
|
|
|
29
30
|
) : (
|
|
30
31
|
omitted
|
|
31
32
|
);
|
|
33
|
+
|
|
32
34
|
if (!sourceAllowed) {
|
|
33
35
|
return omitted;
|
|
34
36
|
}
|
|
37
|
+
|
|
35
38
|
if (legacyMode) {
|
|
36
39
|
return fallback;
|
|
37
40
|
}
|
|
41
|
+
|
|
38
42
|
const unavailable = <DiffUnavailable path={change.path} />;
|
|
43
|
+
|
|
39
44
|
return (
|
|
40
45
|
<DiffFallbackBoundary fallback={unavailable} resetKeys={[change.path, change.before, change.after, loadEngine]}>
|
|
41
46
|
<LazyBoundary fallback={<div className="px-3 py-2 text-xs text-text-faint">{t('work.loadingDiff')}</div>}>
|
package/src/chat/ui/GoalDock.tsx
CHANGED
|
@@ -11,13 +11,16 @@ import { useChatScope } from '../../scope';
|
|
|
11
11
|
import { useChats } from '../../state/chats';
|
|
12
12
|
import { useChatActions } from '../actions';
|
|
13
13
|
import { goalView } from '../logic/goal';
|
|
14
|
+
import { errorMessage } from '../../transport';
|
|
14
15
|
|
|
15
16
|
/* How far the goal got, in what its CLI counts: the checks that sent the agent on, or the tokens it spent. */
|
|
16
17
|
function progressOf(goal: ChatGoal, t: TFunction<'agent-chat'>): string | null {
|
|
17
18
|
const parts: string[] = [];
|
|
19
|
+
|
|
18
20
|
if (goal.checks !== undefined && goal.checks > 0) {
|
|
19
21
|
parts.push(t('goal.checks', { count: goal.checks }));
|
|
20
22
|
}
|
|
23
|
+
|
|
21
24
|
if (goal.tokensUsed !== undefined && (goal.tokensUsed > 0 || goal.tokenBudget !== undefined)) {
|
|
22
25
|
parts.push(
|
|
23
26
|
goal.tokenBudget === undefined
|
|
@@ -25,6 +28,7 @@ function progressOf(goal: ChatGoal, t: TFunction<'agent-chat'>): string | null {
|
|
|
25
28
|
: t('goal.budget', { tokens: formatTokens(goal.tokensUsed), budget: formatTokens(goal.tokenBudget) })
|
|
26
29
|
);
|
|
27
30
|
}
|
|
31
|
+
|
|
28
32
|
return parts.length === 0 ? null : parts.join(' · ');
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -39,21 +43,27 @@ export function GoalDock({ chatId, info, goals }: { chatId: string; info: ChatIn
|
|
|
39
43
|
const actions = useChatActions();
|
|
40
44
|
const [changing, setChanging] = useState(false);
|
|
41
45
|
const view = goalView(info, goals, actions.goal !== undefined);
|
|
46
|
+
|
|
42
47
|
if (view === null) {
|
|
43
48
|
return null;
|
|
44
49
|
}
|
|
50
|
+
|
|
45
51
|
const { goal } = view;
|
|
46
52
|
const progress = progressOf(goal, t);
|
|
53
|
+
|
|
47
54
|
const act = (action: ChatGoalAction): void => {
|
|
48
55
|
if (!actions.goal) {
|
|
49
56
|
return;
|
|
50
57
|
}
|
|
58
|
+
|
|
51
59
|
setChanging(true);
|
|
60
|
+
|
|
52
61
|
actions
|
|
53
62
|
.goal(chatId, action)
|
|
54
|
-
.catch((e: unknown) => host.notify({ kind: 'error', title: t('goal.failed'), description:
|
|
63
|
+
.catch((e: unknown) => host.notify({ kind: 'error', title: t('goal.failed'), description: errorMessage(e) }))
|
|
55
64
|
.finally(() => setChanging(false));
|
|
56
65
|
};
|
|
66
|
+
|
|
57
67
|
return (
|
|
58
68
|
<div className="flex items-center gap-2 border-b border-border px-3.5 py-2.5">
|
|
59
69
|
<Icon icon={Target} size={16} className={clsx('shrink-0', goal.status === 'active' ? 'text-accent' : 'text-text-faint')} />
|
|
@@ -91,9 +101,11 @@ export function GoalPill({ chatId }: { chatId: string }) {
|
|
|
91
101
|
const { t } = useTranslation('agent-chat');
|
|
92
102
|
const { keyOf } = useChatScope();
|
|
93
103
|
const goal = useChats((s) => s.statusByKey[keyOf(chatId)]?.info.goal);
|
|
104
|
+
|
|
94
105
|
if (goal?.status !== 'active') {
|
|
95
106
|
return null;
|
|
96
107
|
}
|
|
108
|
+
|
|
97
109
|
return (
|
|
98
110
|
<Tooltip label={`${t('goal.status.active')}: ${goal.objective}`}>
|
|
99
111
|
<Pill tone="accent" icon={<Icon icon={Target} size={12} />}>
|
|
@@ -26,10 +26,13 @@ const START: View = { scale: 1, x: 0, y: 0 };
|
|
|
26
26
|
*/
|
|
27
27
|
function zoomed(view: View, factor: number, pointX: number, pointY: number): View {
|
|
28
28
|
const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, view.scale * factor));
|
|
29
|
+
|
|
29
30
|
if (scale === MIN_SCALE) {
|
|
30
31
|
return START;
|
|
31
32
|
}
|
|
33
|
+
|
|
32
34
|
const ratio = scale / view.scale;
|
|
35
|
+
|
|
33
36
|
return { scale, x: pointX - (pointX - view.x) * ratio, y: pointY - (pointY - view.y) * ratio };
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -62,9 +65,11 @@ function Lightbox({
|
|
|
62
65
|
/* Where the pointer sits against the middle of the frame, which is what the transform counts from. */
|
|
63
66
|
const pointIn = (event: { clientX: number; clientY: number }): { x: number; y: number } => {
|
|
64
67
|
const rect = frameRef.current?.getBoundingClientRect();
|
|
68
|
+
|
|
65
69
|
if (!rect) {
|
|
66
70
|
return { x: 0, y: 0 };
|
|
67
71
|
}
|
|
72
|
+
|
|
68
73
|
return { x: event.clientX - rect.left - rect.width / 2, y: event.clientY - rect.top - rect.height / 2 };
|
|
69
74
|
};
|
|
70
75
|
|
|
@@ -114,11 +119,13 @@ function Lightbox({
|
|
|
114
119
|
if (view.scale <= MIN_SCALE) {
|
|
115
120
|
return;
|
|
116
121
|
}
|
|
122
|
+
|
|
117
123
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
118
124
|
dragRef.current = { pointerId: e.pointerId, x: e.clientX - view.x, y: e.clientY - view.y };
|
|
119
125
|
}}
|
|
120
126
|
onPointerMove={(e) => {
|
|
121
127
|
const drag = dragRef.current;
|
|
128
|
+
|
|
122
129
|
if (drag?.pointerId === e.pointerId) {
|
|
123
130
|
setView((current) => ({ ...current, x: e.clientX - drag.x, y: e.clientY - drag.y }));
|
|
124
131
|
}
|
|
@@ -163,12 +170,15 @@ export interface ImageThumbProps {
|
|
|
163
170
|
export function ImageThumb({ source, alt, className, aspect, actions, open: openProp, onOpenChange }: ImageThumbProps) {
|
|
164
171
|
const [ownOpen, setOwnOpen] = useState(false);
|
|
165
172
|
const open = openProp ?? ownOpen;
|
|
173
|
+
|
|
166
174
|
const setOpen = (next: boolean): void => {
|
|
167
175
|
setOwnOpen(next);
|
|
168
176
|
onOpenChange?.(next);
|
|
169
177
|
};
|
|
178
|
+
|
|
170
179
|
const { url: src, failure } = source;
|
|
171
180
|
const ratio = aspect === undefined ? undefined : { aspectRatio: aspect };
|
|
181
|
+
|
|
172
182
|
if (src === null) {
|
|
173
183
|
return (
|
|
174
184
|
<span
|
|
@@ -186,6 +196,7 @@ export function ImageThumb({ source, alt, className, aspect, actions, open: open
|
|
|
186
196
|
</span>
|
|
187
197
|
);
|
|
188
198
|
}
|
|
199
|
+
|
|
189
200
|
return (
|
|
190
201
|
<>
|
|
191
202
|
<button
|
|
@@ -18,6 +18,7 @@ import { useChatActions } from '../actions';
|
|
|
18
18
|
import { useUsageStore } from '../../state/usage';
|
|
19
19
|
import { SessionLine } from './SessionLine';
|
|
20
20
|
import { useHostResumeAtReset } from '../../host-hooks';
|
|
21
|
+
import { errorMessage } from '../../transport';
|
|
21
22
|
|
|
22
23
|
// A time a limit names reads as a clock today and with its day after that, so the words follow the minute.
|
|
23
24
|
const MINUTE_MS = 60_000;
|
|
@@ -32,10 +33,13 @@ const refreshedAt = new Map<string, number>();
|
|
|
32
33
|
*/
|
|
33
34
|
function readUnreadAccounts(scope: ChatScope): void {
|
|
34
35
|
const last = refreshedAt.get(scope.id);
|
|
36
|
+
|
|
35
37
|
if (last !== undefined && Date.now() - last < UNREAD_REFRESH_MS) {
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
40
|
+
|
|
38
41
|
refreshedAt.set(scope.id, Date.now());
|
|
42
|
+
|
|
39
43
|
void scope.transport
|
|
40
44
|
.request('usage.refreshLimits', {})
|
|
41
45
|
.then((snapshot) => useUsageStore.getState().setLimits(scope.id, snapshot))
|
|
@@ -51,9 +55,11 @@ function ResumeAtResetToggle({ chatId, info }: { chatId: string; info: ChatInfo
|
|
|
51
55
|
const { id } = useChatScope();
|
|
52
56
|
const actions = useChatActions();
|
|
53
57
|
const machineAllows = useHostResumeAtReset(id);
|
|
58
|
+
|
|
54
59
|
const set = (on: boolean): void => {
|
|
55
60
|
void actions.configure(chatId, { resumeAtReset: on }).catch(() => undefined);
|
|
56
61
|
};
|
|
62
|
+
|
|
57
63
|
return (
|
|
58
64
|
<Tooltip label={machineAllows ? t('limit.toggle') : t('limit.machineOff')}>
|
|
59
65
|
{/* A disabled switch does not take the pointer, so the tooltip hangs on a wrapper. */}
|
|
@@ -65,11 +71,8 @@ function ResumeAtResetToggle({ chatId, info }: { chatId: string; info: ChatInfo
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
/*
|
|
68
|
-
* Another account of the chat's CLI
|
|
69
|
-
*
|
|
70
|
-
* work. Only ever offered, since going on there spends someone's other plan, and nothing is
|
|
71
|
-
* remembered: the next limit suggests the one with the most room again. An account that reads the
|
|
72
|
-
* chat's conversation takes it over; any other goes on in a fork, which then opens the way a fork does.
|
|
74
|
+
* Another account of the chat's CLI with room, only ever offered since it spends someone's other plan, and
|
|
75
|
+
* never remembered. One that reads the chat's conversation takes it over; any other goes on in a fork.
|
|
73
76
|
*/
|
|
74
77
|
function ContinueOn({ chatId, info, choice }: { chatId: string; info: ChatInfo; choice: AccountChoice }) {
|
|
75
78
|
const host = useChatHost();
|
|
@@ -88,24 +91,29 @@ function ContinueOn({ chatId, info, choice }: { chatId: string; info: ChatInfo;
|
|
|
88
91
|
}, [scope, unread]);
|
|
89
92
|
|
|
90
93
|
const target = targets[0];
|
|
94
|
+
|
|
91
95
|
if (target === undefined) {
|
|
92
96
|
return null;
|
|
93
97
|
}
|
|
98
|
+
|
|
94
99
|
const go = (entry: AccountEntry): void => {
|
|
95
100
|
setBusy(true);
|
|
101
|
+
|
|
96
102
|
actions
|
|
97
103
|
.continueOn(chatId, entry.id)
|
|
98
104
|
.catch((e: unknown) =>
|
|
99
105
|
host.notify({
|
|
100
106
|
kind: 'error',
|
|
101
107
|
title: t('limit.continueFailed', { account: choice.nameOf(entry) }),
|
|
102
|
-
description:
|
|
108
|
+
description: errorMessage(e)
|
|
103
109
|
})
|
|
104
110
|
)
|
|
105
111
|
.finally(() => setBusy(false));
|
|
106
112
|
};
|
|
113
|
+
|
|
107
114
|
const name = choice.nameOf(target);
|
|
108
115
|
const split = targets.length > 1;
|
|
116
|
+
|
|
109
117
|
const suggestion = (
|
|
110
118
|
<Tooltip label={t('limit.continueOnTip', { account: name })}>
|
|
111
119
|
<Button
|
|
@@ -122,9 +130,11 @@ function ContinueOn({ chatId, info, choice }: { chatId: string; info: ChatInfo;
|
|
|
122
130
|
</Button>
|
|
123
131
|
</Tooltip>
|
|
124
132
|
);
|
|
133
|
+
|
|
125
134
|
if (!split) {
|
|
126
135
|
return suggestion;
|
|
127
136
|
}
|
|
137
|
+
|
|
128
138
|
return (
|
|
129
139
|
<ButtonGroup className="shrink-0">
|
|
130
140
|
{suggestion}
|
|
@@ -160,10 +170,12 @@ function ContinueOnRows({
|
|
|
160
170
|
const { t } = useTranslation('agent-chat');
|
|
161
171
|
const limits = useUsageLimits();
|
|
162
172
|
const now = useNow(MINUTE_MS);
|
|
173
|
+
|
|
163
174
|
return targets.map((entry) => {
|
|
164
175
|
const name = choice.nameOf(entry);
|
|
165
176
|
const session = sessionWindow(limitsOfAccount(limits, entry.id));
|
|
166
177
|
const fork = !canContinueOn(choice.accounts, info.provider, info.account, entry.id);
|
|
178
|
+
|
|
167
179
|
return (
|
|
168
180
|
<Menu.Item
|
|
169
181
|
key={entry.id}
|
|
@@ -191,12 +203,15 @@ export function LimitDock({ chatId, info }: { chatId: string; info: ChatInfo })
|
|
|
191
203
|
const now = useNow(MINUTE_MS, info.limit !== undefined);
|
|
192
204
|
const choice = useAccountChoice(info.provider, info.account);
|
|
193
205
|
const view = limitView(info, now);
|
|
206
|
+
|
|
194
207
|
if (view === null) {
|
|
195
208
|
return null;
|
|
196
209
|
}
|
|
210
|
+
|
|
197
211
|
// With a choice of accounts the line names the one that ran into the limit.
|
|
198
212
|
const account = choice === null ? null : choice.current === null ? choice.currentId : choice.nameOf(choice.current);
|
|
199
213
|
const detail = account === null ? view.detail : view.detail === null ? account : `${account} · ${view.detail}`;
|
|
214
|
+
|
|
200
215
|
return (
|
|
201
216
|
<div className="flex items-center gap-2 border-b border-border px-3.5 py-2.5">
|
|
202
217
|
<Icon icon={Hourglass} size={16} className="shrink-0 text-text-faint" />
|
|
@@ -218,9 +233,11 @@ export function LimitPill({ chatId }: { chatId: string }) {
|
|
|
218
233
|
useFormatLocale();
|
|
219
234
|
const now = useNow(MINUTE_MS, info?.limit !== undefined);
|
|
220
235
|
const view = info === undefined ? null : limitView(info, now);
|
|
236
|
+
|
|
221
237
|
if (info === undefined || view === null) {
|
|
222
238
|
return null;
|
|
223
239
|
}
|
|
240
|
+
|
|
224
241
|
return (
|
|
225
242
|
<>
|
|
226
243
|
<Tooltip label={view.detail === null ? view.title : `${view.title}. ${view.detail}`}>
|