@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
package/src/chat/ui/full-diff.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function fullFileDiff(patch: string, name: string, contents: DiffContents
|
|
|
18
18
|
newFile: { name, contents: contents.new },
|
|
19
19
|
throwOnError: true
|
|
20
20
|
});
|
|
21
|
+
|
|
21
22
|
return parsed === undefined || parsed.isPartial || parsed.hunks.length === 0 ? null : parsed;
|
|
22
23
|
} catch {
|
|
23
24
|
return null;
|
|
@@ -5,7 +5,9 @@ import { useChatHost, type ChatHost } from '../../host';
|
|
|
5
5
|
import { shikiThemeOf } from './code-theme';
|
|
6
6
|
|
|
7
7
|
export type { Highlighter };
|
|
8
|
+
|
|
8
9
|
export type GrammarState = ReturnType<Highlighter['getLastGrammarState']>;
|
|
10
|
+
|
|
9
11
|
// A plain language needs no grammar; shiki draws it in the theme's own foreground.
|
|
10
12
|
const PLAIN = 'text';
|
|
11
13
|
|
|
@@ -27,8 +29,10 @@ async function grammarOf(loaded: Highlighter, lang: string): Promise<string> {
|
|
|
27
29
|
if (lang === PLAIN || loaded.getLoadedLanguages().includes(lang)) {
|
|
28
30
|
return lang;
|
|
29
31
|
}
|
|
32
|
+
|
|
30
33
|
try {
|
|
31
34
|
await loaded.loadLanguage(lang as BundledLanguage);
|
|
35
|
+
|
|
32
36
|
return lang;
|
|
33
37
|
} catch {
|
|
34
38
|
return PLAIN;
|
|
@@ -48,8 +52,10 @@ function createCodeHighlighter(source: () => Promise<Highlighter>): CodeHighligh
|
|
|
48
52
|
const load = (): Promise<Highlighter> => {
|
|
49
53
|
highlighterLoad ??= source().then((loaded) => {
|
|
50
54
|
highlighter = loaded;
|
|
55
|
+
|
|
51
56
|
return loaded;
|
|
52
57
|
});
|
|
58
|
+
|
|
53
59
|
return highlighterLoad;
|
|
54
60
|
};
|
|
55
61
|
|
|
@@ -57,11 +63,13 @@ function createCodeHighlighter(source: () => Promise<Highlighter>): CodeHighligh
|
|
|
57
63
|
if (highlighter === null || !themes.has(theme)) {
|
|
58
64
|
return null;
|
|
59
65
|
}
|
|
66
|
+
|
|
60
67
|
for (const lang of langs) {
|
|
61
68
|
if (!languages.has(lang)) {
|
|
62
69
|
return null;
|
|
63
70
|
}
|
|
64
71
|
}
|
|
72
|
+
|
|
65
73
|
return highlighter;
|
|
66
74
|
};
|
|
67
75
|
|
|
@@ -70,49 +78,63 @@ function createCodeHighlighter(source: () => Promise<Highlighter>): CodeHighligh
|
|
|
70
78
|
return {
|
|
71
79
|
loadLanguage(lang) {
|
|
72
80
|
let pending = languageLoads.get(lang);
|
|
81
|
+
|
|
73
82
|
if (!pending) {
|
|
74
83
|
pending = load().then(async (loaded) => {
|
|
75
84
|
languages.set(lang, await grammarOf(loaded, lang));
|
|
76
85
|
});
|
|
86
|
+
|
|
77
87
|
languageLoads.set(lang, pending);
|
|
78
88
|
}
|
|
89
|
+
|
|
79
90
|
return pending;
|
|
80
91
|
},
|
|
81
92
|
loadTheme(theme, custom) {
|
|
82
93
|
let pending = themeLoads.get(theme);
|
|
94
|
+
|
|
83
95
|
if (!pending) {
|
|
84
96
|
pending = load().then(async (loaded) => {
|
|
85
97
|
// Loading a theme the highlighter already has replaces it, under whatever else of the host draws with it.
|
|
86
98
|
if (!loaded.getLoadedThemes().includes(theme)) {
|
|
87
99
|
await loaded.loadTheme(shikiThemeOf(theme, custom) as BundledTheme | ThemeRegistration);
|
|
88
100
|
}
|
|
101
|
+
|
|
89
102
|
themes.add(theme);
|
|
90
103
|
});
|
|
104
|
+
|
|
91
105
|
themeLoads.set(theme, pending);
|
|
92
106
|
}
|
|
107
|
+
|
|
93
108
|
return pending;
|
|
94
109
|
},
|
|
95
110
|
ready,
|
|
96
111
|
language,
|
|
97
112
|
tokenizer(lang, theme) {
|
|
98
113
|
const loaded = ready([lang], theme);
|
|
114
|
+
|
|
99
115
|
if (loaded === null) {
|
|
100
116
|
return null;
|
|
101
117
|
}
|
|
118
|
+
|
|
102
119
|
const resolved = language(lang);
|
|
103
120
|
const key = `${resolved}:${theme}`;
|
|
104
121
|
let entry = tokenizers.get(key);
|
|
122
|
+
|
|
105
123
|
if (!entry) {
|
|
106
124
|
const options = { lang: resolved as BundledLanguage, theme };
|
|
125
|
+
|
|
107
126
|
entry = {
|
|
108
127
|
tokenize: tokens.wrap(key, (code, state) => {
|
|
109
128
|
const result = loaded.codeToTokens(code, state ? { ...options, grammarState: state } : options);
|
|
129
|
+
|
|
110
130
|
return { lines: result.tokens, state: result.grammarState };
|
|
111
131
|
}),
|
|
112
132
|
fg: loaded.getTheme(theme).fg
|
|
113
133
|
};
|
|
134
|
+
|
|
114
135
|
tokenizers.set(key, entry);
|
|
115
136
|
}
|
|
137
|
+
|
|
116
138
|
return entry;
|
|
117
139
|
}
|
|
118
140
|
};
|
|
@@ -130,13 +152,17 @@ const borrowed = new WeakMap<() => Promise<Highlighter>, CodeHighlighter>();
|
|
|
130
152
|
export function codeHighlighter(source?: () => Promise<Highlighter>): CodeHighlighter {
|
|
131
153
|
if (source === undefined) {
|
|
132
154
|
bundled ??= createCodeHighlighter(loadBundled);
|
|
155
|
+
|
|
133
156
|
return bundled;
|
|
134
157
|
}
|
|
158
|
+
|
|
135
159
|
let highlighter = borrowed.get(source);
|
|
160
|
+
|
|
136
161
|
if (highlighter === undefined) {
|
|
137
162
|
highlighter = createCodeHighlighter(source);
|
|
138
163
|
borrowed.set(source, highlighter);
|
|
139
164
|
}
|
|
165
|
+
|
|
140
166
|
return highlighter;
|
|
141
167
|
}
|
|
142
168
|
|
|
@@ -155,28 +181,16 @@ export function loadTheme(theme: string, custom: ChatHost['code']['custom']): Pr
|
|
|
155
181
|
return codeHighlighter().loadTheme(theme, custom);
|
|
156
182
|
}
|
|
157
183
|
|
|
158
|
-
/* The web bundle's highlighter once every one of `langs` and `theme` is in, or null while one is still on its way. */
|
|
159
|
-
export function readyHighlighter(langs: Iterable<string>, theme: string): Highlighter | null {
|
|
160
|
-
return codeHighlighter().ready(langs, theme);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/* The language the web bundle draws `lang` in: the bundled grammar, or plain text. Only meaningful once a load settled. */
|
|
164
|
-
export function loadedLanguage(lang: string): string {
|
|
165
|
-
return codeHighlighter().language(lang);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* The web bundle's tokenizer for a language in a theme, or null while shiki, the grammar or the theme is still on its way. */
|
|
169
|
-
export function tokenizerFor(lang: string, theme: string): { tokenize: Tokenize<GrammarState>; fg: string | undefined } | null {
|
|
170
|
-
return codeHighlighter().tokenizer(lang, theme);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
184
|
/* The inline style of one token; undefined when it only has the surrounding color. */
|
|
174
185
|
export function tokenStyle(token: CodeToken): CSSProperties | undefined {
|
|
175
186
|
const style = token.fontStyle ?? 0;
|
|
187
|
+
|
|
176
188
|
if (token.color === undefined && style <= 0) {
|
|
177
189
|
return undefined;
|
|
178
190
|
}
|
|
191
|
+
|
|
179
192
|
const decorations = [style & 4 ? 'underline' : '', style & 8 ? 'line-through' : ''].filter(Boolean).join(' ');
|
|
193
|
+
|
|
180
194
|
return {
|
|
181
195
|
color: token.color,
|
|
182
196
|
...(style & 1 ? { fontStyle: 'italic' } : {}),
|
package/src/chat/ui/icons.tsx
CHANGED
|
@@ -5,10 +5,6 @@ import { Icon } from '@adecore/ui';
|
|
|
5
5
|
|
|
6
6
|
const SIZE = 12;
|
|
7
7
|
|
|
8
|
-
/* A pending dock draws its mark a step up from the lines of a transcript, at the size a sidebar
|
|
9
|
-
row uses, because it is a thing to answer rather than a thing to read past. */
|
|
10
|
-
export const DOCK_ICON_SIZE = 14;
|
|
11
|
-
|
|
12
8
|
/* The gutter every row in the thread opens with: 16 wide, on the line box of its first row of text,
|
|
13
9
|
so the mark of a tool call, a note and a fold all sit in one column and their labels start at the
|
|
14
10
|
same x. The same box the menu rows and the toasts use. */
|
|
@@ -17,8 +13,10 @@ export const ROW_GUTTER = 'grid h-5 w-4 shrink-0 place-items-center';
|
|
|
17
13
|
/* The icon that says what a tool call is about; unknown tools get the wrench. */
|
|
18
14
|
export function toolIcon(name: string, size: number = SIZE): ReactNode {
|
|
19
15
|
const known = toolEntry(name);
|
|
16
|
+
|
|
20
17
|
if (known !== undefined) {
|
|
21
18
|
return <Icon icon={known.icon} size={size} />;
|
|
22
19
|
}
|
|
20
|
+
|
|
23
21
|
return name.startsWith('mcp__') ? <Icon icon={Hammer} size={size} /> : <Icon icon={Wrench} size={size} />;
|
|
24
22
|
}
|
package/src/chat/ui/index.ts
CHANGED
|
@@ -14,6 +14,9 @@ export { ImageThumb, type ImageThumbProps } from './ImageView';
|
|
|
14
14
|
export { LimitDock, LimitPill } from './LimitState';
|
|
15
15
|
export { ReplyMarkdown, MessageMarkdown, Markdown } from './Markdown';
|
|
16
16
|
export { ModelPicker } from './Pickers';
|
|
17
|
+
export { PromptEditor } from './PromptEditor';
|
|
18
|
+
export { promptPicks } from './composer/prompt-picks';
|
|
19
|
+
export type { PromptDraft } from './composer/use-prompt-menus';
|
|
17
20
|
export { ResumeCompactionDock } from './ResumeCompactionDock';
|
|
18
21
|
export { SubagentTitleCrumb, SubagentBreadcrumb } from './SubagentControls';
|
|
19
22
|
export { SubagentInfo } from './SubagentInfo';
|
|
@@ -25,6 +28,7 @@ export { UploadThumb } from './UploadThumb';
|
|
|
25
28
|
export { VisualPageFrame, type VisualPageFrameProps } from './VisualFrame';
|
|
26
29
|
export { WelcomeGreeting } from './Welcome';
|
|
27
30
|
export { externalChange, composerEditorExtensions } from './composer/editor';
|
|
31
|
+
|
|
28
32
|
export {
|
|
29
33
|
createDiffSyntaxRuntime,
|
|
30
34
|
diffSyntaxRuntime,
|
|
@@ -35,6 +39,7 @@ export {
|
|
|
35
39
|
type DiffSourceTokenSession,
|
|
36
40
|
diffSourceTokens
|
|
37
41
|
} from './diff-syntax';
|
|
42
|
+
|
|
38
43
|
export { useDiffTheme } from './diff-theme';
|
|
39
44
|
export type { UnifiedDiffProps } from './diff/diff-props';
|
|
40
45
|
export { FileLinkContext, useFileLinkCwd } from './file-links';
|
|
@@ -45,6 +50,7 @@ export { UiReply } from './intelligent-ui/UiReply';
|
|
|
45
50
|
export { uiChartData, type UiChartSeries, type UiChartValues, type UiChartData } from './intelligent-ui/chart-data';
|
|
46
51
|
export { uiNodeLabel } from './intelligent-ui/node-text';
|
|
47
52
|
export { UI_RENDERERS } from './intelligent-ui/registry';
|
|
53
|
+
|
|
48
54
|
export type {
|
|
49
55
|
UiRenderContext,
|
|
50
56
|
UiRendererProps,
|
|
@@ -57,6 +63,7 @@ export type {
|
|
|
57
63
|
UiRenderer,
|
|
58
64
|
UiRenderers
|
|
59
65
|
} from './intelligent-ui/render-context';
|
|
66
|
+
|
|
60
67
|
export { uiTableColumns, type UiColumnKind, type UiTableColumn } from './intelligent-ui/table-data';
|
|
61
68
|
export type { QuoteTaker } from './quote-selection';
|
|
62
69
|
export { ReplyContext } from './reply-context';
|
|
@@ -34,14 +34,19 @@ function LivePart({ live }: { live: UiLiveStatus }) {
|
|
|
34
34
|
const slow = useLater(live.state === 'reading', READING_SPINNER_MS);
|
|
35
35
|
const { readAt } = live;
|
|
36
36
|
const fresh = live.state === 'fresh' || live.state === 'reading';
|
|
37
|
+
|
|
37
38
|
const ago = useTickingText(() => {
|
|
38
39
|
if (readAt === null) {
|
|
39
40
|
return '';
|
|
40
41
|
}
|
|
42
|
+
|
|
41
43
|
const time = formatAgo(Date.now() - readAt);
|
|
44
|
+
|
|
42
45
|
return fresh ? t('blocks.live.read', { time }) : time;
|
|
43
46
|
});
|
|
47
|
+
|
|
44
48
|
const source = live.source ?? live.sources[0] ?? '';
|
|
49
|
+
|
|
45
50
|
const icon = slow ? (
|
|
46
51
|
<Spinner size={12} />
|
|
47
52
|
) : live.state === 'failed' ? (
|
|
@@ -51,14 +56,17 @@ function LivePart({ live }: { live: UiLiveStatus }) {
|
|
|
51
56
|
) : (
|
|
52
57
|
<Icon icon={Radio} size={12} className="text-positive" />
|
|
53
58
|
);
|
|
59
|
+
|
|
54
60
|
const words =
|
|
55
61
|
live.state === 'failed'
|
|
56
62
|
? t('blocks.live.failed', { source })
|
|
57
63
|
: live.state === 'refused'
|
|
58
64
|
? t('blocks.live.refused', { source })
|
|
59
65
|
: t('blocks.live.label');
|
|
66
|
+
|
|
60
67
|
const reason = live.state === 'failed' || live.state === 'refused' ? uiReasonText(t, live.code, live.reason) : undefined;
|
|
61
68
|
const hint = reason !== undefined ? reason : live.sources.length > 0 ? t('blocks.live.sources', { sources: live.sources.join(', ') }) : null;
|
|
69
|
+
|
|
62
70
|
const body = (
|
|
63
71
|
<span tabIndex={hint === null ? undefined : 0} className="flex items-center gap-1.5">
|
|
64
72
|
{icon}
|
|
@@ -79,6 +87,7 @@ function LivePart({ live }: { live: UiLiveStatus }) {
|
|
|
79
87
|
)}
|
|
80
88
|
</span>
|
|
81
89
|
);
|
|
90
|
+
|
|
82
91
|
return hint === null ? body : <Tooltip label={hint}>{body}</Tooltip>;
|
|
83
92
|
}
|
|
84
93
|
|
|
@@ -87,9 +96,11 @@ function UiBlockFooter({ live = null, answered = null, fixes = NO_FIXES, shownAs
|
|
|
87
96
|
const { t } = useTranslation('agent-chat');
|
|
88
97
|
const [fixesOpen, setFixesOpen] = useState(false);
|
|
89
98
|
const listId = useId();
|
|
99
|
+
|
|
90
100
|
if (live === null && answered === null && fixes.length === 0 && shownAsText === null) {
|
|
91
101
|
return null;
|
|
92
102
|
}
|
|
103
|
+
|
|
93
104
|
return (
|
|
94
105
|
<div className="flex flex-col gap-1.5 text-2xs text-text-faint">
|
|
95
106
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-1">
|
|
@@ -155,34 +166,44 @@ function useRisingFloor(active: boolean): [RefObject<HTMLDivElement | null>, num
|
|
|
155
166
|
const ref = useRef<HTMLDivElement>(null);
|
|
156
167
|
const held = useRef(0);
|
|
157
168
|
const [floor, setFloor] = useState(0);
|
|
169
|
+
|
|
158
170
|
useLayoutEffect(() => {
|
|
159
171
|
const element = ref.current;
|
|
172
|
+
|
|
160
173
|
if (element === null) {
|
|
161
174
|
return;
|
|
162
175
|
}
|
|
176
|
+
|
|
163
177
|
if (active) {
|
|
164
178
|
const observer = new ResizeObserver(() => {
|
|
165
179
|
const height = Math.ceil(element.getBoundingClientRect().height);
|
|
166
180
|
held.current = Math.max(held.current, height);
|
|
167
181
|
setFloor(held.current);
|
|
168
182
|
});
|
|
183
|
+
|
|
169
184
|
observer.observe(element);
|
|
185
|
+
|
|
170
186
|
return () => {
|
|
171
187
|
observer.disconnect();
|
|
172
188
|
setFloor(0);
|
|
173
189
|
};
|
|
174
190
|
}
|
|
191
|
+
|
|
175
192
|
// The final render already let go of the floor, so the card measures its own height here.
|
|
176
193
|
const from = held.current;
|
|
177
194
|
const to = element.getBoundingClientRect().height;
|
|
178
195
|
held.current = 0;
|
|
179
196
|
const reduced = element.ownerDocument.defaultView?.matchMedia('(prefers-reduced-motion: reduce)').matches === true;
|
|
197
|
+
|
|
180
198
|
if (from - to < 1 || reduced) {
|
|
181
199
|
return;
|
|
182
200
|
}
|
|
201
|
+
|
|
183
202
|
const release = element.animate([{ minHeight: `${from}px` }, { minHeight: `${to}px` }], { duration: RELEASE_MS, easing: 'ease-out' });
|
|
203
|
+
|
|
184
204
|
return () => release.cancel();
|
|
185
205
|
}, [active]);
|
|
206
|
+
|
|
186
207
|
return [ref, floor];
|
|
187
208
|
}
|
|
188
209
|
|
|
@@ -198,17 +219,21 @@ export function UiBlockFrame({ nodes, phase, children, className, ...footer }: U
|
|
|
198
219
|
const streaming = phase === 'streaming';
|
|
199
220
|
const head = uiBlockHead(nodes);
|
|
200
221
|
const [ref, floor] = useRisingFloor(streaming);
|
|
222
|
+
|
|
201
223
|
// Only while the focus is inside the block, so the composer keeps the same keys for sending.
|
|
202
224
|
const pickPrimary = (event: KeyboardEvent<HTMLDivElement>): void => {
|
|
203
225
|
if (!matchesShortcut(KEY_SHORTCUTS.modEnter, event, host.isApplePlatform())) {
|
|
204
226
|
return;
|
|
205
227
|
}
|
|
228
|
+
|
|
206
229
|
const primary = event.currentTarget.querySelector<HTMLButtonElement>('[data-ui-primary-choice]');
|
|
230
|
+
|
|
207
231
|
if (primary !== null) {
|
|
208
232
|
event.preventDefault();
|
|
209
233
|
primary.click();
|
|
210
234
|
}
|
|
211
235
|
};
|
|
236
|
+
|
|
212
237
|
return (
|
|
213
238
|
<UiHeadContext value={head?.id ?? null}>
|
|
214
239
|
<div
|
|
@@ -12,6 +12,7 @@ export type UiFallbackProblem = { kind: 'unknown'; component: string } | { kind:
|
|
|
12
12
|
*/
|
|
13
13
|
export function UiFallbackPart({ fallback, problem }: { fallback: string; problem: UiFallbackProblem }) {
|
|
14
14
|
const { t } = useTranslation('agent-chat');
|
|
15
|
+
|
|
15
16
|
return (
|
|
16
17
|
<div className="chat-ui-flat flex flex-col gap-1.5 rounded-xl bg-ui-tile p-1.5">
|
|
17
18
|
<span className="chat-ui-flat-inner flex items-center gap-1.5 px-1.5 pt-0.5 text-2xs text-text-muted">
|
|
@@ -45,15 +45,19 @@ const localStates = new UiPageMemory<UiState>(64);
|
|
|
45
45
|
|
|
46
46
|
function localState(key: string, block: UiBlock, answered?: ChatUiAnswer): UiState {
|
|
47
47
|
let state = localStates.get(key);
|
|
48
|
+
|
|
48
49
|
if (!state) {
|
|
49
50
|
state = new UiState(block);
|
|
51
|
+
|
|
50
52
|
if (answered && answered.revision === block.revision) {
|
|
51
53
|
for (const [name, value] of Object.entries(answered.values ?? {})) {
|
|
52
54
|
state.set(name, value);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
57
|
+
|
|
55
58
|
localStates.set(key, state);
|
|
56
59
|
}
|
|
60
|
+
|
|
57
61
|
return state;
|
|
58
62
|
}
|
|
59
63
|
|
|
@@ -102,6 +106,7 @@ function remarkInlineProse(this: { data(): object }) {
|
|
|
102
106
|
// The field remark-parse reads its syntax extensions from, as remark-gfm adds its own.
|
|
103
107
|
const data = this.data() as { micromarkExtensions?: unknown[] };
|
|
104
108
|
data.micromarkExtensions = [...(data.micromarkExtensions ?? []), { disable: { null: BLOCK_CONSTRUCTS } }];
|
|
109
|
+
|
|
105
110
|
return (tree: ProseTree) => {
|
|
106
111
|
tree.children = [
|
|
107
112
|
{
|
|
@@ -118,26 +123,35 @@ function remarkCitations() {
|
|
|
118
123
|
if (['link', 'inlineCode', 'code'].includes(parent.type)) {
|
|
119
124
|
return;
|
|
120
125
|
}
|
|
126
|
+
|
|
121
127
|
parent.children = parent.children?.flatMap((child) => {
|
|
122
128
|
if (child.type !== 'text' || !child.value) {
|
|
123
129
|
visit(child);
|
|
130
|
+
|
|
124
131
|
return [child];
|
|
125
132
|
}
|
|
133
|
+
|
|
126
134
|
const parts: ProseTree[] = [];
|
|
127
135
|
let start = 0;
|
|
136
|
+
|
|
128
137
|
for (const match of child.value.matchAll(/\[(\d{1,3})\]/g)) {
|
|
129
138
|
parts.push({ type: 'text', value: child.value.slice(start, match.index) });
|
|
139
|
+
|
|
130
140
|
parts.push({
|
|
131
141
|
type: 'citation',
|
|
132
142
|
data: { hName: 'span', hProperties: { 'data-ui-citation': match[1] } },
|
|
133
143
|
children: [{ type: 'text', value: match[0] }]
|
|
134
144
|
});
|
|
145
|
+
|
|
135
146
|
start = match.index + match[0].length;
|
|
136
147
|
}
|
|
148
|
+
|
|
137
149
|
parts.push({ type: 'text', value: child.value.slice(start) });
|
|
150
|
+
|
|
138
151
|
return parts;
|
|
139
152
|
});
|
|
140
153
|
};
|
|
154
|
+
|
|
141
155
|
visit(tree);
|
|
142
156
|
};
|
|
143
157
|
}
|
|
@@ -148,28 +162,35 @@ interface CitationTarget {
|
|
|
148
162
|
url: string;
|
|
149
163
|
ancestors: string[];
|
|
150
164
|
}
|
|
165
|
+
|
|
151
166
|
const CitationContext = createContext<{ sources: ReadonlyMap<number, CitationTarget>; reveal?: (source: CitationTarget) => void }>({ sources: new Map() });
|
|
152
167
|
|
|
153
168
|
function ProseCitation({ number, children }: { number: number; children?: ReactNode }) {
|
|
154
169
|
const { sources, reveal } = useContext(CitationContext);
|
|
155
170
|
const source = sources.get(number);
|
|
171
|
+
|
|
156
172
|
return source ? <UiSourceCitation number={number} title={source.title} url={source.url} onReveal={reveal ? () => reveal(source) : undefined} /> : children;
|
|
157
173
|
}
|
|
158
174
|
|
|
159
175
|
function sourceTargets(nodes: readonly UiViewNode[]): Map<number, CitationTarget> {
|
|
160
176
|
const lists: CitationTarget[][] = [];
|
|
177
|
+
|
|
161
178
|
const visit = (nodes: readonly UiViewNode[], ancestors: string[] = []) => {
|
|
162
179
|
for (const node of nodes) {
|
|
163
180
|
const path = ['Tab', 'Section'].includes(node.type) ? [...ancestors, node.id] : ancestors;
|
|
181
|
+
|
|
164
182
|
if (node.type === 'Sources') {
|
|
165
183
|
lists.push(
|
|
166
184
|
uiChildrenOf(node, 'Source').map((source) => ({ id: source.id, title: source.props.title, url: source.props.url, ancestors: path }))
|
|
167
185
|
);
|
|
168
186
|
}
|
|
187
|
+
|
|
169
188
|
visit(node.children, path);
|
|
170
189
|
}
|
|
171
190
|
};
|
|
191
|
+
|
|
172
192
|
visit(nodes);
|
|
193
|
+
|
|
173
194
|
// Numbers restart in each Sources list, so multiple lists have no unambiguous prose target.
|
|
174
195
|
return new Map(lists.length === 1 ? lists[0].map((source, index) => [index + 1, source]) : []);
|
|
175
196
|
}
|
|
@@ -184,9 +205,11 @@ const ProseUrlContext = createContext<((url: string) => void) | undefined>(undef
|
|
|
184
205
|
/* Through the host only, like a Source: the address is the model's, so nothing here navigates by itself. */
|
|
185
206
|
function ProseLink({ href, children }: { href?: string; children?: ReactNode }) {
|
|
186
207
|
const openUrl = useContext(ProseUrlContext);
|
|
208
|
+
|
|
187
209
|
if (openUrl === undefined || href === undefined || !/^https?:\/\//i.test(href)) {
|
|
188
210
|
return children;
|
|
189
211
|
}
|
|
212
|
+
|
|
190
213
|
return (
|
|
191
214
|
<Tooltip label={uiUrlDomain(href)}>
|
|
192
215
|
<button type="button" className="cursor-pointer text-accent underline underline-offset-2 select-text" onClick={() => openUrl(href)}>
|
|
@@ -215,9 +238,11 @@ const PROSE_COMPONENTS: Components = {
|
|
|
215
238
|
*/
|
|
216
239
|
const UiProse = memo(function UiProse({ text, fade, openUrl }: { text: string; fade: boolean; openUrl?: (url: string) => void }) {
|
|
217
240
|
const [, lead = '', body = '', trail = ''] = /^(\s*)([\s\S]*?)(\s*)$/.exec(text) ?? [];
|
|
241
|
+
|
|
218
242
|
if (body === '') {
|
|
219
243
|
return text;
|
|
220
244
|
}
|
|
245
|
+
|
|
221
246
|
return (
|
|
222
247
|
<ProseUrlContext value={openUrl}>
|
|
223
248
|
{lead}
|
|
@@ -241,8 +266,10 @@ function writtenTexts(nodes: readonly UiNode[], into = new Map<string, boolean>(
|
|
|
241
266
|
if (node.type === '$text') {
|
|
242
267
|
into.set(node.id, node.expressions.text === undefined);
|
|
243
268
|
}
|
|
269
|
+
|
|
244
270
|
writtenTexts(node.children, into);
|
|
245
271
|
}
|
|
272
|
+
|
|
246
273
|
return into;
|
|
247
274
|
}
|
|
248
275
|
|
|
@@ -250,6 +277,7 @@ function isInline(node: UiViewNode): boolean {
|
|
|
250
277
|
if (node.type === '$text') {
|
|
251
278
|
return true;
|
|
252
279
|
}
|
|
280
|
+
|
|
253
281
|
return node.error === undefined && (node.type === 'Tag' || (LINKS.has(node.type) && node.children.length === 0));
|
|
254
282
|
}
|
|
255
283
|
|
|
@@ -271,16 +299,21 @@ function UiNodes({ nodes, parent, texts, context }: UiNodesProps): ReactNode {
|
|
|
271
299
|
<UiNodeBody node={node} parent={parent} texts={texts} context={context} />
|
|
272
300
|
</UiNodeBoundary>
|
|
273
301
|
);
|
|
302
|
+
|
|
274
303
|
const paragraph = PARAGRAPHS[parent];
|
|
304
|
+
|
|
275
305
|
if (parent === 'Choices') {
|
|
276
306
|
// A primary choice stands first in the order a keyboard and a screen reader meet it too, not only on screen.
|
|
277
307
|
return [...nodes].sort((a, b) => Number(b.props.primary === true) - Number(a.props.primary === true)).map(drawn);
|
|
278
308
|
}
|
|
309
|
+
|
|
279
310
|
if (paragraph === undefined) {
|
|
280
311
|
return nodes.map(drawn);
|
|
281
312
|
}
|
|
313
|
+
|
|
282
314
|
const output: ReactNode[] = [];
|
|
283
315
|
let run: UiViewNode[] = [];
|
|
316
|
+
|
|
284
317
|
const close = () => {
|
|
285
318
|
if (run.some((node) => !isBlank(node))) {
|
|
286
319
|
output.push(
|
|
@@ -289,17 +322,23 @@ function UiNodes({ nodes, parent, texts, context }: UiNodesProps): ReactNode {
|
|
|
289
322
|
</div>
|
|
290
323
|
);
|
|
291
324
|
}
|
|
325
|
+
|
|
292
326
|
run = [];
|
|
293
327
|
};
|
|
328
|
+
|
|
294
329
|
for (const node of nodes) {
|
|
295
330
|
if (isInline(node)) {
|
|
296
331
|
run.push(node);
|
|
332
|
+
|
|
297
333
|
continue;
|
|
298
334
|
}
|
|
335
|
+
|
|
299
336
|
close();
|
|
300
337
|
output.push(drawn(node));
|
|
301
338
|
}
|
|
339
|
+
|
|
302
340
|
close();
|
|
341
|
+
|
|
303
342
|
return output;
|
|
304
343
|
}
|
|
305
344
|
|
|
@@ -342,22 +381,28 @@ function UiNodeBody({
|
|
|
342
381
|
}): ReactNode {
|
|
343
382
|
if (node.type === '$text') {
|
|
344
383
|
const text = String(node.props.text ?? '');
|
|
384
|
+
|
|
345
385
|
return LITERAL_PARENTS.has(parent) || texts.get(node.sourceId ?? node.id) !== true ? (
|
|
346
386
|
text
|
|
347
387
|
) : (
|
|
348
388
|
<UiProse text={text} fade={context.phase === 'streaming'} openUrl={context.openUrl} />
|
|
349
389
|
);
|
|
350
390
|
}
|
|
391
|
+
|
|
351
392
|
if (node.error !== undefined) {
|
|
352
393
|
return <UiFallbackPart fallback={node.fallback} problem={{ kind: 'failed' }} />;
|
|
353
394
|
}
|
|
395
|
+
|
|
354
396
|
if (!isUiComponent(node.type)) {
|
|
355
397
|
return <UiFallbackPart fallback={node.fallback} problem={{ kind: 'unknown', component: node.type }} />;
|
|
356
398
|
}
|
|
399
|
+
|
|
357
400
|
const Renderer = UI_RENDERERS[node.type] as UiRenderer<Record<string, unknown>> | null;
|
|
401
|
+
|
|
358
402
|
if (Renderer === null) {
|
|
359
403
|
return null;
|
|
360
404
|
}
|
|
405
|
+
|
|
361
406
|
return (
|
|
362
407
|
<Renderer node={node} context={context}>
|
|
363
408
|
<UiNodes nodes={node.children} parent={node.type} texts={texts} context={context} />
|
|
@@ -374,8 +419,10 @@ function placeReader(frozen: ChatUiQueryState['blocks'][string] | undefined, rev
|
|
|
374
419
|
if (frozen === undefined || frozen.revision !== revision) {
|
|
375
420
|
return () => (unrecorded ? null : undefined);
|
|
376
421
|
}
|
|
422
|
+
|
|
377
423
|
return (nodeId, address) => {
|
|
378
424
|
const reading = Object.hasOwn(frozen.places ?? {}, nodeId) ? frozen.places![nodeId] : undefined;
|
|
425
|
+
|
|
379
426
|
return reading?.address === address.trim() && reading.state === 'found' && reading.lat !== undefined && reading.lon !== undefined
|
|
380
427
|
? { lat: reading.lat, lon: reading.lon, label: reading.label }
|
|
381
428
|
: null;
|
|
@@ -406,14 +453,17 @@ function UiBlockBody({
|
|
|
406
453
|
const key = JSON.stringify([context.scopeId, context.chatId, context.itemId, block.id]);
|
|
407
454
|
const [state] = useState(() => localState(key, block, answered));
|
|
408
455
|
state.sync(block);
|
|
456
|
+
|
|
409
457
|
const stateRevision = useSyncExternalStore(
|
|
410
458
|
(listener) => state.subscribe(listener),
|
|
411
459
|
() => state.snapshot(),
|
|
412
460
|
() => state.snapshot()
|
|
413
461
|
);
|
|
462
|
+
|
|
414
463
|
const [optimistic, setOptimistic] = useState<{ revision: string; choiceId: string; state: 'sending' | 'sent' | 'queued' } | null>(null);
|
|
415
464
|
const [failedChoiceId, setFailedChoiceId] = useState<string | null>(null);
|
|
416
465
|
const pending = useRef(false);
|
|
466
|
+
|
|
417
467
|
useEffect(() => {
|
|
418
468
|
if (answered && answered.revision === block.revision && answered.values) {
|
|
419
469
|
for (const [name, value] of Object.entries(answered.values)) {
|
|
@@ -421,17 +471,21 @@ function UiBlockBody({
|
|
|
421
471
|
}
|
|
422
472
|
}
|
|
423
473
|
}, [state, answered, block.revision]);
|
|
474
|
+
|
|
424
475
|
const answer =
|
|
425
476
|
answered && answered.revision === block.revision
|
|
426
477
|
? { choiceId: answered.choiceId, state: answered.queued ? ('queued' as const) : ('sent' as const) }
|
|
427
478
|
: optimistic?.revision === block.revision
|
|
428
479
|
? optimistic
|
|
429
480
|
: null;
|
|
481
|
+
|
|
430
482
|
const [element, setElement] = useState<HTMLDivElement | null>(null);
|
|
431
483
|
const navigation = useContext(UiReplyNavigationContext);
|
|
432
484
|
const flash = navigation?.flash;
|
|
485
|
+
|
|
433
486
|
const flashing =
|
|
434
487
|
navigation?.chatId === context.chatId && flash?.itemId === context.itemId && flash.blockId === block.id && flash.revision === block.revision;
|
|
488
|
+
|
|
435
489
|
const queries = useUiQueries(block, state, context, element, frozen);
|
|
436
490
|
const links = useUiLinks(block, state, context, queries.reads, frozen);
|
|
437
491
|
// The state changes in place, so its revision is what tells this evaluation it went stale.
|
|
@@ -439,6 +493,7 @@ function UiBlockBody({
|
|
|
439
493
|
const evaluation = useMemo(() => evaluateUiBlock(block, state), [block, state, stateRevision]);
|
|
440
494
|
const texts = useMemo(() => writtenTexts(block.nodes), [block]);
|
|
441
495
|
const sources = useMemo(() => sourceTargets(evaluation.nodes), [evaluation]);
|
|
496
|
+
|
|
442
497
|
const citations = useMemo(
|
|
443
498
|
() => ({
|
|
444
499
|
sources,
|
|
@@ -450,9 +505,11 @@ function UiBlockBody({
|
|
|
450
505
|
}),
|
|
451
506
|
[sources, element]
|
|
452
507
|
);
|
|
508
|
+
|
|
453
509
|
const liveValue = useUiLiveValues(block, evaluation.nodes, queries.currentReadings);
|
|
454
510
|
const sendChoice = host.intelligentUi?.sendChoice;
|
|
455
511
|
const place = useMemo(() => placeReader(frozen, block.revision, unrecorded), [frozen, block.revision, unrecorded]);
|
|
512
|
+
|
|
456
513
|
const rendering: UiRenderContext = {
|
|
457
514
|
...context,
|
|
458
515
|
...links,
|
|
@@ -470,15 +527,19 @@ function UiBlockBody({
|
|
|
470
527
|
if (pending.current || answer || queries.reading) {
|
|
471
528
|
return;
|
|
472
529
|
}
|
|
530
|
+
|
|
473
531
|
const values = uiInputValues(block, state);
|
|
474
532
|
const reads = queries.readsFor(values);
|
|
533
|
+
|
|
475
534
|
if (reads === null) {
|
|
476
535
|
return;
|
|
477
536
|
}
|
|
537
|
+
|
|
478
538
|
pending.current = true;
|
|
479
539
|
setFailedChoiceId(null);
|
|
480
540
|
const revision = block.revision!;
|
|
481
541
|
setOptimistic({ revision, choiceId, state: 'sending' });
|
|
542
|
+
|
|
482
543
|
void sendChoice(context.scopeId, {
|
|
483
544
|
chatId: context.chatId,
|
|
484
545
|
itemId: context.itemId,
|
|
@@ -499,9 +560,11 @@ function UiBlockBody({
|
|
|
499
560
|
}
|
|
500
561
|
: undefined
|
|
501
562
|
};
|
|
563
|
+
|
|
502
564
|
const unknown = block.catalogVersion !== UI_CATALOG_VERSION;
|
|
503
565
|
const failed = !unknown && !block.nodes.length && block.diagnostics.length > 0;
|
|
504
566
|
const shownAsText = unknown ? t('blocks.unreadable') : failed ? block.diagnostics[0].message : undefined;
|
|
567
|
+
|
|
505
568
|
return (
|
|
506
569
|
<div ref={setElement} className="relative" data-ui-block={block.id} data-ui-revision={block.revision} tabIndex={-1}>
|
|
507
570
|
{flashing && <span key={flash.nonce} className="chat-flash pointer-events-none absolute inset-0 rounded-lg" aria-hidden />}
|