@adecore/agents-react 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/AgentModelLabel.d.ts +10 -0
- package/dist/agents/AgentModelLabel.js +98 -0
- package/dist/agents/model-name.js +2 -1
- package/dist/agents/provider-paths.d.ts +2 -1
- package/dist/agents/provider-paths.js +7 -3
- package/dist/chat/account-choice.js +57 -28
- package/dist/chat/bookmarks.js +2 -1
- package/dist/chat/drafts.d.ts +0 -1
- package/dist/chat/drafts.js +9 -28
- package/dist/chat/logic/picker-entries.js +2 -8
- package/dist/chat/logic/resume-compaction.js +2 -5
- package/dist/chat/logic/timeline-copy.js +2 -8
- package/dist/chat/logic/timeline.js +9 -10
- package/dist/chat/logic/workflow.js +2 -4
- package/dist/chat/subagent-conversation.js +2 -1
- package/dist/chat/ui/Composer.js +385 -360
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/Pickers.d.ts +2 -1
- package/dist/chat/ui/Pickers.js +126 -123
- package/dist/chat/ui/PromptEditor.d.ts +19 -0
- package/dist/chat/ui/PromptEditor.js +115 -0
- package/dist/chat/ui/Timeline.d.ts +2 -1
- package/dist/chat/ui/Timeline.js +13 -1
- package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
- package/dist/chat/ui/composer/ComposerControls.js +235 -38
- package/dist/chat/ui/composer/key-handler.d.ts +1 -1
- package/dist/chat/ui/composer/key-handler.js +3 -11
- package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
- package/dist/chat/ui/composer/prompt-picks.js +14 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
- package/dist/chat/ui/composer/use-composer-menus.js +65 -519
- package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
- package/dist/chat/ui/composer/use-notice.js +2 -1
- package/dist/chat/ui/composer/use-prompt-history.js +1 -1
- package/dist/chat/ui/composer/use-prompt-menus.d.ts +73 -0
- package/dist/chat/ui/composer/use-prompt-menus.js +499 -0
- package/dist/chat/ui/composer/use-take-back.js +2 -2
- package/dist/chat/ui/diff/ApprovalReview.js +1 -1
- package/dist/chat/ui/highlighter.d.ts +0 -6
- package/dist/chat/ui/highlighter.js +0 -12
- package/dist/chat/ui/icons.d.ts +0 -1
- package/dist/chat/ui/icons.js +0 -3
- package/dist/chat/ui/index.d.ts +3 -0
- package/dist/chat/ui/index.js +2 -0
- package/dist/chat/ui/rows/MessageRows.js +2 -5
- package/dist/chat/ui/rows/row-rhythm.js +2 -5
- package/dist/chat/ui/theme-watch.js +3 -7
- package/dist/host-hooks.d.ts +2 -1
- package/dist/host-hooks.js +6 -0
- package/dist/host.d.ts +17 -1
- package/dist/index.d.ts +1 -1
- package/dist/locales/en/agent-chat.d.ts +1 -0
- package/dist/locales/en/agent-chat.js +1 -0
- package/dist/locales/nl/agent-chat.d.ts +1 -0
- package/dist/locales/nl/agent-chat.js +1 -0
- package/dist/prompts/logic/usePromptSession.js +2 -1
- package/dist/providers/AddAccountForm.js +2 -1
- package/dist/providers/CliDetail.js +104 -130
- package/dist/providers/account-actions.js +6 -9
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +6 -11
- package/dist/state/provider-accounts.d.ts +1 -1
- package/dist/state/provider-accounts.js +2 -8
- package/dist/theme.css +3 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +14 -0
- package/dist/usage/ChartLogo.d.ts +5 -0
- package/dist/usage/ChartLogo.js +48 -0
- package/dist/usage/LimitsList.js +6 -16
- package/dist/usage/UsageBreakdown.js +17 -40
- package/dist/usage/UsageLimits.js +183 -211
- package/dist/usage/UsagePage.js +3 -17
- package/dist/usage/UsageSummary.js +4 -9
- package/dist/usage/limits.js +16 -31
- package/dist/usage/summary.d.ts +1 -1
- package/dist/usage/summary.js +3 -5
- package/package.json +26 -26
- package/src/agents/AccountDot.tsx +1 -0
- package/src/agents/AgentIcon.tsx +2 -0
- package/src/agents/AgentModelLabel.tsx +38 -0
- package/src/agents/ModelOptionControl.tsx +8 -0
- package/src/agents/account-limits.ts +3 -0
- package/src/agents/accounts.ts +12 -0
- package/src/agents/model-name.ts +13 -1
- package/src/agents/model-options.ts +2 -0
- package/src/agents/model-requirement.ts +4 -0
- package/src/agents/option-words.ts +2 -0
- package/src/agents/provider-paths.ts +8 -3
- package/src/agents/status-look.ts +1 -0
- package/src/chat/account-choice.ts +25 -19
- package/src/chat/actions.ts +5 -0
- package/src/chat/attachments.ts +8 -0
- package/src/chat/bookmarks.ts +13 -1
- package/src/chat/chat-client.ts +63 -0
- package/src/chat/chat-references.ts +3 -0
- package/src/chat/composer-effort.ts +5 -0
- package/src/chat/composer-send.ts +4 -0
- package/src/chat/drafts.ts +21 -17
- package/src/chat/forks.ts +4 -0
- package/src/chat/guards.ts +2 -0
- package/src/chat/index.ts +7 -0
- package/src/chat/logic/ansi.ts +47 -0
- package/src/chat/logic/bookmarks.ts +7 -0
- package/src/chat/logic/command-looks.ts +29 -0
- package/src/chat/logic/context-usage.ts +3 -0
- package/src/chat/logic/continue-after-stop.ts +5 -0
- package/src/chat/logic/effort.ts +4 -0
- package/src/chat/logic/embedded-code.ts +63 -0
- package/src/chat/logic/fork.ts +22 -0
- package/src/chat/logic/generated-image.ts +7 -0
- package/src/chat/logic/goal.ts +7 -0
- package/src/chat/logic/handback.ts +2 -0
- package/src/chat/logic/limit.ts +8 -0
- package/src/chat/logic/picker-entries.ts +16 -4
- package/src/chat/logic/resume-compaction.ts +22 -5
- package/src/chat/logic/scrubber.ts +38 -5
- package/src/chat/logic/thought.ts +5 -0
- package/src/chat/logic/thread-cards.ts +6 -0
- package/src/chat/logic/timeline-copy.ts +4 -9
- package/src/chat/logic/timeline-target.ts +15 -0
- package/src/chat/logic/timeline.ts +113 -10
- package/src/chat/logic/tools.ts +30 -0
- package/src/chat/logic/visual-height.ts +5 -0
- package/src/chat/logic/visual-viewport.ts +1 -0
- package/src/chat/logic/welcome.ts +3 -0
- package/src/chat/logic/workflow.ts +26 -7
- package/src/chat/mentions.ts +56 -0
- package/src/chat/pending-prompts.ts +16 -0
- package/src/chat/persisted-json.ts +15 -0
- package/src/chat/preferences.ts +16 -0
- package/src/chat/prompt-hold.ts +2 -0
- package/src/chat/quote.ts +7 -0
- package/src/chat/reading-width.ts +1 -0
- package/src/chat/recent-messages.ts +5 -0
- package/src/chat/resume-compaction-dismissals.ts +1 -0
- package/src/chat/stash.ts +12 -0
- package/src/chat/subagent-conversation.ts +24 -4
- package/src/chat/subagent-list.ts +14 -0
- package/src/chat/subagent-support.ts +1 -0
- package/src/chat/subagent-view.ts +9 -0
- package/src/chat/thumbnails.ts +14 -0
- package/src/chat/timeline-flash.ts +2 -0
- package/src/chat/timeline-scroll.ts +23 -0
- package/src/chat/ui/AccountPill.tsx +4 -0
- package/src/chat/ui/AnsiOutput.tsx +1 -0
- package/src/chat/ui/BookmarkMarker.tsx +5 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
- package/src/chat/ui/ChatActivity.tsx +13 -0
- package/src/chat/ui/CodeBlock.tsx +8 -0
- package/src/chat/ui/Composer.tsx +27 -3
- package/src/chat/ui/ComposerInput.tsx +22 -0
- package/src/chat/ui/DiffPool.tsx +3 -0
- package/src/chat/ui/EditDiff.tsx +5 -0
- package/src/chat/ui/GoalDock.tsx +13 -1
- package/src/chat/ui/ImageView.tsx +11 -0
- package/src/chat/ui/LimitState.tsx +23 -6
- package/src/chat/ui/Markdown.tsx +41 -0
- package/src/chat/ui/MermaidBlock.tsx +16 -0
- package/src/chat/ui/MessageActions.tsx +8 -0
- package/src/chat/ui/ModelMarks.tsx +1 -0
- package/src/chat/ui/Pickers.tsx +30 -1
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +136 -0
- package/src/chat/ui/QuoteButton.tsx +12 -0
- package/src/chat/ui/ReferenceChip.tsx +3 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
- package/src/chat/ui/RowTime.tsx +1 -0
- package/src/chat/ui/RunSettings.tsx +38 -0
- package/src/chat/ui/Scrubber.tsx +14 -0
- package/src/chat/ui/SessionLine.tsx +1 -0
- package/src/chat/ui/ShellCommand.tsx +32 -0
- package/src/chat/ui/SubagentControls.tsx +7 -0
- package/src/chat/ui/SubagentInfo.tsx +1 -0
- package/src/chat/ui/SubagentStopButton.tsx +6 -0
- package/src/chat/ui/SubagentTimeline.tsx +10 -0
- package/src/chat/ui/Timeline.tsx +141 -1
- package/src/chat/ui/TimelineMenu.tsx +5 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -0
- package/src/chat/ui/UploadThumb.tsx +2 -0
- package/src/chat/ui/VisualDialogs.tsx +3 -0
- package/src/chat/ui/VisualFrame.tsx +15 -0
- package/src/chat/ui/Welcome.tsx +1 -0
- package/src/chat/ui/chip-clipboard.ts +20 -0
- package/src/chat/ui/code-lines.ts +14 -0
- package/src/chat/ui/code-theme.ts +1 -0
- package/src/chat/ui/composer/ComposerControls.tsx +70 -15
- package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
- package/src/chat/ui/composer/DraftParts.tsx +2 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
- package/src/chat/ui/composer/chip-paste.ts +16 -0
- package/src/chat/ui/composer/chips.ts +6 -0
- package/src/chat/ui/composer/editor.ts +9 -0
- package/src/chat/ui/composer/insert.ts +2 -0
- package/src/chat/ui/composer/key-handler.ts +42 -9
- package/src/chat/ui/composer/keys.ts +23 -0
- package/src/chat/ui/composer/placeholder.ts +6 -0
- package/src/chat/ui/composer/prompt-picks.ts +18 -0
- package/src/chat/ui/composer/surface.ts +5 -0
- package/src/chat/ui/composer/use-chat-controls.ts +13 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
- package/src/chat/ui/composer/use-composer-menus.ts +20 -275
- package/src/chat/ui/composer/use-composer-submit.ts +27 -0
- package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
- package/src/chat/ui/composer/use-draft-files.ts +24 -0
- package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
- package/src/chat/ui/composer/use-notice.ts +4 -1
- package/src/chat/ui/composer/use-pending-requests.ts +3 -0
- package/src/chat/ui/composer/use-prompt-history.ts +20 -11
- package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
- package/src/chat/ui/composer/use-prompt-menus.ts +361 -0
- package/src/chat/ui/composer/use-take-back.ts +20 -2
- package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
- package/src/chat/ui/diff/DiffStack.tsx +2 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
- package/src/chat/ui/diff/RawDiff.tsx +1 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
- package/src/chat/ui/diff/diff-source-budget.ts +8 -0
- package/src/chat/ui/diff/editor-edit-model.ts +4 -0
- package/src/chat/ui/diff/review-plan.ts +9 -0
- package/src/chat/ui/diff/review-settle.ts +1 -0
- package/src/chat/ui/diff/syntax-runtime.ts +52 -0
- package/src/chat/ui/diff/use-review-stack.ts +9 -0
- package/src/chat/ui/diff-syntax.ts +12 -0
- package/src/chat/ui/diff-theme.ts +2 -0
- package/src/chat/ui/file-links.ts +2 -0
- package/src/chat/ui/find-reveal.ts +1 -0
- package/src/chat/ui/full-diff.ts +1 -0
- package/src/chat/ui/highlighter.ts +29 -15
- package/src/chat/ui/icons.tsx +2 -4
- package/src/chat/ui/index.ts +7 -0
- package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
- package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
- package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
- package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
- package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
- package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
- package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
- package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
- package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
- package/src/chat/ui/markdown-blocks.ts +64 -0
- package/src/chat/ui/mermaid.ts +4 -0
- package/src/chat/ui/quote-selection.ts +10 -0
- package/src/chat/ui/rehype-chips.ts +5 -0
- package/src/chat/ui/rehype-fade.ts +2 -0
- package/src/chat/ui/remark-html-as-text.ts +5 -0
- package/src/chat/ui/reveal.ts +18 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
- package/src/chat/ui/rows/MessageRows.tsx +29 -5
- package/src/chat/ui/rows/RowContainer.tsx +1 -0
- package/src/chat/ui/rows/Rows.tsx +2 -0
- package/src/chat/ui/rows/SubagentRow.tsx +13 -0
- package/src/chat/ui/rows/TaskRow.tsx +7 -0
- package/src/chat/ui/rows/VisualRow.tsx +4 -0
- package/src/chat/ui/rows/WorkRows.tsx +46 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
- package/src/chat/ui/rows/row-rhythm.ts +7 -5
- package/src/chat/ui/theme-watch.ts +8 -7
- package/src/chat/ui/thread-references.ts +7 -0
- package/src/chat/ui/use-diff-source-tokens.ts +8 -0
- package/src/chat/ui/useToggleSet.ts +3 -0
- package/src/chat/ui/visual-theme.ts +30 -0
- package/src/chat/visual-bridge.ts +23 -0
- package/src/chat/visual-viewport.ts +12 -0
- package/src/host-hooks.ts +15 -5
- package/src/host.ts +29 -1
- package/src/index.ts +5 -0
- package/src/lazy.tsx +9 -0
- package/src/locales/en/agent-chat.ts +1 -0
- package/src/locales/nl/agent-chat.ts +1 -0
- package/src/mounted-registry.ts +4 -0
- package/src/port-transport.ts +32 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/logic/focus.ts +5 -0
- package/src/prompts/logic/keys.ts +12 -0
- package/src/prompts/logic/prompts.ts +8 -0
- package/src/prompts/logic/subjects.ts +8 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
- package/src/prompts/logic/usePromptSession.ts +13 -1
- package/src/prompts/ui/ApprovalBody.tsx +2 -0
- package/src/prompts/ui/PromptActions.tsx +2 -0
- package/src/prompts/ui/PromptCard.tsx +6 -0
- package/src/prompts/ui/PromptView.tsx +6 -0
- package/src/prompts/ui/QuestionBody.tsx +11 -0
- package/src/providers/AccountColors.tsx +1 -0
- package/src/providers/AccountDetail.tsx +7 -0
- package/src/providers/AccountVariables.tsx +2 -0
- package/src/providers/AddAccountForm.tsx +5 -1
- package/src/providers/CliDetail.tsx +6 -5
- package/src/providers/InstallPage.tsx +11 -0
- package/src/providers/ProviderList.tsx +3 -0
- package/src/providers/ProvidersPane.tsx +18 -0
- package/src/providers/account-actions.ts +10 -8
- package/src/providers/index.ts +1 -0
- package/src/providers/install-steps.ts +3 -0
- package/src/providers/pane-target.ts +5 -0
- package/src/providers/provider-abilities.ts +1 -0
- package/src/providers/provider-groups.ts +6 -0
- package/src/providers/variables.ts +12 -0
- package/src/scope.ts +2 -0
- package/src/state/chats.ts +61 -14
- package/src/state/index.ts +2 -0
- package/src/state/provider-accounts.ts +7 -9
- package/src/state/providers.ts +1 -0
- package/src/state/usage.ts +6 -0
- package/src/storage.ts +18 -0
- package/src/testing/prompts.ts +5 -0
- package/src/theme.css +3 -0
- package/src/transport.ts +18 -0
- package/src/usage/ChartLogo.tsx +12 -0
- package/src/usage/LimitsList.tsx +7 -8
- package/src/usage/UsageBreakdown.tsx +11 -4
- package/src/usage/UsageChart.tsx +5 -0
- package/src/usage/UsageLimits.tsx +10 -7
- package/src/usage/UsageLimitsCard.tsx +2 -0
- package/src/usage/UsagePage.tsx +16 -19
- package/src/usage/UsageSummary.tsx +4 -5
- package/src/usage/UsageTiles.tsx +2 -0
- package/src/usage/format.ts +7 -0
- package/src/usage/limit-groups.ts +14 -0
- package/src/usage/limits.ts +11 -13
- package/src/usage/money.ts +1 -0
- package/src/usage/summary.ts +15 -7
|
@@ -63,6 +63,7 @@ const THEMATIC_BREAK = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/;
|
|
|
63
63
|
// A heading or a quote, which ends a list item instead of continuing its paragraph.
|
|
64
64
|
const ENDS_ITEM = /^ {0,3}(?:#{1,6}(?:[ \t]|$)|>)/;
|
|
65
65
|
const HTML_START = /^ {0,3}<[A-Za-z/!?]/;
|
|
66
|
+
|
|
66
67
|
// The HTML that runs on until a marker rather than a blank line, with that marker.
|
|
67
68
|
const HTML_UNTIL: [RegExp, RegExp][] = [
|
|
68
69
|
[/^ {0,3}<(?:pre|script|style|textarea)(?:[ \t>]|$)/i, /<\/(?:pre|script|style|textarea)>/i],
|
|
@@ -71,6 +72,7 @@ const HTML_UNTIL: [RegExp, RegExp][] = [
|
|
|
71
72
|
[/^ {0,3}<![A-Za-z]/, />/],
|
|
72
73
|
[/^ {0,3}<!\[CDATA\[/, /\]\]>/]
|
|
73
74
|
];
|
|
75
|
+
|
|
74
76
|
// A reference or footnote definition may be used anywhere in the text, so a block cannot stand alone.
|
|
75
77
|
const DEFINITION = /^ {0,3}\[[^\]]+\]:/m;
|
|
76
78
|
// Markdown ends a line at a lone carriage return too, which a split at line feeds would not see.
|
|
@@ -94,6 +96,7 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
94
96
|
if (DEFINITION.test(text) || LONE_RETURN.test(text)) {
|
|
95
97
|
return [{ text, openFence: isFenceOpenAtEnd(text) }];
|
|
96
98
|
}
|
|
99
|
+
|
|
97
100
|
const lines = text.split('\n');
|
|
98
101
|
const blocks: MarkdownBlock[] = [];
|
|
99
102
|
let start = 0;
|
|
@@ -121,6 +124,7 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
121
124
|
if (end > start) {
|
|
122
125
|
blocks.push({ text: text.slice(start, end), openFence: false });
|
|
123
126
|
}
|
|
127
|
+
|
|
124
128
|
start = end;
|
|
125
129
|
};
|
|
126
130
|
|
|
@@ -134,6 +138,7 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
134
138
|
const blankCut = lineFence === null && afterBlank && !blank && !CONTINUES.test(line);
|
|
135
139
|
lineFence = nextFence(lineFence, line);
|
|
136
140
|
afterBlank = lineFence === null && blank && index > 0;
|
|
141
|
+
|
|
137
142
|
if (top !== null) {
|
|
138
143
|
if (closesFence(top.marker, content)) {
|
|
139
144
|
closed = { fence: top, source: text.slice(top.from, at + content.length) };
|
|
@@ -143,36 +148,46 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
143
148
|
top.body += (top.ending ?? '') + outdent(content, top.indent);
|
|
144
149
|
top.ending = line.length > content.length ? '\r\n' : '\n';
|
|
145
150
|
}
|
|
151
|
+
|
|
146
152
|
continue;
|
|
147
153
|
}
|
|
154
|
+
|
|
148
155
|
if (closed !== null) {
|
|
149
156
|
if (empty) {
|
|
150
157
|
continue;
|
|
151
158
|
}
|
|
159
|
+
|
|
152
160
|
blocks.push(fenceBlock(text.slice(start, at), closed.fence, closed.source, true));
|
|
153
161
|
start = at;
|
|
154
162
|
closed = null;
|
|
155
163
|
}
|
|
164
|
+
|
|
156
165
|
if (html !== null) {
|
|
157
166
|
// Raw HTML holds no fence and no blank line to cut at until its marker.
|
|
158
167
|
if (html.test(content)) {
|
|
159
168
|
html = null;
|
|
160
169
|
}
|
|
170
|
+
|
|
161
171
|
continue;
|
|
162
172
|
}
|
|
173
|
+
|
|
163
174
|
if (known && fence !== null && item !== null && !empty && columns(content) < item.column) {
|
|
164
175
|
// Code is no paragraph a line may continue lazily, so a line left of the item's content ends
|
|
165
176
|
// the item and its code. A next item of the same list keeps the list going.
|
|
166
177
|
fence = null;
|
|
178
|
+
|
|
167
179
|
if (!LIST_MARKER.test(content)) {
|
|
168
180
|
item = null;
|
|
169
181
|
}
|
|
170
182
|
}
|
|
183
|
+
|
|
171
184
|
if (blankCut && fence === null) {
|
|
172
185
|
cutAt(at);
|
|
173
186
|
}
|
|
187
|
+
|
|
174
188
|
const above = index > 0 ? withoutReturn(lines[index - 1]!) : '';
|
|
175
189
|
const opening = known && fence === null ? openingFence(content) : null;
|
|
190
|
+
|
|
176
191
|
if (opening !== null && EMPTY_ITEM.test(above)) {
|
|
177
192
|
// Markdown reads an empty item right above a fence as a list or as text depending on what
|
|
178
193
|
// stands before it, so the two stay in one block.
|
|
@@ -185,12 +200,15 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
185
200
|
top = { ...opening, from: at + opening.indent, body: '', ending: null };
|
|
186
201
|
item = null;
|
|
187
202
|
blankAbove = null;
|
|
203
|
+
|
|
188
204
|
continue;
|
|
189
205
|
}
|
|
206
|
+
|
|
190
207
|
if (known && item?.empty === true && empty) {
|
|
191
208
|
// An item with nothing on its first line ends at a blank line right under it.
|
|
192
209
|
item = null;
|
|
193
210
|
}
|
|
211
|
+
|
|
194
212
|
if (known && fence === null && !empty) {
|
|
195
213
|
// A fence in an item is followed only while the item is known to be open, and less than four
|
|
196
214
|
// columns deeper than its content: deeper it is code of the item, or of an item inside it.
|
|
@@ -200,19 +218,24 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
200
218
|
item = nextItem(item, content, index > 0 && !BLANK.test(above));
|
|
201
219
|
}
|
|
202
220
|
}
|
|
221
|
+
|
|
203
222
|
if (fence === null && HTML_START.test(content)) {
|
|
204
223
|
known = false;
|
|
205
224
|
const until = HTML_UNTIL.find(([begins]) => begins.test(content))?.[1];
|
|
206
225
|
html = until !== undefined && !until.test(content) ? until : null;
|
|
207
226
|
}
|
|
227
|
+
|
|
208
228
|
if (known && (fence !== null || item !== null)) {
|
|
209
229
|
fence = nextFence(fence, fence === null ? itemContent(content) : content);
|
|
210
230
|
}
|
|
231
|
+
|
|
211
232
|
if (item?.empty === true && !empty && columns(content) >= item.column) {
|
|
212
233
|
item = { ...item, empty: false };
|
|
213
234
|
}
|
|
235
|
+
|
|
214
236
|
blankAbove = empty ? at : null;
|
|
215
237
|
}
|
|
238
|
+
|
|
216
239
|
if (top !== null) {
|
|
217
240
|
blocks.push(fenceBlock(text.slice(start), top, text.slice(top.from), false));
|
|
218
241
|
} else if (closed !== null) {
|
|
@@ -220,6 +243,7 @@ export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
|
|
|
220
243
|
} else {
|
|
221
244
|
blocks.push({ text: text.slice(start), openFence: (known ? fence : lineFence) !== null });
|
|
222
245
|
}
|
|
246
|
+
|
|
223
247
|
return blocks;
|
|
224
248
|
}
|
|
225
249
|
|
|
@@ -241,21 +265,28 @@ export function settledBlocksText(text: string): string {
|
|
|
241
265
|
function withoutTrailingTitles(text: string): string {
|
|
242
266
|
const lines = text.split('\n');
|
|
243
267
|
let end = lines.length;
|
|
268
|
+
|
|
244
269
|
for (let index = lines.length - 1; index >= 0; index--) {
|
|
245
270
|
const line = lines[index]!;
|
|
271
|
+
|
|
246
272
|
if (BLANK.test(line)) {
|
|
247
273
|
continue;
|
|
248
274
|
}
|
|
275
|
+
|
|
249
276
|
// A bold line right under a line of text continues that paragraph.
|
|
250
277
|
const title = HEADING.test(line) || (BOLD_LINE.test(line) && (index === 0 || BLANK.test(lines[index - 1]!)));
|
|
278
|
+
|
|
251
279
|
if (!title) {
|
|
252
280
|
break;
|
|
253
281
|
}
|
|
282
|
+
|
|
254
283
|
end = index;
|
|
255
284
|
}
|
|
285
|
+
|
|
256
286
|
if (end === lines.length) {
|
|
257
287
|
return text;
|
|
258
288
|
}
|
|
289
|
+
|
|
259
290
|
return lines
|
|
260
291
|
.slice(0, end)
|
|
261
292
|
.map((line) => `${line}\n`)
|
|
@@ -265,46 +296,58 @@ function withoutTrailingTitles(text: string): string {
|
|
|
265
296
|
/* The fence a line leaves open: it opens one, closes the one that is open, or changes nothing. */
|
|
266
297
|
function nextFence(fence: OpenFence | null, line: string): OpenFence | null {
|
|
267
298
|
const match = FENCE.exec(line);
|
|
299
|
+
|
|
268
300
|
if (!match) {
|
|
269
301
|
return fence;
|
|
270
302
|
}
|
|
303
|
+
|
|
271
304
|
const indent = match[1]!.length;
|
|
272
305
|
const run = match[2]!;
|
|
273
306
|
const rest = match[3]!;
|
|
307
|
+
|
|
274
308
|
if (fence === null) {
|
|
275
309
|
// A backtick fence's info string may not hold a backtick, or the line is inline code.
|
|
276
310
|
if (run[0] === '`' && rest.includes('`')) {
|
|
277
311
|
return null;
|
|
278
312
|
}
|
|
313
|
+
|
|
279
314
|
return { marker: run[0]!, length: run.length, indent };
|
|
280
315
|
}
|
|
316
|
+
|
|
281
317
|
// A closing fence carries no info string, and a line far deeper than the opening is code.
|
|
282
318
|
if (run[0] === fence.marker && run.length >= fence.length && BLANK.test(rest) && indent <= fence.indent + CLOSING_INDENT) {
|
|
283
319
|
return null;
|
|
284
320
|
}
|
|
321
|
+
|
|
285
322
|
return fence;
|
|
286
323
|
}
|
|
287
324
|
|
|
288
325
|
function isFenceOpenAtEnd(text: string): boolean {
|
|
289
326
|
let fence: OpenFence | null = null;
|
|
327
|
+
|
|
290
328
|
for (const line of text.split('\n')) {
|
|
291
329
|
fence = nextFence(fence, withoutReturn(line));
|
|
292
330
|
}
|
|
331
|
+
|
|
293
332
|
return fence !== null;
|
|
294
333
|
}
|
|
295
334
|
|
|
296
335
|
/* The fence a line opens wherever it stands, with its indentation in columns. */
|
|
297
336
|
function openingFence(content: string): Pick<TopFence, 'marker' | 'info' | 'indent' | 'direct'> | null {
|
|
298
337
|
const match = FENCE.exec(content);
|
|
338
|
+
|
|
299
339
|
if (!match) {
|
|
300
340
|
return null;
|
|
301
341
|
}
|
|
342
|
+
|
|
302
343
|
const marker = match[2]!;
|
|
303
344
|
const info = match[3]!.replace(/^[ \t]+|[ \t]+$/g, '');
|
|
345
|
+
|
|
304
346
|
// A backtick fence's info string may not hold a backtick, or the line is inline code.
|
|
305
347
|
if (marker[0] === '`' && info.includes('`')) {
|
|
306
348
|
return null;
|
|
307
349
|
}
|
|
350
|
+
|
|
308
351
|
return { marker, info, indent: columns(content), direct: !/[\\&]/.test(info.split(/[ \t]/, 1)[0]!) };
|
|
309
352
|
}
|
|
310
353
|
|
|
@@ -315,6 +358,7 @@ function withoutReturn(line: string): string {
|
|
|
315
358
|
|
|
316
359
|
function closesFence(marker: string, content: string): boolean {
|
|
317
360
|
const run = CLOSING_FENCE.exec(content)?.[1];
|
|
361
|
+
|
|
318
362
|
return run !== undefined && run[0] === marker[0] && run.length >= marker.length;
|
|
319
363
|
}
|
|
320
364
|
|
|
@@ -322,38 +366,48 @@ function fenceBlock(text: string, fence: TopFence, source: string, closed: boole
|
|
|
322
366
|
if (!fence.direct) {
|
|
323
367
|
return { text, openFence: !closed };
|
|
324
368
|
}
|
|
369
|
+
|
|
325
370
|
return { text, openFence: !closed, fence: { marker: fence.marker, info: fence.info, body: fence.body, closed, source } };
|
|
326
371
|
}
|
|
327
372
|
|
|
328
373
|
/* The list item a line that is neither blank nor a fence leaves open, as far as a fence needs to know. */
|
|
329
374
|
function nextItem(item: ListItem | null, content: string, underLine: boolean): ListItem | null {
|
|
330
375
|
const indent = columns(content);
|
|
376
|
+
|
|
331
377
|
if (item !== null && indent >= item.column) {
|
|
332
378
|
return item;
|
|
333
379
|
}
|
|
380
|
+
|
|
334
381
|
if (THEMATIC_BREAK.test(content) || ENDS_ITEM.test(content)) {
|
|
335
382
|
return null;
|
|
336
383
|
}
|
|
384
|
+
|
|
337
385
|
const marker = indent < CODE_INDENT ? itemMarker(content) : null;
|
|
386
|
+
|
|
338
387
|
if (marker === null) {
|
|
339
388
|
if (item === null || !underLine) {
|
|
340
389
|
return null;
|
|
341
390
|
}
|
|
391
|
+
|
|
342
392
|
// A line right under an item may continue its paragraph, or follow an item that ends in something else.
|
|
343
393
|
return { ...item, certain: false };
|
|
344
394
|
}
|
|
395
|
+
|
|
345
396
|
// Right under another line, only a bullet or a 1 with text after it surely starts a list; anything
|
|
346
397
|
// else may be text of a paragraph above.
|
|
347
398
|
const certain = !underLine || (item !== null && item.certain && item.kind === marker.kind) || (marker.first && marker.rest !== '');
|
|
399
|
+
|
|
348
400
|
return { column: marker.column, kind: marker.kind, certain, empty: marker.rest === '' };
|
|
349
401
|
}
|
|
350
402
|
|
|
351
403
|
/* The marker a list item starts with: its kind, whether it may start a list under text, and the column its content starts at. */
|
|
352
404
|
function itemMarker(content: string): { kind: string; first: boolean; column: number; rest: string } | null {
|
|
353
405
|
const match = LIST_MARKER.exec(content);
|
|
406
|
+
|
|
354
407
|
if (match === null) {
|
|
355
408
|
return null;
|
|
356
409
|
}
|
|
410
|
+
|
|
357
411
|
const [, bullet, number, delimiter, spacing = '', rest = ''] = match;
|
|
358
412
|
const indent = columns(content);
|
|
359
413
|
const width = bullet !== undefined ? 1 : number!.length + 1;
|
|
@@ -361,18 +415,21 @@ function itemMarker(content: string): { kind: string; first: boolean; column: nu
|
|
|
361
415
|
// item starts with indented code one column after the marker.
|
|
362
416
|
const gap = rest === '' ? 1 : columns(spacing, indent + width);
|
|
363
417
|
const column = indent + width + (gap > CODE_INDENT ? 1 : gap);
|
|
418
|
+
|
|
364
419
|
return { kind: bullet ?? delimiter!, first: bullet !== undefined || Number(number) === 1, column, rest };
|
|
365
420
|
}
|
|
366
421
|
|
|
367
422
|
/* A list item's line with its marker as spaces, so a fence on that line is seen where the item's content starts. */
|
|
368
423
|
function itemContent(content: string): string {
|
|
369
424
|
const marker = itemMarker(content);
|
|
425
|
+
|
|
370
426
|
return marker === null ? content : ' '.repeat(marker.column) + marker.rest;
|
|
371
427
|
}
|
|
372
428
|
|
|
373
429
|
/* The columns of the indentation of a line, which starts at column `from`; a tab reaches to the next stop of four. */
|
|
374
430
|
function columns(line: string, from = 0): number {
|
|
375
431
|
let column = from;
|
|
432
|
+
|
|
376
433
|
for (const character of line) {
|
|
377
434
|
if (character === '\t') {
|
|
378
435
|
column += TAB_STOP - (column % TAB_STOP);
|
|
@@ -382,6 +439,7 @@ function columns(line: string, from = 0): number {
|
|
|
382
439
|
break;
|
|
383
440
|
}
|
|
384
441
|
}
|
|
442
|
+
|
|
385
443
|
return column - from;
|
|
386
444
|
}
|
|
387
445
|
|
|
@@ -392,20 +450,26 @@ function columns(line: string, from = 0): number {
|
|
|
392
450
|
function outdent(line: string, indent: number): string {
|
|
393
451
|
let column = 0;
|
|
394
452
|
let index = 0;
|
|
453
|
+
|
|
395
454
|
while (column < indent && index < line.length) {
|
|
396
455
|
const character = line[index];
|
|
456
|
+
|
|
397
457
|
if (character === '\t') {
|
|
398
458
|
const stop = column + TAB_STOP - (column % TAB_STOP);
|
|
459
|
+
|
|
399
460
|
if (stop > indent) {
|
|
400
461
|
return ' '.repeat(stop - indent) + line.slice(index + 1);
|
|
401
462
|
}
|
|
463
|
+
|
|
402
464
|
column = stop;
|
|
403
465
|
} else if (character === ' ') {
|
|
404
466
|
column += 1;
|
|
405
467
|
} else {
|
|
406
468
|
break;
|
|
407
469
|
}
|
|
470
|
+
|
|
408
471
|
index += 1;
|
|
409
472
|
}
|
|
473
|
+
|
|
410
474
|
return line.slice(index);
|
|
411
475
|
}
|
package/src/chat/ui/mermaid.ts
CHANGED
|
@@ -57,15 +57,19 @@ function attributeText(value: string): string {
|
|
|
57
57
|
*/
|
|
58
58
|
export function themedSvg(svg: string, colors: Readonly<Record<string, string>>): string {
|
|
59
59
|
const root = ROOT.exec(svg);
|
|
60
|
+
|
|
60
61
|
if (root === null) {
|
|
61
62
|
return svg;
|
|
62
63
|
}
|
|
64
|
+
|
|
63
65
|
const variables = attributeText(
|
|
64
66
|
Object.entries(colors)
|
|
65
67
|
.map(([name, value]) => `${name}:${value};`)
|
|
66
68
|
.join('')
|
|
67
69
|
);
|
|
70
|
+
|
|
68
71
|
const tag = STYLE.test(root[0]) ? root[0].replace(STYLE, (style) => style + variables) : root[0].replace(/^<svg\b/, `<svg style="${variables}"`);
|
|
72
|
+
|
|
69
73
|
return svg.slice(0, root.index) + tag + svg.slice(root.index + root[0].length);
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -21,18 +21,22 @@ function answerAt(node: Node): Element | null {
|
|
|
21
21
|
*/
|
|
22
22
|
function selectsOutside(range: Range, answer: Element): boolean {
|
|
23
23
|
const walker = document.createTreeWalker(range.commonAncestorContainer, NodeFilter.SHOW_TEXT);
|
|
24
|
+
|
|
24
25
|
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
25
26
|
if (!range.intersectsNode(node) || answer.contains(node)) {
|
|
26
27
|
continue;
|
|
27
28
|
}
|
|
29
|
+
|
|
28
30
|
const text = node.textContent ?? '';
|
|
29
31
|
const start = node === range.startContainer ? range.startOffset : 0;
|
|
30
32
|
const end = node === range.endContainer ? range.endOffset : text.length;
|
|
31
33
|
const parent = node.parentElement;
|
|
34
|
+
|
|
32
35
|
if (text.slice(start, end).trim() !== '' && parent !== null && getComputedStyle(parent).userSelect !== 'none') {
|
|
33
36
|
return true;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
|
|
36
40
|
return false;
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -41,15 +45,21 @@ export function selectedAnswerQuote(selection: Selection | null, thread: HTMLEle
|
|
|
41
45
|
if (thread === null || selection === null || selection.isCollapsed || selection.rangeCount === 0) {
|
|
42
46
|
return null;
|
|
43
47
|
}
|
|
48
|
+
|
|
44
49
|
const range = selection.getRangeAt(0);
|
|
45
50
|
const answer = answerAt(range.startContainer) ?? answerAt(range.endContainer);
|
|
51
|
+
|
|
46
52
|
if (answer === null || !thread.contains(answer)) {
|
|
47
53
|
return null;
|
|
48
54
|
}
|
|
55
|
+
|
|
49
56
|
const inside = answer.contains(range.startContainer) && answer.contains(range.endContainer);
|
|
57
|
+
|
|
50
58
|
if (!inside && selectsOutside(range, answer)) {
|
|
51
59
|
return null;
|
|
52
60
|
}
|
|
61
|
+
|
|
53
62
|
const text = quoteOf(selection.toString());
|
|
63
|
+
|
|
54
64
|
return text === null ? null : { text, itemId: answer.closest('[data-find-item]')?.getAttribute('data-find-item') ?? null };
|
|
55
65
|
}
|
|
@@ -35,6 +35,7 @@ function valueOf(segment: Exclude<ChipSegment, { kind: 'text' }>): string {
|
|
|
35
35
|
if (segment.kind === 'mention') {
|
|
36
36
|
return segment.path;
|
|
37
37
|
}
|
|
38
|
+
|
|
38
39
|
return segment.kind === 'skill' ? segment.name : segment.id;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -59,9 +60,11 @@ function split(parent: HastParent, tokenize: (text: string) => ChipSegment[], sk
|
|
|
59
60
|
if (child.type === 'text') {
|
|
60
61
|
return tokenize(child.value).map((segment) => (segment.kind === 'text' ? { type: 'text', value: segment.text } : chipElement(segment)));
|
|
61
62
|
}
|
|
63
|
+
|
|
62
64
|
if (child.type === 'element' && !skipped.has(child.tagName)) {
|
|
63
65
|
split(child, tokenize, skipped);
|
|
64
66
|
}
|
|
67
|
+
|
|
65
68
|
return [child];
|
|
66
69
|
});
|
|
67
70
|
}
|
|
@@ -72,6 +75,7 @@ export function rehypeChips({ mentions = [], skills = [], references = [] }: Chi
|
|
|
72
75
|
if (mentions.length === 0 && skills.length === 0 && references.length === 0) {
|
|
73
76
|
return;
|
|
74
77
|
}
|
|
78
|
+
|
|
75
79
|
split(tree, (text) => tokenizeChips(text, mentions, skills, references), SKIPPED);
|
|
76
80
|
};
|
|
77
81
|
}
|
|
@@ -82,6 +86,7 @@ export function rehypeNamedReferences({ items, referenced = [] }: NamedReference
|
|
|
82
86
|
if (items.length === 0) {
|
|
83
87
|
return;
|
|
84
88
|
}
|
|
89
|
+
|
|
85
90
|
split(tree, (text) => (text.includes('#') ? tokenizeNamedReferences(text, items, referenced) : [{ kind: 'text', text }]), SKIPPED_IN_REPLIES);
|
|
86
91
|
};
|
|
87
92
|
}
|
|
@@ -45,9 +45,11 @@ function wrap(parent: HastParent): void {
|
|
|
45
45
|
: { type: 'element', tagName: 'span', properties: { className: [FADE_CLASS] }, children: [{ type: 'text', value: segment }] }
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
|
+
|
|
48
49
|
if (child.type === 'element' && !SKIPPED.has(child.tagName)) {
|
|
49
50
|
wrap(child);
|
|
50
51
|
}
|
|
52
|
+
|
|
51
53
|
return [child];
|
|
52
54
|
});
|
|
53
55
|
}
|
|
@@ -13,13 +13,18 @@ function literal(parent: MdastNode): void {
|
|
|
13
13
|
if (parent.children === undefined) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
+
|
|
16
17
|
const flow = FLOW_PARENTS.has(parent.type);
|
|
18
|
+
|
|
17
19
|
parent.children = parent.children.map((child): MdastNode => {
|
|
18
20
|
if (child.type !== 'html') {
|
|
19
21
|
literal(child);
|
|
22
|
+
|
|
20
23
|
return child;
|
|
21
24
|
}
|
|
25
|
+
|
|
22
26
|
const text = { ...child, type: 'text' };
|
|
27
|
+
|
|
23
28
|
return flow ? { type: 'paragraph', position: child.position, children: [text] } : text;
|
|
24
29
|
});
|
|
25
30
|
}
|
package/src/chat/ui/reveal.ts
CHANGED
|
@@ -34,9 +34,11 @@ export function advanceReveal(position: number, target: number, elapsedMs: numbe
|
|
|
34
34
|
if (position >= target) {
|
|
35
35
|
return target;
|
|
36
36
|
}
|
|
37
|
+
|
|
37
38
|
const elapsed = Math.min(Math.max(elapsedMs, 0), REVEAL_MAX_FRAME_MS);
|
|
38
39
|
const eased = (target - position) * (1 - Math.exp(-elapsed / REVEAL_TAU_MS));
|
|
39
40
|
const floor = ((finished ? REVEAL_FINISH_MIN_CPS : REVEAL_MIN_CPS) * elapsed) / 1000;
|
|
41
|
+
|
|
40
42
|
return Math.min(target, position + Math.max(eased, floor));
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -48,18 +50,24 @@ export function advanceReveal(position: number, target: number, elapsedMs: numbe
|
|
|
48
50
|
*/
|
|
49
51
|
export function revealBoundary(text: string, position: number, complete: boolean): number {
|
|
50
52
|
const end = Math.min(text.length, Math.max(0, Math.floor(position)));
|
|
53
|
+
|
|
51
54
|
if (end === text.length && complete) {
|
|
52
55
|
return end;
|
|
53
56
|
}
|
|
57
|
+
|
|
54
58
|
let cut = end;
|
|
59
|
+
|
|
55
60
|
while (cut > 0 && !isSpace(text, cut) && !isSpace(text, cut - 1)) {
|
|
56
61
|
cut -= 1;
|
|
57
62
|
}
|
|
63
|
+
|
|
58
64
|
if (end - cut <= REVEAL_MAX_HELD_WORD) {
|
|
59
65
|
return cut;
|
|
60
66
|
}
|
|
67
|
+
|
|
61
68
|
// Half of a surrogate pair renders as a replacement box.
|
|
62
69
|
const last = text.charCodeAt(end - 1);
|
|
70
|
+
|
|
63
71
|
return last >= 0xd800 && last <= 0xdbff ? end - 1 : end;
|
|
64
72
|
}
|
|
65
73
|
|
|
@@ -91,6 +99,7 @@ export function useRevealedText(text: string, streaming: boolean): RevealedText
|
|
|
91
99
|
if (!streaming || activeRef.current) {
|
|
92
100
|
return;
|
|
93
101
|
}
|
|
102
|
+
|
|
94
103
|
position.current = latest.current.text.length;
|
|
95
104
|
shownRef.current = revealBoundary(latest.current.text, position.current, false);
|
|
96
105
|
activeRef.current = true;
|
|
@@ -102,9 +111,11 @@ export function useRevealedText(text: string, streaming: boolean): RevealedText
|
|
|
102
111
|
if (!active) {
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
114
|
+
|
|
105
115
|
let frame = 0;
|
|
106
116
|
let previous: number | null = null;
|
|
107
117
|
let grewAt = -Infinity;
|
|
118
|
+
|
|
108
119
|
const tick = (now: number) => {
|
|
109
120
|
const elapsed = previous === null ? 0 : now - previous;
|
|
110
121
|
previous = now;
|
|
@@ -112,24 +123,31 @@ export function useRevealedText(text: string, streaming: boolean): RevealedText
|
|
|
112
123
|
const finished = !current.streaming;
|
|
113
124
|
position.current = advanceReveal(position.current, current.text.length, elapsed, finished);
|
|
114
125
|
const length = revealBoundary(current.text, position.current, finished);
|
|
126
|
+
|
|
115
127
|
if (length !== shownRef.current) {
|
|
116
128
|
shownRef.current = length;
|
|
117
129
|
grewAt = now;
|
|
118
130
|
setShown(length);
|
|
119
131
|
}
|
|
132
|
+
|
|
120
133
|
if (finished && length >= current.text.length && now - grewAt >= FADE_SETTLE_MS) {
|
|
121
134
|
activeRef.current = false;
|
|
122
135
|
setActive(false);
|
|
136
|
+
|
|
123
137
|
return;
|
|
124
138
|
}
|
|
139
|
+
|
|
125
140
|
frame = requestAnimationFrame(tick);
|
|
126
141
|
};
|
|
142
|
+
|
|
127
143
|
frame = requestAnimationFrame(tick);
|
|
144
|
+
|
|
128
145
|
return () => cancelAnimationFrame(frame);
|
|
129
146
|
}, [active]);
|
|
130
147
|
|
|
131
148
|
if (!active) {
|
|
132
149
|
return { text, active: false };
|
|
133
150
|
}
|
|
151
|
+
|
|
134
152
|
return { text: text.slice(0, Math.min(shown, text.length)), active: true };
|
|
135
153
|
}
|
|
@@ -16,12 +16,15 @@ export function ForksRow({ chatId, turnId }: { chatId: string; turnId: string })
|
|
|
16
16
|
const { t } = useTranslation('agent-chat');
|
|
17
17
|
const forks = useForkIdsAfter(chatId, turnId);
|
|
18
18
|
const label = t('work.forksAfter', { count: forks.length });
|
|
19
|
+
|
|
19
20
|
if (forks.length === 0) {
|
|
20
21
|
return null;
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
if (forks.length === 1) {
|
|
23
25
|
return <OneFork forkId={forks[0]!} label={label} />;
|
|
24
26
|
}
|
|
27
|
+
|
|
25
28
|
return (
|
|
26
29
|
<div className="flex">
|
|
27
30
|
<Menu.Root>
|
|
@@ -53,6 +56,7 @@ function ForksLabel({ label, link = true }: { label: string; link?: boolean }) {
|
|
|
53
56
|
// A fork no longer in this project has nowhere to lead, so it only says it is there.
|
|
54
57
|
function OneFork({ forkId, label }: { forkId: string; label: string }) {
|
|
55
58
|
const place = useHostChatPlace(forkId);
|
|
59
|
+
|
|
56
60
|
return (
|
|
57
61
|
<div className="flex">
|
|
58
62
|
{place.title === null ? (
|
|
@@ -71,6 +75,7 @@ function OneFork({ forkId, label }: { forkId: string; label: string }) {
|
|
|
71
75
|
function ForkItem({ forkId }: { forkId: string }) {
|
|
72
76
|
const { t } = useTranslation('agent-chat');
|
|
73
77
|
const place = useHostChatPlace(forkId);
|
|
78
|
+
|
|
74
79
|
return (
|
|
75
80
|
<Menu.Item disabled={place.title === null} onClick={place.go}>
|
|
76
81
|
<Icon icon={GitFork} size={14} /> <span className="truncate">{place.title ?? t('fork.pill.label')}</span>
|
|
@@ -27,13 +27,17 @@ const savedPaths = new Map<string, string>();
|
|
|
27
27
|
/* True for `ms` after `start()`, for a check mark that says an action landed. */
|
|
28
28
|
function useFlash(ms: number): [boolean, () => void] {
|
|
29
29
|
const [on, setOn] = useState(false);
|
|
30
|
+
|
|
30
31
|
useEffect(() => {
|
|
31
32
|
if (!on) {
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
35
|
+
|
|
34
36
|
const timer = setTimeout(() => setOn(false), ms);
|
|
37
|
+
|
|
35
38
|
return () => clearTimeout(timer);
|
|
36
39
|
}, [on, ms]);
|
|
40
|
+
|
|
37
41
|
return [on, () => setOn(true)];
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -71,15 +75,18 @@ function SavedFile({ path }: { path: string }) {
|
|
|
71
75
|
const { id } = useChatScope();
|
|
72
76
|
const { fileLinks } = useChatHost();
|
|
73
77
|
const openFileLink = useOpenFileLink();
|
|
78
|
+
|
|
74
79
|
const chip = (
|
|
75
80
|
<>
|
|
76
81
|
<FileIcon path={path} size={14} />
|
|
77
82
|
<span className="truncate">{path}</span>
|
|
78
83
|
</>
|
|
79
84
|
);
|
|
85
|
+
|
|
80
86
|
if (fileLinks === null) {
|
|
81
87
|
return <span className={clsx(CHIP_IN_MESSAGE, MENTION_TONE)}>{chip}</span>;
|
|
82
88
|
}
|
|
89
|
+
|
|
83
90
|
return (
|
|
84
91
|
<button
|
|
85
92
|
type="button"
|
|
@@ -124,8 +131,10 @@ function ReadyImage({ chatId, attachment, prompt, transparent }: { chatId: strin
|
|
|
124
131
|
? undefined
|
|
125
132
|
: async (): Promise<void> => {
|
|
126
133
|
setSaving(true);
|
|
134
|
+
|
|
127
135
|
try {
|
|
128
136
|
const path = await saveToProject(scopeId, chatId, attachment.id, attachment.name);
|
|
137
|
+
|
|
129
138
|
if (path !== null) {
|
|
130
139
|
savedPaths.set(savedKey, path);
|
|
131
140
|
setSavedPath(path);
|
|
@@ -229,6 +238,7 @@ export function GeneratedImageRow({ chatId, tool: derived }: { chatId: string; t
|
|
|
229
238
|
const { t } = useTranslation('agent-chat');
|
|
230
239
|
const tool = useCurrentItem(chatId, derived);
|
|
231
240
|
const view = generatedImageView(tool);
|
|
241
|
+
|
|
232
242
|
switch (view.state) {
|
|
233
243
|
case 'generating':
|
|
234
244
|
return <HeadLine label={t('generatedImage.generating')} live startedAt={toolStartedAt(tool)} />;
|