@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
|
@@ -1,40 +1,176 @@
|
|
|
1
1
|
const FENCE = /^([ \t]*)(`{3,}|~{3,})(.*)$/;
|
|
2
|
+
const CLOSING_FENCE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
|
|
2
3
|
// Spaces and tabs only: a line that holds a no-break space is text to markdown, not a blank line.
|
|
3
4
|
const BLANK = /^[ \t]*$/;
|
|
4
5
|
// A line that continues what stood before the blank line: an indented paragraph of a list item, or
|
|
5
6
|
// the next item of a loose list. Splitting there would change what the markdown means.
|
|
6
7
|
const CONTINUES = /^([ \t]|[-*+][ \t]|\d+[.)][ \t])/;
|
|
8
|
+
const LIST_MARKER = /^[ \t]*(?:([-*+])|(\d{1,9})([.)]))(?:([ \t]+)(.*))?$/;
|
|
9
|
+
// A list item with nothing after its marker.
|
|
10
|
+
const EMPTY_ITEM = /^ {0,3}(?:[-*+]|\d{1,9}[.)])[ \t]*$/;
|
|
11
|
+
const THEMATIC_BREAK = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/;
|
|
12
|
+
// A heading or a quote, which ends a list item instead of continuing its paragraph.
|
|
13
|
+
const ENDS_ITEM = /^ {0,3}(?:#{1,6}(?:[ \t]|$)|>)/;
|
|
14
|
+
const HTML_START = /^ {0,3}<[A-Za-z/!?]/;
|
|
15
|
+
// The HTML that runs on until a marker rather than a blank line, with that marker.
|
|
16
|
+
const HTML_UNTIL = [
|
|
17
|
+
[/^ {0,3}<(?:pre|script|style|textarea)(?:[ \t>]|$)/i, /<\/(?:pre|script|style|textarea)>/i],
|
|
18
|
+
[/^ {0,3}<!--/, /-->/],
|
|
19
|
+
[/^ {0,3}<\?/, /\?>/],
|
|
20
|
+
[/^ {0,3}<![A-Za-z]/, />/],
|
|
21
|
+
[/^ {0,3}<!\[CDATA\[/, /\]\]>/]
|
|
22
|
+
];
|
|
7
23
|
// A reference or footnote definition may be used anywhere in the text, so a block cannot stand alone.
|
|
8
24
|
const DEFINITION = /^ {0,3}\[[^\]]+\]:/m;
|
|
25
|
+
// Markdown ends a line at a lone carriage return too, which a split at line feeds would not see.
|
|
26
|
+
const LONE_RETURN = /\r(?!\n|$)/;
|
|
9
27
|
// How much deeper than its opening a closing fence may sit; deeper than that it is a line of code.
|
|
10
28
|
const CLOSING_INDENT = 3;
|
|
29
|
+
// From four columns in, a line is indented code rather than the start of a block.
|
|
30
|
+
const CODE_INDENT = 4;
|
|
31
|
+
const TAB_STOP = 4;
|
|
11
32
|
// A section title: an ATX heading, or a line of only bold text, which models write as a heading too.
|
|
12
33
|
const HEADING = /^ {0,3}#{1,6}(?:[ \t]|$)/;
|
|
13
34
|
const BOLD_LINE = /^ {0,3}\*\*(?:[^*]|\*(?!\*))+\*\*:?[ \t]*$/;
|
|
14
35
|
/*
|
|
15
36
|
* A reply cut into blocks at blank lines outside a code fence, so each block parses on its own and
|
|
16
|
-
* only the one still growing is parsed again.
|
|
37
|
+
* only the one still growing is parsed again. A code fence at the top level is a block of its own,
|
|
38
|
+
* through its closing line and the blank lines under it, so it can be drawn without parsing at all.
|
|
39
|
+
* Joined back together the blocks are the text.
|
|
17
40
|
*/
|
|
18
41
|
export function splitMarkdownBlocks(text) {
|
|
19
|
-
if (DEFINITION.test(text)) {
|
|
42
|
+
if (DEFINITION.test(text) || LONE_RETURN.test(text)) {
|
|
20
43
|
return [{ text, openFence: isFenceOpenAtEnd(text) }];
|
|
21
44
|
}
|
|
22
45
|
const lines = text.split('\n');
|
|
23
46
|
const blocks = [];
|
|
24
47
|
let start = 0;
|
|
25
48
|
let offset = 0;
|
|
26
|
-
|
|
49
|
+
// The fence as a reading line by line sees it, blind to lists and indented code. A blank line is
|
|
50
|
+
// only cut at where this reading agrees, so every such cut is one this split made before it
|
|
51
|
+
// followed fences any further.
|
|
52
|
+
let lineFence = null;
|
|
27
53
|
let afterBlank = false;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
// A fence inside a list item.
|
|
55
|
+
let fence = null;
|
|
56
|
+
let top = null;
|
|
57
|
+
// A top-level fence that has closed; its block takes the blank lines under it.
|
|
58
|
+
let closed = null;
|
|
59
|
+
let item = null;
|
|
60
|
+
// The marker that ends the raw HTML the line stands in.
|
|
61
|
+
let html = null;
|
|
62
|
+
// Whether the top level is still known here. Past HTML, or a line that may or may not end a list
|
|
63
|
+
// item, a fence is left inside the block it stands in, which markdown parses as a whole.
|
|
64
|
+
let known = true;
|
|
65
|
+
// Where the line above starts when it is blank.
|
|
66
|
+
let blankAbove = null;
|
|
67
|
+
const cutAt = (end) => {
|
|
68
|
+
if (end > start) {
|
|
69
|
+
blocks.push({ text: text.slice(start, end), openFence: false });
|
|
32
70
|
}
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
start = end;
|
|
72
|
+
};
|
|
73
|
+
for (const [index, line] of lines.entries()) {
|
|
74
|
+
const at = offset;
|
|
35
75
|
offset += line.length + 1;
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
const content = withoutReturn(line);
|
|
77
|
+
const blank = BLANK.test(line);
|
|
78
|
+
// Blank to markdown, which a line ending in a carriage return is too.
|
|
79
|
+
const empty = BLANK.test(content);
|
|
80
|
+
const blankCut = lineFence === null && afterBlank && !blank && !CONTINUES.test(line);
|
|
81
|
+
lineFence = nextFence(lineFence, line);
|
|
82
|
+
afterBlank = lineFence === null && blank && index > 0;
|
|
83
|
+
if (top !== null) {
|
|
84
|
+
if (closesFence(top.marker, content)) {
|
|
85
|
+
closed = { fence: top, source: text.slice(top.from, at + content.length) };
|
|
86
|
+
top = null;
|
|
87
|
+
}
|
|
88
|
+
else if (index < lines.length - 1 || line !== '') {
|
|
89
|
+
// The empty line after a line ending at the end of the text is no line of the fence.
|
|
90
|
+
top.body += (top.ending ?? '') + outdent(content, top.indent);
|
|
91
|
+
top.ending = line.length > content.length ? '\r\n' : '\n';
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (closed !== null) {
|
|
96
|
+
if (empty) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
blocks.push(fenceBlock(text.slice(start, at), closed.fence, closed.source, true));
|
|
100
|
+
start = at;
|
|
101
|
+
closed = null;
|
|
102
|
+
}
|
|
103
|
+
if (html !== null) {
|
|
104
|
+
// Raw HTML holds no fence and no blank line to cut at until its marker.
|
|
105
|
+
if (html.test(content)) {
|
|
106
|
+
html = null;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (known && fence !== null && item !== null && !empty && columns(content) < item.column) {
|
|
111
|
+
// Code is no paragraph a line may continue lazily, so a line left of the item's content ends
|
|
112
|
+
// the item and its code. A next item of the same list keeps the list going.
|
|
113
|
+
fence = null;
|
|
114
|
+
if (!LIST_MARKER.test(content)) {
|
|
115
|
+
item = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (blankCut && fence === null) {
|
|
119
|
+
cutAt(at);
|
|
120
|
+
}
|
|
121
|
+
const above = index > 0 ? withoutReturn(lines[index - 1]) : '';
|
|
122
|
+
const opening = known && fence === null ? openingFence(content) : null;
|
|
123
|
+
if (opening !== null && EMPTY_ITEM.test(above)) {
|
|
124
|
+
// Markdown reads an empty item right above a fence as a list or as text depending on what
|
|
125
|
+
// stands before it, so the two stay in one block.
|
|
126
|
+
known = false;
|
|
127
|
+
}
|
|
128
|
+
else if (opening !== null && opening.indent < CODE_INDENT && (item === null || opening.indent < item.column)) {
|
|
129
|
+
// The blank line right above goes with the fence. Code in a list item this fence ends takes
|
|
130
|
+
// every blank line under it but the last, and at the end of a block it would take them all.
|
|
131
|
+
cutAt(blankAbove !== null && blankAbove > start ? blankAbove : at);
|
|
132
|
+
// Three columns at most, so the indentation is spaces.
|
|
133
|
+
top = { ...opening, from: at + opening.indent, body: '', ending: null };
|
|
134
|
+
item = null;
|
|
135
|
+
blankAbove = null;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (known && item?.empty === true && empty) {
|
|
139
|
+
// An item with nothing on its first line ends at a blank line right under it.
|
|
140
|
+
item = null;
|
|
141
|
+
}
|
|
142
|
+
if (known && fence === null && !empty) {
|
|
143
|
+
// A fence in an item is followed only while the item is known to be open, and less than four
|
|
144
|
+
// columns deeper than its content: deeper it is code of the item, or of an item inside it.
|
|
145
|
+
if (opening !== null && item !== null && (!item.certain || opening.indent < item.column || opening.indent >= item.column + CODE_INDENT)) {
|
|
146
|
+
known = false;
|
|
147
|
+
}
|
|
148
|
+
else if (opening === null) {
|
|
149
|
+
item = nextItem(item, content, index > 0 && !BLANK.test(above));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (fence === null && HTML_START.test(content)) {
|
|
153
|
+
known = false;
|
|
154
|
+
const until = HTML_UNTIL.find(([begins]) => begins.test(content))?.[1];
|
|
155
|
+
html = until !== undefined && !until.test(content) ? until : null;
|
|
156
|
+
}
|
|
157
|
+
if (known && (fence !== null || item !== null)) {
|
|
158
|
+
fence = nextFence(fence, fence === null ? itemContent(content) : content);
|
|
159
|
+
}
|
|
160
|
+
if (item?.empty === true && !empty && columns(content) >= item.column) {
|
|
161
|
+
item = { ...item, empty: false };
|
|
162
|
+
}
|
|
163
|
+
blankAbove = empty ? at : null;
|
|
164
|
+
}
|
|
165
|
+
if (top !== null) {
|
|
166
|
+
blocks.push(fenceBlock(text.slice(start), top, text.slice(top.from), false));
|
|
167
|
+
}
|
|
168
|
+
else if (closed !== null) {
|
|
169
|
+
blocks.push(fenceBlock(text.slice(start), closed.fence, closed.source, true));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
blocks.push({ text: text.slice(start), openFence: (known ? fence : lineFence) !== null });
|
|
173
|
+
}
|
|
38
174
|
return blocks;
|
|
39
175
|
}
|
|
40
176
|
/*
|
|
@@ -97,7 +233,119 @@ function nextFence(fence, line) {
|
|
|
97
233
|
function isFenceOpenAtEnd(text) {
|
|
98
234
|
let fence = null;
|
|
99
235
|
for (const line of text.split('\n')) {
|
|
100
|
-
fence = nextFence(fence, line);
|
|
236
|
+
fence = nextFence(fence, withoutReturn(line));
|
|
101
237
|
}
|
|
102
238
|
return fence !== null;
|
|
103
239
|
}
|
|
240
|
+
/* The fence a line opens wherever it stands, with its indentation in columns. */
|
|
241
|
+
function openingFence(content) {
|
|
242
|
+
const match = FENCE.exec(content);
|
|
243
|
+
if (!match) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
const marker = match[2];
|
|
247
|
+
const info = match[3].replace(/^[ \t]+|[ \t]+$/g, '');
|
|
248
|
+
// A backtick fence's info string may not hold a backtick, or the line is inline code.
|
|
249
|
+
if (marker[0] === '`' && info.includes('`')) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return { marker, info, indent: columns(content), direct: !/[\\&]/.test(info.split(/[ \t]/, 1)[0]) };
|
|
253
|
+
}
|
|
254
|
+
/* A line without the carriage return of a CRLF line ending. */
|
|
255
|
+
function withoutReturn(line) {
|
|
256
|
+
return line.endsWith('\r') ? line.slice(0, -1) : line;
|
|
257
|
+
}
|
|
258
|
+
function closesFence(marker, content) {
|
|
259
|
+
const run = CLOSING_FENCE.exec(content)?.[1];
|
|
260
|
+
return run !== undefined && run[0] === marker[0] && run.length >= marker.length;
|
|
261
|
+
}
|
|
262
|
+
function fenceBlock(text, fence, source, closed) {
|
|
263
|
+
if (!fence.direct) {
|
|
264
|
+
return { text, openFence: !closed };
|
|
265
|
+
}
|
|
266
|
+
return { text, openFence: !closed, fence: { marker: fence.marker, info: fence.info, body: fence.body, closed, source } };
|
|
267
|
+
}
|
|
268
|
+
/* The list item a line that is neither blank nor a fence leaves open, as far as a fence needs to know. */
|
|
269
|
+
function nextItem(item, content, underLine) {
|
|
270
|
+
const indent = columns(content);
|
|
271
|
+
if (item !== null && indent >= item.column) {
|
|
272
|
+
return item;
|
|
273
|
+
}
|
|
274
|
+
if (THEMATIC_BREAK.test(content) || ENDS_ITEM.test(content)) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const marker = indent < CODE_INDENT ? itemMarker(content) : null;
|
|
278
|
+
if (marker === null) {
|
|
279
|
+
if (item === null || !underLine) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
// A line right under an item may continue its paragraph, or follow an item that ends in something else.
|
|
283
|
+
return { ...item, certain: false };
|
|
284
|
+
}
|
|
285
|
+
// Right under another line, only a bullet or a 1 with text after it surely starts a list; anything
|
|
286
|
+
// else may be text of a paragraph above.
|
|
287
|
+
const certain = !underLine || (item !== null && item.certain && item.kind === marker.kind) || (marker.first && marker.rest !== '');
|
|
288
|
+
return { column: marker.column, kind: marker.kind, certain, empty: marker.rest === '' };
|
|
289
|
+
}
|
|
290
|
+
/* The marker a list item starts with: its kind, whether it may start a list under text, and the column its content starts at. */
|
|
291
|
+
function itemMarker(content) {
|
|
292
|
+
const match = LIST_MARKER.exec(content);
|
|
293
|
+
if (match === null) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
const [, bullet, number, delimiter, spacing = '', rest = ''] = match;
|
|
297
|
+
const indent = columns(content);
|
|
298
|
+
const width = bullet !== undefined ? 1 : number.length + 1;
|
|
299
|
+
// The content starts after the spaces behind the marker, unless there are five or more: then the
|
|
300
|
+
// item starts with indented code one column after the marker.
|
|
301
|
+
const gap = rest === '' ? 1 : columns(spacing, indent + width);
|
|
302
|
+
const column = indent + width + (gap > CODE_INDENT ? 1 : gap);
|
|
303
|
+
return { kind: bullet ?? delimiter, first: bullet !== undefined || Number(number) === 1, column, rest };
|
|
304
|
+
}
|
|
305
|
+
/* A list item's line with its marker as spaces, so a fence on that line is seen where the item's content starts. */
|
|
306
|
+
function itemContent(content) {
|
|
307
|
+
const marker = itemMarker(content);
|
|
308
|
+
return marker === null ? content : ' '.repeat(marker.column) + marker.rest;
|
|
309
|
+
}
|
|
310
|
+
/* The columns of the indentation of a line, which starts at column `from`; a tab reaches to the next stop of four. */
|
|
311
|
+
function columns(line, from = 0) {
|
|
312
|
+
let column = from;
|
|
313
|
+
for (const character of line) {
|
|
314
|
+
if (character === '\t') {
|
|
315
|
+
column += TAB_STOP - (column % TAB_STOP);
|
|
316
|
+
}
|
|
317
|
+
else if (character === ' ') {
|
|
318
|
+
column += 1;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return column - from;
|
|
325
|
+
}
|
|
326
|
+
/*
|
|
327
|
+
* A line of a fence less up to `indent` columns of indentation. A tab reaches to the next stop of
|
|
328
|
+
* four, and what of it lies past `indent` stays as spaces, as markdown reads it.
|
|
329
|
+
*/
|
|
330
|
+
function outdent(line, indent) {
|
|
331
|
+
let column = 0;
|
|
332
|
+
let index = 0;
|
|
333
|
+
while (column < indent && index < line.length) {
|
|
334
|
+
const character = line[index];
|
|
335
|
+
if (character === '\t') {
|
|
336
|
+
const stop = column + TAB_STOP - (column % TAB_STOP);
|
|
337
|
+
if (stop > indent) {
|
|
338
|
+
return ' '.repeat(stop - indent) + line.slice(index + 1);
|
|
339
|
+
}
|
|
340
|
+
column = stop;
|
|
341
|
+
}
|
|
342
|
+
else if (character === ' ') {
|
|
343
|
+
column += 1;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
index += 1;
|
|
349
|
+
}
|
|
350
|
+
return line.slice(index);
|
|
351
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MermaidConfig } from 'mermaid';
|
|
2
|
+
export interface MermaidColors {
|
|
3
|
+
ground: string;
|
|
4
|
+
node: string;
|
|
5
|
+
nodeBorder: string;
|
|
6
|
+
text: string;
|
|
7
|
+
line: string;
|
|
8
|
+
cluster: string;
|
|
9
|
+
clusterBorder: string;
|
|
10
|
+
note: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const MERMAID_TOKENS: Readonly<Record<keyof MermaidColors, string>>;
|
|
13
|
+
export declare function mermaidConfig(colors: MermaidColors, dark: boolean, font: {
|
|
14
|
+
family: string;
|
|
15
|
+
size: string;
|
|
16
|
+
}): MermaidConfig;
|
|
17
|
+
export interface SizedSvg {
|
|
18
|
+
svg: string;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function sizedSvg(svg: string): SizedSvg | null;
|
|
23
|
+
export declare function svgDataUrl(svg: string): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* The token each color reads. Borders carry an alpha, so they are resolved over the ground. */
|
|
2
|
+
export const MERMAID_TOKENS = {
|
|
3
|
+
ground: '--chat-code-bg',
|
|
4
|
+
node: '--surface-raised',
|
|
5
|
+
nodeBorder: '--border-strong',
|
|
6
|
+
text: '--text',
|
|
7
|
+
line: '--text-muted',
|
|
8
|
+
cluster: '--surface',
|
|
9
|
+
clusterBorder: '--border',
|
|
10
|
+
note: '--accent-soft'
|
|
11
|
+
};
|
|
12
|
+
// Mermaid's own list plus what keeps a diagram in the theme and out of HTML: a directive in the text cannot change these.
|
|
13
|
+
const SECURE_KEYS = [
|
|
14
|
+
'secure',
|
|
15
|
+
'securityLevel',
|
|
16
|
+
'startOnLoad',
|
|
17
|
+
'maxTextSize',
|
|
18
|
+
'maxEdges',
|
|
19
|
+
'suppressErrorRendering',
|
|
20
|
+
'htmlLabels',
|
|
21
|
+
'theme',
|
|
22
|
+
'themeVariables',
|
|
23
|
+
'themeCSS',
|
|
24
|
+
'darkMode',
|
|
25
|
+
'fontFamily'
|
|
26
|
+
];
|
|
27
|
+
/*
|
|
28
|
+
* Strict: no HTML in labels, no click handlers, no links that run script. What comes out is drawn as an
|
|
29
|
+
* image as well, which never runs script and loads nothing, whatever the text of the diagram says.
|
|
30
|
+
*/
|
|
31
|
+
export function mermaidConfig(colors, dark, font) {
|
|
32
|
+
return {
|
|
33
|
+
startOnLoad: false,
|
|
34
|
+
securityLevel: 'strict',
|
|
35
|
+
htmlLabels: false,
|
|
36
|
+
suppressErrorRendering: true,
|
|
37
|
+
secure: SECURE_KEYS,
|
|
38
|
+
theme: 'base',
|
|
39
|
+
darkMode: dark,
|
|
40
|
+
fontFamily: font.family,
|
|
41
|
+
themeVariables: {
|
|
42
|
+
darkMode: dark,
|
|
43
|
+
fontFamily: font.family,
|
|
44
|
+
fontSize: font.size,
|
|
45
|
+
background: colors.ground,
|
|
46
|
+
primaryColor: colors.node,
|
|
47
|
+
primaryTextColor: colors.text,
|
|
48
|
+
primaryBorderColor: colors.nodeBorder,
|
|
49
|
+
secondaryColor: colors.note,
|
|
50
|
+
tertiaryColor: colors.cluster,
|
|
51
|
+
mainBkg: colors.node,
|
|
52
|
+
nodeBorder: colors.nodeBorder,
|
|
53
|
+
textColor: colors.text,
|
|
54
|
+
titleColor: colors.text,
|
|
55
|
+
lineColor: colors.line,
|
|
56
|
+
clusterBkg: colors.cluster,
|
|
57
|
+
clusterBorder: colors.clusterBorder,
|
|
58
|
+
edgeLabelBackground: colors.ground,
|
|
59
|
+
noteBkgColor: colors.note,
|
|
60
|
+
noteTextColor: colors.text,
|
|
61
|
+
noteBorderColor: colors.nodeBorder
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const ROOT = /<svg\b[^>]*>/;
|
|
66
|
+
const VIEW_BOX = /\sviewBox="([^"]*)"/;
|
|
67
|
+
const SIZE_ATTRIBUTES = /\s(?:width|height|style)="[^"]*"/g;
|
|
68
|
+
/*
|
|
69
|
+
* The diagram at the size it was laid out at. Mermaid fits it to the width of its container instead,
|
|
70
|
+
* which an image does not have, so the root takes the size its view box names. Null for anything
|
|
71
|
+
* without one.
|
|
72
|
+
*/
|
|
73
|
+
export function sizedSvg(svg) {
|
|
74
|
+
const root = ROOT.exec(svg);
|
|
75
|
+
const viewBox = root === null ? null : VIEW_BOX.exec(root[0]);
|
|
76
|
+
if (root === null || viewBox === null) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const [, , boxWidth, boxHeight] = viewBox[1]
|
|
80
|
+
.trim()
|
|
81
|
+
.split(/[\s,]+/)
|
|
82
|
+
.map(Number);
|
|
83
|
+
if (!Number.isFinite(boxWidth) || !Number.isFinite(boxHeight) || boxWidth <= 0 || boxHeight <= 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const width = Math.ceil(boxWidth);
|
|
87
|
+
const height = Math.ceil(boxHeight);
|
|
88
|
+
const tag = root[0].replace(SIZE_ATTRIBUTES, '').replace(/^<svg\b/, `<svg width="${width}" height="${height}"`);
|
|
89
|
+
return { svg: svg.slice(0, root.index) + tag + svg.slice(root.index + root[0].length), width, height };
|
|
90
|
+
}
|
|
91
|
+
export function svgDataUrl(svg) {
|
|
92
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
93
|
+
}
|
|
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import { ChevronRight, GitFork } from 'lucide-react';
|
|
4
4
|
import { useForkIdsAfter } from '../../forks.js';
|
|
5
5
|
import { ROW_GUTTER } from '../icons.js';
|
|
6
|
-
import {
|
|
6
|
+
import { useChatHost } from '../../../host.js';
|
|
7
7
|
import { Icon, Menu } from '@adecore/ui';
|
|
8
8
|
const LINE = '-mx-1 mb-0.5 flex h-7 items-center gap-2 rounded-md px-1 text-xs text-text-muted';
|
|
9
9
|
const LINK = `${LINE} hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-hover data-[popup-open]:text-text`;
|
|
@@ -28,11 +28,13 @@ function ForksLabel({ label, link = true }) {
|
|
|
28
28
|
}
|
|
29
29
|
// A fork no longer in this project has nowhere to lead, so it only says it is there.
|
|
30
30
|
function OneFork({ forkId, label }) {
|
|
31
|
-
const
|
|
31
|
+
const host = useChatHost();
|
|
32
|
+
const place = host.useChatPlace(forkId);
|
|
32
33
|
return (_jsx("div", { className: "flex", children: place.title === null ? (_jsx("span", { className: LINE, children: _jsx(ForksLabel, { label: label, link: false }) })) : (_jsx("button", { className: LINK, onClick: place.go, children: _jsx(ForksLabel, { label: label }) })) }));
|
|
33
34
|
}
|
|
34
35
|
function ForkItem({ forkId }) {
|
|
36
|
+
const host = useChatHost();
|
|
35
37
|
const { t } = useTranslation('agent-chat');
|
|
36
|
-
const place =
|
|
38
|
+
const place = host.useChatPlace(forkId);
|
|
37
39
|
return (_jsxs(Menu.Item, { disabled: place.title === null, onClick: place.go, children: [_jsx(Icon, { icon: GitFork, size: 14 }), " ", _jsx("span", { className: "truncate", children: place.title ?? t('fork.pill.label') })] }));
|
|
38
40
|
}
|
|
@@ -4,14 +4,14 @@ import clsx from 'clsx';
|
|
|
4
4
|
import { Trans, useTranslation } from 'react-i18next';
|
|
5
5
|
import { Check, Copy, Ellipsis, ExternalLink, FolderDown, ImageIcon, Maximize2 } from 'lucide-react';
|
|
6
6
|
import { Button, ButtonGroup, copyText, ErrorBoundary, FileIcon, Icon, IconButton, Menu, Surface } from '@adecore/ui';
|
|
7
|
-
import {
|
|
7
|
+
import { useChatHost } from '../../../host.js';
|
|
8
8
|
import { useChatScope } from '../../../scope.js';
|
|
9
9
|
import { useCurrentItem } from '../../../state/chats.js';
|
|
10
10
|
import { formatBytes } from '../../attachments.js';
|
|
11
11
|
import { attachmentAspect, generatedImageView } from '../../logic/generated-image.js';
|
|
12
12
|
import { toolStartedAt } from '../../logic/tools.js';
|
|
13
13
|
import { CHIP_IN_MESSAGE, MENTION_TONE } from '../chips.js';
|
|
14
|
-
import {
|
|
14
|
+
import { useFileLinkCwd, useOpenFileLink } from '../file-links.js';
|
|
15
15
|
import { ImageThumb } from '../ImageView.js';
|
|
16
16
|
import { ROW_GUTTER } from '../icons.js';
|
|
17
17
|
import { RunningFor, WorkRow } from './WorkRows.js';
|
|
@@ -50,8 +50,10 @@ function HeadLine({ label, detail, live, startedAt }) {
|
|
|
50
50
|
function SavedFile({ path }) {
|
|
51
51
|
const cwd = useFileLinkCwd();
|
|
52
52
|
const { id } = useChatScope();
|
|
53
|
+
const { fileLinks } = useChatHost();
|
|
54
|
+
const openFileLink = useOpenFileLink();
|
|
53
55
|
const chip = (_jsxs(_Fragment, { children: [_jsx(FileIcon, { path: path, size: 14 }), _jsx("span", { className: "truncate", children: path })] }));
|
|
54
|
-
if (
|
|
56
|
+
if (fileLinks === null) {
|
|
55
57
|
return _jsx("span", { className: clsx(CHIP_IN_MESSAGE, MENTION_TONE), children: chip });
|
|
56
58
|
}
|
|
57
59
|
return (_jsx("button", { type: "button", className: clsx(CHIP_IN_MESSAGE, MENTION_TONE, 'hover:brightness-95'), "data-file-path": path, onClick: () => openFileLink(cwd, { path, directory: false }, id), children: chip }));
|
|
@@ -59,7 +61,7 @@ function SavedFile({ path }) {
|
|
|
59
61
|
function ReadyImage({ chatId, attachment, prompt, transparent }) {
|
|
60
62
|
const { t } = useTranslation('agent-chat');
|
|
61
63
|
const { id: scopeId } = useChatScope();
|
|
62
|
-
const { attachments } =
|
|
64
|
+
const { attachments } = useChatHost();
|
|
63
65
|
const source = attachments.useUrl(scopeId, chatId, attachment.id);
|
|
64
66
|
const savedKey = `${scopeId}\n${chatId}\n${attachment.id}`;
|
|
65
67
|
const [lightbox, setLightbox] = useState(false);
|
|
@@ -19,6 +19,10 @@ export declare function ThinkingRow({ chatId, item: derived }: {
|
|
|
19
19
|
chatId: string;
|
|
20
20
|
item: ChatThinkingItem;
|
|
21
21
|
}): import("react").JSX.Element;
|
|
22
|
+
export declare function LiveThoughtRow({ chatId, item: derived }: {
|
|
23
|
+
chatId: string;
|
|
24
|
+
item: ChatThinkingItem;
|
|
25
|
+
}): import("react").JSX.Element | null;
|
|
22
26
|
export declare function NoteRow({ id, level, text, from }: {
|
|
23
27
|
id: string;
|
|
24
28
|
level: 'info' | 'warning' | 'error';
|
|
@@ -6,21 +6,22 @@ import { Bot, Brain, Check, ChevronDown, CircleAlert, File, Info, MessageCircleQ
|
|
|
6
6
|
import { WAITING_NOTE_PREFIX } from '@adecore/agent-contracts';
|
|
7
7
|
import { fileBadge, formatBytes, isImageAttachment } from '../../attachments.js';
|
|
8
8
|
import { useChatRow, useCurrentItem } from '../../../state/chats.js';
|
|
9
|
-
import {
|
|
9
|
+
import { useChatHost } from '../../../host.js';
|
|
10
10
|
import { useChatScope } from '../../../scope.js';
|
|
11
11
|
import { splitQuote } from '../../quote.js';
|
|
12
12
|
import { jumpToTimelineItem } from '../../timeline-scroll.js';
|
|
13
13
|
import { useProviders } from '../../../state/providers.js';
|
|
14
14
|
import { ChatReferenceChip } from '../ChatReferenceChip.js';
|
|
15
15
|
import { ImageThumb } from '../ImageView.js';
|
|
16
|
-
import { MessageMarkdown, ReplyMarkdown } from '../Markdown.js';
|
|
16
|
+
import { Markdown, MessageMarkdown, ReplyMarkdown } from '../Markdown.js';
|
|
17
17
|
import { FadingWords } from '../FadingWords.js';
|
|
18
18
|
import { settledBlocksText } from '../markdown-blocks.js';
|
|
19
19
|
import { WHOLE_FADE_CLASS } from '../rehype-fade.js';
|
|
20
20
|
import { useRevealedText } from '../reveal.js';
|
|
21
21
|
import { formatAgo, formatElapsedShort, formatMoment, formatTokens } from '@adecore/ui/format';
|
|
22
|
-
import { Icon, Tooltip, useTickingText } from '@adecore/ui';
|
|
23
|
-
import {
|
|
22
|
+
import { Icon, MiddleTruncate, Tooltip, useTickingText } from '@adecore/ui';
|
|
23
|
+
import { thoughtLine, thoughtPreview } from '../../logic/thought.js';
|
|
24
|
+
import { toolSummary, toolSummaryIsPath } from '../../logic/tools.js';
|
|
24
25
|
import { ROW_GUTTER } from '../icons.js';
|
|
25
26
|
import { useOpenForFind } from '../find-reveal.js';
|
|
26
27
|
import { ReplyContext, UiReplyNavigationContext } from '../reply-context.js';
|
|
@@ -31,9 +32,10 @@ const USER_FOLD_CHARS = 600;
|
|
|
31
32
|
/* A folded user prompt fades out at the bottom instead of cutting a line in half. */
|
|
32
33
|
const FOLD = 'max-h-[10em] overflow-hidden [mask-image:linear-gradient(to_bottom,black_70%,transparent)]';
|
|
33
34
|
function useReplyAuthor(chatId) {
|
|
35
|
+
const host = useChatHost();
|
|
34
36
|
const { id } = useChatScope();
|
|
35
37
|
const context = useContext(ReplyContext);
|
|
36
|
-
const author =
|
|
38
|
+
const author = host.useReplyAuthor(id, context?.chatId ?? chatId);
|
|
37
39
|
return context !== null && context.chatId === undefined ? null : author;
|
|
38
40
|
}
|
|
39
41
|
/*
|
|
@@ -41,15 +43,17 @@ function useReplyAuthor(chatId) {
|
|
|
41
43
|
* hands a link that opens a window to the system browser, which cannot reach a blob of this page.
|
|
42
44
|
*/
|
|
43
45
|
function AttachmentLink({ chatId, attachment }) {
|
|
46
|
+
const host = useChatHost();
|
|
44
47
|
const { id } = useChatScope();
|
|
45
|
-
const { url } =
|
|
48
|
+
const { url } = host.attachments.useUrl(id, chatId, attachment.id);
|
|
46
49
|
const badge = fileBadge(attachment.name);
|
|
47
50
|
return (_jsx(Tooltip, { label: attachment.name, children: _jsxs("a", { href: url ?? undefined, download: attachment.name, target: "_blank", rel: "noreferrer", "aria-disabled": url === null, className: "flex h-28 w-36 flex-col rounded-xl border border-border bg-surface-raised bg-clip-padding p-2.5 text-left hover:bg-surface-hover", children: [_jsx("span", { className: "flex h-6 min-w-6 items-center justify-center self-start rounded-md border border-border px-1.5 text-xs font-medium text-text-muted", children: badge ?? _jsx(Icon, { icon: File, size: 12 }) }), _jsx("span", { className: "mt-auto line-clamp-2 text-xs break-all text-text", children: attachment.name }), _jsx("span", { className: "text-xs text-text-faint", children: formatBytes(attachment.size) })] }) }));
|
|
48
51
|
}
|
|
49
52
|
/* A picture attached to a message, which opens large. */
|
|
50
53
|
function AttachmentThumb({ chatId, attachment }) {
|
|
54
|
+
const host = useChatHost();
|
|
51
55
|
const { id } = useChatScope();
|
|
52
|
-
const source =
|
|
56
|
+
const source = host.attachments.useUrl(id, chatId, attachment.id);
|
|
53
57
|
return _jsx(ImageThumb, { source: source, alt: attachment.name, className: "max-h-32 max-w-48 object-cover" });
|
|
54
58
|
}
|
|
55
59
|
export function UserRow({ chatId, item }) {
|
|
@@ -139,11 +143,12 @@ function Writing() {
|
|
|
139
143
|
* written fades anything: an old reply scrolled back into view just stands there.
|
|
140
144
|
*/
|
|
141
145
|
export function AssistantRow({ chatId, item: derived }) {
|
|
146
|
+
const host = useChatHost();
|
|
142
147
|
const item = useCurrentItem(chatId, derived);
|
|
143
148
|
const scope = useChatScope();
|
|
144
149
|
const context = useContext(ReplyContext);
|
|
145
150
|
const reply = useMemo(() => (context === null && !item.parentToolUseId ? { scopeId: scope.id, chatId, itemId: item.id, streaming: item.streaming } : undefined), [context, item.parentToolUseId, scope.id, chatId, item.id, item.streaming]);
|
|
146
|
-
const mode =
|
|
151
|
+
const mode = host.useStreaming();
|
|
147
152
|
const [sawWriting] = useState(item.streaming);
|
|
148
153
|
const live = mode === 'words' && item.streaming;
|
|
149
154
|
const reveal = useRevealedText(item.text, live);
|
|
@@ -161,12 +166,14 @@ export function AssistantRow({ chatId, item: derived }) {
|
|
|
161
166
|
}
|
|
162
167
|
/*
|
|
163
168
|
* What the model thought before it answered. It shimmers while it streams and folds itself away
|
|
164
|
-
* once the answer starts, because the thought is worth a glance and rarely worth reading twice
|
|
169
|
+
* once the answer starts, because the thought is worth a glance and rarely worth reading twice:
|
|
170
|
+
* folded, it shows the heading it opens with or itself on one line, and opened, it reads as Markdown.
|
|
165
171
|
*/
|
|
166
172
|
export function ThinkingRow({ chatId, item: derived }) {
|
|
173
|
+
const host = useChatHost();
|
|
167
174
|
const { t } = useTranslation('agent-chat');
|
|
168
175
|
const item = useCurrentItem(chatId, derived);
|
|
169
|
-
const mode =
|
|
176
|
+
const mode = host.useStreaming();
|
|
170
177
|
const [open, setOpen] = useState(false);
|
|
171
178
|
const reveal = useRevealedText(item.text, mode === 'words' && item.streaming);
|
|
172
179
|
// A block at a time, the paragraphs of the thought that closed are shown as they land.
|
|
@@ -174,8 +181,23 @@ export function ThinkingRow({ chatId, item: derived }) {
|
|
|
174
181
|
// Whole, the thought stays behind "Thinking…" until it is done and then folds like any other.
|
|
175
182
|
const shown = open || reveal.active || (settled !== null && settled !== '');
|
|
176
183
|
const text = settled ?? reveal.text;
|
|
184
|
+
const live = useMemo(() => (item.streaming && mode !== 'whole' ? thoughtLine(text) : ''), [item.streaming, mode, text]);
|
|
185
|
+
const preview = useMemo(() => (item.streaming ? '' : thoughtPreview(item.text)), [item.streaming, item.text]);
|
|
186
|
+
const duration = formatElapsedShort((item.endedAt ?? item.createdAt) - item.createdAt);
|
|
177
187
|
useOpenForFind(item.id, 'text', setOpen);
|
|
178
|
-
return (_jsxs("div", { "data-find-item": item.id, className: "-mx-1 px-1 pb-2", children: [_jsxs("button", { className: "flex items-center gap-2 text-xs text-text-muted hover:text-text", disabled: item.streaming, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: ROW_GUTTER, children: _jsx(Icon, { icon: Brain, size: 12 }) }), item.streaming ? (_jsx("span", { className: "chat-live-text", children: t('rows.thinking.live') })) : (_jsxs(_Fragment, { children: [_jsx("span", { children: t('rows.thinking.thoughtFor', { duration:
|
|
188
|
+
return (_jsxs("div", { "data-find-item": item.id, className: "-mx-1 px-1 pb-2", children: [_jsxs("button", { className: "flex w-full min-w-0 items-center gap-2 text-left text-xs text-text-muted hover:text-text", disabled: item.streaming, "aria-expanded": item.streaming ? undefined : open, onClick: () => setOpen((o) => !o), children: [_jsx("span", { className: ROW_GUTTER, children: _jsx(Icon, { icon: Brain, size: 12 }) }), item.streaming ? (_jsx("span", { className: "chat-live-text min-w-0 truncate", children: live || t('rows.thinking.live') })) : preview === '' ? (_jsxs(_Fragment, { children: [_jsx("span", { children: t('rows.thinking.thoughtFor', { duration }) }), _jsx(Icon, { icon: ChevronDown, size: 12, className: clsx('shrink-0 transition-transform', open && 'rotate-180') })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "min-w-0 truncate", children: open ? t('rows.thinking.thought') : preview }), _jsx("span", { className: "grow" }), _jsx("span", { className: "shrink-0 text-text-faint tabular-nums", children: duration }), _jsx(Icon, { icon: ChevronDown, size: 12, className: clsx('shrink-0 transition-transform', open && 'rotate-180') })] }))] }), shown && text !== '' && (_jsx("div", { "data-find-field": "text", className: "mt-1 border-l border-border pl-2.5 select-text", children: reveal.active ? (_jsx("div", { className: "text-sm whitespace-pre-wrap text-text-faint", children: _jsx(FadingWords, { text: reveal.text }) })) : (_jsx(Markdown, { text: text, muted: true })) }))] }));
|
|
189
|
+
}
|
|
190
|
+
/*
|
|
191
|
+
* The latest thought of a running turn over the call it led to, as its heading or first sentence,
|
|
192
|
+
* so what the model found stays in view while it checks it. Nothing while the thought has no line yet.
|
|
193
|
+
*/
|
|
194
|
+
export function LiveThoughtRow({ chatId, item: derived }) {
|
|
195
|
+
const item = useCurrentItem(chatId, derived);
|
|
196
|
+
const line = useMemo(() => thoughtLine(item.text), [item.text]);
|
|
197
|
+
if (line === '') {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return (_jsxs("div", { className: "-mx-1 mb-0.5 flex min-h-7 items-start gap-2 px-1 py-1 text-xs text-text-muted", children: [_jsx("span", { className: ROW_GUTTER, children: _jsx(Icon, { icon: Brain, size: 12 }) }), _jsx("span", { className: "line-clamp-4 min-w-0 wrap-anywhere select-text", children: line })] }));
|
|
179
201
|
}
|
|
180
202
|
const NOTE_ICON = {
|
|
181
203
|
info: _jsx(Icon, { icon: Info, size: 12 }),
|
|
@@ -197,8 +219,9 @@ export function NoteRow({ id, level, text, from }) {
|
|
|
197
219
|
}
|
|
198
220
|
/* "Open fork" on a note a fork sent, or "Open to answer" on one about a waiting child, while that chat is still in the project. */
|
|
199
221
|
function OpenChatButton({ chatId, label }) {
|
|
222
|
+
const host = useChatHost();
|
|
200
223
|
const { t } = useTranslation('agent-chat');
|
|
201
|
-
const place =
|
|
224
|
+
const place = host.useChatPlace(chatId);
|
|
202
225
|
if (place.title === null) {
|
|
203
226
|
return null;
|
|
204
227
|
}
|
|
@@ -234,7 +257,7 @@ export function ApprovalHistoryRow({ item }) {
|
|
|
234
257
|
if (item.decision === 'pending') {
|
|
235
258
|
return null;
|
|
236
259
|
}
|
|
237
|
-
return (_jsxs("div", { className: "-mx-1 mb-0.5 flex h-7 items-center gap-2 px-1 text-xs text-text-faint", children: [_jsx("span", { className: clsx(ROW_GUTTER, item.decision === 'deny' && 'text-status-error'), children: DECISION_ICON[item.decision] }), _jsx("span", { className: clsx(item.decision === 'deny' && 'text-status-error'), children: t(`rows.approval.${item.decision}`) }), _jsx("span", { className: "text-text-muted", children: item.toolName }), _jsx("span", { className: "min-w-0 truncate font-mono", children: toolSummary(item.toolName, item.input) })] }));
|
|
260
|
+
return (_jsxs("div", { className: "-mx-1 mb-0.5 flex h-7 items-center gap-2 px-1 text-xs text-text-faint", children: [_jsx("span", { className: clsx(ROW_GUTTER, item.decision === 'deny' && 'text-status-error'), children: DECISION_ICON[item.decision] }), _jsx("span", { className: clsx(item.decision === 'deny' && 'text-status-error'), children: t(`rows.approval.${item.decision}`) }), _jsx("span", { className: "text-text-muted", children: item.toolName }), _jsx("span", { className: "min-w-0 truncate font-mono", children: toolSummaryIsPath(item.toolName, item.input) ? (_jsx(MiddleTruncate, { text: toolSummary(item.toolName, item.input) })) : (toolSummary(item.toolName, item.input)) })] }));
|
|
238
261
|
}
|
|
239
262
|
export function QuestionHistoryRow({ item }) {
|
|
240
263
|
const { t } = useTranslation('agent-chat');
|