@adecore/agents-react 0.38.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.d.ts +10 -0
- package/dist/agents/AgentModelLabel.js +98 -0
- 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/Composer.js +385 -360
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/Pickers.d.ts +2 -1
- package/dist/chat/ui/Pickers.js +126 -123
- package/dist/chat/ui/PromptEditor.d.ts +19 -0
- package/dist/chat/ui/PromptEditor.js +115 -0
- package/dist/chat/ui/Timeline.d.ts +2 -1
- package/dist/chat/ui/Timeline.js +13 -1
- package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
- package/dist/chat/ui/composer/ComposerControls.js +235 -38
- package/dist/chat/ui/composer/key-handler.d.ts +1 -1
- package/dist/chat/ui/composer/key-handler.js +3 -11
- package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
- package/dist/chat/ui/composer/prompt-picks.js +14 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
- package/dist/chat/ui/composer/use-composer-menus.js +65 -519
- 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.d.ts +73 -0
- package/dist/chat/ui/composer/use-prompt-menus.js +499 -0
- 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/index.d.ts +3 -0
- package/dist/chat/ui/index.js +2 -0
- 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/host-hooks.d.ts +2 -1
- package/dist/host-hooks.js +6 -0
- package/dist/host.d.ts +17 -1
- package/dist/index.d.ts +1 -1
- package/dist/locales/en/agent-chat.d.ts +1 -0
- package/dist/locales/en/agent-chat.js +1 -0
- package/dist/locales/nl/agent-chat.d.ts +1 -0
- package/dist/locales/nl/agent-chat.js +1 -0
- 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/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- 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/theme.css +3 -0
- 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 +38 -0
- 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 +27 -3
- 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 +30 -1
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +136 -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 +141 -1
- 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 +70 -15
- 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 +42 -9
- 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 +18 -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 +20 -275
- 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 +361 -0
- 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 +7 -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 +15 -5
- package/src/host.ts +29 -1
- package/src/index.ts +5 -0
- package/src/lazy.tsx +9 -0
- package/src/locales/en/agent-chat.ts +1 -0
- package/src/locales/nl/agent-chat.ts +1 -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/index.ts +1 -0
- 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/theme.css +3 -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
|
@@ -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
|
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { useEffect, useId, useState, type Dispatch, type RefObject, type SetStateAction } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { ensureSyntaxTree, syntaxTree } from '@codemirror/language';
|
|
4
|
+
import type { EditorState } from '@codemirror/state';
|
|
5
|
+
import type { ChatSkill } from '@adecore/agent-contracts';
|
|
6
|
+
import { useChatHost } from '../../../host';
|
|
7
|
+
import { useChatScope } from '../../../scope';
|
|
8
|
+
import { referenceSuggestions, referenceTokens, type Referable, type Referables } from '../../chat-references';
|
|
9
|
+
import type { ChatDraft } from '../../drafts';
|
|
10
|
+
import { usableSlashCommands } from '../../guards';
|
|
11
|
+
import {
|
|
12
|
+
chipRanges,
|
|
13
|
+
findMentionQuery,
|
|
14
|
+
findReferenceQuery,
|
|
15
|
+
findSkillQuery,
|
|
16
|
+
insertMention,
|
|
17
|
+
insertReference,
|
|
18
|
+
insertSkill,
|
|
19
|
+
mentionPick,
|
|
20
|
+
NO_MENTION_SEARCH,
|
|
21
|
+
queryOutsideChips,
|
|
22
|
+
type MentionQuery,
|
|
23
|
+
type MentionSearch,
|
|
24
|
+
type TextRange
|
|
25
|
+
} from '../../mentions';
|
|
26
|
+
import type { ComposerInputHandle } from '../ComposerInput';
|
|
27
|
+
import { inCode } from './keys';
|
|
28
|
+
import { clampMenuIndex, composerMenuAria } from './menu-aria';
|
|
29
|
+
|
|
30
|
+
const SEARCH_DEBOUNCE_MS = 80;
|
|
31
|
+
// What fits above the box without turning the picker into a file tree.
|
|
32
|
+
const MENTION_RESULTS = 8;
|
|
33
|
+
const REFERENCE_RESULTS = 8;
|
|
34
|
+
|
|
35
|
+
/* The text of a prompt and what the person picked in it with `@`, `$` and `#`. */
|
|
36
|
+
export type PromptDraft = Pick<ChatDraft, 'text' | 'mentions' | 'skills' | 'chats' | 'references' | 'referenceNames'>;
|
|
37
|
+
|
|
38
|
+
export interface PromptDraftBox<D extends PromptDraft> {
|
|
39
|
+
draft: D;
|
|
40
|
+
setDraft: Dispatch<SetStateAction<D>>;
|
|
41
|
+
/* The mentions and skills default to the draft's own as last rendered. */
|
|
42
|
+
setText(next: string, mentions?: string[], skills?: string[]): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ComposerCommand {
|
|
46
|
+
name: string;
|
|
47
|
+
hint: string;
|
|
48
|
+
local: boolean;
|
|
49
|
+
skill: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PromptMenusInput<D extends PromptDraft> {
|
|
53
|
+
draft: PromptDraftBox<D>;
|
|
54
|
+
inputRef: RefObject<ComposerInputHandle | null>;
|
|
55
|
+
/* Where `@` searches for files; null offers none. */
|
|
56
|
+
cwd: string | null;
|
|
57
|
+
skills: readonly ChatSkill[];
|
|
58
|
+
/* What the CLI said it answers after `/`. */
|
|
59
|
+
slashCommands: readonly string[];
|
|
60
|
+
/* Commands the box runs itself, named in `composer.commands`; the rest go to the CLI as text. */
|
|
61
|
+
localCommands?: readonly string[];
|
|
62
|
+
/* The CLI reads `/goal`. */
|
|
63
|
+
goals: boolean;
|
|
64
|
+
providerName: string | undefined;
|
|
65
|
+
referables: Referables;
|
|
66
|
+
/* What `#` never offers, such as the chat the box writes in. */
|
|
67
|
+
ownId?: string;
|
|
68
|
+
/* The editor's accessible name. */
|
|
69
|
+
label: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const NO_COMMANDS: readonly string[] = [];
|
|
73
|
+
|
|
74
|
+
function sameQuery(left: MentionQuery | null, right: MentionQuery | null): boolean {
|
|
75
|
+
return left?.start === right?.start && left?.query === right?.query;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* The pickers over a prompt: `/` for a command, `$` for a skill, `@` for a file and `#` for a view,
|
|
80
|
+
* a node or a chat. One is open at a time, and the arrow keys and the editor's ARIA attributes
|
|
81
|
+
* follow that one.
|
|
82
|
+
*/
|
|
83
|
+
export function usePromptMenus<D extends PromptDraft>({
|
|
84
|
+
draft: { draft, setDraft, setText },
|
|
85
|
+
inputRef,
|
|
86
|
+
cwd,
|
|
87
|
+
skills,
|
|
88
|
+
slashCommands,
|
|
89
|
+
localCommands = NO_COMMANDS,
|
|
90
|
+
goals,
|
|
91
|
+
providerName,
|
|
92
|
+
referables,
|
|
93
|
+
ownId,
|
|
94
|
+
label
|
|
95
|
+
}: PromptMenusInput<D>) {
|
|
96
|
+
const { t } = useTranslation('agent-chat');
|
|
97
|
+
const scope = useChatScope();
|
|
98
|
+
const { searchFiles } = useChatHost();
|
|
99
|
+
const [menuIndex, setMenuIndex] = useState(0);
|
|
100
|
+
const [mention, setMention] = useState<MentionQuery | null>(null);
|
|
101
|
+
const [referenceQuery, setReferenceQuery] = useState<MentionQuery | null>(null);
|
|
102
|
+
const [skillQuery, setSkillQuery] = useState<MentionQuery | null>(null);
|
|
103
|
+
const [searched, setSearched] = useState<MentionSearch>(NO_MENTION_SEARCH);
|
|
104
|
+
const text = draft.text;
|
|
105
|
+
|
|
106
|
+
// Every keystroke inside an `@word` searches again; a slow answer must not overwrite a newer one.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (mention === null || cwd === null) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let stale = false;
|
|
113
|
+
|
|
114
|
+
const search = (): void => {
|
|
115
|
+
if (searchFiles === null) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
searchFiles(scope.id, cwd, mention.query, MENTION_RESULTS)
|
|
120
|
+
.then((files) => {
|
|
121
|
+
if (!stale) {
|
|
122
|
+
setSearched({ query: mention.query, files });
|
|
123
|
+
setMenuIndex(0);
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
.catch(() => undefined);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// A bare `@` is not a keystroke in a query. It opens the list of what is there, at once.
|
|
130
|
+
if (mention.query === '') {
|
|
131
|
+
search();
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
stale = true;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const timer = setTimeout(search, SEARCH_DEBOUNCE_MS);
|
|
139
|
+
|
|
140
|
+
return () => {
|
|
141
|
+
stale = true;
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
};
|
|
144
|
+
}, [scope, cwd, mention, searchFiles]);
|
|
145
|
+
|
|
146
|
+
const commandQuery = text.startsWith('/') && !text.includes('\n') ? text.slice(1).trim().toLowerCase() : null;
|
|
147
|
+
|
|
148
|
+
const commandsOf = (query: string): ComposerCommand[] => {
|
|
149
|
+
const known = new Set(skills.map((skill) => skill.name));
|
|
150
|
+
const own = localCommands.map((name) => ({ name, hint: t(`composer.commands.${name}`), local: true, skill: false }));
|
|
151
|
+
// Every CLI with goals reads `/goal`, also one that lists no commands of its own.
|
|
152
|
+
const goal = goals ? [{ name: 'goal', hint: t('composer.commands.goal'), local: false, skill: false }] : [];
|
|
153
|
+
|
|
154
|
+
const cli = usableSlashCommands([...slashCommands])
|
|
155
|
+
.filter((name) => !localCommands.includes(name) && (!goals || name !== 'goal'))
|
|
156
|
+
.map((name) => ({
|
|
157
|
+
name,
|
|
158
|
+
hint: known.has(name) ? t('composer.commands.skill') : t('composer.commands.cli', { cli: providerName ?? t('composer.commands.anyCli') }),
|
|
159
|
+
local: false,
|
|
160
|
+
skill: known.has(name)
|
|
161
|
+
}));
|
|
162
|
+
|
|
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);
|
|
168
|
+
|
|
169
|
+
const commandMenuOpen = commandQuery !== null && commands.length > 0;
|
|
170
|
+
const skillMenuOpen = !commandMenuOpen && skillQuery !== null && skillMatches.length > 0;
|
|
171
|
+
const mentionMenuOpen = !commandMenuOpen && !skillMenuOpen && mention !== null;
|
|
172
|
+
// Results belong to the query that asked for them; a closed picker shows none while the next answer is on its way.
|
|
173
|
+
const files = mention === null ? [] : searched.files;
|
|
174
|
+
const mentionCount = files.length;
|
|
175
|
+
// A host with nothing to point at keeps `#fff` out of the way rather than saying nothing matches.
|
|
176
|
+
const offersReferences = referables.items.some((item) => !referables.chatsOnly || item.kind === 'chat');
|
|
177
|
+
const referenceMenuOpen = !commandMenuOpen && !skillMenuOpen && !mentionMenuOpen && referenceQuery !== null && offersReferences;
|
|
178
|
+
const excluded = ownId === undefined ? [...draft.chats, ...draft.references] : [ownId, ...draft.chats, ...draft.references];
|
|
179
|
+
const referenceMatches = referenceMenuOpen ? referenceSuggestions(referables, referenceQuery.query, excluded, REFERENCE_RESULTS) : [];
|
|
180
|
+
|
|
181
|
+
// What the editor draws as chips and the send checks are still in the text, under the name each was picked with.
|
|
182
|
+
const referenceChips = referenceTokens([...draft.chats, ...draft.references], referables.items, draft.referenceNames).map((token) => ({
|
|
183
|
+
...token,
|
|
184
|
+
kind: referables.items.find((item) => item.id === token.id)?.kind ?? null
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
// One menu is open at a time, so they share an id.
|
|
188
|
+
const listId = useId();
|
|
189
|
+
const menuOpen = commandMenuOpen || skillMenuOpen || mentionMenuOpen || referenceMenuOpen;
|
|
190
|
+
const menuCount = commandMenuOpen ? commands.length : skillMenuOpen ? skillMatches.length : mentionMenuOpen ? mentionCount : referenceMatches.length;
|
|
191
|
+
// The list can shrink under a stale index; every reader takes the one that still points at an option.
|
|
192
|
+
const activeIndex = clampMenuIndex(menuIndex, menuCount);
|
|
193
|
+
const inputAria = composerMenuAria({ label, listId, open: menuOpen, activeIndex, count: menuCount });
|
|
194
|
+
|
|
195
|
+
/* Which picker the caret opens: `@` for a file, `$` for a skill, `#` for a reference, or none. In code a sigil is just text. */
|
|
196
|
+
const trackTriggers = (value: string, selection: TextRange, state: EditorState): void => {
|
|
197
|
+
const empty = selection.from === selection.to;
|
|
198
|
+
const tree = empty ? (ensureSyntaxTree(state, selection.from, 50) ?? syntaxTree(state)) : null;
|
|
199
|
+
const caret = tree !== null && !inCode(tree, value, selection.from) ? selection.from : null;
|
|
200
|
+
const chips = caret === null ? [] : chipRanges(value, draft.mentions, draft.skills, [], referenceChips);
|
|
201
|
+
const skill = caret === null ? null : queryOutsideChips(findSkillQuery(value, caret), chips);
|
|
202
|
+
setSkillQuery((current) => (sameQuery(current, skill) ? current : skill));
|
|
203
|
+
// A CLI that does not expand `@path` gets the text as it is, so the picker stays out of the way.
|
|
204
|
+
const next = caret === null || cwd === null ? null : queryOutsideChips(findMentionQuery(value, caret), chips);
|
|
205
|
+
setMention((current) => (sameQuery(current, next) ? current : next));
|
|
206
|
+
const reference = caret === null ? null : queryOutsideChips(findReferenceQuery(value, caret), chips);
|
|
207
|
+
setReferenceQuery((current) => (sameQuery(current, reference) ? current : reference));
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// The inserted text reaches the editor with the render it causes, so the caret waits a frame for it.
|
|
211
|
+
const moveCaret = (caret: number): void => {
|
|
212
|
+
requestAnimationFrame(() => inputRef.current?.setCaret(caret));
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const close = (): void => {
|
|
216
|
+
setMention(null);
|
|
217
|
+
setSkillQuery(null);
|
|
218
|
+
setReferenceQuery(null);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const chooseMention = (path: string): void => {
|
|
222
|
+
if (!mention) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const result = insertMention(text, mention, path);
|
|
227
|
+
setText(result.text, draft.mentions.includes(path) ? draft.mentions : [...draft.mentions, path]);
|
|
228
|
+
setMention(null);
|
|
229
|
+
moveCaret(result.caret);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const chooseMentionAt = (index: number): void => {
|
|
233
|
+
const path = mention === null ? null : mentionPick(index, searched, mention.query);
|
|
234
|
+
|
|
235
|
+
if (path !== null) {
|
|
236
|
+
chooseMention(path);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/*
|
|
241
|
+
* A picked view, node or chat becomes a `#name` chip in the text. The draft keeps the name it was
|
|
242
|
+
* spelled with, since a rename would otherwise unmake the chip. A chat goes apart, as `chats`.
|
|
243
|
+
*/
|
|
244
|
+
const chooseReference = (item: Referable): void => {
|
|
245
|
+
if (!referenceQuery) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const result = insertReference(text, referenceQuery, item.name);
|
|
250
|
+
const chat = item.kind === 'chat';
|
|
251
|
+
|
|
252
|
+
const add = (ids: string[]): string[] => (ids.includes(item.id) ? ids : [...ids, item.id]);
|
|
253
|
+
|
|
254
|
+
setDraft((current) => ({
|
|
255
|
+
...current,
|
|
256
|
+
text: result.text,
|
|
257
|
+
chats: chat ? add(current.chats) : current.chats,
|
|
258
|
+
references: chat ? current.references : add(current.references),
|
|
259
|
+
referenceNames: { ...current.referenceNames, [item.id]: item.name }
|
|
260
|
+
}));
|
|
261
|
+
|
|
262
|
+
setReferenceQuery(null);
|
|
263
|
+
moveCaret(result.caret);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const chooseReferenceAt = (index: number): void => {
|
|
267
|
+
const item = referenceMatches[index] ?? referenceMatches[0];
|
|
268
|
+
|
|
269
|
+
if (item !== undefined) {
|
|
270
|
+
chooseReference(item);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/*
|
|
275
|
+
* A picked skill becomes a `$name` chip in the text and a name on the send, which is what
|
|
276
|
+
* dispatches it. The `/` menu lands here too, so both spellings end up on the same path.
|
|
277
|
+
*/
|
|
278
|
+
const chooseSkill = (name: string): void => {
|
|
279
|
+
const result = skillQuery ? insertSkill(text, skillQuery, name) : { text: `$${name} `, caret: name.length + 2 };
|
|
280
|
+
setText(result.text, draft.mentions, draft.skills.includes(name) ? draft.skills : [...draft.skills, name]);
|
|
281
|
+
setSkillQuery(null);
|
|
282
|
+
moveCaret(result.caret);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/* A clicked command; Tab completes one with a space after it instead. */
|
|
286
|
+
const pickCommand = (command: ComposerCommand, index: number): void => {
|
|
287
|
+
setMenuIndex(index);
|
|
288
|
+
|
|
289
|
+
if (command.skill) {
|
|
290
|
+
chooseSkill(command.name);
|
|
291
|
+
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
setText(`/${command.name}`);
|
|
296
|
+
inputRef.current?.focus();
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/* Tab on a command: a skill becomes its chip, any other is written out with a space for its arguments. */
|
|
300
|
+
const completeCommand = (command: ComposerCommand): void => {
|
|
301
|
+
if (command.skill) {
|
|
302
|
+
chooseSkill(command.name);
|
|
303
|
+
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
setText(`/${command.name} `);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/* The arrows walk an open picker round; true when the key was one of them. */
|
|
311
|
+
const moveInMenu = (key: string, count: number): boolean => {
|
|
312
|
+
if (key === 'ArrowDown') {
|
|
313
|
+
setMenuIndex((i) => (i + 1) % count);
|
|
314
|
+
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (key === 'ArrowUp') {
|
|
319
|
+
setMenuIndex((i) => (i - 1 + count) % count);
|
|
320
|
+
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return false;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
listId,
|
|
329
|
+
activeIndex,
|
|
330
|
+
inputAria,
|
|
331
|
+
menuOpen,
|
|
332
|
+
hasSkills: skills.length > 0,
|
|
333
|
+
skillNames: skills.map((skill) => skill.name),
|
|
334
|
+
commandQuery,
|
|
335
|
+
commands,
|
|
336
|
+
commandMenuOpen,
|
|
337
|
+
skillMatches,
|
|
338
|
+
skillMenuOpen,
|
|
339
|
+
mention,
|
|
340
|
+
mentionMenuOpen,
|
|
341
|
+
mentionCount,
|
|
342
|
+
files,
|
|
343
|
+
referenceMenuOpen,
|
|
344
|
+
referenceMatches,
|
|
345
|
+
referables,
|
|
346
|
+
referenceChips,
|
|
347
|
+
setMenuIndex,
|
|
348
|
+
trackTriggers,
|
|
349
|
+
close,
|
|
350
|
+
chooseMention,
|
|
351
|
+
chooseMentionAt,
|
|
352
|
+
chooseReference,
|
|
353
|
+
chooseReferenceAt,
|
|
354
|
+
chooseSkill,
|
|
355
|
+
pickCommand,
|
|
356
|
+
completeCommand,
|
|
357
|
+
moveInMenu
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export type PromptMenus = ReturnType<typeof usePromptMenus>;
|
|
@@ -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>
|