@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
|
@@ -17,15 +17,18 @@ export class UiPageMemory<Value> {
|
|
|
17
17
|
/* Reading a value counts as using it. */
|
|
18
18
|
get(key: string): Value | undefined {
|
|
19
19
|
const value = this.entries.get(key);
|
|
20
|
+
|
|
20
21
|
if (value !== undefined) {
|
|
21
22
|
this.set(key, value);
|
|
22
23
|
}
|
|
24
|
+
|
|
23
25
|
return value;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
set(key: string, value: Value): void {
|
|
27
29
|
this.entries.delete(key);
|
|
28
30
|
this.entries.set(key, value);
|
|
31
|
+
|
|
29
32
|
if (this.entries.size > this.limit) {
|
|
30
33
|
this.entries.delete(this.entries.keys().next().value!);
|
|
31
34
|
}
|
|
@@ -43,9 +46,11 @@ const remembered = new UiPageMemory<unknown>(512);
|
|
|
43
46
|
export function useBlockLocal<Value>(context: UiRenderContext, nodeId: string, initial: () => Value): [Value, (next: Value) => void] {
|
|
44
47
|
const key = `${context.scopeId}\n${context.chatId}\n${context.itemId}\n${context.blockId}\n${nodeId}`;
|
|
45
48
|
const [value, setValue] = useState<Value>(() => (remembered.has(key) ? (remembered.get(key) as Value) : initial()));
|
|
49
|
+
|
|
46
50
|
const set = (next: Value): void => {
|
|
47
51
|
remembered.set(key, next);
|
|
48
52
|
setValue(next);
|
|
49
53
|
};
|
|
54
|
+
|
|
50
55
|
return [value, set];
|
|
51
56
|
}
|
|
@@ -36,6 +36,7 @@ export interface UiChartData extends UiChartValues {
|
|
|
36
36
|
|
|
37
37
|
function numericKeys(rows: readonly ChartRow[]): string[] {
|
|
38
38
|
const keys: string[] = [];
|
|
39
|
+
|
|
39
40
|
for (const row of rows) {
|
|
40
41
|
for (const [key, value] of Object.entries(row)) {
|
|
41
42
|
if (key !== 'label' && !keys.includes(key) && typeof value === 'number' && Number.isFinite(value)) {
|
|
@@ -43,6 +44,7 @@ function numericKeys(rows: readonly ChartRow[]): string[] {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
|
|
46
48
|
return keys;
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -53,6 +55,7 @@ function valuesOf(rows: readonly ChartRow[], keys: readonly string[]): UiChartVa
|
|
|
53
55
|
key,
|
|
54
56
|
values: rows.map((row) => {
|
|
55
57
|
const value = row[key];
|
|
58
|
+
|
|
56
59
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
57
60
|
})
|
|
58
61
|
}))
|
|
@@ -72,6 +75,7 @@ export function uiChartRange(min: number, max: number): { low: number; high: num
|
|
|
72
75
|
const low = min < 0 ? -niceCeiling(-min) : 0;
|
|
73
76
|
const high = max > 0 ? niceCeiling(max) : low === 0 ? 1 : 0;
|
|
74
77
|
const span = high - low;
|
|
78
|
+
|
|
75
79
|
return span > 0 && Number.isFinite(span) && Number.isFinite(1 / span) ? { low, high } : null;
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -83,21 +87,27 @@ export function uiChartRange(min: number, max: number): { low: number; high: num
|
|
|
83
87
|
export function uiChartData(data: UiProps<'Chart'>['data'], kind: ChartKind): UiChartData | null {
|
|
84
88
|
const rows = data.slice(0, UI_CHART_ROWS);
|
|
85
89
|
const keys = numericKeys(rows);
|
|
90
|
+
|
|
86
91
|
if (rows.length === 0 || keys.length === 0) {
|
|
87
92
|
return null;
|
|
88
93
|
}
|
|
94
|
+
|
|
89
95
|
const { labels, series } = valuesOf(rows, keys.slice(0, UI_CHART_SERIES));
|
|
90
96
|
const negative = series.some(({ values }) => values.some((value) => value !== null && value < 0));
|
|
91
97
|
const drawn = kind === 'stacked' && negative ? 'bar' : kind;
|
|
98
|
+
|
|
92
99
|
const extents =
|
|
93
100
|
drawn === 'stacked'
|
|
94
101
|
? labels.map((_, index) => series.reduce((sum, { values }) => sum + (values[index] ?? 0), 0))
|
|
95
102
|
: series.flatMap(({ values }) => values.filter((value) => value !== null));
|
|
103
|
+
|
|
96
104
|
const min = Math.min(0, ...extents);
|
|
97
105
|
const max = Math.max(0, ...extents);
|
|
106
|
+
|
|
98
107
|
if (uiChartRange(min, max) === null) {
|
|
99
108
|
return null;
|
|
100
109
|
}
|
|
110
|
+
|
|
101
111
|
return {
|
|
102
112
|
labels,
|
|
103
113
|
series,
|
|
@@ -112,6 +122,7 @@ export function uiChartData(data: UiProps<'Chart'>['data'], kind: ChartKind): Ui
|
|
|
112
122
|
|
|
113
123
|
/* The width of one character of a chart's type at a 16px root, an estimate since SVG text cannot wrap or be measured before it draws. */
|
|
114
124
|
export const UI_CHART_CHARACTER = 7.5;
|
|
125
|
+
|
|
115
126
|
/* The narrowest a value axis is, and the gap between a tick label and the plot. */
|
|
116
127
|
const AXIS_WIDTH = 44;
|
|
117
128
|
const AXIS_GAP = 8;
|
|
@@ -128,6 +139,7 @@ export function uiChartTextWidth(text: string, character = UI_CHART_CHARACTER):
|
|
|
128
139
|
/* Text cut to the room it has, so a label never reaches past its place; the whole of it is in the values. */
|
|
129
140
|
export function uiChartText(text: string, room: number, character = UI_CHART_CHARACTER): string {
|
|
130
141
|
const fit = Math.floor(room / character);
|
|
142
|
+
|
|
131
143
|
return text.length <= fit ? text : `${text.slice(0, Math.max(1, fit - 1))}…`;
|
|
132
144
|
}
|
|
133
145
|
|
|
@@ -136,6 +148,7 @@ export function uiChartAxis(ticks: readonly string[], width: number, character =
|
|
|
136
148
|
const scale = character / UI_CHART_CHARACTER;
|
|
137
149
|
const room = Math.max(AXIS_WIDTH * scale, Math.floor(width * 0.4));
|
|
138
150
|
const tickTexts = ticks.map((tick) => uiChartText(tick, room - AXIS_GAP, character));
|
|
151
|
+
|
|
139
152
|
return {
|
|
140
153
|
axisWidth: Math.round(Math.min(room, Math.max(AXIS_WIDTH * scale, ...tickTexts.map((text) => uiChartTextWidth(text, character) + AXIS_GAP)))),
|
|
141
154
|
tickTexts
|
|
@@ -147,7 +160,9 @@ export function niceCeiling(value: number): number {
|
|
|
147
160
|
if (value <= 0) {
|
|
148
161
|
return 0;
|
|
149
162
|
}
|
|
163
|
+
|
|
150
164
|
const power = 10 ** Math.floor(Math.log10(value));
|
|
151
165
|
const step = [1, 2, 2.5, 5, 10].find((factor) => factor * power >= value) ?? 10;
|
|
166
|
+
|
|
152
167
|
return step * power;
|
|
153
168
|
}
|
|
@@ -39,9 +39,11 @@ function withoutMarks(text: string): string {
|
|
|
39
39
|
/* The Summary that heads a block, which is its first node; a Summary further down is a subheading. */
|
|
40
40
|
export function uiBlockHead(nodes: readonly UiViewNode[]): { id: string; label: string } | null {
|
|
41
41
|
const first = nodes[0];
|
|
42
|
+
|
|
42
43
|
if (first === undefined || first.type !== 'Summary' || first.error !== undefined) {
|
|
43
44
|
return null;
|
|
44
45
|
}
|
|
46
|
+
|
|
45
47
|
return { id: first.id, label: withoutMarks(uiNodeLabel(first)) };
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -52,22 +54,28 @@ export function uiBlockHead(nodes: readonly UiViewNode[]): { id: string; label:
|
|
|
52
54
|
export async function revealUiSource(root: ParentNode, nodeId: string, ancestors: readonly string[] = []): Promise<boolean> {
|
|
53
55
|
for (const id of ancestors) {
|
|
54
56
|
const control = [...root.querySelectorAll<HTMLButtonElement>('[data-ui-reveal]')].find((element) => element.dataset.uiReveal === id);
|
|
57
|
+
|
|
55
58
|
if (control && (control.getAttribute('aria-expanded') === 'false' || control.getAttribute('aria-selected') === 'false')) {
|
|
56
59
|
control.click();
|
|
57
60
|
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
58
61
|
}
|
|
59
62
|
}
|
|
63
|
+
|
|
60
64
|
const row = [...root.querySelectorAll<HTMLElement>('[data-ui-source]')].find((element) => element.dataset.uiSource === nodeId);
|
|
65
|
+
|
|
61
66
|
if (row === undefined) {
|
|
62
67
|
return false;
|
|
63
68
|
}
|
|
69
|
+
|
|
64
70
|
// The light runs when focus arrives, so a row that already has it lets go first.
|
|
65
71
|
if (row.ownerDocument.activeElement === row) {
|
|
66
72
|
row.blur();
|
|
67
73
|
}
|
|
74
|
+
|
|
68
75
|
row.focus({ preventScroll: true });
|
|
69
76
|
const reduced = row.ownerDocument.defaultView?.matchMedia('(prefers-reduced-motion: reduce)').matches === true;
|
|
70
77
|
row.scrollIntoView({ block: 'nearest', behavior: reduced ? 'auto' : 'smooth' });
|
|
78
|
+
|
|
71
79
|
return true;
|
|
72
80
|
}
|
|
73
81
|
|
|
@@ -105,6 +113,7 @@ export function uiWithUnit(text: string, unit: string | undefined): string {
|
|
|
105
113
|
if (unit === undefined || unit === '') {
|
|
106
114
|
return text;
|
|
107
115
|
}
|
|
116
|
+
|
|
108
117
|
return unit === '%' ? `${text}${unit}` : `${text}\u00a0${unit}`;
|
|
109
118
|
}
|
|
110
119
|
|
|
@@ -113,10 +122,13 @@ export function uiLiveChoiceReason(t: TFunction<'agent-chat'>, live: UiLiveStatu
|
|
|
113
122
|
if (live === null || live === undefined || live.state === 'fresh') {
|
|
114
123
|
return null;
|
|
115
124
|
}
|
|
125
|
+
|
|
116
126
|
if (live.state === 'reading') {
|
|
117
127
|
return t('blocks.choice.reading');
|
|
118
128
|
}
|
|
129
|
+
|
|
119
130
|
const source = live.source ?? live.sources[0] ?? '';
|
|
131
|
+
|
|
120
132
|
return uiReasonText(t, live.code, live.reason) ?? t(live.state === 'failed' ? 'blocks.live.failed' : 'blocks.live.refused', { source });
|
|
121
133
|
}
|
|
122
134
|
|
|
@@ -136,19 +148,24 @@ export function uiUrlDomain(url: string): string {
|
|
|
136
148
|
*/
|
|
137
149
|
export function uiEventTime(at: string, now: Date | number = Date.now()): string {
|
|
138
150
|
const moment = parseUiEventTime(at);
|
|
151
|
+
|
|
139
152
|
if (moment === null) {
|
|
140
153
|
return at;
|
|
141
154
|
}
|
|
155
|
+
|
|
142
156
|
const zone = moment.written ? 'UTC' : undefined;
|
|
143
157
|
const year = moment.written ? moment.date.getUTCFullYear() : moment.date.getFullYear();
|
|
144
158
|
const withYear = year !== new Date(now).getFullYear();
|
|
159
|
+
|
|
145
160
|
if (moment.kind === 'time') {
|
|
146
161
|
return moment.seconds
|
|
147
162
|
? formatDateTime(moment.date, { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC' })
|
|
148
163
|
: formatClock(moment.date, zone);
|
|
149
164
|
}
|
|
165
|
+
|
|
150
166
|
if (moment.kind === 'date') {
|
|
151
167
|
return withYear ? formatDayWithYear(moment.date, zone) : formatDay(moment.date, zone);
|
|
152
168
|
}
|
|
169
|
+
|
|
153
170
|
return withYear ? formatDayClockWithYear(moment.date, zone) : formatDayClock(moment.date, zone);
|
|
154
171
|
}
|
|
@@ -7,6 +7,7 @@ import type { UiRenderContext } from '../render-context';
|
|
|
7
7
|
*/
|
|
8
8
|
export function bindingOf<Value>(node: UiViewNode<{ value?: Value }>, context: UiRenderContext): UiBinding<Value> | null {
|
|
9
9
|
const binding = node.bindings.value as UiBinding<Value> | undefined;
|
|
10
|
+
|
|
10
11
|
return binding === undefined || !node.complete || context.answer !== null ? null : binding;
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -57,13 +57,16 @@ function verticalLayout(chart: UiChartData, unit: string | undefined, width: num
|
|
|
57
57
|
const { low, high } = uiChartRange(chart.min, chart.max)!;
|
|
58
58
|
const ticks = low === 0 ? [0, high / 2, high] : [low, 0, high];
|
|
59
59
|
const tickUnit = unitApart(unit) ? undefined : unit;
|
|
60
|
+
|
|
60
61
|
const { axisWidth, tickTexts } = uiChartAxis(
|
|
61
62
|
ticks.map((tick) => valueText(tick, tickUnit)),
|
|
62
63
|
width,
|
|
63
64
|
character
|
|
64
65
|
);
|
|
66
|
+
|
|
65
67
|
const band = Math.max(0, width - axisWidth) / chart.labels.length;
|
|
66
68
|
const labelEvery = Math.max(1, Math.ceil(LABEL_BAND / Math.max(1, band)));
|
|
69
|
+
|
|
67
70
|
return { low, high, ticks, tickTexts, axisWidth, band, labelEvery, labelRoom: Math.min(band * labelEvery, Math.max(0, width - axisWidth)) };
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -72,7 +75,9 @@ function labelsCut(chart: UiChartData, unit: string | undefined, width: number,
|
|
|
72
75
|
if (width <= 0 || chart.kind === 'hbar' || chart.kind === 'stacked') {
|
|
73
76
|
return false;
|
|
74
77
|
}
|
|
78
|
+
|
|
75
79
|
const { labelRoom } = verticalLayout(chart, unit, width, character);
|
|
80
|
+
|
|
76
81
|
return chart.labels.some((label) => uiChartTextWidth(label, character) > labelRoom);
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -81,29 +86,35 @@ function useChartCharacter(): number {
|
|
|
81
86
|
const [character] = useState(() =>
|
|
82
87
|
typeof document === 'undefined' ? UI_CHART_CHARACTER : uiChartCharacter(Number.parseFloat(getComputedStyle(document.documentElement).fontSize))
|
|
83
88
|
);
|
|
89
|
+
|
|
84
90
|
return character;
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
/* The height a chart will take, which its place holds while the node streams. */
|
|
88
94
|
function chartHeight(kind: ChartKind, rows: number): number {
|
|
89
95
|
const count = Math.max(1, Math.min(rows, UI_CHART_ROWS));
|
|
96
|
+
|
|
90
97
|
return kind === 'hbar' ? count * HBAR_ROW : kind === 'stacked' ? count * STACKED_ROW : HEIGHT;
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
/* One sentence with the main point over all of the data, drawn or not, which a screen reader hears in place of the drawing. */
|
|
94
101
|
function chartSummary(values: UiChartValues, unit: string | undefined, t: TFunction<'agent-chat'>): string {
|
|
95
102
|
let top: { label: string; series: string; value: number } | null = null;
|
|
103
|
+
|
|
96
104
|
for (const series of values.series) {
|
|
97
105
|
for (let i = 0; i < series.values.length; i++) {
|
|
98
106
|
const value = series.values[i];
|
|
107
|
+
|
|
99
108
|
if (value !== null && value !== undefined && (top === null || value > top.value)) {
|
|
100
109
|
top = { label: values.labels[i] ?? '', series: series.key, value };
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
}
|
|
113
|
+
|
|
104
114
|
if (top === null) {
|
|
105
115
|
return t('blocks.chart.values');
|
|
106
116
|
}
|
|
117
|
+
|
|
107
118
|
return values.series.length > 1
|
|
108
119
|
? t('blocks.chart.summarySeries', { count: values.labels.length, series: top.series, label: top.label, value: valueText(top.value, unit) })
|
|
109
120
|
: t('blocks.chart.summary', { count: values.labels.length, label: top.label, value: valueText(top.value, unit) });
|
|
@@ -125,6 +136,7 @@ function Legend({ chart }: { chart: UiChartData }) {
|
|
|
125
136
|
/* Every number of the data, for a screen reader, and on screen once a person opens it. */
|
|
126
137
|
function ValuesTable({ values, unit, shown }: { values: UiChartValues; unit: string | undefined; shown: boolean }) {
|
|
127
138
|
const { t } = useTranslation('agent-chat');
|
|
139
|
+
|
|
128
140
|
const table = (
|
|
129
141
|
<table className={shown ? 'w-full border-collapse text-xs tabular-nums' : undefined}>
|
|
130
142
|
<caption className={shown ? 'sr-only' : undefined}>{t('blocks.chart.values')}</caption>
|
|
@@ -154,6 +166,7 @@ function ValuesTable({ values, unit, shown }: { values: UiChartValues; unit: str
|
|
|
154
166
|
</tbody>
|
|
155
167
|
</table>
|
|
156
168
|
);
|
|
169
|
+
|
|
157
170
|
// A table grows to its widest cell whatever width it is given, so it is hidden inside a block that clips.
|
|
158
171
|
return <div className={shown ? 'max-h-80 w-full overflow-auto rounded-md border border-border-soft' : 'sr-only'}>{table}</div>;
|
|
159
172
|
}
|
|
@@ -195,13 +208,19 @@ function VerticalChart({
|
|
|
195
208
|
const [hover, setHover] = useState<number | null>(null);
|
|
196
209
|
const { low, high, ticks, tickTexts, axisWidth, band, labelEvery, labelRoom } = verticalLayout(chart, unit, width, character);
|
|
197
210
|
const plotHeight = HEIGHT - PLOT_TOP - Math.round((LABEL_ROOM * character) / UI_CHART_CHARACTER);
|
|
211
|
+
|
|
198
212
|
const y = (value: number): number => Math.round(PLOT_TOP + ((high - value) / (high - low)) * plotHeight);
|
|
213
|
+
|
|
199
214
|
const baseline = y(0);
|
|
215
|
+
|
|
200
216
|
const center = (index: number): number => Math.round(axisWidth + band * index + band / 2);
|
|
217
|
+
|
|
201
218
|
const gap = band * 0.2;
|
|
202
219
|
const barWidth = Math.max(1, (band - gap) / chart.series.length);
|
|
220
|
+
|
|
203
221
|
const labelAt = (index: number, text: string): number => {
|
|
204
222
|
const half = uiChartTextWidth(text, character) / 2;
|
|
223
|
+
|
|
205
224
|
return Math.round(Math.min(Math.max(center(index), axisWidth + half), width - half));
|
|
206
225
|
};
|
|
207
226
|
|
|
@@ -212,6 +231,7 @@ function VerticalChart({
|
|
|
212
231
|
if (point !== null) {
|
|
213
232
|
paths.push(`${index === 0 || all[index - 1] === null ? 'M' : 'L'}${point}`);
|
|
214
233
|
}
|
|
234
|
+
|
|
215
235
|
return paths;
|
|
216
236
|
}, [])
|
|
217
237
|
.join(' ');
|
|
@@ -241,7 +261,9 @@ function VerticalChart({
|
|
|
241
261
|
if (index % labelEvery !== 0) {
|
|
242
262
|
return null;
|
|
243
263
|
}
|
|
264
|
+
|
|
244
265
|
const text = uiChartText(label, labelRoom, character);
|
|
266
|
+
|
|
245
267
|
return (
|
|
246
268
|
<text key={index} x={labelAt(index, text)} y={HEIGHT - 4} textAnchor="middle" className="fill-text-faint text-xs">
|
|
247
269
|
{text}
|
|
@@ -310,6 +332,7 @@ function HorizontalChart({ chart, unit }: { chart: UiChartData; unit: string | u
|
|
|
310
332
|
const span = high - low;
|
|
311
333
|
const zero = (-low / span) * 100;
|
|
312
334
|
const single = chart.series.length === 1;
|
|
335
|
+
|
|
313
336
|
return (
|
|
314
337
|
<div aria-hidden className="flex flex-col">
|
|
315
338
|
{chart.labels.map((label, index) => (
|
|
@@ -319,6 +342,7 @@ function HorizontalChart({ chart, unit }: { chart: UiChartData; unit: string | u
|
|
|
319
342
|
{low < 0 && <span className="absolute inset-y-0 w-px bg-border" style={{ left: `${zero}%` }} />}
|
|
320
343
|
{chart.series.map((series, at) => {
|
|
321
344
|
const value = series.values[index] ?? 0;
|
|
345
|
+
|
|
322
346
|
return (
|
|
323
347
|
<span key={series.key} className={clsx('relative block', single ? 'h-3' : 'h-1')}>
|
|
324
348
|
<span
|
|
@@ -389,14 +413,17 @@ function ChartNotes({
|
|
|
389
413
|
}) {
|
|
390
414
|
const { t } = useTranslation('agent-chat');
|
|
391
415
|
const [shown, setShown] = useBlockLocal(context, `${nodeId}:values`, () => false);
|
|
416
|
+
|
|
392
417
|
const notes = [
|
|
393
418
|
...(chart.truncated ? [t('blocks.chart.rowsLeftOut', { shown: formatNumber(chart.labels.length), total: formatNumber(chart.total) })] : []),
|
|
394
419
|
...(chart.omitted.length > 0 ? [t('blocks.chart.seriesLeftOut', { count: chart.omitted.length, series: chart.omitted.join(', ') })] : []),
|
|
395
420
|
...(requested === 'stacked' && chart.kind !== 'stacked' ? [t('blocks.chart.sideBySide')] : [])
|
|
396
421
|
];
|
|
422
|
+
|
|
397
423
|
const partial = chart.truncated || chart.omitted.length > 0 || cut;
|
|
398
424
|
const [measure, truncated] = useTruncated<HTMLSpanElement>();
|
|
399
425
|
const toggle = shown ? t('blocks.chart.hideValues') : t('blocks.chart.showValues');
|
|
426
|
+
|
|
400
427
|
return (
|
|
401
428
|
<>
|
|
402
429
|
{notes.length > 0 && (
|
|
@@ -441,6 +468,7 @@ function DrawnChart({
|
|
|
441
468
|
const [measureRows, rowsCut] = useTruncated<HTMLDivElement>(true);
|
|
442
469
|
const values = uiChartValues(data);
|
|
443
470
|
const { kind } = chart;
|
|
471
|
+
|
|
444
472
|
return (
|
|
445
473
|
<figure className="chat-ui-flat flex flex-col gap-2.5 rounded-xl bg-ui-tile p-3.5">
|
|
446
474
|
{chart.series.length > 1 && <Legend chart={chart} />}
|
|
@@ -474,11 +502,14 @@ function DrawnChart({
|
|
|
474
502
|
export function ChartRenderer({ node, context }: UiRendererProps<UiProps<'Chart'>>) {
|
|
475
503
|
const { kind, data, unit } = node.props;
|
|
476
504
|
const chart = node.complete ? uiChartData(data, kind) : null;
|
|
505
|
+
|
|
477
506
|
if (!node.complete) {
|
|
478
507
|
return <div aria-hidden className="rounded-xl bg-ui-tile" style={{ height: chartHeight(kind, data.length) }} />;
|
|
479
508
|
}
|
|
509
|
+
|
|
480
510
|
if (chart === null) {
|
|
481
511
|
return <UiFallbackPart fallback={node.fallback} problem={{ kind: 'failed' }} />;
|
|
482
512
|
}
|
|
513
|
+
|
|
483
514
|
return <DrawnChart chart={chart} requested={kind} data={data} unit={unit} context={context} nodeId={node.id} />;
|
|
484
515
|
}
|
|
@@ -17,6 +17,7 @@ const SPINNER_AFTER_MS = 300;
|
|
|
17
17
|
export function ChoicesRenderer({ children, context }: UiRendererProps<UiProps<'Choices'>>) {
|
|
18
18
|
const { t } = useTranslation('agent-chat');
|
|
19
19
|
const [streamed] = useState(context.phase === 'streaming');
|
|
20
|
+
|
|
20
21
|
return (
|
|
21
22
|
<div role="group" aria-label={t('blocks.choices')} className="chat-ui-nodes @container flex flex-wrap items-start gap-2">
|
|
22
23
|
{children}
|
|
@@ -46,14 +47,17 @@ export function ChoiceRenderer({ node, children, context }: UiRendererProps<UiPr
|
|
|
46
47
|
const failed = answer === null && context.failedChoiceId === node.id;
|
|
47
48
|
const closed = waiting || unavailable || answer !== null;
|
|
48
49
|
const slow = useLater(chosen && answer.state === 'sending', SPINNER_AFTER_MS);
|
|
50
|
+
|
|
49
51
|
if (!waiting && uiChoiceLabel(node) === null) {
|
|
50
52
|
return <UiFallbackPart fallback={node.fallback} problem={{ kind: 'failed' }} />;
|
|
51
53
|
}
|
|
54
|
+
|
|
52
55
|
const reason = waiting
|
|
53
56
|
? t('blocks.choice.waiting')
|
|
54
57
|
: answer === null && unavailable
|
|
55
58
|
? ((disabled === true ? null : context.choiceReason) ?? t('blocks.choice.unavailable'))
|
|
56
59
|
: null;
|
|
60
|
+
|
|
57
61
|
const described = detail !== undefined && detail !== '';
|
|
58
62
|
const strong = primary === true;
|
|
59
63
|
|
|
@@ -20,6 +20,7 @@ const SourceNumbers = createContext<ReadonlyMap<string, number>>(new Map());
|
|
|
20
20
|
/* Plain text in the same measure until the node closes, so highlighting never makes it jump. */
|
|
21
21
|
export function CodeBlockRenderer({ node }: UiRendererProps<UiProps<'CodeBlock'>>) {
|
|
22
22
|
const { language } = node.props;
|
|
23
|
+
|
|
23
24
|
return (
|
|
24
25
|
<div className="chat-ui-code chat-ui-flat flex flex-col rounded-xl bg-ui-tile p-1.5">
|
|
25
26
|
{language !== undefined && language !== '' && (
|
|
@@ -57,9 +58,11 @@ function AttachedImage({
|
|
|
57
58
|
unnamed: boolean;
|
|
58
59
|
}) {
|
|
59
60
|
const source = useHostAttachmentUrl(scopeId, chatId, attachmentId);
|
|
61
|
+
|
|
60
62
|
if (!cell) {
|
|
61
63
|
return <ImageThumb source={source} alt={alt} className="max-h-70 max-w-full rounded-xl object-contain" />;
|
|
62
64
|
}
|
|
65
|
+
|
|
63
66
|
return (
|
|
64
67
|
<>
|
|
65
68
|
<ImageThumb source={source} alt={alt} aspect={1} />
|
|
@@ -78,6 +81,7 @@ export function ImageRenderer({ node, children, context }: UiRendererProps<UiPro
|
|
|
78
81
|
const { attachment, alt } = node.props;
|
|
79
82
|
const caption = uiNodeLabel(node);
|
|
80
83
|
const name = alt ?? (caption === '' ? t('blocks.image.label') : caption);
|
|
84
|
+
|
|
81
85
|
return (
|
|
82
86
|
<figure className={cell ? 'flex min-w-0 flex-col gap-1' : 'flex flex-col items-start gap-1.5'}>
|
|
83
87
|
{attachment === undefined ? (
|
|
@@ -96,6 +100,7 @@ export function ImageRenderer({ node, children, context }: UiRendererProps<UiPro
|
|
|
96
100
|
/* Three columns, two in a narrow block; the container decides, not the window, since a chat can be any width. */
|
|
97
101
|
export function GalleryRenderer({ node, children }: UiRendererProps<UiProps<'Gallery'>>) {
|
|
98
102
|
const { t } = useTranslation('agent-chat');
|
|
103
|
+
|
|
99
104
|
return (
|
|
100
105
|
<GalleryContext value>
|
|
101
106
|
<div role="group" aria-label={t('blocks.gallery.label', { count: uiChildrenOf(node, 'Image').length })} className="@container">
|
|
@@ -125,11 +130,15 @@ export function CompareRenderer({ node, children, context }: UiRendererProps<UiP
|
|
|
125
130
|
const [split, setSplit] = useState(0.5);
|
|
126
131
|
const [aspect, setAspect] = useState(COMPARE_ASPECT);
|
|
127
132
|
const caption = uiNodeLabel(node);
|
|
133
|
+
|
|
128
134
|
// Each picture is named by its side and by what the whole comparison shows.
|
|
129
135
|
const describe = (side: string): string => (caption === '' ? side : `${side}: ${caption}`);
|
|
136
|
+
|
|
130
137
|
const loading = (source: typeof first) => source.url === null && source.failure === null;
|
|
138
|
+
|
|
131
139
|
const label = t('blocks.compare.label', { before: beforeLabel, after: afterLabel });
|
|
132
140
|
let body: ReactNode;
|
|
141
|
+
|
|
133
142
|
if (loading(first) || loading(second)) {
|
|
134
143
|
body = <ImageThumb source={{ url: null, failure: null }} alt={label} aspect={COMPARE_ASPECT} />;
|
|
135
144
|
} else if (first.url !== null && second.url !== null) {
|
|
@@ -151,6 +160,7 @@ export function CompareRenderer({ node, children, context }: UiRendererProps<UiP
|
|
|
151
160
|
className="size-full object-contain select-none"
|
|
152
161
|
onLoad={(event) => {
|
|
153
162
|
const { naturalWidth, naturalHeight } = event.currentTarget;
|
|
163
|
+
|
|
154
164
|
if (naturalWidth > 0 && naturalHeight > 0) {
|
|
155
165
|
setAspect(naturalWidth / naturalHeight);
|
|
156
166
|
}
|
|
@@ -167,6 +177,7 @@ export function CompareRenderer({ node, children, context }: UiRendererProps<UiP
|
|
|
167
177
|
body = <ImageThumb source={first} alt={label} aspect={COMPARE_ASPECT} />;
|
|
168
178
|
} else {
|
|
169
179
|
const shown = first.url !== null ? { source: first, name: describe(beforeLabel) } : { source: second, name: describe(afterLabel) };
|
|
180
|
+
|
|
170
181
|
body = (
|
|
171
182
|
<>
|
|
172
183
|
<ImageThumb source={shown.source} alt={shown.name} className="max-h-70 max-w-full object-contain" />
|
|
@@ -174,6 +185,7 @@ export function CompareRenderer({ node, children, context }: UiRendererProps<UiP
|
|
|
174
185
|
</>
|
|
175
186
|
);
|
|
176
187
|
}
|
|
188
|
+
|
|
177
189
|
return (
|
|
178
190
|
<figure className="flex w-full flex-col items-start gap-1.5">
|
|
179
191
|
{body}
|
|
@@ -209,6 +221,7 @@ export function ModelRenderer({ node, children, context }: UiRendererProps<UiPro
|
|
|
209
221
|
const label = alt ?? (caption === '' ? t('blocks.model.label') : caption);
|
|
210
222
|
const View = host.ModelView;
|
|
211
223
|
let body: ReactNode;
|
|
224
|
+
|
|
212
225
|
if (View !== undefined && source.url !== null) {
|
|
213
226
|
body = (
|
|
214
227
|
<div role="group" aria-label={label} className="relative h-80 w-full overflow-hidden rounded-xl bg-ui-tile">
|
|
@@ -236,6 +249,7 @@ export function ModelRenderer({ node, children, context }: UiRendererProps<UiPro
|
|
|
236
249
|
</div>
|
|
237
250
|
);
|
|
238
251
|
}
|
|
252
|
+
|
|
239
253
|
return (
|
|
240
254
|
<figure className="flex w-full flex-col items-start gap-1.5">
|
|
241
255
|
{body}
|
|
@@ -248,6 +262,7 @@ export function SourcesRenderer({ node, children }: UiRendererProps<UiProps<'Sou
|
|
|
248
262
|
const { t } = useTranslation('agent-chat');
|
|
249
263
|
const headingId = useId();
|
|
250
264
|
const numbers = new Map(uiChildrenOf(node, 'Source').map((source, index) => [source.id, index + 1]));
|
|
265
|
+
|
|
251
266
|
return (
|
|
252
267
|
<SourceNumbers value={numbers}>
|
|
253
268
|
<section aria-labelledby={headingId} className="chat-ui-flat flex flex-col rounded-xl bg-ui-tile p-1.5">
|
|
@@ -277,6 +292,7 @@ export interface UiSourceCitationProps {
|
|
|
277
292
|
*/
|
|
278
293
|
export function UiSourceCitation({ number, title, url, onReveal, className }: UiSourceCitationProps) {
|
|
279
294
|
const { t } = useTranslation('agent-chat');
|
|
295
|
+
|
|
280
296
|
return (
|
|
281
297
|
<PreviewCard.Root>
|
|
282
298
|
<PreviewCard.Trigger
|
|
@@ -310,6 +326,7 @@ export function SourceRenderer({ node, context }: UiRendererProps<UiProps<'Sourc
|
|
|
310
326
|
const { title, url } = node.props;
|
|
311
327
|
const { openUrl } = context;
|
|
312
328
|
const [measureDomain, domainCut] = useTruncated<HTMLSpanElement>();
|
|
329
|
+
|
|
313
330
|
const face = (
|
|
314
331
|
<>
|
|
315
332
|
<span className="grid size-5 shrink-0 place-items-center rounded-md bg-ui-inset text-2xs font-semibold text-text tabular-nums">{number}</span>
|
|
@@ -321,6 +338,7 @@ export function SourceRenderer({ node, context }: UiRendererProps<UiProps<'Sourc
|
|
|
321
338
|
</span>
|
|
322
339
|
</>
|
|
323
340
|
);
|
|
341
|
+
|
|
324
342
|
return (
|
|
325
343
|
<li data-ui-source={node.id} tabIndex={-1} className="chat-ui-source scroll-my-2 rounded-md">
|
|
326
344
|
{/* The title wraps and the domain may be cut short, so the tooltip says both whole; a row that opens nothing takes focus only while it cuts. */}
|
|
@@ -26,6 +26,7 @@ const EntityTiles = createContext(false);
|
|
|
26
26
|
*/
|
|
27
27
|
function LiveFigure({ text, previous, live, className }: { text: string; previous?: string; live?: UiLiveValue; className?: string }) {
|
|
28
28
|
const lit = live !== undefined && live.changed && !live.stale && previous !== text;
|
|
29
|
+
|
|
29
30
|
return (
|
|
30
31
|
<span key={text} className={clsx(live?.stale ? 'text-text-muted' : 'text-text', lit && 'chat-ui-changed', className)}>
|
|
31
32
|
{text}
|
|
@@ -48,6 +49,7 @@ export function StatRenderer({ node, context }: UiRendererProps<UiProps<'Stat'>>
|
|
|
48
49
|
// A change from zero has no percentage.
|
|
49
50
|
const percent = change === null || previous === undefined || previous === 0 ? null : formatPercent(Math.abs((change / previous) * 100));
|
|
50
51
|
const said = percent === null ? t('blocks.stat.from', { previous: from }) : t('blocks.stat.change', { change: percent, previous: from });
|
|
52
|
+
|
|
51
53
|
return (
|
|
52
54
|
<div className="flex min-w-0 flex-col items-start gap-1.5 rounded-xl bg-ui-tile p-3">
|
|
53
55
|
<span className="max-w-full truncate text-2xs text-text-muted">{label}</span>
|
|
@@ -80,6 +82,7 @@ export function StatRenderer({ node, context }: UiRendererProps<UiProps<'Stat'>>
|
|
|
80
82
|
|
|
81
83
|
export function EntityListRenderer({ node, children }: UiRendererProps<UiProps<'EntityList'>>) {
|
|
82
84
|
const count = uiChildrenOf(node, 'Entry').length;
|
|
85
|
+
|
|
83
86
|
if (count <= TILED_UP_TO) {
|
|
84
87
|
return (
|
|
85
88
|
<EntityTiles value>
|
|
@@ -87,6 +90,7 @@ export function EntityListRenderer({ node, children }: UiRendererProps<UiProps<'
|
|
|
87
90
|
</EntityTiles>
|
|
88
91
|
);
|
|
89
92
|
}
|
|
93
|
+
|
|
90
94
|
return (
|
|
91
95
|
<KeyValueList.Root divided={count >= DIVIDED_FROM} className="chat-ui-nodes chat-ui-flat rounded-xl bg-ui-tile px-3 py-2">
|
|
92
96
|
{children}
|
|
@@ -96,6 +100,7 @@ export function EntityListRenderer({ node, children }: UiRendererProps<UiProps<'
|
|
|
96
100
|
|
|
97
101
|
export function EntryRenderer({ node, children }: UiRendererProps<UiProps<'Entry'>>) {
|
|
98
102
|
const tiled = useContext(EntityTiles);
|
|
103
|
+
|
|
99
104
|
if (tiled) {
|
|
100
105
|
return (
|
|
101
106
|
<div className="flex min-w-0 flex-col gap-0.5 rounded-xl bg-ui-tile px-3 py-2">
|
|
@@ -104,6 +109,7 @@ export function EntryRenderer({ node, children }: UiRendererProps<UiProps<'Entry
|
|
|
104
109
|
</div>
|
|
105
110
|
);
|
|
106
111
|
}
|
|
112
|
+
|
|
107
113
|
return (
|
|
108
114
|
<KeyValueList.Item>
|
|
109
115
|
<KeyValueList.Name>{node.props.label}</KeyValueList.Name>
|
|
@@ -118,6 +124,7 @@ function numberText(cell: Extract<UiTableCell, { kind: 'number' }>, unit: string
|
|
|
118
124
|
|
|
119
125
|
function Cell({ value, column, live }: { value: unknown; column: UiTableColumn; live?: UiLiveValue }) {
|
|
120
126
|
const cell = uiTableCell(value, column);
|
|
127
|
+
|
|
121
128
|
switch (cell.kind) {
|
|
122
129
|
case 'empty':
|
|
123
130
|
return null;
|
|
@@ -125,6 +132,7 @@ function Cell({ value, column, live }: { value: unknown; column: UiTableColumn;
|
|
|
125
132
|
return <>{cell.text}</>;
|
|
126
133
|
case 'number': {
|
|
127
134
|
const before = live?.previous === undefined ? undefined : uiTableCell(live.previous, column);
|
|
135
|
+
|
|
128
136
|
return (
|
|
129
137
|
<LiveFigure
|
|
130
138
|
text={numberText(cell, column.unit)}
|
|
@@ -155,11 +163,14 @@ export function TableRenderer({ node, context }: UiRendererProps<UiProps<'Table'
|
|
|
155
163
|
const { t } = useTranslation('agent-chat');
|
|
156
164
|
const [all, setAll] = useBlockLocal(context, node.id, () => false);
|
|
157
165
|
const columns = uiTableColumns(node);
|
|
166
|
+
|
|
158
167
|
if (columns.length === 0) {
|
|
159
168
|
return <UiFallbackPart fallback={node.fallback} problem={{ kind: 'failed' }} />;
|
|
160
169
|
}
|
|
170
|
+
|
|
161
171
|
const { rows } = node.props;
|
|
162
172
|
const shown = all ? rows : rows.slice(0, UI_TABLE_ROWS);
|
|
173
|
+
|
|
163
174
|
return (
|
|
164
175
|
<div className="flex flex-col items-start gap-1.5">
|
|
165
176
|
<div className="chat-ui-flat max-h-80 w-full overflow-auto rounded-xl bg-ui-tile px-1.5 pb-1.5">
|