@adecore/agents-react 0.39.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.js +2 -1
- 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/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- 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.js +216 -253
- 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/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/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/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/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 +4 -6
- 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 +15 -0
- 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 +26 -0
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +6 -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 +119 -0
- 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 +5 -0
- 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 +39 -0
- 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 +1 -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 +2 -0
- 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 +34 -14
- 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 +4 -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 +6 -4
- package/src/host.ts +2 -0
- package/src/index.ts +3 -0
- package/src/lazy.tsx +9 -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/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/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
|
@@ -39,6 +39,7 @@ const TIME = 'shrink-0 text-xs text-text-faint tabular-nums';
|
|
|
39
39
|
|
|
40
40
|
function Elapsed({ startedAt }: { startedAt: number }) {
|
|
41
41
|
const ref = useTickingText(() => formatElapsedShort(Date.now() - startedAt));
|
|
42
|
+
|
|
42
43
|
return <span ref={ref} className={TIME} />;
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -49,6 +50,7 @@ function AgentLine({ view, provider, onOpen }: { view: WorkflowAgentView; provid
|
|
|
49
50
|
const agentId = agent.agentId;
|
|
50
51
|
// One still waiting has no transcript yet.
|
|
51
52
|
const open = onOpen !== undefined && agentId !== null ? () => onOpen({ toolUseId: workflowAgentRef(agentId), description: agent.label }) : undefined;
|
|
53
|
+
|
|
52
54
|
return (
|
|
53
55
|
<button
|
|
54
56
|
className="-mx-1 mb-0.5 flex w-[calc(100%+8px)] items-start gap-2 rounded-md px-1 py-1 text-left enabled:hover:bg-surface-hover"
|
|
@@ -87,6 +89,7 @@ function AgentLine({ view, provider, onOpen }: { view: WorkflowAgentView; provid
|
|
|
87
89
|
function PhaseBlock({ phase, provider, onOpen }: { phase: WorkflowPhaseView; provider: AgentKind | undefined; onOpen?(step: SubagentStep): void }) {
|
|
88
90
|
const { t } = useTranslation('agent-chat');
|
|
89
91
|
const settled = phase.agents.filter((view) => view.state === 'done' || view.state === 'failed').length;
|
|
92
|
+
|
|
90
93
|
return (
|
|
91
94
|
<div className="mt-3">
|
|
92
95
|
{phase.title !== null && (
|
|
@@ -106,11 +109,13 @@ function PhaseBlock({ phase, provider, onOpen }: { phase: WorkflowPhaseView; pro
|
|
|
106
109
|
|
|
107
110
|
function Counts({ counts }: { counts: WorkflowCounts }) {
|
|
108
111
|
const { t } = useTranslation('agent-chat');
|
|
112
|
+
|
|
109
113
|
const parts = [
|
|
110
114
|
{ key: 'working', count: counts.running, tone: 'text-status-running' },
|
|
111
115
|
{ key: 'finished', count: counts.done, tone: 'text-status-idle' },
|
|
112
116
|
{ key: 'failedCount', count: counts.failed, tone: 'text-status-error' }
|
|
113
117
|
].filter((part) => part.count > 0);
|
|
118
|
+
|
|
114
119
|
return (
|
|
115
120
|
<span className="shrink-0 truncate text-xs text-text-faint tabular-nums">
|
|
116
121
|
{t('rows.workflow.agents', { count: counts.total })}
|
|
@@ -150,6 +155,7 @@ export function WorkflowRow({
|
|
|
150
155
|
const opens = hasToolBody(tool);
|
|
151
156
|
const onOpen = refused ? undefined : onOpenAgent;
|
|
152
157
|
const actions = useChatActions();
|
|
158
|
+
|
|
153
159
|
const stop = (): void => {
|
|
154
160
|
if (chatId && workflow.taskId) {
|
|
155
161
|
void actions.stopTask(chatId, workflow.taskId).catch((error: unknown) => {
|
|
@@ -161,7 +167,9 @@ export function WorkflowRow({
|
|
|
161
167
|
});
|
|
162
168
|
}
|
|
163
169
|
};
|
|
170
|
+
|
|
164
171
|
const failed = tool.state === 'error';
|
|
172
|
+
|
|
165
173
|
const header = (
|
|
166
174
|
<>
|
|
167
175
|
<span className={clsx(ROW_GUTTER, running && 'text-accent')}>
|
|
@@ -173,11 +181,13 @@ export function WorkflowRow({
|
|
|
173
181
|
{opens && <Icon icon={ChevronRight} size={12} className={clsx('shrink-0 text-text-faint transition-transform', open && 'rotate-90')} />}
|
|
174
182
|
</>
|
|
175
183
|
);
|
|
184
|
+
|
|
176
185
|
const headerClasses = clsx(
|
|
177
186
|
'-mx-1 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs',
|
|
178
187
|
failed ? 'text-status-error' : 'text-text-muted',
|
|
179
188
|
opens && 'hover:bg-surface-hover'
|
|
180
189
|
);
|
|
190
|
+
|
|
181
191
|
return (
|
|
182
192
|
<div data-find-item={tool.id} className="mb-1">
|
|
183
193
|
<div className="flex items-center gap-1">
|
|
@@ -6,16 +6,14 @@ import { isBlock, type TimelineRow } from '../../logic/timeline';
|
|
|
6
6
|
export const FOLLOW_THRESHOLD_PX = 40;
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* app's card have no margin of their own like prose does, so the row under one takes the block gap
|
|
12
|
-
* too. A question already carries the gap before it, and the row after one is the answer, so neither
|
|
13
|
-
* takes a seam.
|
|
9
|
+
* A seam where tool lines meet prose gets the block gap, and so does the row under a visual or an app's
|
|
10
|
+
* card, which have no margin of their own. A question and the answer after it never take a seam.
|
|
14
11
|
*/
|
|
15
12
|
export function rowRhythm(row: TimelineRow, previous: TimelineRow | null): string {
|
|
16
13
|
const question = row.kind === 'user';
|
|
17
14
|
const bare = previous?.kind === 'visual' || previous?.kind === 'app-card';
|
|
18
15
|
const seam = !question && previous !== null && previous.kind !== 'user' && (isBlock(row) !== isBlock(previous) || bare);
|
|
16
|
+
|
|
19
17
|
return clsx(question && 'pb-(--chat-answer-gap)', seam && 'pt-(--chat-block-gap)');
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -28,15 +26,19 @@ export function replyHeader(row: TimelineRow, previous: TimelineRow | null): { a
|
|
|
28
26
|
if (row.kind === 'user' || row.kind === 'turn-start') {
|
|
29
27
|
return null;
|
|
30
28
|
}
|
|
29
|
+
|
|
31
30
|
if (previous === null) {
|
|
32
31
|
return { at: null };
|
|
33
32
|
}
|
|
33
|
+
|
|
34
34
|
if (previous.kind === 'user') {
|
|
35
35
|
return { at: previous.item.createdAt };
|
|
36
36
|
}
|
|
37
|
+
|
|
37
38
|
if (previous.kind === 'turn-start') {
|
|
38
39
|
return { at: previous.turn.createdAt };
|
|
39
40
|
}
|
|
41
|
+
|
|
40
42
|
return null;
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
const THEME_ATTRIBUTES = ['class', 'style', 'data-theme'];
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
|
-
* Calls `changed` after the root of `owner`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* well, the signal the terminal follows too. Returns what stops it.
|
|
9
|
-
*
|
|
10
|
-
* Any change of those attributes counts, also one that leaves every token as it was (a dialog that
|
|
11
|
-
* locks the scroll writes the root's `style`), so a caller compares what it reads with what it drew.
|
|
5
|
+
* Calls `changed` at most once a frame after the root or body of `owner` changes a theme attribute. An app
|
|
6
|
+
* that rewrites its palette through a stylesheet touches the root's `style` as well, as the terminal expects.
|
|
7
|
+
* Any change counts, also one that leaves every token alone, so a caller compares what it reads with what it drew.
|
|
12
8
|
*/
|
|
13
9
|
export function watchTheme(owner: Document, changed: () => void): () => void {
|
|
14
10
|
const view = owner.defaultView ?? window;
|
|
15
11
|
let frame = 0;
|
|
12
|
+
|
|
16
13
|
const schedule = (): void => {
|
|
17
14
|
if (frame === 0) {
|
|
18
15
|
frame = view.requestAnimationFrame(() => {
|
|
@@ -21,14 +18,18 @@ export function watchTheme(owner: Document, changed: () => void): () => void {
|
|
|
21
18
|
});
|
|
22
19
|
}
|
|
23
20
|
};
|
|
21
|
+
|
|
24
22
|
const observer = new view.MutationObserver(schedule);
|
|
23
|
+
|
|
25
24
|
for (const root of [owner.documentElement, owner.body]) {
|
|
26
25
|
if (root !== null) {
|
|
27
26
|
observer.observe(root, { attributes: true, attributeFilter: THEME_ATTRIBUTES });
|
|
28
27
|
}
|
|
29
28
|
}
|
|
29
|
+
|
|
30
30
|
return () => {
|
|
31
31
|
observer.disconnect();
|
|
32
|
+
|
|
32
33
|
if (frame !== 0) {
|
|
33
34
|
view.cancelAnimationFrame(frame);
|
|
34
35
|
frame = 0;
|
|
@@ -16,11 +16,15 @@ export function threadReferencedKey(structure: Record<string, ChatItem> | undefi
|
|
|
16
16
|
if (structure === undefined) {
|
|
17
17
|
return '';
|
|
18
18
|
}
|
|
19
|
+
|
|
19
20
|
const cached = referencedOf.get(structure);
|
|
21
|
+
|
|
20
22
|
if (cached !== undefined) {
|
|
21
23
|
return cached;
|
|
22
24
|
}
|
|
25
|
+
|
|
23
26
|
const ids = new Set<string>();
|
|
27
|
+
|
|
24
28
|
for (const item of Object.values(structure)) {
|
|
25
29
|
if (item.kind === 'user') {
|
|
26
30
|
for (const id of [...(item.chats ?? []), ...(item.references ?? [])]) {
|
|
@@ -28,8 +32,10 @@ export function threadReferencedKey(structure: Record<string, ChatItem> | undefi
|
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
}
|
|
35
|
+
|
|
31
36
|
const key = [...ids].join('\n');
|
|
32
37
|
referencedOf.set(structure, key);
|
|
38
|
+
|
|
33
39
|
return key;
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -39,6 +45,7 @@ export function useReferenceLookup(chatId: string): ReferenceLookup {
|
|
|
39
45
|
const { openReference } = useChatHost();
|
|
40
46
|
const referables = useHostReferables(scopeId, chatId);
|
|
41
47
|
const key = useChatRow(chatId, (chat) => threadReferencedKey(chat?.structure));
|
|
48
|
+
|
|
42
49
|
return {
|
|
43
50
|
items: referables.items,
|
|
44
51
|
open: openReference ? (id: string) => openReference(scopeId, id) : null,
|
|
@@ -8,22 +8,27 @@ function createSourceBinding(document: DiffDocument, language: string, theme: st
|
|
|
8
8
|
const mapping = diffSyntaxDocument(document, language, theme);
|
|
9
9
|
const listeners = new Set<() => void>();
|
|
10
10
|
let source: DiffSourceTokenSession | undefined;
|
|
11
|
+
|
|
11
12
|
const current = (): DiffSourceTokenSession => {
|
|
12
13
|
if (source === undefined) {
|
|
13
14
|
source = diffSourceTokens(document, mapping, runtime());
|
|
15
|
+
|
|
14
16
|
source.subscribe(() => {
|
|
15
17
|
for (const listener of [...listeners]) {
|
|
16
18
|
listener();
|
|
17
19
|
}
|
|
18
20
|
});
|
|
19
21
|
}
|
|
22
|
+
|
|
20
23
|
return source;
|
|
21
24
|
};
|
|
25
|
+
|
|
22
26
|
return {
|
|
23
27
|
tokens: {
|
|
24
28
|
get: (side, index) => (mapping.locate(side, index) === undefined ? null : current().get(side, index)),
|
|
25
29
|
subscribe(listener) {
|
|
26
30
|
listeners.add(listener);
|
|
31
|
+
|
|
27
32
|
return () => {
|
|
28
33
|
listeners.delete(listener);
|
|
29
34
|
};
|
|
@@ -40,10 +45,13 @@ function createSourceBinding(document: DiffDocument, language: string, theme: st
|
|
|
40
45
|
/* Without a `runtime`, the shared one of the host's `code`. */
|
|
41
46
|
export function useDiffSourceTokens(document: DiffDocument, language: string, theme: string, runtime?: DiffSyntaxRuntime): DiffSourceTokens {
|
|
42
47
|
const { code } = useChatHost();
|
|
48
|
+
|
|
43
49
|
const binding = useMemo(
|
|
44
50
|
() => createSourceBinding(document, language, theme, () => runtime ?? diffSyntaxRuntime(code)),
|
|
45
51
|
[document, language, theme, runtime, code]
|
|
46
52
|
);
|
|
53
|
+
|
|
47
54
|
useLayoutEffect(() => () => binding.release(), [binding]);
|
|
55
|
+
|
|
48
56
|
return binding.tokens;
|
|
49
57
|
}
|
|
@@ -18,13 +18,16 @@ export function useToggleSet(onToggle?: () => void): ToggleSet {
|
|
|
18
18
|
const toggle = (id: string): void => {
|
|
19
19
|
setIds((current) => {
|
|
20
20
|
const next = new Set(current);
|
|
21
|
+
|
|
21
22
|
if (next.has(id)) {
|
|
22
23
|
next.delete(id);
|
|
23
24
|
} else {
|
|
24
25
|
next.add(id);
|
|
25
26
|
}
|
|
27
|
+
|
|
26
28
|
return next;
|
|
27
29
|
});
|
|
30
|
+
|
|
28
31
|
onToggle?.();
|
|
29
32
|
};
|
|
30
33
|
|
|
@@ -25,6 +25,7 @@ export function visualThemeOf(tokens: VisualTokens, appearance: VisualAppearance
|
|
|
25
25
|
const raised = tokens.color('--surface-raised');
|
|
26
26
|
const accent = tokens.color('--accent');
|
|
27
27
|
const onAccent = tokens.color('--accent-text');
|
|
28
|
+
|
|
28
29
|
const variables: Partial<Record<VisualThemeVariable, string>> = {
|
|
29
30
|
'--background': background,
|
|
30
31
|
'--foreground': text,
|
|
@@ -61,6 +62,7 @@ export function visualThemeOf(tokens: VisualTokens, appearance: VisualAppearance
|
|
|
61
62
|
'--font-sans': tokens.value('--font-sans'),
|
|
62
63
|
'--font-mono': tokens.value('--font-mono')
|
|
63
64
|
};
|
|
65
|
+
|
|
64
66
|
return { appearance, variables: cleanVisualVariables(variables) };
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -75,10 +77,13 @@ interface Rgba {
|
|
|
75
77
|
|
|
76
78
|
function rgbaOf(color: string): Rgba | null {
|
|
77
79
|
const match = RGB.exec(color.trim());
|
|
80
|
+
|
|
78
81
|
if (match === null) {
|
|
79
82
|
return null;
|
|
80
83
|
}
|
|
84
|
+
|
|
81
85
|
const alpha = match[4] === undefined ? 1 : Number(match[4]) / (match[5] === '%' ? 100 : 1);
|
|
86
|
+
|
|
82
87
|
return { red: Number(match[1]), green: Number(match[2]), blue: Number(match[3]), alpha };
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -93,27 +98,36 @@ function isTransparent(color: string): boolean {
|
|
|
93
98
|
*/
|
|
94
99
|
export function backgroundBehind(layers: readonly string[]): string | null {
|
|
95
100
|
const translucent: Rgba[] = [];
|
|
101
|
+
|
|
96
102
|
for (const layer of layers) {
|
|
97
103
|
if (layer === '' || isTransparent(layer)) {
|
|
98
104
|
continue;
|
|
99
105
|
}
|
|
106
|
+
|
|
100
107
|
const color = rgbaOf(layer);
|
|
108
|
+
|
|
101
109
|
if (color !== null && color.alpha < 1) {
|
|
102
110
|
translucent.push(color);
|
|
111
|
+
|
|
103
112
|
continue;
|
|
104
113
|
}
|
|
114
|
+
|
|
105
115
|
if (color === null || translucent.length === 0) {
|
|
106
116
|
// A color in a space other than sRGB stays as the engine wrote it, since there is no laying another over it here.
|
|
107
117
|
return color === null ? layer : `rgb(${color.red}, ${color.green}, ${color.blue})`;
|
|
108
118
|
}
|
|
119
|
+
|
|
109
120
|
let { red, green, blue } = color;
|
|
121
|
+
|
|
110
122
|
for (const over of translucent.reverse()) {
|
|
111
123
|
red = over.red * over.alpha + red * (1 - over.alpha);
|
|
112
124
|
green = over.green * over.alpha + green * (1 - over.alpha);
|
|
113
125
|
blue = over.blue * over.alpha + blue * (1 - over.alpha);
|
|
114
126
|
}
|
|
127
|
+
|
|
115
128
|
return `rgb(${Math.round(red)}, ${Math.round(green)}, ${Math.round(blue)})`;
|
|
116
129
|
}
|
|
130
|
+
|
|
117
131
|
return null;
|
|
118
132
|
}
|
|
119
133
|
|
|
@@ -132,27 +146,37 @@ function readTheme(element: HTMLElement, mode: VisualAppearance): VisualTheme {
|
|
|
132
146
|
probe.style.position = 'absolute';
|
|
133
147
|
probe.style.visibility = 'hidden';
|
|
134
148
|
element.append(probe);
|
|
149
|
+
|
|
135
150
|
try {
|
|
136
151
|
const probed = view.getComputedStyle(probe);
|
|
152
|
+
|
|
137
153
|
const declared = (token: string): string => style.getPropertyValue(token).trim();
|
|
154
|
+
|
|
138
155
|
const computed = (property: 'color' | 'borderTopLeftRadius', token: string): string => {
|
|
139
156
|
if (declared(token) === '') {
|
|
140
157
|
return '';
|
|
141
158
|
}
|
|
159
|
+
|
|
142
160
|
probe.style[property] = `var(${token})`;
|
|
161
|
+
|
|
143
162
|
return probed[property];
|
|
144
163
|
};
|
|
164
|
+
|
|
145
165
|
const tokens: VisualTokens = {
|
|
146
166
|
color: (token) => computed('color', token),
|
|
147
167
|
length: (token) => computed('borderTopLeftRadius', token),
|
|
148
168
|
value: declared
|
|
149
169
|
};
|
|
170
|
+
|
|
150
171
|
const layers: string[] = [];
|
|
172
|
+
|
|
151
173
|
for (let node: Element | null = element; node !== null; node = node.parentElement) {
|
|
152
174
|
layers.push(view.getComputedStyle(node).backgroundColor);
|
|
153
175
|
}
|
|
176
|
+
|
|
154
177
|
const scheme = style.colorScheme.trim();
|
|
155
178
|
const appearance = scheme === 'light' || scheme === 'dark' ? scheme : mode;
|
|
179
|
+
|
|
156
180
|
return visualThemeOf(tokens, appearance, backgroundBehind(layers) ?? tokens.color('--surface'));
|
|
157
181
|
} finally {
|
|
158
182
|
probe.remove();
|
|
@@ -167,24 +191,30 @@ function readTheme(element: HTMLElement, mode: VisualAppearance): VisualTheme {
|
|
|
167
191
|
export function useVisualTheme(ref: RefObject<HTMLElement | null>): VisualTheme | null {
|
|
168
192
|
const mode = useHostCodeMode();
|
|
169
193
|
const [theme, setTheme] = useState<VisualTheme | null>(null);
|
|
194
|
+
|
|
170
195
|
useLayoutEffect(() => {
|
|
171
196
|
const element = ref.current;
|
|
197
|
+
|
|
172
198
|
if (element === null) {
|
|
173
199
|
return;
|
|
174
200
|
}
|
|
201
|
+
|
|
175
202
|
const read = (): void => {
|
|
176
203
|
const next = readTheme(element, mode);
|
|
177
204
|
setTheme((current) => (current !== null && sameTheme(current, next) ? current : next));
|
|
178
205
|
};
|
|
206
|
+
|
|
179
207
|
read();
|
|
180
208
|
const owner = element.ownerDocument;
|
|
181
209
|
const stopWatching = watchTheme(owner, read);
|
|
182
210
|
const scheme = owner.defaultView?.matchMedia?.('(prefers-color-scheme: dark)') ?? null;
|
|
183
211
|
scheme?.addEventListener('change', read);
|
|
212
|
+
|
|
184
213
|
return () => {
|
|
185
214
|
stopWatching();
|
|
186
215
|
scheme?.removeEventListener('change', read);
|
|
187
216
|
};
|
|
188
217
|
}, [ref, mode]);
|
|
218
|
+
|
|
189
219
|
return theme;
|
|
190
220
|
}
|
|
@@ -49,6 +49,7 @@ export interface VisualBridgeOptions {
|
|
|
49
49
|
|
|
50
50
|
function timeout(ms: number, run: () => void): () => void {
|
|
51
51
|
const timer = setTimeout(run, ms);
|
|
52
|
+
|
|
52
53
|
return () => clearTimeout(timer);
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -78,6 +79,7 @@ export class VisualBridge {
|
|
|
78
79
|
constructor(options: VisualBridgeOptions) {
|
|
79
80
|
this.options = options;
|
|
80
81
|
this.theme = options.theme;
|
|
82
|
+
|
|
81
83
|
options.page.then(
|
|
82
84
|
(html) => {
|
|
83
85
|
this.html = html;
|
|
@@ -94,10 +96,13 @@ export class VisualBridge {
|
|
|
94
96
|
/* A `message` event of the window the frame is in. */
|
|
95
97
|
receive(event: { source: unknown; data: unknown }): void {
|
|
96
98
|
const frame = this.options.frame();
|
|
99
|
+
|
|
97
100
|
if (this.disposed || frame === null || event.source !== frame) {
|
|
98
101
|
return;
|
|
99
102
|
}
|
|
103
|
+
|
|
100
104
|
const message = parseVisualMessage(event.data);
|
|
105
|
+
|
|
101
106
|
switch (message?.method) {
|
|
102
107
|
case VISUAL_BRIDGE_METHODS.sandboxProxyReady:
|
|
103
108
|
// A frame that loads again starts over from the sandbox host page.
|
|
@@ -108,40 +113,50 @@ export class VisualBridge {
|
|
|
108
113
|
this.viewportReady = false;
|
|
109
114
|
this.cancelSilence();
|
|
110
115
|
this.sendPage();
|
|
116
|
+
|
|
111
117
|
return;
|
|
112
118
|
case VISUAL_BRIDGE_METHODS.sizeChanged:
|
|
113
119
|
if (this.sent === null) {
|
|
114
120
|
return;
|
|
115
121
|
}
|
|
122
|
+
|
|
116
123
|
if (!this.live) {
|
|
117
124
|
this.live = true;
|
|
118
125
|
this.options.onAppearance(this.sent.appearance);
|
|
119
126
|
}
|
|
127
|
+
|
|
120
128
|
this.options.onHeight(
|
|
121
129
|
this.options.onViewportReady ? visualContentHeight(message.height) : clampVisualHeight(this.options.visual, message.height)
|
|
122
130
|
);
|
|
131
|
+
|
|
123
132
|
return;
|
|
124
133
|
case VISUAL_BRIDGE_METHODS.viewportReady:
|
|
125
134
|
if (this.sent === null) {
|
|
126
135
|
return;
|
|
127
136
|
}
|
|
137
|
+
|
|
128
138
|
this.viewportReady = true;
|
|
129
139
|
this.options.onViewportReady?.();
|
|
130
140
|
this.setViewport(this.viewport);
|
|
141
|
+
|
|
131
142
|
return;
|
|
132
143
|
case VISUAL_BRIDGE_METHODS.scrollRequest:
|
|
133
144
|
if (this.sent !== null && this.viewportReady && this.viewport !== null) {
|
|
134
145
|
this.options.onScroll?.(message.request);
|
|
135
146
|
}
|
|
147
|
+
|
|
136
148
|
return;
|
|
137
149
|
case VISUAL_BRIDGE_METHODS.openLink:
|
|
138
150
|
if (this.sent === null) {
|
|
139
151
|
return;
|
|
140
152
|
}
|
|
153
|
+
|
|
141
154
|
if (this.options.mayOpenLink()) {
|
|
142
155
|
this.options.openLink(message.url);
|
|
143
156
|
}
|
|
157
|
+
|
|
144
158
|
frame.postMessage(visualOpenLinkResult(message.id), '*');
|
|
159
|
+
|
|
145
160
|
return;
|
|
146
161
|
default:
|
|
147
162
|
return;
|
|
@@ -150,6 +165,7 @@ export class VisualBridge {
|
|
|
150
165
|
|
|
151
166
|
setViewport(viewport: VisualViewport | null): void {
|
|
152
167
|
this.viewport = viewport;
|
|
168
|
+
|
|
153
169
|
if (!this.disposed && this.sent !== null && this.viewportReady) {
|
|
154
170
|
this.options.frame()?.postMessage(visualViewportMessage(viewport), '*');
|
|
155
171
|
}
|
|
@@ -159,10 +175,13 @@ export class VisualBridge {
|
|
|
159
175
|
setTheme(theme: VisualTheme): void {
|
|
160
176
|
this.theme = theme;
|
|
161
177
|
const frame = this.options.frame();
|
|
178
|
+
|
|
162
179
|
if (this.disposed || this.sent === null || frame === null) {
|
|
163
180
|
return;
|
|
164
181
|
}
|
|
182
|
+
|
|
165
183
|
this.postTheme(frame);
|
|
184
|
+
|
|
166
185
|
if (this.live) {
|
|
167
186
|
this.options.onAppearance(theme.appearance);
|
|
168
187
|
}
|
|
@@ -173,7 +192,9 @@ export class VisualBridge {
|
|
|
173
192
|
if (this.heard || this.disposed) {
|
|
174
193
|
return;
|
|
175
194
|
}
|
|
195
|
+
|
|
176
196
|
this.cancelSilence();
|
|
197
|
+
|
|
177
198
|
this.cancelSilence = (this.options.after ?? timeout)(SILENCE_MS, () => {
|
|
178
199
|
if (!this.heard && !this.disposed) {
|
|
179
200
|
this.options.onFailure();
|
|
@@ -188,9 +209,11 @@ export class VisualBridge {
|
|
|
188
209
|
|
|
189
210
|
private sendPage(): void {
|
|
190
211
|
const frame = this.options.frame();
|
|
212
|
+
|
|
191
213
|
if (this.disposed || !this.waiting || this.html === null || frame === null) {
|
|
192
214
|
return;
|
|
193
215
|
}
|
|
216
|
+
|
|
194
217
|
this.waiting = false;
|
|
195
218
|
// Any origin, since the frame's own is opaque; `receive` checks every answer's source instead.
|
|
196
219
|
frame.postMessage(visualResourceReadyMessage(this.html), '*');
|
|
@@ -29,9 +29,11 @@ export class VisualViewportController {
|
|
|
29
29
|
this.observer.observe(scroller);
|
|
30
30
|
this.attributes = new MutationObserver(this.onScroll);
|
|
31
31
|
const row = box.closest('[data-index]');
|
|
32
|
+
|
|
32
33
|
if (row) {
|
|
33
34
|
this.attributes.observe(row, { attributes: true, attributeFilter: ['style'] });
|
|
34
35
|
}
|
|
36
|
+
|
|
35
37
|
scroller.addEventListener('scroll', this.onScroll, { passive: true });
|
|
36
38
|
this.refresh();
|
|
37
39
|
}
|
|
@@ -40,11 +42,13 @@ export class VisualViewportController {
|
|
|
40
42
|
if (this.disposed) {
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
45
|
+
|
|
43
46
|
const top = this.offsetInScroller(this.scroller.getBoundingClientRect(), this.box.getBoundingClientRect());
|
|
44
47
|
const available = this.scroller.clientHeight;
|
|
45
48
|
const geometry = visualViewportGeometry(this.box.offsetHeight, available, top);
|
|
46
49
|
this.viewport.style.height = `${geometry.height}px`;
|
|
47
50
|
const key = `${geometry.top}:${geometry.height}`;
|
|
51
|
+
|
|
48
52
|
if (key !== this.last) {
|
|
49
53
|
this.last = key;
|
|
50
54
|
this.bridge.setViewport(geometry);
|
|
@@ -54,14 +58,18 @@ export class VisualViewportController {
|
|
|
54
58
|
scroll(request: VisualScrollRequest): void {
|
|
55
59
|
const outer = this.scroller.getBoundingClientRect();
|
|
56
60
|
const box = this.box.getBoundingClientRect();
|
|
61
|
+
|
|
57
62
|
if (this.disposed || box.bottom <= outer.top || box.top >= outer.bottom) {
|
|
58
63
|
return;
|
|
59
64
|
}
|
|
65
|
+
|
|
60
66
|
if ('by' in request) {
|
|
61
67
|
this.pendingDelta += request.by;
|
|
62
68
|
this.schedule();
|
|
69
|
+
|
|
63
70
|
return;
|
|
64
71
|
}
|
|
72
|
+
|
|
65
73
|
this.pendingDelta = 0;
|
|
66
74
|
const start = this.scroller.scrollTop + this.offsetInScroller(outer, box);
|
|
67
75
|
const top = 'to' in request ? start + request.to : request.edge === 'start' ? 0 : this.scroller.scrollHeight;
|
|
@@ -82,6 +90,7 @@ export class VisualViewportController {
|
|
|
82
90
|
private offsetInScroller(outer: DOMRect, box: DOMRect): number {
|
|
83
91
|
// A chat on a canvas can be scaled; messages inside its frame use unscaled CSS pixels.
|
|
84
92
|
const scale = outer.height / this.scroller.offsetHeight || 1;
|
|
93
|
+
|
|
85
94
|
return (box.top - outer.top) / scale - this.scroller.clientTop;
|
|
86
95
|
}
|
|
87
96
|
|
|
@@ -89,12 +98,15 @@ export class VisualViewportController {
|
|
|
89
98
|
if (this.frame || this.disposed) {
|
|
90
99
|
return;
|
|
91
100
|
}
|
|
101
|
+
|
|
92
102
|
this.frame = this.view.requestAnimationFrame(() => {
|
|
93
103
|
this.frame = 0;
|
|
104
|
+
|
|
94
105
|
if (this.pendingDelta !== 0) {
|
|
95
106
|
this.scroller.scrollBy({ top: this.pendingDelta, behavior: 'instant' });
|
|
96
107
|
this.pendingDelta = 0;
|
|
97
108
|
}
|
|
109
|
+
|
|
98
110
|
this.refresh();
|
|
99
111
|
});
|
|
100
112
|
}
|
package/src/host-hooks.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
'use no memo';
|
|
2
2
|
/*
|
|
3
|
-
* The
|
|
4
|
-
*
|
|
5
|
-
* calls one as written. `ChatHostProvider` pins its host and a command look's hooks stay with its row's
|
|
6
|
-
* key, so they never change under a mounted view; only these wrappers go uncompiled for it.
|
|
3
|
+
* The React Compiler leaves a component that calls a hook read from a context as written, so the host's
|
|
4
|
+
* hooks are called through these wrappers only. `ChatHostProvider` pins its host, so they never change.
|
|
7
5
|
*/
|
|
8
6
|
import type { ProviderPlatform } from '@adecore/agent-contracts';
|
|
9
7
|
import { referablesOfChats, type Referables } from './chat/chat-references';
|
|
@@ -54,6 +52,7 @@ const useReferablesFromChats = (_scopeId: string, _chatId: string): Referables =
|
|
|
54
52
|
/* A host without views of its own offers its chats. */
|
|
55
53
|
export function useHostReferables(scopeId: string, chatId: string): Referables {
|
|
56
54
|
const useReferables = useChatHost().useReferables ?? useReferablesFromChats;
|
|
55
|
+
|
|
57
56
|
return useReferables(scopeId, chatId);
|
|
58
57
|
}
|
|
59
58
|
|
|
@@ -66,6 +65,7 @@ const noPlatform = (): ProviderPlatform | null => null;
|
|
|
66
65
|
/* Null where the host does not say, as it need not. */
|
|
67
66
|
export function useHostMachinePlatform(scopeId: string): ProviderPlatform | null {
|
|
68
67
|
const useMachinePlatform = useChatHost().useMachinePlatform ?? noPlatform;
|
|
68
|
+
|
|
69
69
|
return useMachinePlatform(scopeId);
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -102,10 +102,12 @@ export function useHostComposerPlaceholder(...args: Parameters<ChatHost['useComp
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const NO_SEND_ACTIONS: readonly ComposerSendAction[] = [];
|
|
105
|
+
|
|
105
106
|
const noSendActions = (): readonly ComposerSendAction[] => NO_SEND_ACTIONS;
|
|
106
107
|
|
|
107
108
|
export function useHostComposerSendActions(scopeId: string, chatId: string): readonly ComposerSendAction[] {
|
|
108
109
|
const useSendActions = useChatHost().useComposerSendActions ?? noSendActions;
|
|
110
|
+
|
|
109
111
|
return useSendActions(scopeId, chatId);
|
|
110
112
|
}
|
|
111
113
|
|
package/src/host.ts
CHANGED
|
@@ -551,9 +551,11 @@ const ChatHostContext = createContext<ChatHost>(DEFAULT_HOST);
|
|
|
551
551
|
export function ChatHostProvider({ host, children }: { host: Partial<ChatHost>; children?: ReactNode }) {
|
|
552
552
|
const parent = useContext(ChatHostContext);
|
|
553
553
|
const [pinned] = useState(() => ({ host, value: { ...parent, ...host } }));
|
|
554
|
+
|
|
554
555
|
if (pinned.host !== host) {
|
|
555
556
|
throw new Error('A ChatHostProvider reads its host once: hand it the same object on every render, or a new key to mount it with another');
|
|
556
557
|
}
|
|
558
|
+
|
|
557
559
|
return createElement(ChatHostContext.Provider, { value: pinned.value }, children);
|
|
558
560
|
}
|
|
559
561
|
|
package/src/index.ts
CHANGED
|
@@ -29,7 +29,9 @@ export {
|
|
|
29
29
|
type TextareaProps,
|
|
30
30
|
type ResourceUrl
|
|
31
31
|
} from './host';
|
|
32
|
+
|
|
32
33
|
export { type ChatScope, ChatScopeContext, useChatScope } from './scope';
|
|
34
|
+
|
|
33
35
|
export {
|
|
34
36
|
type ChatRequestMap,
|
|
35
37
|
type ChatEventMap,
|
|
@@ -39,6 +41,7 @@ export {
|
|
|
39
41
|
errorCode,
|
|
40
42
|
isConnectionError
|
|
41
43
|
} from './transport';
|
|
44
|
+
|
|
42
45
|
export { portTransport, type PortTransport } from './port-transport';
|
|
43
46
|
export { configureAgentMarks, type AgentMark } from './agents/agent-marks';
|
|
44
47
|
export { configureChatStorage, type ChatStorageRecord, type ChatStorageAdapter, type ChatStorageOptions } from './storage';
|