@adecore/agents-react 0.23.1 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +130 -907
- 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/surface.d.ts +1 -0
- package/dist/chat/ui/composer/surface.js +44 -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 +212 -1358
- 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/surface.ts +45 -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
package/src/chat/ui/Timeline.tsx
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CSSProperties,
|
|
3
|
+
memo,
|
|
4
|
+
type RefObject,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useReducer,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
type ReactNode
|
|
13
|
+
} from 'react';
|
|
2
14
|
import { useTranslation } from 'react-i18next';
|
|
3
15
|
import { useVirtualizer, type Virtualizer } from '@tanstack/react-virtual';
|
|
4
16
|
import clsx from 'clsx';
|
|
@@ -21,6 +33,8 @@ import {
|
|
|
21
33
|
type TurnTarget
|
|
22
34
|
} from '../timeline-scroll';
|
|
23
35
|
import { useTimelineFlash } from '../timeline-flash';
|
|
36
|
+
import { useChatPreferences } from '../preferences';
|
|
37
|
+
import { readingMeasure, readingWidthStyle } from '../reading-width';
|
|
24
38
|
import {
|
|
25
39
|
SCRUBBER_MIN_TICKS,
|
|
26
40
|
STRIP_INSET_PX,
|
|
@@ -55,11 +69,11 @@ import { QuoteButton } from './QuoteButton';
|
|
|
55
69
|
import { QuoteTakerContext, type QuoteTaker } from './quote-selection';
|
|
56
70
|
import { useToggleSet } from './useToggleSet';
|
|
57
71
|
import { WelcomeGreeting } from './Welcome';
|
|
58
|
-
import { Row } from './rows/Rows';
|
|
72
|
+
import { Row, type RowProps } from './rows/Rows';
|
|
59
73
|
import { RowContainer } from './rows/RowContainer';
|
|
60
74
|
import { ReplyHeader } from './rows/MessageRows';
|
|
61
75
|
import { useChatRow, useChats } from '../../state/chats';
|
|
62
|
-
import {
|
|
76
|
+
import { useChatHost, type TimelineFindOptions } from '../../host';
|
|
63
77
|
import { useChatScope } from '../../scope';
|
|
64
78
|
import { FileLinkContext } from './file-links';
|
|
65
79
|
import { AgentIcon } from '../../agents/AgentIcon';
|
|
@@ -81,11 +95,12 @@ function showRowAtTop(virtualizer: Virtualizer<HTMLDivElement, Element>, followR
|
|
|
81
95
|
* and the folder it works in, so a first question can lean on all three.
|
|
82
96
|
*/
|
|
83
97
|
function EmptyThread({ chatId }: { chatId: string }) {
|
|
98
|
+
const host = useChatHost();
|
|
84
99
|
const { t } = useTranslation('agent-chat');
|
|
85
100
|
const info = useChatRow(chatId, (row) => row?.info ?? null);
|
|
86
101
|
const model = useModelName(info?.provider, info?.selection.model ?? '');
|
|
87
|
-
const sources =
|
|
88
|
-
const hidesFolder =
|
|
102
|
+
const sources = host.useContextSources(chatId);
|
|
103
|
+
const hidesFolder = host.useHidesFolder(chatId);
|
|
89
104
|
return (
|
|
90
105
|
<div className="flex grow items-center justify-center">
|
|
91
106
|
<div className="flex max-w-sm flex-col items-center gap-3 px-6 py-8 text-center">
|
|
@@ -133,11 +148,42 @@ function MarkableRow({ row, chatId, bookmark, children }: { row: TimelineRow; ch
|
|
|
133
148
|
);
|
|
134
149
|
}
|
|
135
150
|
|
|
151
|
+
interface ThreadRowProps extends Omit<RowProps, 'openConversation'> {
|
|
152
|
+
previous: TimelineRow | null;
|
|
153
|
+
index: number;
|
|
154
|
+
top: number;
|
|
155
|
+
measure: (element: HTMLDivElement | null) => void;
|
|
156
|
+
/* The nonce of the flash on this row, or null when it has none. */
|
|
157
|
+
flashNonce: number | null;
|
|
158
|
+
bookmark: ChatBookmark | null;
|
|
159
|
+
openConversation(step: SubagentStep): void;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Memoized with stable props, so a streamed word or a scroll draws only the rows it changed. */
|
|
163
|
+
const ThreadRow = memo(function ThreadRow({ row, previous, index, top, measure, chatId, flashNonce, bookmark, ...actions }: ThreadRowProps) {
|
|
164
|
+
const header = replyHeader(row, previous);
|
|
165
|
+
return (
|
|
166
|
+
<RowContainer row={row} previous={previous} index={index} top={top} ref={measure}>
|
|
167
|
+
{flashNonce !== null && <span key={flashNonce} className="chat-flash pointer-events-none absolute inset-0 rounded-lg" aria-hidden />}
|
|
168
|
+
{header !== null && (
|
|
169
|
+
<div className="chat-column-content w-full">
|
|
170
|
+
<ReplyHeader chatId={chatId} at={header.at} />
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
<MarkableRow row={row} chatId={chatId} bookmark={bookmark}>
|
|
174
|
+
<Row row={row} chatId={chatId} {...actions} />
|
|
175
|
+
</MarkableRow>
|
|
176
|
+
</RowContainer>
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
136
180
|
/*
|
|
137
181
|
* `overlay` stands in the thread's place and ends where the composer starts, so the thread keeps
|
|
138
182
|
* where it was underneath and the composer stays in reach for the prompts it answers.
|
|
139
183
|
*/
|
|
140
184
|
export function Timeline({ chatId, composer, overlay }: { chatId: string; composer?: ReactNode; overlay?: ReactNode }) {
|
|
185
|
+
const host = useChatHost();
|
|
186
|
+
const { fork } = host;
|
|
141
187
|
const { t } = useTranslation('agent-chat');
|
|
142
188
|
const order = useChatRow(chatId, (row) => row?.order);
|
|
143
189
|
// The structure, not the items. A delta growing a reply must not derive every row again.
|
|
@@ -209,15 +255,15 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
209
255
|
{ expandedGroups: groups.ids, expandedTurns: turns.ids, expandedSubagents: subagents.ids, activeTurnId, forkedTurns }
|
|
210
256
|
);
|
|
211
257
|
}, [order, items, groups.ids, turns.ids, subagents.ids, activeTurnId, forkedTurns]);
|
|
212
|
-
const cards =
|
|
213
|
-
const drawsVisuals =
|
|
258
|
+
const cards = host.useThreadCards(scopeId, chatId);
|
|
259
|
+
const drawsVisuals = host.visuals !== null;
|
|
214
260
|
// Where the part of the thread this client holds begins, while there is a page before it.
|
|
215
261
|
const heldFrom = cursor === null ? null : (items?.[order?.[0] ?? '']?.createdAt ?? null);
|
|
216
262
|
const rows = useMemo(
|
|
217
263
|
() => withTimedRows(threadRows, timedRowsOf(cards, drawsVisuals ? (visuals ?? []) : null, heldFrom)),
|
|
218
264
|
[threadRows, cards, drawsVisuals, visuals, heldFrom]
|
|
219
265
|
);
|
|
220
|
-
const welcomes =
|
|
266
|
+
const welcomes = host.useWelcome(chatId);
|
|
221
267
|
|
|
222
268
|
const empty = rows.length === 0;
|
|
223
269
|
// A chat that took a turn before is one whose thread has not come in yet, not one to greet.
|
|
@@ -308,17 +354,11 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
308
354
|
scrollPaddingEnd: coveredHeight
|
|
309
355
|
});
|
|
310
356
|
|
|
311
|
-
// A change in what the last row says (a streaming delta) must also pull the view down.
|
|
312
|
-
const lastRow = rows[rows.length - 1];
|
|
313
|
-
const lastReplyLength = useChatRow(chatId, (row) => {
|
|
314
|
-
const item = lastRow?.kind === 'assistant' ? row?.items[lastRow.id] : undefined;
|
|
315
|
-
return item?.kind === 'assistant' ? item.text.length : null;
|
|
316
|
-
});
|
|
317
|
-
const tail = lastReplyLength ?? rows.length;
|
|
318
357
|
/*
|
|
319
|
-
* Rows are measured as they render, so the end moves while it is drawn (a diff that
|
|
320
|
-
* image that loads). `scrollToIndex` aims at where a row was when asked and fell
|
|
321
|
-
* scroller's own bottom is a fact, and a new total size says a row was measured again.
|
|
358
|
+
* Rows are measured as they render, so the end moves while it is drawn (a streamed line, a diff that
|
|
359
|
+
* highlights, an image that loads). `scrollToIndex` aims at where a row was when asked and fell
|
|
360
|
+
* short; the scroller's own bottom is a fact, and a new total size says a row was measured again.
|
|
361
|
+
* A word that does not add a line moves nothing, so the thread does not listen to the words.
|
|
322
362
|
*/
|
|
323
363
|
const totalSize = virtualizer.getTotalSize();
|
|
324
364
|
useLayoutEffect(() => {
|
|
@@ -329,7 +369,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
329
369
|
// The composer is in the scroll flow, so its measured height is part of this end position.
|
|
330
370
|
element.scrollTop = element.scrollHeight;
|
|
331
371
|
scrollGeometry.current = { top: element.scrollTop, height: element.scrollHeight, viewport: element.clientHeight };
|
|
332
|
-
}, [rows.length,
|
|
372
|
+
}, [rows.length, totalSize, composerHeight, frame.height]);
|
|
333
373
|
|
|
334
374
|
// A page that went in above moves every row down by what it measures; the reader stays on the row they were on.
|
|
335
375
|
useLayoutEffect(() => {
|
|
@@ -364,7 +404,8 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
364
404
|
const onMainAgent = trail.length === 0;
|
|
365
405
|
// A node on a canvas has no strip. It is too narrow to give up a column of its width.
|
|
366
406
|
const showsScrubber = frame.column && onMainAgent && ticks.length >= SCRUBBER_MIN_TICKS;
|
|
367
|
-
const
|
|
407
|
+
const readingWidth = useChatPreferences((preferences) => preferences.readingWidth);
|
|
408
|
+
const paddingLeft = threadPaddingLeft(frame.width, showsScrubber, readingMeasure(readingWidth));
|
|
368
409
|
|
|
369
410
|
/*
|
|
370
411
|
* Where each message starts, from the virtualizer's measurements (an estimate for a row it never
|
|
@@ -375,35 +416,59 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
375
416
|
const scrollTop = virtualizer.scrollOffset ?? 0;
|
|
376
417
|
const viewport = virtualizer.scrollRect?.height ?? 0;
|
|
377
418
|
const visibleHeight = Math.max(0, viewport - coveredHeight);
|
|
419
|
+
const visibleHeightRef = useRef(visibleHeight);
|
|
420
|
+
visibleHeightRef.current = visibleHeight;
|
|
378
421
|
const inView = showsScrubber ? messagesInView({ starts, scrollTop, visibleHeight }) : null;
|
|
422
|
+
/*
|
|
423
|
+
* The virtualizer draws again only when its range of rows changes, which a scroll inside one long
|
|
424
|
+
* row never does; the scroll itself redraws the strip once the messages in view are others.
|
|
425
|
+
*/
|
|
426
|
+
const [, redrawStrip] = useReducer((count: number) => count + 1, 0);
|
|
427
|
+
const stripRef = useRef({ starts, inView });
|
|
428
|
+
stripRef.current = { starts, inView };
|
|
429
|
+
const followStrip = (element: HTMLElement): void => {
|
|
430
|
+
const { starts: current, inView: shown } = stripRef.current;
|
|
431
|
+
if (shown === null) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const next = messagesInView({ starts: current, scrollTop: element.scrollTop, visibleHeight: visibleHeightRef.current });
|
|
435
|
+
if (next?.first !== shown.first || next?.last !== shown.last) {
|
|
436
|
+
redrawStrip();
|
|
437
|
+
}
|
|
438
|
+
};
|
|
379
439
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
440
|
+
// One object while what it holds stays, so a find that keys an effect on it does not run it for every scroll.
|
|
441
|
+
const findOptions = useMemo<TimelineFindOptions>(
|
|
442
|
+
() => ({
|
|
443
|
+
chatId,
|
|
444
|
+
frame: frameRef,
|
|
445
|
+
thread: threadRef,
|
|
446
|
+
scroller: scrollRef,
|
|
447
|
+
enabled: onMainAgent,
|
|
448
|
+
rows,
|
|
449
|
+
structure: items,
|
|
450
|
+
order,
|
|
451
|
+
firstRowInView: () => {
|
|
452
|
+
const top = scrollRef.current?.scrollTop ?? 0;
|
|
453
|
+
return virtualizer.getVirtualItems().find((virtualRow) => virtualRow.end > top)?.index ?? 0;
|
|
454
|
+
},
|
|
455
|
+
openTurn: turns.add,
|
|
456
|
+
openGroup: groups.add,
|
|
457
|
+
openSubagent: subagents.add,
|
|
458
|
+
// A row already on screen stays put; the find scrolls to the match itself once it is drawn.
|
|
459
|
+
scrollToRow: (index) => {
|
|
460
|
+
const top = scrollRef.current?.scrollTop ?? 0;
|
|
461
|
+
const bottom = top + visibleHeightRef.current;
|
|
462
|
+
if (!virtualizer.getVirtualItems().some((virtualRow) => virtualRow.index === index && virtualRow.end > top && virtualRow.start < bottom)) {
|
|
463
|
+
virtualizer.scrollToIndex(index, { align: 'center' });
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
coveredHeight: () => coveredHeightRef.current,
|
|
467
|
+
stopFollowing
|
|
468
|
+
}),
|
|
469
|
+
[chatId, onMainAgent, rows, items, order, virtualizer, turns.add, groups.add, subagents.add, stopFollowing]
|
|
470
|
+
);
|
|
471
|
+
const chatFind = host.useTimelineFind(findOptions);
|
|
407
472
|
const searching = chatFind.open;
|
|
408
473
|
// A find covers the whole thread, so every page before this one comes in while it is open.
|
|
409
474
|
useEffect(() => {
|
|
@@ -548,15 +613,15 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
548
613
|
() => ({
|
|
549
614
|
chatId,
|
|
550
615
|
canFork: (turnId) => {
|
|
551
|
-
if (
|
|
616
|
+
if (fork === null) {
|
|
552
617
|
return false;
|
|
553
618
|
}
|
|
554
619
|
const row = useChats.getState().byKey[keyOf(chatId)];
|
|
555
620
|
return forkRefusal(row?.info ?? null, row?.structure[turnId]) === null;
|
|
556
621
|
},
|
|
557
|
-
fork: (turnId) =>
|
|
622
|
+
fork: (turnId) => fork?.(chatId, turnId)
|
|
558
623
|
}),
|
|
559
|
-
[keyOf, chatId]
|
|
624
|
+
[keyOf, chatId, fork]
|
|
560
625
|
);
|
|
561
626
|
|
|
562
627
|
const stepRef = useRef<(direction: -1 | 1) => boolean>(() => false);
|
|
@@ -573,26 +638,40 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
573
638
|
};
|
|
574
639
|
useEffect(() => registerMessageStepper(keyOf(chatId), (direction) => stepRef.current(direction)), [keyOf, chatId]);
|
|
575
640
|
|
|
641
|
+
// Read through a ref, so the rows keep one callback for their life and their memo holds.
|
|
642
|
+
const opensRef = useRef({ rows, show });
|
|
643
|
+
opensRef.current = { rows, show };
|
|
576
644
|
/* The whole of what a sub-agent did, in the thread's place. Its row only keeps the beginning. */
|
|
577
|
-
const openConversation = (step: SubagentStep): void => {
|
|
578
|
-
show(openFromMain(step));
|
|
579
|
-
};
|
|
645
|
+
const openConversation = useCallback((step: SubagentStep): void => {
|
|
646
|
+
opensRef.current.show(openFromMain(step));
|
|
647
|
+
}, []);
|
|
580
648
|
|
|
649
|
+
const addSubagent = subagents.add;
|
|
581
650
|
/* The header of a turn a sub-agent woke. It points at the row that agent worked in, or the one it hangs under. */
|
|
582
|
-
const openSubagent = (
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
651
|
+
const openSubagent = useCallback(
|
|
652
|
+
(toolUseId: string): void => {
|
|
653
|
+
const found = findSubagentBranch(opensRef.current.rows, toolUseId);
|
|
654
|
+
if (!found) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
addSubagent(found.branch.id);
|
|
658
|
+
showRowAtTop(virtualizer, followRef, found.index);
|
|
659
|
+
},
|
|
660
|
+
[addSubagent, virtualizer]
|
|
661
|
+
);
|
|
662
|
+
const flashedRow = flash !== null && flash.key === keyOf(chatId) && !flash.block ? flash.rowId : null;
|
|
590
663
|
|
|
591
664
|
return (
|
|
592
665
|
// A file an answer names is relative to the folder this chat runs in, which is a worktree as
|
|
593
666
|
// often as it is the project itself.
|
|
594
667
|
<FileLinkContext.Provider value={info?.cwd ?? null}>
|
|
595
|
-
<div
|
|
668
|
+
<div
|
|
669
|
+
ref={frameRef}
|
|
670
|
+
className="relative flex min-h-0 grow flex-col"
|
|
671
|
+
style={readingWidthStyle(readingWidth)}
|
|
672
|
+
data-file-cwd={info?.cwd ?? ''}
|
|
673
|
+
data-file-scope-id={scope.id}
|
|
674
|
+
>
|
|
596
675
|
{searching && chatFind.bar}
|
|
597
676
|
<ContextMenu.Root>
|
|
598
677
|
<div
|
|
@@ -618,6 +697,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
618
697
|
if (wantsEarlier(el)) {
|
|
619
698
|
loadEarlier();
|
|
620
699
|
}
|
|
700
|
+
followStrip(el);
|
|
621
701
|
}}
|
|
622
702
|
>
|
|
623
703
|
{/* A welcome centers the greeting and the composer as one group. The composer keeps its
|
|
@@ -643,49 +723,31 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
643
723
|
<EmptyThread chatId={chatId} />
|
|
644
724
|
) : (
|
|
645
725
|
<div className="relative w-full shrink-0" style={{ height: virtualizer.getTotalSize() }}>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
726
|
+
<UiReplyNavigationContext.Provider value={uiNavigation}>
|
|
727
|
+
<FindRevealContext.Provider value={chatFind.reveal}>
|
|
728
|
+
{virtualizer.getVirtualItems().map((virtualRow) => {
|
|
729
|
+
const row = rows[virtualRow.index]!;
|
|
730
|
+
return (
|
|
731
|
+
<ThreadRow
|
|
732
|
+
key={row.id}
|
|
733
|
+
row={row}
|
|
734
|
+
previous={virtualRow.index > 0 ? rows[virtualRow.index - 1]! : null}
|
|
735
|
+
index={virtualRow.index}
|
|
736
|
+
top={virtualRow.start}
|
|
737
|
+
measure={virtualizer.measureElement}
|
|
738
|
+
chatId={chatId}
|
|
739
|
+
flashNonce={flash !== null && flashedRow === row.id ? flash.nonce : null}
|
|
740
|
+
bookmark={marks.get(row.id) ?? null}
|
|
741
|
+
toggleGroup={groups.toggle}
|
|
742
|
+
toggleTurn={turns.toggle}
|
|
743
|
+
toggleSubagent={subagents.toggle}
|
|
744
|
+
openSubagent={openSubagent}
|
|
745
|
+
openConversation={openConversation}
|
|
664
746
|
/>
|
|
665
|
-
)
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
</div>
|
|
670
|
-
)}
|
|
671
|
-
<UiReplyNavigationContext.Provider value={uiNavigation}>
|
|
672
|
-
<FindRevealContext.Provider value={chatFind.reveal}>
|
|
673
|
-
<MarkableRow row={row} chatId={chatId} bookmark={marks.get(row.id) ?? null}>
|
|
674
|
-
<Row
|
|
675
|
-
row={row}
|
|
676
|
-
chatId={chatId}
|
|
677
|
-
toggleGroup={groups.toggle}
|
|
678
|
-
toggleTurn={turns.toggle}
|
|
679
|
-
toggleSubagent={subagents.toggle}
|
|
680
|
-
openSubagent={openSubagent}
|
|
681
|
-
openConversation={openConversation}
|
|
682
|
-
/>
|
|
683
|
-
</MarkableRow>
|
|
684
|
-
</FindRevealContext.Provider>
|
|
685
|
-
</UiReplyNavigationContext.Provider>
|
|
686
|
-
</RowContainer>
|
|
687
|
-
);
|
|
688
|
-
})}
|
|
747
|
+
);
|
|
748
|
+
})}
|
|
749
|
+
</FindRevealContext.Provider>
|
|
750
|
+
</UiReplyNavigationContext.Provider>
|
|
689
751
|
</div>
|
|
690
752
|
)}
|
|
691
753
|
</ContextMenu.Trigger>
|
|
@@ -703,7 +765,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
703
765
|
</div>
|
|
704
766
|
</div>
|
|
705
767
|
</div>
|
|
706
|
-
<TimelineMenuPopup target={target} thread={threadRef} chatId={onMainAgent ? chatId : null} />
|
|
768
|
+
<TimelineMenuPopup target={target} thread={threadRef} chatId={onMainAgent ? chatId : null} column={frame.column} />
|
|
707
769
|
</ContextMenu.Root>
|
|
708
770
|
{drawsVisuals && <VisualDialogs chatId={chatId} />}
|
|
709
771
|
{loadingEarlier && !overlay && (
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { BookmarkPlus, BookmarkX, Braces, Copy, Eye, FileText, GitFork, MessageSquare, Pencil, Scan } from 'lucide-react';
|
|
3
|
+
import { BookmarkPlus, BookmarkX, Braces, Copy, Eye, FileText, GitFork, MessageSquare, MoveHorizontal, Pencil, Scan } from 'lucide-react';
|
|
4
4
|
import { placeBookmark, removeBookmark, useBookmarkNaming } from '../bookmarks';
|
|
5
|
+
import { rememberReadingWidth, useChatPreferences } from '../preferences';
|
|
6
|
+
import { CHAT_READING_WIDTHS, readingWidthLabel, type ChatReadingWidth } from '../reading-width';
|
|
5
7
|
import { forkRefusal, turnIdOfRow } from '../logic/fork';
|
|
6
8
|
import { markdownOf, messageTextOf } from '../logic/timeline-copy';
|
|
7
9
|
import type { TimelineTarget } from '../logic/timeline-target';
|
|
8
|
-
import {
|
|
10
|
+
import { useChatHost } from '../../host';
|
|
9
11
|
import { useChatScope } from '../../scope';
|
|
10
12
|
import { useChatRow } from '../../state/chats';
|
|
11
|
-
import {
|
|
13
|
+
import { useFileLinkCwd, useFileLinkScopeId, useOpenFileLink } from './file-links';
|
|
12
14
|
import { ContextMenu, copyText, DisabledReason, EDIT_SHORTCUTS, Icon, Kbd, selectAllWithin } from '@adecore/ui';
|
|
13
15
|
|
|
14
16
|
/* The menu behind a right-click in a thread, mostly for copying: a whole message, a code block or an answer's markdown. */
|
|
15
17
|
export function TimelineMenuPopup({
|
|
16
18
|
target,
|
|
17
19
|
thread,
|
|
18
|
-
chatId = null
|
|
20
|
+
chatId = null,
|
|
21
|
+
column = false
|
|
19
22
|
}: {
|
|
20
23
|
target: TimelineTarget;
|
|
21
24
|
thread: RefObject<HTMLDivElement | null>;
|
|
22
25
|
/* The chat whose own thread this is; a thread in its place (a sub-agent's) has nothing to fork. */
|
|
23
26
|
chatId?: string | null;
|
|
27
|
+
/* The thread is a view of its own (`.chat-column`), the one place its reading width shows. */
|
|
28
|
+
column?: boolean;
|
|
24
29
|
}) {
|
|
25
30
|
const { t } = useTranslation('agent-chat');
|
|
26
31
|
const message = target.row === null ? null : messageTextOf(target.row);
|
|
@@ -31,7 +36,8 @@ export function TimelineMenuPopup({
|
|
|
31
36
|
// A thread outside a chat (a sub-agent's transcript) has no cwd of its own; the app answers for it then.
|
|
32
37
|
const cwd = useFileLinkCwd();
|
|
33
38
|
const scopeId = useFileLinkScopeId();
|
|
34
|
-
const { fork, fileLinks } =
|
|
39
|
+
const { fork, fileLinks } = useChatHost();
|
|
40
|
+
const openFileLink = useOpenFileLink();
|
|
35
41
|
const openInPreview = (): void => {
|
|
36
42
|
if (target.file != null) {
|
|
37
43
|
openFileLink(target.file.cwd, target.file.ref, target.file.scopeId);
|
|
@@ -79,6 +85,12 @@ export function TimelineMenuPopup({
|
|
|
79
85
|
</ContextMenu.Item>
|
|
80
86
|
</>
|
|
81
87
|
)}
|
|
88
|
+
{column && (
|
|
89
|
+
<>
|
|
90
|
+
<ContextMenu.Separator />
|
|
91
|
+
<ReadingWidthSubmenu />
|
|
92
|
+
</>
|
|
93
|
+
)}
|
|
82
94
|
</ContextMenu.Popup>
|
|
83
95
|
);
|
|
84
96
|
}
|
|
@@ -90,12 +102,13 @@ export function TimelineMenuPopup({
|
|
|
90
102
|
export function BookmarkMenuItems({ chatId, itemId, onName }: { chatId: string; itemId: string; onName?: () => void }) {
|
|
91
103
|
const { t } = useTranslation('agent-chat');
|
|
92
104
|
const scope = useChatScope();
|
|
105
|
+
const host = useChatHost();
|
|
93
106
|
const bookmark = useChatRow(chatId, (row) => row?.bookmarks?.find((candidate) => candidate.itemId === itemId) ?? null);
|
|
94
107
|
if (bookmark === null) {
|
|
95
108
|
return (
|
|
96
109
|
<ContextMenu.Item
|
|
97
110
|
onClick={() => {
|
|
98
|
-
void placeBookmark(scope, chatId, itemId);
|
|
111
|
+
void placeBookmark(scope, host, chatId, itemId);
|
|
99
112
|
onName?.();
|
|
100
113
|
}}
|
|
101
114
|
>
|
|
@@ -113,9 +126,31 @@ export function BookmarkMenuItems({ chatId, itemId, onName }: { chatId: string;
|
|
|
113
126
|
>
|
|
114
127
|
<Icon icon={Pencil} size={14} /> {t('bookmarks.rename')}
|
|
115
128
|
</ContextMenu.Item>
|
|
116
|
-
<ContextMenu.Item onClick={() => void removeBookmark(scope, chatId, bookmark)}>
|
|
129
|
+
<ContextMenu.Item onClick={() => void removeBookmark(scope, host, chatId, bookmark)}>
|
|
117
130
|
<Icon icon={BookmarkX} size={14} /> {t('bookmarks.remove')}
|
|
118
131
|
</ContextMenu.Item>
|
|
119
132
|
</>
|
|
120
133
|
);
|
|
121
134
|
}
|
|
135
|
+
|
|
136
|
+
/* The person's reading width for every chat of its own on this client. */
|
|
137
|
+
function ReadingWidthSubmenu() {
|
|
138
|
+
const { t } = useTranslation('agent-chat');
|
|
139
|
+
const readingWidth = useChatPreferences((preferences) => preferences.readingWidth);
|
|
140
|
+
return (
|
|
141
|
+
<ContextMenu.SubmenuRoot>
|
|
142
|
+
<ContextMenu.SubmenuTrigger>
|
|
143
|
+
<Icon icon={MoveHorizontal} size={14} /> {t('readingWidth.label')}
|
|
144
|
+
</ContextMenu.SubmenuTrigger>
|
|
145
|
+
<ContextMenu.Popup className="min-w-40">
|
|
146
|
+
<ContextMenu.RadioGroup value={readingWidth} onValueChange={(width: ChatReadingWidth) => rememberReadingWidth(width)}>
|
|
147
|
+
{CHAT_READING_WIDTHS.map((width) => (
|
|
148
|
+
<ContextMenu.RadioItem key={width} value={width}>
|
|
149
|
+
{readingWidthLabel(width)}
|
|
150
|
+
</ContextMenu.RadioItem>
|
|
151
|
+
))}
|
|
152
|
+
</ContextMenu.RadioGroup>
|
|
153
|
+
</ContextMenu.Popup>
|
|
154
|
+
</ContextMenu.SubmenuRoot>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -6,16 +6,17 @@ import { DiffOmitted } from './diff/DiffOmitted';
|
|
|
6
6
|
import { DiffUnavailable } from './diff/DiffUnavailable';
|
|
7
7
|
import { RawDiff } from './diff/RawDiff';
|
|
8
8
|
import { DiffFallbackBoundary } from './diff/DiffFallbackBoundary';
|
|
9
|
-
import {
|
|
9
|
+
import { useChatHost } from '../../host';
|
|
10
10
|
import type { UnifiedDiffProps } from './diff/diff-props';
|
|
11
11
|
|
|
12
12
|
const LegacyUnifiedDiff = lazyNamed(() => import('./diff/LegacyUnifiedDiff'), 'default', { prefetch: false });
|
|
13
13
|
const EditorPatchDiff = lazyNamed(() => import('./diff/EditorPatchDiff'), 'default');
|
|
14
14
|
|
|
15
15
|
export default function UnifiedDiff(props: UnifiedDiffProps) {
|
|
16
|
+
const host = useChatHost();
|
|
16
17
|
const { t } = useTranslation('agent-chat');
|
|
17
18
|
const { change, contents, allowEditor = true, ignoreWhitespace = false, overflow = 'wrap', diffStyle = 'unified', fill = false, maxHeight } = props;
|
|
18
|
-
const loadEngine =
|
|
19
|
+
const loadEngine = host.code.editor;
|
|
19
20
|
const oldText = contents?.old ?? '';
|
|
20
21
|
const newText = contents?.new ?? '';
|
|
21
22
|
const sourceAllowed = useMemo(() => diffSourceBudget(oldText, newText) && patchSourceBudget(change.diff), [oldText, newText, change.diff]);
|
|
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { visualThemeFragment, type ChatVisual, type VisualAppearance, type VisualTheme } from '@adecore/agent-contracts/visual';
|
|
5
5
|
import { Spinner, useMeasuredWidth } from '@adecore/ui';
|
|
6
|
-
import {
|
|
6
|
+
import { useChatHost } from '../../host';
|
|
7
7
|
import { useChatScope } from '../../scope';
|
|
8
8
|
import { initialVisualContentHeight, rememberVisualHeight } from '../logic/visual-height';
|
|
9
9
|
import { VisualBridge } from '../visual-bridge';
|
|
@@ -33,9 +33,10 @@ function personActsIn(frame: HTMLIFrameElement): boolean {
|
|
|
33
33
|
* when the host page is served on the app's own. Its box keeps one height while it loads or fails.
|
|
34
34
|
*/
|
|
35
35
|
export function VisualFrame({ chatId, visual, fill = false, className }: VisualFrameProps) {
|
|
36
|
+
const { visuals, attachments } = useChatHost();
|
|
36
37
|
const { t } = useTranslation('agent-chat');
|
|
37
38
|
const scope = useChatScope();
|
|
38
|
-
const frameUrl =
|
|
39
|
+
const frameUrl = visuals?.frameUrl ?? null;
|
|
39
40
|
const boxRef = useRef<HTMLDivElement | null>(null);
|
|
40
41
|
const frameRef = useRef<HTMLIFrameElement>(null);
|
|
41
42
|
const bridgeRef = useRef<VisualBridge | null>(null);
|
|
@@ -75,12 +76,10 @@ export function VisualFrame({ chatId, visual, fill = false, className }: VisualF
|
|
|
75
76
|
const bridge = new VisualBridge({
|
|
76
77
|
frame: () => frame.contentWindow,
|
|
77
78
|
visual: { maxHeight: visual.maxHeight },
|
|
78
|
-
page:
|
|
79
|
-
.attachments.read(scope.id, chatId, visual.id)
|
|
80
|
-
.then((blob) => blob.text()),
|
|
79
|
+
page: attachments.read(scope.id, chatId, visual.id).then((blob) => blob.text()),
|
|
81
80
|
theme: opening.theme,
|
|
82
81
|
mayOpenLink: () => personActsIn(frame),
|
|
83
|
-
openLink: (url) =>
|
|
82
|
+
openLink: (url) => visuals?.openLink(url),
|
|
84
83
|
onHeight: (next) => {
|
|
85
84
|
if (fill) {
|
|
86
85
|
return;
|
|
@@ -106,7 +105,7 @@ export function VisualFrame({ chatId, visual, fill = false, className }: VisualF
|
|
|
106
105
|
bridge.dispose();
|
|
107
106
|
bridgeRef.current = null;
|
|
108
107
|
};
|
|
109
|
-
}, [opening, scope.id, chatId, visual.id, visual.maxHeight, fill]);
|
|
108
|
+
}, [opening, scope.id, chatId, visual.id, visual.maxHeight, fill, visuals, attachments]);
|
|
110
109
|
|
|
111
110
|
useLayoutEffect(() => {
|
|
112
111
|
const box = boxRef.current;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { ThemeRegistration } from 'shiki';
|
|
2
|
-
import {
|
|
2
|
+
import { useChatHost, type ChatHost } from '../../host';
|
|
3
3
|
|
|
4
4
|
/* The Shiki theme code is drawn in under the app's current light or dark. */
|
|
5
5
|
export function useCodeTheme(): string {
|
|
6
|
-
const { code } =
|
|
6
|
+
const { code } = useChatHost();
|
|
7
7
|
const mode = code.useMode();
|
|
8
8
|
const themes = code.useThemes();
|
|
9
9
|
return mode === 'dark' ? themes.dark : themes.light;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/* What to hand Shiki for a theme id: a theme the app ships itself, or the id of a bundled one for Shiki to load. */
|
|
13
|
-
export function shikiThemeOf(id: string): string | ThemeRegistration {
|
|
14
|
-
return
|
|
13
|
+
export function shikiThemeOf(id: string, custom: ChatHost['code']['custom']): string | ThemeRegistration {
|
|
14
|
+
return custom.find((theme) => theme.name === id) ?? id;
|
|
15
15
|
}
|