@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
|
@@ -54,9 +54,11 @@ export function useDraftFiles({
|
|
|
54
54
|
/* Paths dragged in from a list of files. With no mention query to insert into, they land at the end of the prompt. */
|
|
55
55
|
const addMentions = (paths: string[]): void => {
|
|
56
56
|
const fresh = paths.filter((path) => path !== '');
|
|
57
|
+
|
|
57
58
|
if (fresh.length === 0 || capabilities?.mentions === false) {
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
61
|
+
|
|
60
62
|
const appended = fresh.map((path) => `@${path}`).join(' ');
|
|
61
63
|
const before = draft.text.replace(/\s+$/, '');
|
|
62
64
|
setText(before === '' ? `${appended} ` : `${before} ${appended} `, [...draft.mentions, ...fresh.filter((path) => !draft.mentions.includes(path))]);
|
|
@@ -66,21 +68,27 @@ export function useDraftFiles({
|
|
|
66
68
|
if (incoming.length === 0) {
|
|
67
69
|
return;
|
|
68
70
|
}
|
|
71
|
+
|
|
69
72
|
if (capabilities?.attachments === false) {
|
|
70
73
|
setNotice(t('composer.notice.noAttachments', { agent: providerName ?? t('composer.notice.thisAgent') }));
|
|
74
|
+
|
|
71
75
|
return;
|
|
72
76
|
}
|
|
77
|
+
|
|
73
78
|
const checked = checkAttachmentLimits(
|
|
74
79
|
draft.attachments.length,
|
|
75
80
|
incoming.map((file) => ({ name: file.name, mime: file.type, bytes: file.size, file })),
|
|
76
81
|
draft.attachments.reduce((bytes, attachment) => bytes + uploadBytes(attachment), 0)
|
|
77
82
|
);
|
|
83
|
+
|
|
78
84
|
if (checked.rejected[0]) {
|
|
79
85
|
setNotice(t('composer.notice.rejected', { name: checked.rejected[0].name || t('composer.notice.thatFile'), reason: checked.rejected[0].reason }));
|
|
80
86
|
}
|
|
87
|
+
|
|
81
88
|
if (checked.accepted.length === 0) {
|
|
82
89
|
return;
|
|
83
90
|
}
|
|
91
|
+
|
|
84
92
|
readAttachments(checked.accepted.map((entry) => entry.file))
|
|
85
93
|
.then((attachments) => setDraft((current) => ({ ...current, attachments: [...current.attachments, ...attachments] })))
|
|
86
94
|
.catch(() => setNotice(t('composer.notice.unreadable')));
|
|
@@ -94,24 +102,32 @@ export function useDraftFiles({
|
|
|
94
102
|
/* Files become attachments and a long text a file of its own; anything else the editor pastes as plain text. */
|
|
95
103
|
const onPaste = (e: ClipboardEvent, view: EditorView): boolean => {
|
|
96
104
|
const data = e.clipboardData;
|
|
105
|
+
|
|
97
106
|
if (!data) {
|
|
98
107
|
return false;
|
|
99
108
|
}
|
|
109
|
+
|
|
100
110
|
const pasted = filesOf(data);
|
|
111
|
+
|
|
101
112
|
if (pasted.length > 0) {
|
|
102
113
|
addFiles(pasted);
|
|
114
|
+
|
|
103
115
|
return true;
|
|
104
116
|
}
|
|
117
|
+
|
|
105
118
|
const inline = pasteInline.current;
|
|
106
119
|
pasteInline.current = false;
|
|
107
120
|
const clip = data.getData('text/plain');
|
|
108
121
|
const mentions = capabilities?.mentions === false ? [] : pastedMentions(clip);
|
|
122
|
+
|
|
109
123
|
if (mentions.length > 0) {
|
|
110
124
|
// The editor's own change reads the draft as it was rendered, so the mentions join it after that change.
|
|
111
125
|
view.dispatch(view.state.replaceSelection(clip), { scrollIntoView: true, userEvent: 'input.paste' });
|
|
112
126
|
setDraft((current) => ({ ...current, mentions: [...current.mentions, ...mentions.filter((path) => !current.mentions.includes(path))] }));
|
|
127
|
+
|
|
113
128
|
return true;
|
|
114
129
|
}
|
|
130
|
+
|
|
115
131
|
// Without attachments there is nowhere else for the text to go, so it pastes as it always did.
|
|
116
132
|
if (inline || capabilities?.attachments === false || !pasteBecomesAttachment(clip)) {
|
|
117
133
|
const choices: PasteChoices = {
|
|
@@ -120,21 +136,29 @@ export function useDraftFiles({
|
|
|
120
136
|
referenced: [...draft.chats, ...draft.references],
|
|
121
137
|
mentions: capabilities?.mentions !== false
|
|
122
138
|
};
|
|
139
|
+
|
|
123
140
|
const chips = pasteWithChips(view, clip, readClipboardChips(data), choices);
|
|
141
|
+
|
|
124
142
|
if (chips === null) {
|
|
125
143
|
return false;
|
|
126
144
|
}
|
|
145
|
+
|
|
127
146
|
// After the editor's own change, which reads the draft as it was rendered.
|
|
128
147
|
setDraft((current) => withPastedChips(current, chips, choices));
|
|
148
|
+
|
|
129
149
|
return true;
|
|
130
150
|
}
|
|
151
|
+
|
|
131
152
|
// The paste still replaces what was selected; it just puts nothing in its place.
|
|
132
153
|
const { from, to } = view.state.selection.main;
|
|
154
|
+
|
|
133
155
|
if (from !== to) {
|
|
134
156
|
view.dispatch({ changes: { from, to }, userEvent: 'delete' });
|
|
135
157
|
}
|
|
158
|
+
|
|
136
159
|
const name = pastedTextName(draft.attachments.map((attachment) => attachment.name));
|
|
137
160
|
addFiles([new File([clip], name, { type: 'text/plain' })]);
|
|
161
|
+
|
|
138
162
|
return true;
|
|
139
163
|
};
|
|
140
164
|
|
|
@@ -27,12 +27,16 @@ export function useEffortStepper({ chatId, info, model, writable, controls, setN
|
|
|
27
27
|
if (!writable || controls.configurationPending) {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
+
|
|
30
31
|
const name = model?.name ?? info.selection.model;
|
|
31
32
|
const result = stepEffort(model, info.selection, direction);
|
|
33
|
+
|
|
32
34
|
if (result.kind === 'none') {
|
|
33
35
|
setNotice(t('composer.notice.noEffort', { model: name }), 'info');
|
|
36
|
+
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
39
|
+
|
|
36
40
|
if (result.kind === 'edge') {
|
|
37
41
|
setNotice(
|
|
38
42
|
t(direction === 'up' ? 'composer.notice.effortHighest' : 'composer.notice.effortLowest', {
|
|
@@ -41,16 +45,20 @@ export function useEffortStepper({ chatId, info, model, writable, controls, setN
|
|
|
41
45
|
}),
|
|
42
46
|
'info'
|
|
43
47
|
);
|
|
48
|
+
|
|
44
49
|
return;
|
|
45
50
|
}
|
|
51
|
+
|
|
46
52
|
controls.configure({ selection: result.selection });
|
|
47
53
|
setNotice(t('composer.notice.effort', { effort: choiceWord(EFFORT_OPTION, result.choice) }), 'info');
|
|
48
54
|
};
|
|
49
55
|
|
|
50
56
|
// A command means the chat as it stands when it is given, so the registration reads the latest step.
|
|
51
57
|
const stepRef = useRef(step);
|
|
58
|
+
|
|
52
59
|
useEffect(() => {
|
|
53
60
|
stepRef.current = step;
|
|
54
61
|
});
|
|
62
|
+
|
|
55
63
|
useEffect(() => registerEffortStepper(scope.keyOf(chatId), (direction) => stepRef.current(direction)), [scope, chatId]);
|
|
56
64
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
+
import { errorMessage } from '../../../transport';
|
|
2
3
|
|
|
3
4
|
const NOTICE_MS = 4000;
|
|
4
5
|
|
|
@@ -20,7 +21,9 @@ export function useNotice(): ComposerNotice {
|
|
|
20
21
|
if (!shown) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
24
|
+
|
|
23
25
|
const timer = setTimeout(() => setShown(null), NOTICE_MS);
|
|
26
|
+
|
|
24
27
|
return () => clearTimeout(timer);
|
|
25
28
|
}, [shown]);
|
|
26
29
|
|
|
@@ -30,6 +33,6 @@ export function useNotice(): ComposerNotice {
|
|
|
30
33
|
notice: shown?.text ?? null,
|
|
31
34
|
tone: shown?.tone ?? 'error',
|
|
32
35
|
setNotice,
|
|
33
|
-
showError: (error) => setNotice(
|
|
36
|
+
showError: (error) => setNotice(errorMessage(error))
|
|
34
37
|
};
|
|
35
38
|
}
|
|
@@ -6,10 +6,13 @@ import { ChatPromptFeed } from '../../pending-prompts';
|
|
|
6
6
|
export function usePendingRequests(chatId: string): PromptSubject[] {
|
|
7
7
|
const scope = useChatScope();
|
|
8
8
|
const [subjects, setSubjects] = useState<PromptSubject[]>([]);
|
|
9
|
+
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
const feed = new ChatPromptFeed(scope, chatId, setSubjects);
|
|
11
12
|
feed.start();
|
|
13
|
+
|
|
12
14
|
return () => feed.dispose();
|
|
13
15
|
}, [scope, chatId]);
|
|
16
|
+
|
|
14
17
|
return subjects;
|
|
15
18
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react';
|
|
2
2
|
import { useChatRow } from '../../../state/chats';
|
|
3
3
|
|
|
4
4
|
// How many sent prompts the arrow keys walk back through, per chat.
|
|
@@ -23,33 +23,40 @@ export function usePromptHistory(chatId: string, setText: (next: string, mention
|
|
|
23
23
|
const items = useChatRow(chatId, (row) => row?.structure);
|
|
24
24
|
const [index, setIndex] = useState<number | null>(null);
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
26
|
+
const prompts: Array<{ text: string; mentions: string[] }> = [];
|
|
27
|
+
|
|
28
|
+
for (const id of order ?? []) {
|
|
29
|
+
const item = items?.[id];
|
|
30
|
+
|
|
31
|
+
if (item?.kind === 'user') {
|
|
32
|
+
prompts.push({ text: item.text, mentions: item.mentions ?? [] });
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Walking back further than this is faster with the timeline than with the arrow key.
|
|
37
|
+
const history = prompts.slice(-PROMPT_HISTORY);
|
|
37
38
|
|
|
38
39
|
const recall = (direction: -1 | 1): boolean => {
|
|
39
40
|
if (history.length === 0) {
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
43
|
+
|
|
42
44
|
const next = index === null ? (direction === -1 ? history.length - 1 : null) : index + direction;
|
|
45
|
+
|
|
43
46
|
if (next === null || next >= history.length) {
|
|
44
47
|
setIndex(null);
|
|
45
48
|
setText('', [], []);
|
|
49
|
+
|
|
46
50
|
return true;
|
|
47
51
|
}
|
|
52
|
+
|
|
48
53
|
if (next < 0) {
|
|
49
54
|
return true;
|
|
50
55
|
}
|
|
56
|
+
|
|
51
57
|
setIndex(next);
|
|
52
58
|
setText(history[next]!.text, history[next]!.mentions);
|
|
59
|
+
|
|
53
60
|
return true;
|
|
54
61
|
};
|
|
55
62
|
|
|
@@ -57,8 +64,10 @@ export function usePromptHistory(chatId: string, setText: (next: string, mention
|
|
|
57
64
|
if (index === null) {
|
|
58
65
|
return false;
|
|
59
66
|
}
|
|
67
|
+
|
|
60
68
|
setIndex(null);
|
|
61
69
|
setText('', [], []);
|
|
70
|
+
|
|
62
71
|
return true;
|
|
63
72
|
};
|
|
64
73
|
|
|
@@ -31,6 +31,7 @@ export function usePromptHold(): ComposerPromptHold {
|
|
|
31
31
|
const schedule = (): void => {
|
|
32
32
|
clearTimer();
|
|
33
33
|
const wait = holdLiftsIn({ typedAt: typedAt.current, composing: composing.current }, Date.now());
|
|
34
|
+
|
|
34
35
|
if (wait !== null) {
|
|
35
36
|
timer.current = setTimeout(() => release('pause'), wait);
|
|
36
37
|
}
|
|
@@ -44,11 +45,13 @@ export function usePromptHold(): ComposerPromptHold {
|
|
|
44
45
|
|
|
45
46
|
const setComposing = (next: boolean): void => {
|
|
46
47
|
composing.current = next;
|
|
48
|
+
|
|
47
49
|
if (next) {
|
|
48
50
|
setState({ held: true, releasedBy: null });
|
|
49
51
|
} else {
|
|
50
52
|
typedAt.current = Date.now();
|
|
51
53
|
}
|
|
54
|
+
|
|
52
55
|
schedule();
|
|
53
56
|
};
|
|
54
57
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useId,
|
|
1
|
+
import { useEffect, useId, useState, type Dispatch, type RefObject, type SetStateAction } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { ensureSyntaxTree, syntaxTree } from '@codemirror/language';
|
|
4
4
|
import type { EditorState } from '@codemirror/state';
|
|
@@ -108,11 +108,14 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
108
108
|
if (mention === null || cwd === null) {
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
+
|
|
111
112
|
let stale = false;
|
|
113
|
+
|
|
112
114
|
const search = (): void => {
|
|
113
115
|
if (searchFiles === null) {
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
118
|
+
|
|
116
119
|
searchFiles(scope.id, cwd, mention.query, MENTION_RESULTS)
|
|
117
120
|
.then((files) => {
|
|
118
121
|
if (!stale) {
|
|
@@ -122,14 +125,18 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
122
125
|
})
|
|
123
126
|
.catch(() => undefined);
|
|
124
127
|
};
|
|
128
|
+
|
|
125
129
|
// A bare `@` is not a keystroke in a query. It opens the list of what is there, at once.
|
|
126
130
|
if (mention.query === '') {
|
|
127
131
|
search();
|
|
132
|
+
|
|
128
133
|
return () => {
|
|
129
134
|
stale = true;
|
|
130
135
|
};
|
|
131
136
|
}
|
|
137
|
+
|
|
132
138
|
const timer = setTimeout(search, SEARCH_DEBOUNCE_MS);
|
|
139
|
+
|
|
133
140
|
return () => {
|
|
134
141
|
stale = true;
|
|
135
142
|
clearTimeout(timer);
|
|
@@ -137,14 +144,13 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
137
144
|
}, [scope, cwd, mention, searchFiles]);
|
|
138
145
|
|
|
139
146
|
const commandQuery = text.startsWith('/') && !text.includes('\n') ? text.slice(1).trim().toLowerCase() : null;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
147
|
+
|
|
148
|
+
const commandsOf = (query: string): ComposerCommand[] => {
|
|
144
149
|
const known = new Set(skills.map((skill) => skill.name));
|
|
145
150
|
const own = localCommands.map((name) => ({ name, hint: t(`composer.commands.${name}`), local: true, skill: false }));
|
|
146
151
|
// Every CLI with goals reads `/goal`, also one that lists no commands of its own.
|
|
147
152
|
const goal = goals ? [{ name: 'goal', hint: t('composer.commands.goal'), local: false, skill: false }] : [];
|
|
153
|
+
|
|
148
154
|
const cli = usableSlashCommands([...slashCommands])
|
|
149
155
|
.filter((name) => !localCommands.includes(name) && (!goals || name !== 'goal'))
|
|
150
156
|
.map((name) => ({
|
|
@@ -153,16 +159,12 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
153
159
|
local: false,
|
|
154
160
|
skill: known.has(name)
|
|
155
161
|
}));
|
|
156
|
-
return [...own, ...goal, ...cli].filter((command) => command.name.startsWith(commandQuery)).slice(0, 8);
|
|
157
|
-
}, [commandQuery, goals, localCommands, slashCommands, providerName, skills, t]);
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return skills.filter((skill) => skill.name.toLowerCase().includes(query)).slice(0, 8);
|
|
165
|
-
}, [skillQuery, skills]);
|
|
163
|
+
return [...own, ...goal, ...cli].filter((command) => command.name.startsWith(query)).slice(0, 8);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const commands = commandQuery === null ? [] : commandsOf(commandQuery);
|
|
167
|
+
const skillMatches = skillQuery === null ? [] : skills.filter((skill) => skill.name.toLowerCase().includes(skillQuery.query.toLowerCase())).slice(0, 8);
|
|
166
168
|
|
|
167
169
|
const commandMenuOpen = commandQuery !== null && commands.length > 0;
|
|
168
170
|
const skillMenuOpen = !commandMenuOpen && skillQuery !== null && skillMatches.length > 0;
|
|
@@ -175,11 +177,13 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
175
177
|
const referenceMenuOpen = !commandMenuOpen && !skillMenuOpen && !mentionMenuOpen && referenceQuery !== null && offersReferences;
|
|
176
178
|
const excluded = ownId === undefined ? [...draft.chats, ...draft.references] : [ownId, ...draft.chats, ...draft.references];
|
|
177
179
|
const referenceMatches = referenceMenuOpen ? referenceSuggestions(referables, referenceQuery.query, excluded, REFERENCE_RESULTS) : [];
|
|
180
|
+
|
|
178
181
|
// What the editor draws as chips and the send checks are still in the text, under the name each was picked with.
|
|
179
182
|
const referenceChips = referenceTokens([...draft.chats, ...draft.references], referables.items, draft.referenceNames).map((token) => ({
|
|
180
183
|
...token,
|
|
181
184
|
kind: referables.items.find((item) => item.id === token.id)?.kind ?? null
|
|
182
185
|
}));
|
|
186
|
+
|
|
183
187
|
// One menu is open at a time, so they share an id.
|
|
184
188
|
const listId = useId();
|
|
185
189
|
const menuOpen = commandMenuOpen || skillMenuOpen || mentionMenuOpen || referenceMenuOpen;
|
|
@@ -218,6 +222,7 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
218
222
|
if (!mention) {
|
|
219
223
|
return;
|
|
220
224
|
}
|
|
225
|
+
|
|
221
226
|
const result = insertMention(text, mention, path);
|
|
222
227
|
setText(result.text, draft.mentions.includes(path) ? draft.mentions : [...draft.mentions, path]);
|
|
223
228
|
setMention(null);
|
|
@@ -226,6 +231,7 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
226
231
|
|
|
227
232
|
const chooseMentionAt = (index: number): void => {
|
|
228
233
|
const path = mention === null ? null : mentionPick(index, searched, mention.query);
|
|
234
|
+
|
|
229
235
|
if (path !== null) {
|
|
230
236
|
chooseMention(path);
|
|
231
237
|
}
|
|
@@ -239,9 +245,12 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
239
245
|
if (!referenceQuery) {
|
|
240
246
|
return;
|
|
241
247
|
}
|
|
248
|
+
|
|
242
249
|
const result = insertReference(text, referenceQuery, item.name);
|
|
243
250
|
const chat = item.kind === 'chat';
|
|
251
|
+
|
|
244
252
|
const add = (ids: string[]): string[] => (ids.includes(item.id) ? ids : [...ids, item.id]);
|
|
253
|
+
|
|
245
254
|
setDraft((current) => ({
|
|
246
255
|
...current,
|
|
247
256
|
text: result.text,
|
|
@@ -249,12 +258,14 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
249
258
|
references: chat ? current.references : add(current.references),
|
|
250
259
|
referenceNames: { ...current.referenceNames, [item.id]: item.name }
|
|
251
260
|
}));
|
|
261
|
+
|
|
252
262
|
setReferenceQuery(null);
|
|
253
263
|
moveCaret(result.caret);
|
|
254
264
|
};
|
|
255
265
|
|
|
256
266
|
const chooseReferenceAt = (index: number): void => {
|
|
257
267
|
const item = referenceMatches[index] ?? referenceMatches[0];
|
|
268
|
+
|
|
258
269
|
if (item !== undefined) {
|
|
259
270
|
chooseReference(item);
|
|
260
271
|
}
|
|
@@ -274,10 +285,13 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
274
285
|
/* A clicked command; Tab completes one with a space after it instead. */
|
|
275
286
|
const pickCommand = (command: ComposerCommand, index: number): void => {
|
|
276
287
|
setMenuIndex(index);
|
|
288
|
+
|
|
277
289
|
if (command.skill) {
|
|
278
290
|
chooseSkill(command.name);
|
|
291
|
+
|
|
279
292
|
return;
|
|
280
293
|
}
|
|
294
|
+
|
|
281
295
|
setText(`/${command.name}`);
|
|
282
296
|
inputRef.current?.focus();
|
|
283
297
|
};
|
|
@@ -286,8 +300,10 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
286
300
|
const completeCommand = (command: ComposerCommand): void => {
|
|
287
301
|
if (command.skill) {
|
|
288
302
|
chooseSkill(command.name);
|
|
303
|
+
|
|
289
304
|
return;
|
|
290
305
|
}
|
|
306
|
+
|
|
291
307
|
setText(`/${command.name} `);
|
|
292
308
|
};
|
|
293
309
|
|
|
@@ -295,12 +311,16 @@ export function usePromptMenus<D extends PromptDraft>({
|
|
|
295
311
|
const moveInMenu = (key: string, count: number): boolean => {
|
|
296
312
|
if (key === 'ArrowDown') {
|
|
297
313
|
setMenuIndex((i) => (i + 1) % count);
|
|
314
|
+
|
|
298
315
|
return true;
|
|
299
316
|
}
|
|
317
|
+
|
|
300
318
|
if (key === 'ArrowUp') {
|
|
301
319
|
setMenuIndex((i) => (i - 1 + count) % count);
|
|
320
|
+
|
|
302
321
|
return true;
|
|
303
322
|
}
|
|
323
|
+
|
|
304
324
|
return false;
|
|
305
325
|
};
|
|
306
326
|
|
|
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import type { ChatAttachmentUpload, ChatQueuedMessage } from '@adecore/agent-contracts';
|
|
4
4
|
import { useChatHost } from '../../../host';
|
|
5
5
|
import { useChatScope } from '../../../scope';
|
|
6
|
-
import { errorCode } from '../../../transport';
|
|
6
|
+
import { errorCode, errorMessage } from '../../../transport';
|
|
7
7
|
import { useChatActions } from '../../actions';
|
|
8
8
|
import { readStoredAttachments } from '../../attachments';
|
|
9
9
|
import { takeBackIntoDraft, type ChatDraft } from '../../drafts';
|
|
@@ -33,26 +33,36 @@ export function useTakeBack({ chatId, draft: { latest, setDraft }, setNotice, on
|
|
|
33
33
|
if (takingBack !== null) {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
+
|
|
36
37
|
const failed = (e: unknown): void => {
|
|
37
|
-
host.notify({ kind: 'error', title: t('composer.queue.editFailed'), description:
|
|
38
|
+
host.notify({ kind: 'error', title: t('composer.queue.editFailed'), description: errorMessage(e) });
|
|
38
39
|
};
|
|
40
|
+
|
|
39
41
|
const readFiles = async (): Promise<ChatAttachmentUpload[]> => {
|
|
40
42
|
const stored = message.attachments ?? [];
|
|
43
|
+
|
|
41
44
|
if (stored.length === 0) {
|
|
42
45
|
return [];
|
|
43
46
|
}
|
|
47
|
+
|
|
44
48
|
const files = host.attachments;
|
|
49
|
+
|
|
45
50
|
return readStoredAttachments((attachment) => files.read(scope.id, chatId, attachment.id), stored);
|
|
46
51
|
};
|
|
52
|
+
|
|
47
53
|
setTakingBack(message.id);
|
|
54
|
+
|
|
48
55
|
try {
|
|
49
56
|
let uploads: ChatAttachmentUpload[];
|
|
57
|
+
|
|
50
58
|
try {
|
|
51
59
|
uploads = await readFiles();
|
|
52
60
|
} catch (e) {
|
|
53
61
|
failed(e);
|
|
62
|
+
|
|
54
63
|
return;
|
|
55
64
|
}
|
|
65
|
+
|
|
56
66
|
const taken: Omit<ChatDraft, 'quote' | 'quoteSource'> = {
|
|
57
67
|
text: message.text,
|
|
58
68
|
mentions: message.mentions ?? [],
|
|
@@ -61,12 +71,16 @@ export function useTakeBack({ chatId, draft: { latest, setDraft }, setNotice, on
|
|
|
61
71
|
attachments: uploads,
|
|
62
72
|
references: message.references ?? []
|
|
63
73
|
};
|
|
74
|
+
|
|
64
75
|
// A file that would not fit is refused here, while the message still holds it on the machine.
|
|
65
76
|
const wouldReject = takeBackIntoDraft(latest.current, taken).rejected[0];
|
|
77
|
+
|
|
66
78
|
if (wouldReject) {
|
|
67
79
|
setNotice(t('composer.notice.queuedDoesNotFit', { name: wouldReject.name || t('composer.notice.thatFile'), reason: wouldReject.reason }));
|
|
80
|
+
|
|
68
81
|
return;
|
|
69
82
|
}
|
|
83
|
+
|
|
70
84
|
try {
|
|
71
85
|
await actions.unqueue(chatId, message.id);
|
|
72
86
|
} catch (e) {
|
|
@@ -75,13 +89,17 @@ export function useTakeBack({ chatId, draft: { latest, setDraft }, setNotice, on
|
|
|
75
89
|
} else {
|
|
76
90
|
failed(e);
|
|
77
91
|
}
|
|
92
|
+
|
|
78
93
|
return;
|
|
79
94
|
}
|
|
95
|
+
|
|
80
96
|
const merged = takeBackIntoDraft(latest.current, taken);
|
|
81
97
|
setDraft(merged.draft);
|
|
98
|
+
|
|
82
99
|
if (merged.rejected[0]) {
|
|
83
100
|
setNotice(t('composer.notice.rejected', { name: merged.rejected[0].name, reason: merged.rejected[0].reason }));
|
|
84
101
|
}
|
|
102
|
+
|
|
85
103
|
onTaken();
|
|
86
104
|
} finally {
|
|
87
105
|
setTakingBack(null);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Button } from '@adecore/ui';
|
|
4
4
|
import { formatNumber } from '@adecore/ui/format';
|
|
@@ -9,23 +9,26 @@ export function ApprovalReview({ entries, maxHeight }: { entries: readonly Revie
|
|
|
9
9
|
const { t } = useTranslation('agent-prompts');
|
|
10
10
|
const [selected, setSelected] = useState<string | null>(null);
|
|
11
11
|
const active = entries.find((entry) => entry.key === selected) ?? entries[0];
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
const totals = new Map<string, number>();
|
|
13
|
+
const counts = new Map<string, number>();
|
|
14
|
+
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
totals.set(entry.path, (totals.get(entry.path) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const labels = entries.map((entry) => {
|
|
20
|
+
const count = (counts.get(entry.path) ?? 0) + 1;
|
|
21
|
+
counts.set(entry.path, count);
|
|
22
|
+
const total = totals.get(entry.path)!;
|
|
23
|
+
const suffix = total > 1 ? ` (${formatNumber(count)}/${formatNumber(total)})` : '';
|
|
24
|
+
|
|
25
|
+
return { short: `${entry.path.split(/[\\/]/).at(-1)}${suffix}`, full: `${entry.path}${suffix}` };
|
|
26
|
+
});
|
|
27
|
+
|
|
26
28
|
if (active === undefined) {
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
31
|
+
|
|
29
32
|
return (
|
|
30
33
|
<div>
|
|
31
34
|
{entries.length > 1 && (
|
|
@@ -23,6 +23,7 @@ export class DiffFallbackBoundary extends Component<Props, State> {
|
|
|
23
23
|
if (state.resetKeys.length !== props.resetKeys.length || state.resetKeys.some((value, index) => !Object.is(value, props.resetKeys[index]))) {
|
|
24
24
|
return { failed: false, resetKeys: props.resetKeys };
|
|
25
25
|
}
|
|
26
|
+
|
|
26
27
|
return null;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
4
4
|
export function DiffOmitted({ path }: { path: string }) {
|
|
5
5
|
useReviewSettled();
|
|
6
6
|
const { t } = useTranslation('agent-chat');
|
|
7
|
+
|
|
7
8
|
return (
|
|
8
9
|
<div role="status" data-diff-omitted className="px-3 py-2 text-xs text-text-muted">
|
|
9
10
|
<p className="truncate font-mono">{path}</p>
|
|
@@ -7,6 +7,7 @@ import { useReviewStack, type ReviewStack } from './use-review-stack';
|
|
|
7
7
|
|
|
8
8
|
export function ReviewStackView({ entries, stack }: { entries: readonly ReviewEntry[]; stack: ReviewStack }) {
|
|
9
9
|
const { t } = useTranslation('agent-chat');
|
|
10
|
+
|
|
10
11
|
return (
|
|
11
12
|
<div data-review-stack>
|
|
12
13
|
{entries.map((entry) => (
|
|
@@ -41,5 +42,6 @@ export function ReviewStackView({ entries, stack }: { entries: readonly ReviewEn
|
|
|
41
42
|
|
|
42
43
|
export function DiffStack({ entries }: { entries: readonly ReviewEntry[] }) {
|
|
43
44
|
const stack = useReviewStack(entries);
|
|
45
|
+
|
|
44
46
|
return <ReviewStackView entries={entries} stack={stack} />;
|
|
45
47
|
}
|
|
@@ -4,6 +4,7 @@ import { useReviewSettled } from './review-settle';
|
|
|
4
4
|
export function DiffUnavailable({ path }: { path: string }) {
|
|
5
5
|
useReviewSettled();
|
|
6
6
|
const { t } = useTranslation('agent-chat');
|
|
7
|
+
|
|
7
8
|
return (
|
|
8
9
|
<div role="status" data-diff-unavailable className="px-3 py-2 text-xs text-text-muted">
|
|
9
10
|
<p className="truncate font-mono">{path}</p>
|