@adecore/agents-react 0.23.1 → 0.24.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/README.md +14 -11
- package/dist/agents/AccountDot.js +3 -1
- package/dist/agents/accounts.d.ts +3 -2
- package/dist/agents/accounts.js +5 -6
- package/dist/chat/actions.d.ts +2 -2
- package/dist/chat/actions.js +8 -5
- package/dist/chat/bookmarks.d.ts +5 -3
- package/dist/chat/bookmarks.js +13 -14
- package/dist/chat/chat-client.d.ts +1 -0
- package/dist/chat/chat-client.js +7 -0
- package/dist/chat/composer-send.d.ts +3 -0
- package/dist/chat/composer-send.js +22 -0
- package/dist/chat/forks.js +22 -5
- package/dist/chat/index.d.ts +31 -0
- package/dist/chat/index.js +28 -0
- package/dist/chat/logic/embedded-code.d.ts +8 -0
- package/dist/chat/logic/embedded-code.js +371 -0
- package/dist/chat/logic/fork.js +1 -0
- package/dist/chat/logic/goal.d.ts +6 -0
- package/dist/chat/logic/goal.js +26 -0
- package/dist/chat/logic/scrubber.d.ts +1 -1
- package/dist/chat/logic/scrubber.js +6 -5
- package/dist/chat/logic/thought.d.ts +3 -0
- package/dist/chat/logic/thought.js +35 -0
- package/dist/chat/logic/thread-cards.js +2 -0
- package/dist/chat/logic/timeline.d.ts +5 -0
- package/dist/chat/logic/timeline.js +22 -1
- package/dist/chat/logic/tool-catalog.d.ts +1 -0
- package/dist/chat/logic/tool-catalog.js +3 -1
- package/dist/chat/logic/tools.d.ts +14 -0
- package/dist/chat/logic/tools.js +57 -0
- package/dist/chat/preferences.d.ts +6 -0
- package/dist/chat/preferences.js +19 -3
- package/dist/chat/reading-width.d.ts +9 -0
- package/dist/chat/reading-width.js +27 -0
- package/dist/chat/send-key.d.ts +10 -0
- package/dist/chat/send-key.js +24 -0
- package/dist/chat/ui/BookmarkMarker.js +4 -2
- package/dist/chat/ui/BookmarkSubmenu.js +4 -3
- package/dist/chat/ui/ChatActivity.js +14 -10
- package/dist/chat/ui/ChatReferenceChip.js +3 -4
- package/dist/chat/ui/CodeBlock.d.ts +2 -1
- package/dist/chat/ui/CodeBlock.js +14 -96
- package/dist/chat/ui/Composer.d.ts +1 -1
- package/dist/chat/ui/Composer.js +117 -906
- package/dist/chat/ui/ComposerInput.js +5 -4
- package/dist/chat/ui/EditDiff.js +3 -2
- package/dist/chat/ui/GoalDock.d.ts +9 -0
- package/dist/chat/ui/GoalDock.js +63 -0
- package/dist/chat/ui/LimitState.js +5 -3
- package/dist/chat/ui/Markdown.d.ts +2 -1
- package/dist/chat/ui/Markdown.js +70 -31
- package/dist/chat/ui/MermaidBlock.d.ts +3 -0
- package/dist/chat/ui/MermaidBlock.js +73 -0
- package/dist/chat/ui/MessageActions.js +12 -7
- package/dist/chat/ui/Pickers.js +6 -5
- package/dist/chat/ui/PromptComposer.js +4 -3
- package/dist/chat/ui/RowTime.d.ts +3 -0
- package/dist/chat/ui/RowTime.js +12 -0
- package/dist/chat/ui/RunSettings.js +5 -4
- package/dist/chat/ui/ShellCommand.d.ts +3 -0
- package/dist/chat/ui/ShellCommand.js +110 -0
- package/dist/chat/ui/SubagentInfo.js +4 -3
- package/dist/chat/ui/SubagentStopButton.js +4 -3
- package/dist/chat/ui/SubagentTimeline.js +4 -1
- package/dist/chat/ui/Timeline.js +70 -40
- package/dist/chat/ui/TimelineMenu.d.ts +2 -1
- package/dist/chat/ui/TimelineMenu.js +18 -8
- package/dist/chat/ui/UnifiedDiff.js +3 -2
- package/dist/chat/ui/VisualFrame.js +6 -7
- package/dist/chat/ui/code-theme.d.ts +2 -1
- package/dist/chat/ui/code-theme.js +4 -4
- package/dist/chat/ui/composer/ComposerControls.d.ts +27 -0
- package/dist/chat/ui/composer/ComposerControls.js +34 -0
- package/dist/chat/ui/composer/ComposerMenu.d.ts +4 -0
- package/dist/chat/ui/composer/ComposerMenu.js +46 -0
- package/dist/chat/ui/composer/DraftParts.d.ts +18 -0
- package/dist/chat/ui/composer/DraftParts.js +25 -0
- package/dist/chat/ui/composer/QueuedMessages.d.ts +10 -0
- package/dist/chat/ui/composer/QueuedMessages.js +20 -0
- package/dist/chat/ui/composer/key-handler.d.ts +20 -0
- package/dist/chat/ui/composer/key-handler.js +150 -0
- package/dist/chat/ui/composer/keys.d.ts +7 -2
- package/dist/chat/ui/composer/keys.js +19 -9
- package/dist/chat/ui/composer/placeholder.d.ts +9 -0
- package/dist/chat/ui/composer/placeholder.js +38 -0
- package/dist/chat/ui/composer/use-chat-controls.d.ts +28 -0
- package/dist/chat/ui/composer/use-chat-controls.js +112 -0
- package/dist/chat/ui/composer/use-compaction-offer.d.ts +2 -0
- package/dist/chat/ui/composer/use-compaction-offer.js +23 -0
- package/dist/chat/ui/composer/use-composer-draft.d.ts +14 -0
- package/dist/chat/ui/composer/use-composer-draft.js +39 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +52 -0
- package/dist/chat/ui/composer/use-composer-menus.js +232 -0
- package/dist/chat/ui/composer/use-composer-submit.d.ts +32 -0
- package/dist/chat/ui/composer/use-composer-submit.js +113 -0
- package/dist/chat/ui/composer/use-draft-files.d.ts +27 -0
- package/dist/chat/ui/composer/use-draft-files.js +111 -0
- package/dist/chat/ui/composer/use-notice.d.ts +6 -0
- package/dist/chat/ui/composer/use-notice.js +18 -0
- package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -0
- package/dist/chat/ui/composer/use-pending-requests.js +22 -0
- package/dist/chat/ui/composer/use-prompt-history.d.ts +8 -0
- package/dist/chat/ui/composer/use-prompt-history.js +57 -0
- package/dist/chat/ui/composer/use-prompt-hold.d.ts +6 -0
- package/dist/chat/ui/composer/use-prompt-hold.js +48 -0
- package/dist/chat/ui/composer/use-take-back.d.ts +13 -0
- package/dist/chat/ui/composer/use-take-back.js +82 -0
- package/dist/chat/ui/diff/EditorEditDiff.js +4 -3
- package/dist/chat/ui/diff/LegacyDiffPool.js +3 -2
- package/dist/chat/ui/diff/LegacyEditDiff.js +3 -2
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +3 -2
- package/dist/chat/ui/diff/syntax-runtime.d.ts +2 -1
- package/dist/chat/ui/diff/syntax-runtime.js +9 -5
- package/dist/chat/ui/diff-syntax.d.ts +2 -2
- package/dist/chat/ui/diff-syntax.js +1 -2
- package/dist/chat/ui/diff-theme.js +10 -9
- package/dist/chat/ui/file-links.d.ts +1 -1
- package/dist/chat/ui/file-links.js +7 -5
- package/dist/chat/ui/highlighter.d.ts +27 -0
- package/dist/chat/ui/highlighter.js +151 -0
- package/dist/chat/ui/index.d.ts +67 -0
- package/dist/chat/ui/index.js +64 -0
- package/dist/chat/ui/intelligent-ui/UiReply.js +3 -2
- package/dist/chat/ui/intelligent-ui/renderers/content.js +3 -2
- package/dist/chat/ui/intelligent-ui/use-ui-links.js +2 -2
- package/dist/chat/ui/intelligent-ui/use-ui-queries.js +4 -3
- package/dist/chat/ui/markdown-blocks.d.ts +8 -0
- package/dist/chat/ui/markdown-blocks.js +260 -12
- package/dist/chat/ui/mermaid.d.ts +23 -0
- package/dist/chat/ui/mermaid.js +93 -0
- package/dist/chat/ui/rows/ForksRow.js +5 -3
- package/dist/chat/ui/rows/GeneratedImageRow.js +6 -4
- package/dist/chat/ui/rows/MessageRows.d.ts +4 -0
- package/dist/chat/ui/rows/MessageRows.js +36 -13
- package/dist/chat/ui/rows/Rows.js +3 -1
- package/dist/chat/ui/rows/SubagentRow.js +3 -2
- package/dist/chat/ui/rows/VisualRow.js +3 -2
- package/dist/chat/ui/rows/WorkRows.d.ts +6 -3
- package/dist/chat/ui/rows/WorkRows.js +57 -20
- package/dist/chat/ui/rows/WorkflowRow.js +3 -2
- package/dist/chat/ui/timestamp-format.d.ts +1 -0
- package/dist/chat/ui/timestamp-format.js +2 -0
- package/dist/chat/ui/use-diff-source-tokens.js +6 -3
- package/dist/chat/ui/visual-theme.js +3 -2
- package/dist/host.d.ts +9 -6
- package/dist/host.js +19 -8
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/locales/en/agent-chat.d.ts +658 -0
- package/dist/locales/en/agent-chat.js +657 -0
- package/dist/locales/en/agent-prompts.d.ts +44 -0
- package/dist/locales/en/agent-prompts.js +43 -0
- package/dist/locales/en/agent-providers.d.ts +132 -0
- package/dist/locales/en/agent-providers.js +131 -0
- package/dist/locales/en/agent-usage.d.ts +132 -0
- package/dist/locales/en/agent-usage.js +131 -0
- package/dist/locales/nl/agent-chat.d.ts +658 -0
- package/dist/locales/nl/agent-chat.js +657 -0
- package/dist/locales/nl/agent-prompts.d.ts +44 -0
- package/dist/locales/nl/agent-prompts.js +43 -0
- package/dist/locales/nl/agent-providers.d.ts +132 -0
- package/dist/locales/nl/agent-providers.js +131 -0
- package/dist/locales/nl/agent-usage.d.ts +132 -0
- package/dist/locales/nl/agent-usage.js +131 -0
- package/dist/locales.js +9 -8
- package/dist/prompts/index.d.ts +11 -0
- package/dist/prompts/index.js +12 -0
- package/dist/prompts/logic/subjects.d.ts +2 -1
- package/dist/prompts/logic/subjects.js +6 -4
- package/dist/prompts/ui/ApprovalBody.js +6 -4
- package/dist/prompts/ui/PromptCard.js +5 -4
- package/dist/prompts/ui/PromptView.js +3 -2
- package/dist/prompts/ui/QuestionBody.js +3 -2
- package/dist/providers/AccountColors.js +2 -2
- package/dist/providers/AccountDetail.js +7 -6
- package/dist/providers/AccountVariables.js +3 -1
- package/dist/providers/AddAccountForm.js +3 -2
- package/dist/providers/ProvidersPane.js +4 -3
- package/dist/providers/account-actions.d.ts +4 -3
- package/dist/providers/account-actions.js +6 -7
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/provider-abilities.d.ts +2 -1
- package/dist/providers/provider-abilities.js +4 -0
- package/dist/state/chats.js +4 -2
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +5 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/{prompts/logic/prompts.fixtures.d.ts → testing/prompts.d.ts} +1 -1
- package/dist/theme.css +13 -3
- package/dist/usage/UsageBreakdown.js +3 -2
- package/dist/usage/UsageLimits.js +4 -3
- package/dist/usage/UsagePage.js +3 -2
- package/dist/usage/index.d.ts +15 -0
- package/dist/usage/index.js +15 -0
- package/package.json +53 -1031
- package/src/agents/AccountDot.tsx +3 -1
- package/src/agents/accounts.ts +6 -6
- package/src/chat/actions.ts +8 -5
- package/src/chat/bookmarks.ts +23 -14
- package/src/chat/chat-client.ts +8 -0
- package/src/chat/composer-send.ts +27 -0
- package/src/chat/forks.ts +27 -6
- package/src/chat/index.ts +98 -0
- package/src/chat/logic/embedded-code.ts +409 -0
- package/src/chat/logic/fork.ts +1 -0
- package/src/chat/logic/goal.ts +33 -0
- package/src/chat/logic/scrubber.ts +6 -5
- package/src/chat/logic/thought.ts +40 -0
- package/src/chat/logic/thread-cards.ts +2 -0
- package/src/chat/logic/timeline.ts +25 -1
- package/src/chat/logic/tool-catalog.ts +5 -1
- package/src/chat/logic/tools.ts +76 -0
- package/src/chat/preferences.ts +26 -3
- package/src/chat/reading-width.ts +39 -0
- package/src/chat/send-key.ts +38 -0
- package/src/chat/ui/BookmarkMarker.tsx +4 -2
- package/src/chat/ui/BookmarkSubmenu.tsx +4 -3
- package/src/chat/ui/ChatActivity.tsx +14 -10
- package/src/chat/ui/ChatReferenceChip.tsx +3 -4
- package/src/chat/ui/CodeBlock.tsx +33 -124
- package/src/chat/ui/Composer.tsx +162 -1324
- package/src/chat/ui/ComposerInput.tsx +5 -4
- package/src/chat/ui/EditDiff.tsx +3 -2
- package/src/chat/ui/GoalDock.tsx +104 -0
- package/src/chat/ui/LimitState.tsx +5 -3
- package/src/chat/ui/Markdown.tsx +113 -39
- package/src/chat/ui/MermaidBlock.tsx +119 -0
- package/src/chat/ui/MessageActions.tsx +13 -8
- package/src/chat/ui/Pickers.tsx +8 -7
- package/src/chat/ui/PromptComposer.tsx +4 -3
- package/src/chat/ui/RowTime.tsx +21 -0
- package/src/chat/ui/RunSettings.tsx +9 -4
- package/src/chat/ui/ShellCommand.tsx +134 -0
- package/src/chat/ui/SubagentInfo.tsx +4 -3
- package/src/chat/ui/SubagentStopButton.tsx +4 -3
- package/src/chat/ui/SubagentTimeline.tsx +5 -1
- package/src/chat/ui/Timeline.tsx +167 -105
- package/src/chat/ui/TimelineMenu.tsx +42 -7
- package/src/chat/ui/UnifiedDiff.tsx +3 -2
- package/src/chat/ui/VisualFrame.tsx +6 -7
- package/src/chat/ui/code-theme.ts +4 -4
- package/src/chat/ui/composer/ComposerControls.tsx +147 -0
- package/src/chat/ui/composer/ComposerMenu.tsx +138 -0
- package/src/chat/ui/composer/DraftParts.tsx +86 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +64 -0
- package/src/chat/ui/composer/key-handler.ts +174 -0
- package/src/chat/ui/composer/keys.ts +32 -10
- package/src/chat/ui/composer/placeholder.ts +48 -0
- package/src/chat/ui/composer/use-chat-controls.ts +134 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +25 -0
- package/src/chat/ui/composer/use-composer-draft.ts +71 -0
- package/src/chat/ui/composer/use-composer-menus.ts +289 -0
- package/src/chat/ui/composer/use-composer-submit.ts +163 -0
- package/src/chat/ui/composer/use-draft-files.ts +135 -0
- package/src/chat/ui/composer/use-notice.ts +28 -0
- package/src/chat/ui/composer/use-pending-requests.ts +23 -0
- package/src/chat/ui/composer/use-prompt-history.ts +76 -0
- package/src/chat/ui/composer/use-prompt-hold.ts +58 -0
- package/src/chat/ui/composer/use-take-back.ts +91 -0
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +2 -2
- package/src/chat/ui/diff/EditorEditDiff.tsx +4 -3
- package/src/chat/ui/diff/LegacyDiffPool.tsx +3 -2
- package/src/chat/ui/diff/LegacyEditDiff.tsx +3 -2
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +3 -2
- package/src/chat/ui/diff/syntax-runtime.ts +10 -5
- package/src/chat/ui/diff-syntax.ts +2 -2
- package/src/chat/ui/diff-theme.ts +10 -9
- package/src/chat/ui/file-links.ts +7 -5
- package/src/chat/ui/highlighter.ts +185 -0
- package/src/chat/ui/index.ts +109 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +5 -4
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +3 -2
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +2 -2
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +4 -3
- package/src/chat/ui/markdown-blocks.ts +300 -12
- package/src/chat/ui/mermaid.ts +118 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -3
- package/src/chat/ui/rows/GeneratedImageRow.tsx +6 -4
- package/src/chat/ui/rows/MessageRows.tsx +73 -18
- package/src/chat/ui/rows/Rows.tsx +14 -1
- package/src/chat/ui/rows/SubagentRow.tsx +3 -2
- package/src/chat/ui/rows/VisualRow.tsx +3 -2
- package/src/chat/ui/rows/WorkRows.tsx +148 -47
- package/src/chat/ui/rows/WorkflowRow.tsx +3 -2
- package/src/chat/ui/timestamp-format.ts +2 -0
- package/src/chat/ui/use-diff-source-tokens.ts +10 -4
- package/src/chat/ui/visual-theme.ts +3 -2
- package/src/host.ts +31 -15
- package/src/index.ts +49 -0
- package/src/locales/en/agent-chat.ts +657 -0
- package/src/locales/en/agent-prompts.ts +43 -0
- package/src/locales/en/agent-providers.ts +131 -0
- package/src/locales/en/agent-usage.ts +131 -0
- package/src/locales/nl/agent-chat.ts +657 -0
- package/src/locales/nl/agent-prompts.ts +43 -0
- package/src/locales/nl/agent-providers.ts +131 -0
- package/src/locales/nl/agent-usage.ts +132 -0
- package/src/locales.ts +9 -8
- package/src/prompts/index.ts +35 -0
- package/src/prompts/logic/subjects.ts +12 -4
- package/src/prompts/ui/ApprovalBody.tsx +6 -4
- package/src/prompts/ui/PromptCard.tsx +11 -4
- package/src/prompts/ui/PromptView.tsx +3 -2
- package/src/prompts/ui/QuestionBody.tsx +3 -2
- package/src/providers/AccountColors.tsx +2 -2
- package/src/providers/AccountDetail.tsx +13 -7
- package/src/providers/AccountVariables.tsx +3 -1
- package/src/providers/AddAccountForm.tsx +3 -2
- package/src/providers/ProvidersPane.tsx +4 -3
- package/src/providers/account-actions.ts +7 -7
- package/src/providers/index.ts +17 -0
- package/src/providers/provider-abilities.ts +6 -1
- package/src/state/chats.ts +4 -2
- package/src/state/index.ts +39 -0
- package/src/testing/index.ts +2 -0
- package/src/{prompts/logic/prompts.fixtures.ts → testing/prompts.ts} +1 -1
- package/src/theme.css +13 -3
- package/src/usage/UsageBreakdown.tsx +3 -2
- package/src/usage/UsageLimits.tsx +4 -3
- package/src/usage/UsagePage.tsx +3 -2
- package/src/usage/index.ts +16 -0
- package/dist/locales/en/agent-chat.json +0 -597
- package/dist/locales/en/agent-prompts.json +0 -43
- package/dist/locales/en/agent-providers.json +0 -131
- package/dist/locales/en/agent-usage.json +0 -131
- package/dist/locales/nl/agent-chat.json +0 -597
- package/dist/locales/nl/agent-prompts.json +0 -43
- package/dist/locales/nl/agent-providers.json +0 -131
- package/dist/locales/nl/agent-usage.json +0 -131
- package/src/locales/en/agent-chat.json +0 -597
- package/src/locales/en/agent-prompts.json +0 -43
- package/src/locales/en/agent-providers.json +0 -131
- package/src/locales/en/agent-usage.json +0 -131
- package/src/locales/nl/agent-chat.json +0 -597
- package/src/locales/nl/agent-prompts.json +0 -43
- package/src/locales/nl/agent-providers.json +0 -131
- package/src/locales/nl/agent-usage.json +0 -131
- /package/dist/{prompts/logic/prompts.fixtures.js → testing/prompts.js} +0 -0
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useImperativeHandle, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Compartment, EditorState, Prec, Transaction } from '@codemirror/state';
|
|
4
4
|
import { EditorView, placeholder as placeholderText } from '@codemirror/view';
|
|
5
|
-
import {
|
|
5
|
+
import { useChatHost } from '../../host.js';
|
|
6
6
|
import { composerEditorExtensions, externalChange } from './composer/editor.js';
|
|
7
7
|
import { insertAtSelection } from './composer/insert.js';
|
|
8
8
|
const NO_EXTENSIONS = [];
|
|
@@ -13,6 +13,7 @@ const NO_ARIA = {};
|
|
|
13
13
|
* character, which a textarea with a painted layer behind it could not.
|
|
14
14
|
*/
|
|
15
15
|
export function ComposerInput({ ref, value, placeholder, disabled, tabbable, className, dictationToolbar, extensions = NO_EXTENSIONS, aria = NO_ARIA, ...handlers }) {
|
|
16
|
+
const { dictation } = useChatHost();
|
|
16
17
|
const hostRef = useRef(null);
|
|
17
18
|
const targetRef = useRef(null);
|
|
18
19
|
const viewRef = useRef(null);
|
|
@@ -41,7 +42,7 @@ export function ComposerInput({ ref, value, placeholder, disabled, tabbable, cla
|
|
|
41
42
|
selection: { anchor: initial.length },
|
|
42
43
|
extensions: [
|
|
43
44
|
composerEditorExtensions(),
|
|
44
|
-
|
|
45
|
+
dictation.composer?.extensions ?? [],
|
|
45
46
|
compartments.attributes.of([]),
|
|
46
47
|
compartments.editable.of([]),
|
|
47
48
|
compartments.placeholder.of([]),
|
|
@@ -84,7 +85,7 @@ export function ComposerInput({ ref, value, placeholder, disabled, tabbable, cla
|
|
|
84
85
|
view.destroy();
|
|
85
86
|
viewRef.current = null;
|
|
86
87
|
};
|
|
87
|
-
}, [compartments]);
|
|
88
|
+
}, [compartments, dictation.composer?.extensions]);
|
|
88
89
|
useLayoutEffect(() => {
|
|
89
90
|
viewRef.current?.dispatch({
|
|
90
91
|
effects: [
|
|
@@ -131,6 +132,6 @@ export function ComposerInput({ ref, value, placeholder, disabled, tabbable, cla
|
|
|
131
132
|
view.focus();
|
|
132
133
|
}
|
|
133
134
|
}), []);
|
|
134
|
-
const Dictation =
|
|
135
|
+
const Dictation = dictation.composer?.Control;
|
|
135
136
|
return (_jsxs("div", { ref: targetRef, className: "min-w-0", children: [_jsx("div", { ref: hostRef, className: className }), Dictation && _jsx(Dictation, { buttonContainer: dictationToolbar, targetRef: targetRef, disabled: disabled, editor: () => viewRef.current })] }));
|
|
136
137
|
}
|
package/dist/chat/ui/EditDiff.js
CHANGED
|
@@ -6,12 +6,13 @@ import { diffSourceBudget, legacyDiffBudget } from './diff/diff-source-budget.js
|
|
|
6
6
|
import { DiffOmitted } from './diff/DiffOmitted.js';
|
|
7
7
|
import { DiffUnavailable } from './diff/DiffUnavailable.js';
|
|
8
8
|
import { DiffFallbackBoundary } from './diff/DiffFallbackBoundary.js';
|
|
9
|
-
import {
|
|
9
|
+
import { useChatHost } from '../../host.js';
|
|
10
10
|
const LegacyEditDiff = lazyNamed(() => import('./diff/LegacyEditDiff.js'), 'default', { prefetch: false });
|
|
11
11
|
const EditorEditDiff = lazyNamed(() => import('./diff/EditorEditDiff.js'), 'default');
|
|
12
12
|
export default function EditDiff({ change, allowEditor = true, maxHeight }) {
|
|
13
|
+
const host = useChatHost();
|
|
13
14
|
const { t } = useTranslation('agent-chat');
|
|
14
|
-
const loadEngine =
|
|
15
|
+
const loadEngine = host.code.editor;
|
|
15
16
|
const sourceAllowed = useMemo(() => diffSourceBudget(change.before, change.after), [change.before, change.after]);
|
|
16
17
|
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
17
18
|
const legacyAllowed = useMemo(() => legacyMode && legacyDiffBudget(change.before, change.after), [legacyMode, change.before, change.after]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ChatInfo, ProviderCapabilities } from '@adecore/agent-contracts';
|
|
2
|
+
export declare function GoalDock({ chatId, info, goals }: {
|
|
3
|
+
chatId: string;
|
|
4
|
+
info: ChatInfo;
|
|
5
|
+
goals: ProviderCapabilities['goals'];
|
|
6
|
+
}): import("react").JSX.Element | null;
|
|
7
|
+
export declare function GoalPill({ chatId }: {
|
|
8
|
+
chatId: string;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
import { Target } from 'lucide-react';
|
|
6
|
+
import { Button, Icon, Pill, Tooltip } from '@adecore/ui';
|
|
7
|
+
import { formatTokens, useFormatLocale } from '@adecore/ui/format';
|
|
8
|
+
import { useChatHost } from '../../host.js';
|
|
9
|
+
import { useChatScope } from '../../scope.js';
|
|
10
|
+
import { useChats } from '../../state/chats.js';
|
|
11
|
+
import { useChatActions } from '../actions.js';
|
|
12
|
+
import { goalView } from '../logic/goal.js';
|
|
13
|
+
/* How far the goal got, in what its CLI counts: the checks that sent the agent on, or the tokens it spent. */
|
|
14
|
+
function progressOf(goal, t) {
|
|
15
|
+
const parts = [];
|
|
16
|
+
if (goal.checks !== undefined && goal.checks > 0) {
|
|
17
|
+
parts.push(t('goal.checks', { count: goal.checks }));
|
|
18
|
+
}
|
|
19
|
+
if (goal.tokensUsed !== undefined && (goal.tokensUsed > 0 || goal.tokenBudget !== undefined)) {
|
|
20
|
+
parts.push(goal.tokenBudget === undefined
|
|
21
|
+
? t('goal.tokens', { tokens: formatTokens(goal.tokensUsed) })
|
|
22
|
+
: t('goal.budget', { tokens: formatTokens(goal.tokensUsed), budget: formatTokens(goal.tokenBudget) }));
|
|
23
|
+
}
|
|
24
|
+
return parts.length === 0 ? null : parts.join(' · ');
|
|
25
|
+
}
|
|
26
|
+
/*
|
|
27
|
+
* At the top of the composer while the chat has a goal: where it stands, the objective, how far it got,
|
|
28
|
+
* and what a person may do to it. The whole objective and what the last check said are in the tooltip.
|
|
29
|
+
*/
|
|
30
|
+
export function GoalDock({ chatId, info, goals }) {
|
|
31
|
+
const host = useChatHost();
|
|
32
|
+
const { t } = useTranslation('agent-chat');
|
|
33
|
+
useFormatLocale();
|
|
34
|
+
const actions = useChatActions();
|
|
35
|
+
const [changing, setChanging] = useState(false);
|
|
36
|
+
const view = goalView(info, goals, actions.goal !== undefined);
|
|
37
|
+
if (view === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const { goal } = view;
|
|
41
|
+
const progress = progressOf(goal, t);
|
|
42
|
+
const act = (action) => {
|
|
43
|
+
if (!actions.goal) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
setChanging(true);
|
|
47
|
+
actions
|
|
48
|
+
.goal(chatId, action)
|
|
49
|
+
.catch((e) => host.notify({ kind: 'error', title: t('goal.failed'), description: e instanceof Error ? e.message : String(e) }))
|
|
50
|
+
.finally(() => setChanging(false));
|
|
51
|
+
};
|
|
52
|
+
return (_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3.5 py-2.5", children: [_jsx(Icon, { icon: Target, size: 16, className: clsx('shrink-0', goal.status === 'active' ? 'text-accent' : 'text-text-faint') }), _jsxs("span", { className: "flex min-w-0 grow flex-col", children: [_jsx("span", { className: "truncate text-sm text-text", children: t(`goal.status.${goal.status}`) }), _jsx(Tooltip, { label: goal.lastCheck === undefined ? (goal.objective) : (_jsxs(_Fragment, { children: [goal.objective, _jsx("br", {}), t('goal.lastCheck', { check: goal.lastCheck })] })), children: _jsx("span", { className: "truncate text-xs tabular-nums text-text-faint", children: progress === null ? goal.objective : `${goal.objective} · ${progress}` }) })] }), view.actions.map((action) => (_jsx(Button, { size: "sm", disabled: changing, onClick: () => act(action), children: t(`goal.${action}`) }, action)))] }));
|
|
53
|
+
}
|
|
54
|
+
/* The same in a chat node's header while a goal works, read from the status every client gets, so a node nobody opened says it too. */
|
|
55
|
+
export function GoalPill({ chatId }) {
|
|
56
|
+
const { t } = useTranslation('agent-chat');
|
|
57
|
+
const { keyOf } = useChatScope();
|
|
58
|
+
const goal = useChats((s) => s.statusByKey[keyOf(chatId)]?.info.goal);
|
|
59
|
+
if (goal?.status !== 'active') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return (_jsx(Tooltip, { label: `${t('goal.status.active')}: ${goal.objective}`, children: _jsx(Pill, { tone: "accent", icon: _jsx(Icon, { icon: Target, size: 12 }), children: t('goal.pill') }) }));
|
|
63
|
+
}
|
|
@@ -10,7 +10,7 @@ import { useFormatLocale } from '@adecore/ui/format';
|
|
|
10
10
|
import { Switch, Button, Icon, Pill, Tooltip, useNow } from '@adecore/ui';
|
|
11
11
|
import { useUsageLimits } from '../../usage/limits.js';
|
|
12
12
|
import { useChats } from '../../state/chats.js';
|
|
13
|
-
import {
|
|
13
|
+
import { useChatHost } from '../../host.js';
|
|
14
14
|
import { useChatScope } from '../../scope.js';
|
|
15
15
|
import { useChatActions } from '../actions.js';
|
|
16
16
|
import { useUsageStore } from '../../state/usage.js';
|
|
@@ -39,10 +39,11 @@ function readUnreadAccounts(scope) {
|
|
|
39
39
|
* switch off it stands off and says where that switch is.
|
|
40
40
|
*/
|
|
41
41
|
function ResumeAtResetToggle({ chatId, info }) {
|
|
42
|
+
const host = useChatHost();
|
|
42
43
|
const { t } = useTranslation('agent-chat');
|
|
43
44
|
const { id } = useChatScope();
|
|
44
45
|
const actions = useChatActions();
|
|
45
|
-
const machineAllows =
|
|
46
|
+
const machineAllows = host.useResumeAtReset(id);
|
|
46
47
|
const set = (on) => {
|
|
47
48
|
void actions.configure(chatId, { resumeAtReset: on }).catch(() => undefined);
|
|
48
49
|
};
|
|
@@ -54,6 +55,7 @@ function ResumeAtResetToggle({ chatId, info }) {
|
|
|
54
55
|
* conversation takes it over; any other goes on in a fork, which then opens the way a fork does.
|
|
55
56
|
*/
|
|
56
57
|
function ContinueOn({ chatId, info, choice }) {
|
|
58
|
+
const host = useChatHost();
|
|
57
59
|
const { t } = useTranslation('agent-chat');
|
|
58
60
|
const scope = useChatScope();
|
|
59
61
|
const actions = useChatActions();
|
|
@@ -74,7 +76,7 @@ function ContinueOn({ chatId, info, choice }) {
|
|
|
74
76
|
setBusy(true);
|
|
75
77
|
actions
|
|
76
78
|
.continueOn(chatId, target.id)
|
|
77
|
-
.catch((e) =>
|
|
79
|
+
.catch((e) => host.notify({
|
|
78
80
|
kind: 'error',
|
|
79
81
|
title: t('limit.continueFailed', { account: name }),
|
|
80
82
|
description: e instanceof Error ? e.message : String(e)
|
|
@@ -14,10 +14,11 @@ export declare const MessageMarkdown: import("react").MemoExoticComponent<({ tex
|
|
|
14
14
|
mentions?: string[];
|
|
15
15
|
skills?: string[];
|
|
16
16
|
}) => import("react").JSX.Element>;
|
|
17
|
-
export declare const Markdown: import("react").MemoExoticComponent<({ text, breaks, fileLinks, rehypePlugins, componentOverrides }: {
|
|
17
|
+
export declare const Markdown: import("react").MemoExoticComponent<({ text, breaks, fileLinks, muted, rehypePlugins, componentOverrides }: {
|
|
18
18
|
text: string;
|
|
19
19
|
breaks?: boolean;
|
|
20
20
|
fileLinks?: boolean;
|
|
21
|
+
muted?: boolean;
|
|
21
22
|
rehypePlugins?: Options['rehypePlugins'];
|
|
22
23
|
componentOverrides?: Components;
|
|
23
24
|
}) => import("react").JSX.Element>;
|
package/dist/chat/ui/Markdown.js
CHANGED
|
@@ -7,16 +7,29 @@ import remarkBreaks from 'remark-breaks';
|
|
|
7
7
|
import remarkGfm from 'remark-gfm';
|
|
8
8
|
import { CHIP_IN_MESSAGE, MENTION_TONE, SKILL_TONE } from './chips.js';
|
|
9
9
|
import { CodeBlock } from './CodeBlock.js';
|
|
10
|
+
import { MermaidBlock } from './MermaidBlock.js';
|
|
10
11
|
import { CodeStreamingContext } from './code-streaming.js';
|
|
11
12
|
import { splitMarkdownBlocks } from './markdown-blocks.js';
|
|
12
13
|
import { rehypeChips } from './rehype-chips.js';
|
|
13
14
|
import { rehypeFadeWords } from './rehype-fade.js';
|
|
14
15
|
import { remarkHtmlAsText } from './remark-html-as-text.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { FileIcon, Icon } from '@adecore/ui';
|
|
16
|
+
import { useChatHost } from '../../host.js';
|
|
17
|
+
import { useFileLinkCwd, useFileLinkScopeId, useFileLinkTarget, useOpenFileLink } from './file-links.js';
|
|
18
|
+
import { ErrorBoundary, FileIcon, Icon } from '@adecore/ui';
|
|
19
|
+
import { useTranslation } from 'react-i18next';
|
|
18
20
|
const ShellCodeBlockReplyContext = createContext(null);
|
|
19
21
|
const BlockCodeContext = createContext(false);
|
|
22
|
+
/* The command a shell fence holds, read from the fence as written, so it keeps the indentation and line endings it has. */
|
|
23
|
+
function shellFence(source) {
|
|
24
|
+
const opening = /^ {0,3}(`{3,}|~{3,})[ \t]*(sh|bash|zsh)[ \t]*\r?\n/.exec(source);
|
|
25
|
+
if (opening === null) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const body = source.slice(opening[0].length);
|
|
29
|
+
const closing = /(?:^|\r?\n) {0,3}(`{3,}|~{3,})[ \t]*$/.exec(body);
|
|
30
|
+
const closed = closing !== null && closing[1][0] === opening[1][0] && closing[1].length >= opening[1].length;
|
|
31
|
+
return { language: opening[2], code: closed ? body.slice(0, closing.index) : body, closed };
|
|
32
|
+
}
|
|
20
33
|
/* Only root children qualify; Markdown's parser decides whether a fence belongs to a list or quote. */
|
|
21
34
|
function remarkShellCodeBlocks() {
|
|
22
35
|
return (tree, file) => {
|
|
@@ -29,26 +42,30 @@ function remarkShellCodeBlocks() {
|
|
|
29
42
|
if (start === undefined || end === undefined) {
|
|
30
43
|
continue;
|
|
31
44
|
}
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
if (opening === null) {
|
|
45
|
+
const shell = shellFence(String(file.value).slice(start, end));
|
|
46
|
+
if (shell === null) {
|
|
35
47
|
continue;
|
|
36
48
|
}
|
|
37
|
-
const body = raw.slice(opening[0].length);
|
|
38
|
-
const closing = /(?:^|\r?\n) {0,3}(`{3,}|~{3,})[ \t]*$/.exec(body);
|
|
39
|
-
const closed = closing !== null && closing[1][0] === opening[1][0] && closing[1].length >= opening[1].length;
|
|
40
49
|
node.data = {
|
|
41
50
|
...node.data,
|
|
42
51
|
hProperties: {
|
|
43
52
|
...node.data?.hProperties,
|
|
44
|
-
'data-shell-language':
|
|
45
|
-
'data-shell-code':
|
|
46
|
-
'data-shell-closed': closed
|
|
53
|
+
'data-shell-language': shell.language,
|
|
54
|
+
'data-shell-code': shell.code,
|
|
55
|
+
'data-shell-closed': shell.closed
|
|
47
56
|
}
|
|
48
57
|
};
|
|
49
58
|
}
|
|
50
59
|
};
|
|
51
60
|
}
|
|
61
|
+
/* What the host adds to a shell fence of a main reply, which is complete once the fence closed and the reply stopped. */
|
|
62
|
+
function shellCodeBlockActions(reply, shell, render) {
|
|
63
|
+
if (reply === null || shell === null) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const { scopeId, chatId, itemId } = reply;
|
|
67
|
+
return render?.({ scopeId, chatId, itemId, language: shell.language, code: shell.code, complete: !reply.streaming && shell.closed });
|
|
68
|
+
}
|
|
52
69
|
function languageOf(className) {
|
|
53
70
|
return /language-([\w-]+)/.exec(className ?? '')?.[1] ?? 'text';
|
|
54
71
|
}
|
|
@@ -60,6 +77,7 @@ const INLINE_CODE = 'rounded-sm bg-surface-sunken px-1 py-px font-mono text-code
|
|
|
60
77
|
function FileLink({ target, className, children }) {
|
|
61
78
|
const cwd = useFileLinkCwd();
|
|
62
79
|
const scopeId = useFileLinkScopeId();
|
|
80
|
+
const openFileLink = useOpenFileLink();
|
|
63
81
|
return (_jsx("button", { type: "button", className: clsx('cursor-pointer select-text', className), "data-file-path": target.path, "data-file-line": target.line, "data-file-column": target.column, "data-file-end-line": target.endLine, "data-file-directory": target.directory, "data-file-cwd": cwd ?? '', "data-file-scope-id": scopeId ?? '', onClick: () => openFileLink(cwd, target, scopeId), children: children }));
|
|
64
82
|
}
|
|
65
83
|
/* Inline code, which is a file reference often enough that it is worth asking every time. */
|
|
@@ -94,11 +112,18 @@ function MarkdownLink({ href, children }) {
|
|
|
94
112
|
function ExternalLink({ href, children }) {
|
|
95
113
|
return (_jsx("a", { href: href, target: "_blank", rel: "noreferrer", children: children }));
|
|
96
114
|
}
|
|
115
|
+
/* A ```mermaid fence, which draws what it says. */
|
|
116
|
+
function Diagram({ code }) {
|
|
117
|
+
const { t } = useTranslation('agent-chat');
|
|
118
|
+
return (_jsx(ErrorBoundary, { label: t('code.mermaid.crashed'), resetKeys: [code], compact: true, className: "rounded-lg border border-border", children: _jsx(MermaidBlock, { code: code }) }));
|
|
119
|
+
}
|
|
97
120
|
function MarkdownCode({ className, children, plain = false }) {
|
|
98
121
|
const block = useContext(BlockCodeContext);
|
|
99
122
|
const text = String(children ?? '');
|
|
100
123
|
if (block) {
|
|
101
|
-
|
|
124
|
+
const lang = languageOf(className);
|
|
125
|
+
const code = text.replace(/\n$/, '');
|
|
126
|
+
return lang === 'mermaid' ? _jsx(Diagram, { code: code }) : _jsx(CodeBlock, { code: code, lang: lang });
|
|
102
127
|
}
|
|
103
128
|
return plain ? _jsx("code", { className: INLINE_CODE, children: text }) : _jsx(InlineCode, { text: text });
|
|
104
129
|
}
|
|
@@ -145,22 +170,19 @@ const replyComponents = {
|
|
|
145
170
|
function ReplyCode({ className, children, node }) {
|
|
146
171
|
const reply = useContext(ShellCodeBlockReplyContext);
|
|
147
172
|
const block = useContext(BlockCodeContext);
|
|
173
|
+
const { renderShellCodeBlock } = useChatHost();
|
|
148
174
|
const properties = node?.properties;
|
|
149
175
|
const language = properties?.['data-shell-language'];
|
|
150
176
|
const code = properties?.['data-shell-code'];
|
|
151
|
-
const
|
|
152
|
-
? {
|
|
153
|
-
scopeId: reply.scopeId,
|
|
154
|
-
chatId: reply.chatId,
|
|
155
|
-
itemId: reply.itemId,
|
|
156
|
-
language,
|
|
157
|
-
code,
|
|
158
|
-
complete: !reply.streaming && properties?.['data-shell-closed'] === true
|
|
159
|
-
}
|
|
177
|
+
const shell = (language === 'sh' || language === 'bash' || language === 'zsh') && typeof code === 'string'
|
|
178
|
+
? { language, code, closed: properties?.['data-shell-closed'] === true }
|
|
160
179
|
: null;
|
|
161
180
|
const text = String(children ?? '');
|
|
181
|
+
if (block && languageOf(className) === 'mermaid') {
|
|
182
|
+
return _jsx(Diagram, { code: text.replace(/\n$/, '') });
|
|
183
|
+
}
|
|
162
184
|
if (block) {
|
|
163
|
-
return
|
|
185
|
+
return _jsx(CodeBlock, { code: text.replace(/\n$/, ''), lang: languageOf(className), actions: shellCodeBlockActions(reply, shell, renderShellCodeBlock) });
|
|
164
186
|
}
|
|
165
187
|
return _jsx(InlineCode, { text: text });
|
|
166
188
|
}
|
|
@@ -192,14 +214,28 @@ const ReplyBlock = memo(function ReplyBlock({ text, fade, open, reply }) {
|
|
|
192
214
|
return (_jsx(CodeStreamingContext.Provider, { value: open, children: _jsx(ShellCodeBlockReplyContext.Provider, { value: reply ?? null, children: _jsx(ReactMarkdown, { remarkPlugins: REPLY_PLUGINS, rehypePlugins: fade ? FADE_PLUGINS : NO_PLUGINS, components: replyComponents, children: text }) }) }));
|
|
193
215
|
});
|
|
194
216
|
/*
|
|
195
|
-
* A
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
|
|
217
|
+
* A code fence at the top level of a reply, drawn as markdown draws it but without parsing, so a word
|
|
218
|
+
* added to a long fence costs no more than one added to a short one. It is the same tree whether the
|
|
219
|
+
* fence is open or closed, so closing it keeps the lines its code block already has.
|
|
220
|
+
*/
|
|
221
|
+
const ReplyFence = memo(function ReplyFence({ info, body, source, open, reply }) {
|
|
222
|
+
const { renderShellCodeBlock } = useChatHost();
|
|
223
|
+
// The class markdown gives the code, read back the way a parsed fence's is.
|
|
224
|
+
const lang = languageOf(`language-${info.split(/[ \t]/, 1)[0]}`);
|
|
225
|
+
const shell = reply === undefined ? null : shellFence(source);
|
|
226
|
+
return (_jsx(CodeStreamingContext.Provider, { value: open, children: lang === 'mermaid' ? (_jsx(Diagram, { code: body })) : (_jsx(CodeBlock, { code: body, lang: lang, actions: shellCodeBlockActions(reply ?? null, shell, renderShellCodeBlock) })) }));
|
|
227
|
+
});
|
|
228
|
+
/*
|
|
229
|
+
* A reply in a chat thread, parsed a block at a time so a delta only parses the block that grows,
|
|
230
|
+
* and a code fence at the top level not at all. While it streams every new word fades in, and a fence
|
|
231
|
+
* that has not closed yet is highlighted a line at a time as it grows. `arriving` fades in each block
|
|
232
|
+
* as it is added instead, for a reply that is shown a block at a time.
|
|
199
233
|
*/
|
|
200
234
|
export const ReplyMarkdown = memo(function ReplyMarkdown({ text, streaming, arriving = false, reply }) {
|
|
201
235
|
const blocks = useMemo(() => splitMarkdownBlocks(text), [text]);
|
|
202
|
-
return (_jsx("div", { className: "chat-markdown prose prose-sm max-w-none text-sm", "data-arriving": arriving || undefined, children: blocks.map((block, index) =>
|
|
236
|
+
return (_jsx("div", { className: "chat-markdown prose prose-sm max-w-none text-sm", "data-arriving": arriving || undefined, children: blocks.map((block, index) =>
|
|
237
|
+
// Blocks only ever grow at the end, so the place of a block is a stable key.
|
|
238
|
+
block.fence !== undefined ? (_jsx(ReplyFence, { info: block.fence.info, body: block.fence.body, source: block.fence.source, open: streaming && !block.fence.closed, reply: reply }, index)) : (_jsx(ReplyBlock, { text: block.text, fade: streaming, open: streaming && block.openFence, reply: reply }, index))) }));
|
|
203
239
|
});
|
|
204
240
|
/*
|
|
205
241
|
* A message a person sent. Enter meant a line break, a tag typed without backticks stays the text
|
|
@@ -209,8 +245,11 @@ export const MessageMarkdown = memo(function MessageMarkdown({ text, mentions, s
|
|
|
209
245
|
const rehypePlugins = useMemo(() => [[rehypeChips, { mentions, skills }]], [mentions, skills]);
|
|
210
246
|
return (_jsx("div", { className: "chat-markdown prose prose-sm max-w-none text-sm", children: _jsx(ReactMarkdown, { remarkPlugins: MESSAGE_PLUGINS, rehypePlugins: rehypePlugins, components: messageComponents, children: text }) }));
|
|
211
247
|
});
|
|
212
|
-
/*
|
|
213
|
-
|
|
248
|
+
/*
|
|
249
|
+
* `breaks` is for person-authored notes; applying it to CLI markdown would change its layout. `muted`
|
|
250
|
+
* sets the text a step back, for what stands beside an answer rather than being one.
|
|
251
|
+
*/
|
|
252
|
+
export const Markdown = memo(function Markdown({ text, breaks = false, fileLinks = true, muted = false, rehypePlugins, componentOverrides }) {
|
|
214
253
|
const renderComponents = useMemo(() => ({ ...(fileLinks ? components : plainComponents), ...componentOverrides }), [fileLinks, componentOverrides]);
|
|
215
|
-
return (_jsx("div", { className: "chat-markdown prose prose-sm max-w-none text-sm", children: _jsx(ReactMarkdown, { remarkPlugins: breaks ? PLUGINS_WITH_BREAKS : PLUGINS, rehypePlugins: rehypePlugins, components: renderComponents, children: text }) }));
|
|
254
|
+
return (_jsx("div", { className: "chat-markdown prose prose-sm max-w-none text-sm", "data-muted": muted || undefined, children: _jsx(ReactMarkdown, { remarkPlugins: breaks ? PLUGINS_WITH_BREAKS : PLUGINS, rehypePlugins: rehypePlugins, components: renderComponents, children: text }) }));
|
|
216
255
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useEffect, useId, useRef, useState } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Code, Workflow } from 'lucide-react';
|
|
5
|
+
import { IconButton } from '@adecore/ui';
|
|
6
|
+
import { useChatHost } from '../../host.js';
|
|
7
|
+
import { CodeBlock } from './CodeBlock.js';
|
|
8
|
+
import { CodeStreamingContext } from './code-streaming.js';
|
|
9
|
+
import { MERMAID_TOKENS, mermaidConfig, sizedSvg, svgDataUrl } from './mermaid.js';
|
|
10
|
+
// Mermaid keeps one configuration for the page, so one diagram is drawn at a time with its own.
|
|
11
|
+
let queue = Promise.resolve();
|
|
12
|
+
let probeCanvas = null;
|
|
13
|
+
/* A token as an opaque `#rrggbb` over `ground`, which is what Mermaid's color math reads. */
|
|
14
|
+
function resolveColor(element, token, ground) {
|
|
15
|
+
probeCanvas ??= document.createElement('canvas');
|
|
16
|
+
probeCanvas.width = 1;
|
|
17
|
+
probeCanvas.height = 1;
|
|
18
|
+
const context = probeCanvas.getContext('2d', { willReadFrequently: true });
|
|
19
|
+
const value = getComputedStyle(element).getPropertyValue(token).trim();
|
|
20
|
+
if (context === null || value === '') {
|
|
21
|
+
return ground;
|
|
22
|
+
}
|
|
23
|
+
context.fillStyle = ground;
|
|
24
|
+
context.fillRect(0, 0, 1, 1);
|
|
25
|
+
context.fillStyle = value;
|
|
26
|
+
context.fillRect(0, 0, 1, 1);
|
|
27
|
+
const [red, green, blue] = context.getImageData(0, 0, 1, 1).data;
|
|
28
|
+
return `#${[red, green, blue].map((channel) => channel.toString(16).padStart(2, '0')).join('')}`;
|
|
29
|
+
}
|
|
30
|
+
function themeColors(element) {
|
|
31
|
+
const ground = resolveColor(element, MERMAID_TOKENS.ground, '#808080');
|
|
32
|
+
const entries = Object.entries(MERMAID_TOKENS).map(([name, token]) => [name, name === 'ground' ? ground : resolveColor(element, token, ground)]);
|
|
33
|
+
return Object.fromEntries(entries);
|
|
34
|
+
}
|
|
35
|
+
async function drawDiagram(id, source, element, dark) {
|
|
36
|
+
const { default: mermaid } = await import('mermaid');
|
|
37
|
+
const style = getComputedStyle(element);
|
|
38
|
+
mermaid.initialize(mermaidConfig(themeColors(element), dark, { family: style.fontFamily, size: style.fontSize }));
|
|
39
|
+
const { svg } = await mermaid.render(id, source);
|
|
40
|
+
return sizedSvg(svg);
|
|
41
|
+
}
|
|
42
|
+
/*
|
|
43
|
+
* A ```mermaid fence, drawn as the diagram once the fence is closed. Mermaid is large, so it loads on
|
|
44
|
+
* the first diagram of the page. While the fence grows, and when the text is no diagram Mermaid can
|
|
45
|
+
* draw, it is the code block it would otherwise be.
|
|
46
|
+
*/
|
|
47
|
+
export function MermaidBlock({ code }) {
|
|
48
|
+
const host = useChatHost();
|
|
49
|
+
const { t } = useTranslation('agent-chat');
|
|
50
|
+
const streaming = useContext(CodeStreamingContext);
|
|
51
|
+
const dark = host.code.useMode() === 'dark';
|
|
52
|
+
const id = `mermaid-${useId().replace(/[^\w-]/g, '')}`;
|
|
53
|
+
const frame = useRef(null);
|
|
54
|
+
const [source, setSource] = useState(false);
|
|
55
|
+
const [drawing, setDrawing] = useState({ state: 'drawing' });
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const element = frame.current;
|
|
58
|
+
if (streaming || element === null) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
setDrawing({ state: 'drawing' });
|
|
63
|
+
const drawn = queue.then(() => (cancelled ? null : drawDiagram(id, code, element, dark)));
|
|
64
|
+
queue = drawn.catch(() => undefined);
|
|
65
|
+
drawn.then((image) => !cancelled && setDrawing(image === null ? { state: 'failed' } : { state: 'drawn', image }), () => !cancelled && setDrawing({ state: 'failed' }));
|
|
66
|
+
return () => {
|
|
67
|
+
cancelled = true;
|
|
68
|
+
};
|
|
69
|
+
}, [code, dark, id, streaming]);
|
|
70
|
+
const showsDiagram = !streaming && !source && drawing.state !== 'failed';
|
|
71
|
+
const toggle = streaming || drawing.state === 'failed' ? undefined : (_jsx(IconButton, { icon: source ? Workflow : Code, size: "sm", label: source ? t('code.mermaid.showDiagram') : t('code.mermaid.showSource'), onClick: () => setSource((shown) => !shown) }));
|
|
72
|
+
return (_jsxs("div", { ref: frame, children: [_jsx(CodeBlock, { code: code, lang: "mermaid", actions: toggle, view: showsDiagram ? (_jsx("div", { className: "overflow-x-auto p-3", "aria-busy": drawing.state === 'drawing' || undefined, children: drawing.state === 'drawn' && (_jsx("img", { src: svgDataUrl(drawing.image.svg), width: drawing.image.width, height: drawing.image.height, alt: t('code.mermaid.diagram'), className: "m-0 max-w-none", draggable: false })) })) : undefined }), drawing.state === 'failed' && _jsx("p", { className: "m-0 mt-1 text-xs text-text-faint", children: t('code.mermaid.failed') })] }));
|
|
73
|
+
}
|
|
@@ -6,30 +6,35 @@ import { Bookmark, Check, Copy, GitFork } from 'lucide-react';
|
|
|
6
6
|
import { placeBookmark } from '../bookmarks.js';
|
|
7
7
|
import { forkRefusal } from '../logic/fork.js';
|
|
8
8
|
import { markdownOf, messageTextOf } from '../logic/timeline-copy.js';
|
|
9
|
-
import {
|
|
9
|
+
import { useChatHost } from '../../host.js';
|
|
10
10
|
import { useChatScope } from '../../scope.js';
|
|
11
11
|
import { useChatRow, useChats } from '../../state/chats.js';
|
|
12
12
|
import { ButtonGroup, copyText, IconButton, Tooltip } from '@adecore/ui';
|
|
13
13
|
import { formatClock, formatDateTime, useFormatLocale } from '@adecore/ui/format';
|
|
14
|
+
import { TIMESTAMP_FORMAT } from './timestamp-format.js';
|
|
14
15
|
const COPIED_MS = 1500;
|
|
15
|
-
const TIMESTAMP_FORMAT = { dateStyle: 'full', timeStyle: 'medium' };
|
|
16
16
|
/* Hidden actions keep their height, so hovering never forces the virtualizer to remeasure. */
|
|
17
17
|
export function MessageActions({ chatId, row }) {
|
|
18
18
|
const { t } = useTranslation('agent-chat');
|
|
19
19
|
useFormatLocale();
|
|
20
20
|
const scope = useChatScope();
|
|
21
|
-
const
|
|
21
|
+
const host = useChatHost();
|
|
22
|
+
const { fork } = host;
|
|
22
23
|
const turnId = row.item.turnId;
|
|
23
|
-
|
|
24
|
-
const
|
|
24
|
+
// Every selector runs on every streamed word of the thread, so each only looks a field up; the work happens in render.
|
|
25
|
+
const bookmarks = useChatRow(chatId, (chat) => chat?.bookmarks);
|
|
26
|
+
const info = useChatRow(chatId, (chat) => chat?.info ?? null);
|
|
27
|
+
const turn = useChatRow(chatId, (chat) => (turnId === null ? undefined : chat?.structure[turnId]));
|
|
25
28
|
const streaming = useChatRow(chatId, (chat) => {
|
|
26
29
|
const item = chat?.items[row.id];
|
|
27
30
|
return item?.kind === 'assistant' && item.streaming;
|
|
28
31
|
});
|
|
29
32
|
const hasText = useChatRow(chatId, (chat) => {
|
|
30
33
|
const item = chat?.items[row.id];
|
|
31
|
-
return (item?.kind === 'user' || item?.kind === 'assistant') && item.text
|
|
34
|
+
return (item?.kind === 'user' || item?.kind === 'assistant') && /\S/.test(item.text);
|
|
32
35
|
});
|
|
36
|
+
const bookmarked = bookmarks?.some((candidate) => candidate.itemId === row.id) === true;
|
|
37
|
+
const canFork = turnId !== null && forkRefusal(info, turn) === null;
|
|
33
38
|
const [copied, setCopied] = useState(false);
|
|
34
39
|
useEffect(() => {
|
|
35
40
|
if (!copied) {
|
|
@@ -48,5 +53,5 @@ export function MessageActions({ chatId, row }) {
|
|
|
48
53
|
setCopied(true);
|
|
49
54
|
}
|
|
50
55
|
};
|
|
51
|
-
return (_jsxs("div", { role: "toolbar", "aria-label": t('timeline.actions.label'), className: clsx('flex h-7 items-center gap-2 opacity-0 transition-opacity group-has-focus-visible/message:opacity-100 group-hover/message:opacity-100', row.kind === 'user' && 'mt-1.5 justify-end'), children: [!streaming && (_jsxs(ButtonGroup, { render: _jsx("span", {}), children: [
|
|
56
|
+
return (_jsxs("div", { role: "toolbar", "aria-label": t('timeline.actions.label'), className: clsx('flex h-7 items-center gap-2 opacity-0 transition-opacity group-has-focus-visible/message:opacity-100 group-hover/message:opacity-100', row.kind === 'user' && 'mt-1.5 justify-end'), children: [!streaming && (_jsxs(ButtonGroup, { render: _jsx("span", {}), children: [!bookmarked && (_jsx(IconButton, { icon: Bookmark, size: "sm", label: t('bookmarks.add'), onClick: () => void placeBookmark(scope, host, chatId, row.id) })), canFork && turnId !== null && fork !== null && (_jsx(IconButton, { icon: GitFork, size: "sm", label: t('timeline.menu.forkFromHere'), onClick: () => fork(chatId, turnId) })), hasText && (_jsx(IconButton, { icon: copied ? Check : Copy, size: "sm", label: copied ? t('timeline.actions.copied') : t('timeline.menu.copyMessage'), onClick: (e) => copy(e.shiftKey) }))] })), _jsx(Tooltip, { label: formatDateTime(row.item.createdAt, TIMESTAMP_FORMAT), children: _jsx("time", { dateTime: new Date(row.item.createdAt).toISOString(), className: clsx('shrink-0 text-xs text-text-faint tabular-nums select-none', row.kind === 'assistant' && 'order-first'), children: formatClock(row.item.createdAt) }) })] }));
|
|
52
57
|
}
|
package/dist/chat/ui/Pickers.js
CHANGED
|
@@ -6,12 +6,12 @@ import { AgentIcon } from '../../agents/AgentIcon.js';
|
|
|
6
6
|
import { ProviderLogo } from '../../agents/ProviderLogo.js';
|
|
7
7
|
import { agentChipOf, modelName } from '../../agents/model-name.js';
|
|
8
8
|
import { modelRequirementOf } from '../../agents/model-requirement.js';
|
|
9
|
-
import {
|
|
9
|
+
import { useChatHost } from '../../host.js';
|
|
10
10
|
import { ChatScopeContext } from '../../scope.js';
|
|
11
11
|
import { useReportedModels } from '../../state/chats.js';
|
|
12
12
|
import { pickerEntriesOf } from '../logic/picker-entries.js';
|
|
13
13
|
import { forgetStashed, STASH_SHORTCUT, useStash } from '../stash.js';
|
|
14
|
-
import { Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
|
|
14
|
+
import { Icon, IconButton, Menu, Pill, Popover, Tooltip } from '@adecore/ui';
|
|
15
15
|
const triggerClass = 'flex h-7 shrink-0 items-center gap-1 whitespace-nowrap rounded-md px-2 text-xs text-text-muted hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-active data-[popup-open]:text-text';
|
|
16
16
|
const chipClass = 'flex h-6 min-w-0 shrink items-center gap-1.5 whitespace-nowrap rounded-md bg-surface-hover px-2 text-xs text-text hover:bg-surface-active data-[popup-open]:bg-surface-active';
|
|
17
17
|
/*
|
|
@@ -20,6 +20,7 @@ const chipClass = 'flex h-6 min-w-0 shrink items-center gap-1.5 whitespace-nowra
|
|
|
20
20
|
* provider's model is also how a chat picks its provider; with one CLI the group headers drop.
|
|
21
21
|
*/
|
|
22
22
|
export function ModelPicker({ providers, provider, selection, open, onOpenChange, onChange, kbd = '/model', side = 'top', trigger = 'plain', finalFocus, footer }) {
|
|
23
|
+
const host = useChatHost();
|
|
23
24
|
const { t } = useTranslation('agent-chat');
|
|
24
25
|
const [query, setQuery] = useState('');
|
|
25
26
|
const [legacyOpen, setLegacyOpen] = useState(false);
|
|
@@ -33,7 +34,7 @@ export function ModelPicker({ providers, provider, selection, open, onOpenChange
|
|
|
33
34
|
const trimmed = query.trim().toLowerCase();
|
|
34
35
|
const entries = useMemo(() => pickerEntriesOf(providers, trimmed, legacyOpen), [providers, trimmed, legacyOpen]);
|
|
35
36
|
const scope = useContext(ChatScopeContext);
|
|
36
|
-
const machine =
|
|
37
|
+
const machine = host.useMachineLabel(scope?.id ?? '');
|
|
37
38
|
const reported = useReportedModels(provider);
|
|
38
39
|
const requirementOf = (entry) => modelRequirementOf(entry.model, entry.provider, reported);
|
|
39
40
|
// A CLI that is too old blocks all its models; one way to its settings under the group says it once.
|
|
@@ -93,11 +94,11 @@ export function ModelPicker({ providers, provider, selection, open, onOpenChange
|
|
|
93
94
|
const next = entries[i + 1];
|
|
94
95
|
const lastOfGroup = next === undefined || next.kind !== 'model' || next.provider.kind !== entry.provider.kind;
|
|
95
96
|
const chosen = entry.provider.kind === provider && entry.model.slug === selection.model;
|
|
96
|
-
return (_jsxs("div", { children: [first && (_jsxs(Menu.Label, { className: "flex items-center gap-1.5", children: [_jsx(AgentIcon, { kind: entry.provider.kind, size: 12 }), entry.provider.name] })), _jsxs("button", { role: "option", "aria-selected": chosen, "aria-disabled": requirement !== null || undefined, "data-active": i === index, className: `cursor-row flex w-full flex-col items-stretch gap-0.5 rounded-md px-2.5 py-1.5 text-left text-sm ${requirement === null ? 'text-text-muted' : 'text-text-faint'}`, onMouseEnter: () => setIndex(i), onClick: () => choose(entry), children: [_jsxs("span", { className: "flex w-full items-center gap-2", children: [_jsx(Menu.Check, { kind: "radio", checked: chosen }), _jsx("span", { className: "min-w-0 truncate", children: entry.model.name }), entry.model.badge && (_jsx(
|
|
97
|
+
return (_jsxs("div", { children: [first && (_jsxs(Menu.Label, { className: "flex items-center gap-1.5", children: [_jsx(AgentIcon, { kind: entry.provider.kind, size: 12 }), entry.provider.name] })), _jsxs("button", { role: "option", "aria-selected": chosen, "aria-disabled": requirement !== null || undefined, "data-active": i === index, className: `cursor-row flex w-full flex-col items-stretch gap-0.5 rounded-md px-2.5 py-1.5 text-left text-sm ${requirement === null ? 'text-text-muted' : 'text-text-faint'}`, onMouseEnter: () => setIndex(i), onClick: () => choose(entry), children: [_jsxs("span", { className: "flex w-full items-center gap-2", children: [_jsx(Menu.Check, { kind: "radio", checked: chosen }), _jsx("span", { className: "min-w-0 truncate", children: entry.model.name }), entry.model.badge && (_jsx(Pill, { tone: "accent", shape: "tag", size: "sm", children: entry.model.badge.charAt(0).toLocaleUpperCase() + entry.model.badge.slice(1) })), _jsx("span", { className: "grow" }), entry.model.legacy && _jsx("span", { className: "text-xs text-text-faint", children: t('pickers.model.legacy') })] }), requirement !== null && (_jsx("span", { className: "pl-6 text-xs text-text-faint", children: machine === null
|
|
97
98
|
? t('pickers.model.requiresHere', { cli: requirement.cli, version: requirement.minVersion })
|
|
98
99
|
: t('pickers.model.requires', { cli: requirement.cli, version: requirement.minVersion, machine }) }))] }), lastOfGroup && blockedKinds.has(entry.provider.kind) && (_jsx("button", { type: "button", className: "cursor-row flex w-full items-center rounded-md px-2.5 py-1.5 text-left text-xs text-text-muted", onClick: () => {
|
|
99
100
|
setOpen(false);
|
|
100
|
-
|
|
101
|
+
host.openSettings('providers');
|
|
101
102
|
}, children: t('pickers.model.providerSettings') }))] }, `${entry.provider.kind}/${entry.model.slug}`));
|
|
102
103
|
})] }), footer !== undefined && _jsx("div", { className: "border-t border-border-soft px-3 py-2 text-xs text-text-faint", children: footer })] })] }));
|
|
103
104
|
}
|
|
@@ -8,7 +8,7 @@ import { answerPrompt, isBlockingSubject, promptCreatedAt, promptIdOf } from '..
|
|
|
8
8
|
import { useFocusAfterAnswer } from '../../prompts/logic/useFocusAfterAnswer.js';
|
|
9
9
|
import { usePromptSession } from '../../prompts/logic/usePromptSession.js';
|
|
10
10
|
import { PromptView } from '../../prompts/ui/PromptView.js';
|
|
11
|
-
import {
|
|
11
|
+
import { useChatHost } from '../../host.js';
|
|
12
12
|
import { useChatScope } from '../../scope.js';
|
|
13
13
|
import { useChatActions } from '../actions.js';
|
|
14
14
|
import { Button, Icon } from '@adecore/ui';
|
|
@@ -19,10 +19,11 @@ function pickSubject(waiting, activeId) {
|
|
|
19
19
|
export function PromptComposer({ chatId, pending, focused, onShowElsewhere, disabled, hasDraft, denyReason, onAllAnswered, hold, children }) {
|
|
20
20
|
const { t } = useTranslation('agent-chat');
|
|
21
21
|
const { id } = useChatScope();
|
|
22
|
+
const host = useChatHost();
|
|
22
23
|
const actions = useChatActions();
|
|
23
24
|
const elsewhere = onShowElsewhere !== undefined;
|
|
24
25
|
// A card the app raises itself waits in the chat beside the CLI's own requests.
|
|
25
|
-
const extra =
|
|
26
|
+
const extra = host.prompts.useExtra(id, chatId);
|
|
26
27
|
const subjects = useMemo(() => [
|
|
27
28
|
...pending.map((item) => ({ kind: 'chat', nodeId: chatId, item })),
|
|
28
29
|
...extra.map((prompt) => ({ kind: 'host', nodeId: chatId, prompt }))
|
|
@@ -58,7 +59,7 @@ export function PromptComposer({ chatId, pending, focused, onShowElsewhere, disa
|
|
|
58
59
|
return (_jsxs("div", { ref: ref, onPointerDownCapture: session.onPointerDownCapture, onClickCapture: session.onClickCapture, children: [active && elsewhere && (_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-3.5 py-3 text-left text-sm text-text-muted hover:text-text", onClick: onShowElsewhere, children: [_jsx(Icon, { icon: asksApproval ? Hand : MessageCircleQuestionMark, size: 16, className: "shrink-0 text-status-needs-you" }), _jsx("span", { className: "grow", children: t('composer.waitingBelow') }), _jsx(Icon, { icon: ArrowDown, size: 16, className: "shrink-0" })] })), active && !elsewhere && (_jsx("div", { hidden: !expanded, children: _jsx(PromptView, { subject: active, draft: session.draftOf(promptIdOf(active)), onDraft: (draft) => session.setDraft(promptIdOf(active), draft), onAction: (action) => {
|
|
59
60
|
refocus.hold();
|
|
60
61
|
void session
|
|
61
|
-
.act(active, () => answerPrompt(active, action, actions))
|
|
62
|
+
.act(active, () => answerPrompt(active, action, actions, host))
|
|
62
63
|
.then((result) => {
|
|
63
64
|
if (result === 'failed') {
|
|
64
65
|
refocus.release();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip } from '@adecore/ui';
|
|
3
|
+
import { formatClock, formatDateTime, useFormatLocale } from '@adecore/ui/format';
|
|
4
|
+
import { TIMESTAMP_FORMAT } from './timestamp-format.js';
|
|
5
|
+
/*
|
|
6
|
+
* The wall-clock time of a row at its right edge, shown only while an element marked `group/line`
|
|
7
|
+
* around it is hovered or has the keyboard focus. Hidden, it takes no room and is no tab stop.
|
|
8
|
+
*/
|
|
9
|
+
export function RowTime({ at }) {
|
|
10
|
+
useFormatLocale();
|
|
11
|
+
return (_jsx(Tooltip, { label: formatDateTime(at, TIMESTAMP_FORMAT), children: _jsx("time", { dateTime: new Date(at).toISOString(), className: "hidden shrink-0 text-xs text-text-faint tabular-nums select-none group-hover/line:inline group-focus-visible/line:inline group-has-focus-visible/line:inline", children: formatClock(at) }) }));
|
|
12
|
+
}
|