@adecore/agents-react 0.31.12 → 0.32.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/AccountDot.js +57 -6
- package/dist/agents/AgentIcon.js +88 -12
- package/dist/agents/ModelOptionControl.js +153 -18
- package/dist/agents/ProviderLogo.js +49 -4
- package/dist/agents/account-limits.js +17 -14
- package/dist/agents/accounts.js +99 -67
- package/dist/agents/agent-marks.js +5 -3
- package/dist/agents/model-name.js +42 -31
- package/dist/agents/model-options.js +8 -8
- package/dist/agents/model-requirement.js +17 -14
- package/dist/agents/option-words.d.ts +7 -0
- package/dist/agents/option-words.js +35 -0
- package/dist/agents/provider-paths.js +6 -6
- package/dist/agents/status-look.js +32 -12
- package/dist/chat/account-choice.d.ts +13 -0
- package/dist/chat/account-choice.js +90 -20
- package/dist/chat/actions.js +126 -59
- package/dist/chat/activity.js +5 -5
- package/dist/chat/attachments.js +74 -53
- package/dist/chat/bookmarks.js +58 -48
- package/dist/chat/chat-client.js +344 -263
- package/dist/chat/chat-references.js +3 -3
- package/dist/chat/composer-effort.d.ts +5 -0
- package/dist/chat/composer-effort.js +26 -0
- package/dist/chat/composer-send.js +13 -13
- package/dist/chat/drafts.js +115 -79
- package/dist/chat/forks.js +81 -24
- package/dist/chat/guards.js +15 -43
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.js +3 -2
- package/dist/chat/logic/ansi.js +170 -167
- package/dist/chat/logic/answers.js +2 -2
- package/dist/chat/logic/bookmarks.js +19 -19
- package/dist/chat/logic/command-looks.js +138 -96
- package/dist/chat/logic/context-usage.js +13 -9
- package/dist/chat/logic/continue-after-stop.js +15 -19
- package/dist/chat/logic/effort.d.ts +14 -0
- package/dist/chat/logic/effort.js +34 -0
- package/dist/chat/logic/embedded-code.js +387 -326
- package/dist/chat/logic/fork.js +134 -110
- package/dist/chat/logic/generated-image.js +54 -23
- package/dist/chat/logic/goal.js +28 -20
- package/dist/chat/logic/handback.js +6 -6
- package/dist/chat/logic/json.js +2 -2
- package/dist/chat/logic/limit.js +46 -30
- package/dist/chat/logic/picker-entries.d.ts +5 -3
- package/dist/chat/logic/picker-entries.js +83 -43
- package/dist/chat/logic/resume-compaction.d.ts +14 -1
- package/dist/chat/logic/resume-compaction.js +93 -24
- package/dist/chat/logic/scrubber.js +188 -136
- package/dist/chat/logic/thought.js +13 -16
- package/dist/chat/logic/thread-cards.js +60 -48
- package/dist/chat/logic/timeline-copy.js +30 -38
- package/dist/chat/logic/timeline-target.js +92 -63
- package/dist/chat/logic/timeline.js +499 -355
- package/dist/chat/logic/tool-catalog.js +54 -34
- package/dist/chat/logic/tools.js +106 -82
- package/dist/chat/logic/visual-height.js +18 -18
- package/dist/chat/logic/visual-viewport.js +6 -3
- package/dist/chat/logic/welcome.js +9 -9
- package/dist/chat/logic/workflow.js +45 -34
- package/dist/chat/mentions.js +149 -109
- package/dist/chat/persisted-json.js +51 -54
- package/dist/chat/preferences.d.ts +4 -0
- package/dist/chat/preferences.js +162 -84
- package/dist/chat/prompt-hold.js +9 -9
- package/dist/chat/quote.js +21 -30
- package/dist/chat/reading-width.js +12 -10
- package/dist/chat/recent-messages.js +34 -21
- package/dist/chat/resume-compaction-dismissals.js +25 -5
- package/dist/chat/runtime-modes.js +3 -3
- package/dist/chat/send-key.js +11 -7
- package/dist/chat/stash.js +89 -69
- package/dist/chat/subagent-conversation.js +181 -139
- package/dist/chat/subagent-list.js +65 -67
- package/dist/chat/subagent-support.js +12 -7
- package/dist/chat/subagent-view.js +132 -44
- package/dist/chat/thumbnails.js +88 -60
- package/dist/chat/timeline-flash.js +24 -12
- package/dist/chat/timeline-scroll.js +116 -86
- package/dist/chat/ui/AccountPill.js +126 -15
- package/dist/chat/ui/AnsiOutput.js +62 -6
- package/dist/chat/ui/BookmarkMarker.js +231 -41
- package/dist/chat/ui/BookmarkSubmenu.js +165 -18
- package/dist/chat/ui/ChatActivity.js +649 -70
- package/dist/chat/ui/ChatReferenceChip.js +104 -9
- package/dist/chat/ui/CodeBlock.js +132 -58
- package/dist/chat/ui/Composer.js +866 -132
- package/dist/chat/ui/ComposerInput.js +309 -120
- package/dist/chat/ui/DiffPool.js +75 -14
- package/dist/chat/ui/EditDiff.js +160 -21
- package/dist/chat/ui/FadingWords.js +32 -3
- package/dist/chat/ui/GoalDock.js +235 -42
- package/dist/chat/ui/ImageView.js +617 -61
- package/dist/chat/ui/LimitState.js +446 -72
- package/dist/chat/ui/Markdown.js +991 -173
- package/dist/chat/ui/MermaidBlock.js +238 -68
- package/dist/chat/ui/MessageActions.js +251 -39
- package/dist/chat/ui/ModelMarks.js +55 -5
- package/dist/chat/ui/Pickers.js +664 -113
- package/dist/chat/ui/PromptComposer.js +281 -51
- package/dist/chat/ui/QuoteButton.js +151 -57
- package/dist/chat/ui/ResumeCompactionDock.d.ts +6 -2
- package/dist/chat/ui/ResumeCompactionDock.js +141 -6
- package/dist/chat/ui/RowTime.js +59 -5
- package/dist/chat/ui/RunSettings.js +1744 -154
- package/dist/chat/ui/Scrubber.js +517 -88
- package/dist/chat/ui/ShellCommand.js +183 -107
- package/dist/chat/ui/SubagentControls.js +212 -24
- package/dist/chat/ui/SubagentInfo.js +170 -13
- package/dist/chat/ui/SubagentStopButton.js +115 -24
- package/dist/chat/ui/SubagentTimeline.js +357 -91
- package/dist/chat/ui/Timeline.js +988 -476
- package/dist/chat/ui/TimelineMenu.js +552 -37
- package/dist/chat/ui/UnifiedDiff.js +234 -27
- package/dist/chat/ui/UploadThumb.js +37 -7
- package/dist/chat/ui/VisualDialogs.js +251 -20
- package/dist/chat/ui/VisualFrame.js +321 -95
- package/dist/chat/ui/Welcome.js +72 -7
- package/dist/chat/ui/chips.js +1 -1
- package/dist/chat/ui/code-lines.js +36 -36
- package/dist/chat/ui/code-streaming.js +1 -1
- package/dist/chat/ui/code-theme.js +7 -7
- package/dist/chat/ui/composer/ComposerControls.js +329 -21
- package/dist/chat/ui/composer/ComposerMenu.js +358 -30
- package/dist/chat/ui/composer/DraftParts.js +185 -11
- package/dist/chat/ui/composer/QueuedMessages.js +138 -13
- package/dist/chat/ui/composer/chips.js +29 -19
- package/dist/chat/ui/composer/editor.js +79 -63
- package/dist/chat/ui/composer/insert.js +26 -12
- package/dist/chat/ui/composer/key-handler.js +180 -127
- package/dist/chat/ui/composer/keys.js +90 -73
- package/dist/chat/ui/composer/menu-aria.js +18 -9
- package/dist/chat/ui/composer/placeholder.js +78 -31
- package/dist/chat/ui/composer/surface.js +13 -37
- package/dist/chat/ui/composer/use-chat-controls.js +230 -90
- package/dist/chat/ui/composer/use-compaction-offer.js +50 -18
- package/dist/chat/ui/composer/use-composer-draft.js +65 -34
- package/dist/chat/ui/composer/use-composer-menus.js +478 -193
- package/dist/chat/ui/composer/use-composer-submit.js +251 -109
- package/dist/chat/ui/composer/use-continuable-turn.js +33 -6
- package/dist/chat/ui/composer/use-draft-files.js +245 -93
- package/dist/chat/ui/composer/use-effort-stepper.d.ts +13 -0
- package/dist/chat/ui/composer/use-effort-stepper.js +88 -0
- package/dist/chat/ui/composer/use-notice.js +61 -16
- package/dist/chat/ui/composer/use-pending-requests.js +38 -17
- package/dist/chat/ui/composer/use-prompt-history.js +117 -45
- package/dist/chat/ui/composer/use-prompt-hold.js +119 -35
- package/dist/chat/ui/composer/use-take-back.js +94 -72
- package/dist/chat/ui/diff/ApprovalReview.js +101 -23
- package/dist/chat/ui/diff/DiffFallbackBoundary.js +21 -13
- package/dist/chat/ui/diff/DiffOmitted.js +56 -6
- package/dist/chat/ui/diff/DiffStack.js +92 -8
- package/dist/chat/ui/diff/DiffUnavailable.js +56 -6
- package/dist/chat/ui/diff/EditorEditDiff.js +263 -51
- package/dist/chat/ui/diff/EditorPatchDiff.js +39 -14
- package/dist/chat/ui/diff/LegacyDiffPool.js +27 -10
- package/dist/chat/ui/diff/LegacyEditDiff.js +97 -20
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +172 -42
- package/dist/chat/ui/diff/RawDiff.js +65 -5
- package/dist/chat/ui/diff/ReviewDiff.js +77 -5
- package/dist/chat/ui/diff/diff-props.js +1 -1
- package/dist/chat/ui/diff/diff-source-budget.js +46 -35
- package/dist/chat/ui/diff/editor-edit-model.js +18 -14
- package/dist/chat/ui/diff/review-plan.js +63 -40
- package/dist/chat/ui/diff/review-settle.js +24 -9
- package/dist/chat/ui/diff/syntax-runtime.js +236 -201
- package/dist/chat/ui/diff/use-review-stack.js +146 -25
- package/dist/chat/ui/diff-syntax.js +70 -58
- package/dist/chat/ui/diff-theme.js +31 -12
- package/dist/chat/ui/file-links.js +39 -10
- package/dist/chat/ui/find-reveal.js +29 -8
- package/dist/chat/ui/full-diff.js +17 -12
- package/dist/chat/ui/highlighter.js +143 -113
- package/dist/chat/ui/icons.js +16 -7
- package/dist/chat/ui/index.js +1 -1
- package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +556 -82
- package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +73 -5
- package/dist/chat/ui/intelligent-ui/UiReply.js +666 -262
- package/dist/chat/ui/intelligent-ui/block-local.js +59 -31
- package/dist/chat/ui/intelligent-ui/chart-data.js +77 -67
- package/dist/chat/ui/intelligent-ui/node-text.js +77 -91
- package/dist/chat/ui/intelligent-ui/registry.js +46 -46
- package/dist/chat/ui/intelligent-ui/render-context.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/base-land.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/binding.js +3 -3
- package/dist/chat/ui/intelligent-ui/renderers/chart.js +1223 -123
- package/dist/chat/ui/intelligent-ui/renderers/choices.js +312 -34
- package/dist/chat/ui/intelligent-ui/renderers/content.js +1241 -86
- package/dist/chat/ui/intelligent-ui/renderers/data.js +619 -54
- package/dist/chat/ui/intelligent-ui/renderers/inputs.js +868 -67
- package/dist/chat/ui/intelligent-ui/renderers/links.js +366 -62
- package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +118 -60
- package/dist/chat/ui/intelligent-ui/renderers/map-style.js +85 -78
- package/dist/chat/ui/intelligent-ui/renderers/map-view.js +282 -217
- package/dist/chat/ui/intelligent-ui/renderers/map.js +621 -69
- package/dist/chat/ui/intelligent-ui/renderers/place-point.js +15 -8
- package/dist/chat/ui/intelligent-ui/renderers/structure.js +262 -20
- package/dist/chat/ui/intelligent-ui/renderers/text.js +674 -49
- package/dist/chat/ui/intelligent-ui/reply-parts.js +24 -15
- package/dist/chat/ui/intelligent-ui/table-data.js +97 -57
- package/dist/chat/ui/intelligent-ui/tones.js +31 -31
- package/dist/chat/ui/intelligent-ui/use-in-view.js +30 -12
- package/dist/chat/ui/intelligent-ui/use-later.js +28 -13
- package/dist/chat/ui/intelligent-ui/use-truncated.js +45 -21
- package/dist/chat/ui/intelligent-ui/use-ui-links.js +296 -109
- package/dist/chat/ui/intelligent-ui/use-ui-queries.js +256 -246
- package/dist/chat/ui/markdown-blocks.js +296 -262
- package/dist/chat/ui/mermaid.js +60 -72
- package/dist/chat/ui/quote-selection.js +32 -29
- package/dist/chat/ui/rehype-chips.js +35 -23
- package/dist/chat/ui/rehype-fade.js +28 -17
- package/dist/chat/ui/remark-html-as-text.js +22 -15
- package/dist/chat/ui/reply-context.js +1 -1
- package/dist/chat/ui/reveal.js +161 -72
- package/dist/chat/ui/rows/ForksRow.js +226 -24
- package/dist/chat/ui/rows/GeneratedImageRow.js +541 -87
- package/dist/chat/ui/rows/MessageRows.js +1706 -159
- package/dist/chat/ui/rows/RowContainer.js +57 -7
- package/dist/chat/ui/rows/Rows.js +463 -54
- package/dist/chat/ui/rows/SubagentRow.js +577 -60
- package/dist/chat/ui/rows/TaskRow.js +359 -40
- package/dist/chat/ui/rows/VisualRow.js +233 -11
- package/dist/chat/ui/rows/WorkRows.js +1945 -164
- package/dist/chat/ui/rows/WorkflowRow.js +293 -34
- package/dist/chat/ui/rows/row-rhythm.js +27 -17
- package/dist/chat/ui/theme-watch.js +26 -23
- package/dist/chat/ui/timestamp-format.js +4 -1
- package/dist/chat/ui/use-diff-source-tokens.js +70 -34
- package/dist/chat/ui/useToggleSet.js +51 -19
- package/dist/chat/ui/visual-theme.js +153 -128
- package/dist/chat/visual-bridge.js +123 -123
- package/dist/chat/visual-viewport.js +94 -83
- package/dist/chat/visuals.js +13 -5
- package/dist/host-hooks.d.ts +29 -0
- package/dist/host-hooks.js +78 -0
- package/dist/host.js +120 -48
- package/dist/index.js +1 -1
- package/dist/lazy.js +38 -32
- package/dist/locales/en/agent-chat.d.ts +34 -0
- package/dist/locales/en/agent-chat.js +747 -713
- package/dist/locales/en/agent-prompts.js +42 -42
- package/dist/locales/en/agent-providers.d.ts +1 -0
- package/dist/locales/en/agent-providers.js +166 -165
- package/dist/locales/en/agent-usage.d.ts +6 -0
- package/dist/locales/en/agent-usage.js +129 -123
- package/dist/locales/nl/agent-chat.d.ts +34 -0
- package/dist/locales/nl/agent-chat.js +747 -713
- package/dist/locales/nl/agent-prompts.js +42 -42
- package/dist/locales/nl/agent-providers.d.ts +1 -0
- package/dist/locales/nl/agent-providers.js +166 -165
- package/dist/locales/nl/agent-usage.d.ts +6 -0
- package/dist/locales/nl/agent-usage.js +129 -123
- package/dist/locales.js +14 -14
- package/dist/mounted-registry.js +31 -30
- package/dist/port-transport.js +122 -111
- package/dist/prompts/index.js +1 -1
- package/dist/prompts/logic/focus.js +15 -14
- package/dist/prompts/logic/keys.js +93 -59
- package/dist/prompts/logic/prompts.js +27 -14
- package/dist/prompts/logic/subjects.js +60 -46
- package/dist/prompts/logic/useFocusAfterAnswer.js +60 -20
- package/dist/prompts/logic/usePromptSession.js +88 -77
- package/dist/prompts/ui/ApprovalBody.js +246 -27
- package/dist/prompts/ui/PromptActions.js +215 -9
- package/dist/prompts/ui/PromptCard.js +215 -36
- package/dist/prompts/ui/PromptView.js +372 -34
- package/dist/prompts/ui/QuestionBody.js +269 -73
- package/dist/providers/AccountColors.js +36 -5
- package/dist/providers/AccountDetail.js +683 -50
- package/dist/providers/AccountVariables.js +139 -32
- package/dist/providers/AddAccountForm.js +125 -38
- package/dist/providers/CliDetail.js +343 -36
- package/dist/providers/InstallPage.js +644 -59
- package/dist/providers/ProviderList.d.ts +2 -1
- package/dist/providers/ProviderList.js +215 -23
- package/dist/providers/ProvidersPane.d.ts +4 -2
- package/dist/providers/ProvidersPane.js +244 -64
- package/dist/providers/account-actions.js +54 -30
- package/dist/providers/index.js +1 -1
- package/dist/providers/install-steps.js +12 -9
- package/dist/providers/pane-target.d.ts +3 -0
- package/dist/providers/pane-target.js +34 -13
- package/dist/providers/parts.js +62 -12
- package/dist/providers/provider-abilities.js +5 -5
- package/dist/providers/provider-groups.js +75 -40
- package/dist/providers/variables.js +73 -39
- package/dist/scope.js +6 -6
- package/dist/settings/sections.js +18 -18
- package/dist/state/chats.js +404 -192
- package/dist/state/index.js +1 -1
- package/dist/state/provider-accounts.js +70 -47
- package/dist/state/providers.js +45 -16
- package/dist/state/usage.js +154 -77
- package/dist/storage.js +69 -68
- package/dist/testing/index.js +1 -1
- package/dist/testing/prompts.js +105 -67
- package/dist/transport.js +10 -10
- package/dist/ui/PlainTextarea.js +27 -3
- package/dist/usage/LimitsList.js +646 -40
- package/dist/usage/UsageBreakdown.js +879 -38
- package/dist/usage/UsageChart.js +311 -32
- package/dist/usage/UsageDialog.js +46 -4
- package/dist/usage/UsageLimits.js +467 -34
- package/dist/usage/UsageLimitsCard.js +95 -11
- package/dist/usage/UsagePage.js +817 -96
- package/dist/usage/UsagePane.js +164 -10
- package/dist/usage/UsageSummary.js +151 -6
- package/dist/usage/UsageTiles.js +164 -14
- package/dist/usage/format.js +32 -32
- package/dist/usage/index.d.ts +1 -1
- package/dist/usage/index.js +2 -1
- package/dist/usage/limit-groups.d.ts +2 -0
- package/dist/usage/limit-groups.js +77 -59
- package/dist/usage/limits.js +78 -43
- package/dist/usage/money.js +20 -4
- package/dist/usage/summary.js +85 -64
- package/package.json +8 -8
- package/src/agents/ModelOptionControl.tsx +3 -1
- package/src/agents/option-words.ts +27 -0
- package/src/chat/account-choice.ts +41 -1
- package/src/chat/composer-effort.ts +32 -0
- package/src/chat/forks.ts +4 -5
- package/src/chat/index.ts +3 -1
- package/src/chat/logic/effort.ts +32 -0
- package/src/chat/logic/picker-entries.ts +45 -10
- package/src/chat/logic/resume-compaction.ts +95 -17
- package/src/chat/logic/timeline.ts +11 -3
- package/src/chat/preferences.ts +35 -0
- package/src/chat/subagent-view.ts +2 -3
- package/src/chat/ui/AnsiOutput.tsx +1 -2
- package/src/chat/ui/BookmarkSubmenu.tsx +3 -3
- package/src/chat/ui/ChatActivity.tsx +14 -22
- package/src/chat/ui/ChatReferenceChip.tsx +2 -3
- package/src/chat/ui/Composer.tsx +15 -7
- package/src/chat/ui/EditDiff.tsx +2 -3
- package/src/chat/ui/LimitState.tsx +2 -2
- package/src/chat/ui/Markdown.tsx +10 -8
- package/src/chat/ui/MermaidBlock.tsx +2 -3
- package/src/chat/ui/Pickers.tsx +49 -29
- package/src/chat/ui/PromptComposer.tsx +7 -9
- package/src/chat/ui/ResumeCompactionDock.tsx +16 -4
- package/src/chat/ui/RunSettings.tsx +67 -28
- package/src/chat/ui/Scrubber.tsx +3 -3
- package/src/chat/ui/ShellCommand.tsx +2 -0
- package/src/chat/ui/SubagentInfo.tsx +2 -3
- package/src/chat/ui/SubagentTimeline.tsx +12 -16
- package/src/chat/ui/Timeline.tsx +6 -6
- package/src/chat/ui/UnifiedDiff.tsx +3 -4
- package/src/chat/ui/VisualFrame.tsx +5 -8
- package/src/chat/ui/code-theme.ts +4 -4
- package/src/chat/ui/composer/placeholder.ts +2 -2
- package/src/chat/ui/composer/use-compaction-offer.ts +36 -16
- package/src/chat/ui/composer/use-composer-menus.ts +4 -9
- package/src/chat/ui/composer/use-composer-submit.ts +2 -1
- package/src/chat/ui/composer/use-effort-stepper.ts +56 -0
- package/src/chat/ui/composer/use-notice.ts +2 -2
- package/src/chat/ui/diff/EditorEditDiff.tsx +9 -13
- package/src/chat/ui/diff/LegacyEditDiff.tsx +12 -17
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +13 -21
- package/src/chat/ui/diff/use-review-stack.ts +4 -4
- package/src/chat/ui/diff-theme.ts +3 -3
- package/src/chat/ui/file-links.ts +2 -2
- package/src/chat/ui/intelligent-ui/UiReply.tsx +2 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +3 -3
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +5 -6
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +2 -1
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +2 -1
- package/src/chat/ui/intelligent-ui/use-truncated.ts +16 -19
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +3 -3
- package/src/chat/ui/rows/ForksRow.tsx +3 -5
- package/src/chat/ui/rows/GeneratedImageRow.tsx +2 -1
- package/src/chat/ui/rows/MessageRows.tsx +12 -21
- package/src/chat/ui/rows/SubagentRow.tsx +3 -3
- package/src/chat/ui/rows/TaskRow.tsx +1 -1
- package/src/chat/ui/rows/WorkRows.tsx +10 -5
- package/src/chat/ui/useToggleSet.ts +15 -18
- package/src/chat/ui/visual-theme.ts +2 -3
- package/src/host-hooks.ts +113 -0
- package/src/lazy.tsx +2 -0
- package/src/locales/en/agent-chat.ts +134 -108
- package/src/locales/en/agent-prompts.ts +7 -7
- package/src/locales/en/agent-providers.ts +35 -34
- package/src/locales/en/agent-usage.ts +26 -20
- package/src/locales/nl/agent-chat.ts +126 -100
- package/src/locales/nl/agent-prompts.ts +5 -5
- package/src/locales/nl/agent-providers.ts +33 -32
- package/src/locales/nl/agent-usage.ts +25 -20
- package/src/providers/CliDetail.tsx +2 -1
- package/src/providers/InstallPage.tsx +2 -4
- package/src/providers/ProviderList.tsx +12 -2
- package/src/providers/ProvidersPane.tsx +45 -15
- package/src/providers/pane-target.ts +20 -4
- package/src/state/chats.ts +2 -2
- package/src/usage/LimitsList.tsx +12 -13
- package/src/usage/UsageBreakdown.tsx +17 -6
- package/src/usage/UsageLimits.tsx +2 -1
- package/src/usage/UsagePage.tsx +3 -3
- package/src/usage/index.ts +1 -1
- package/src/usage/limit-groups.ts +12 -0
- package/src/usage/limits.ts +2 -2
- package/src/usage/money.ts +1 -2
package/dist/chat/ui/Timeline.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
2
|
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
@@ -38,509 +38,1021 @@ import { FileLinkContext } from './file-links.js';
|
|
|
38
38
|
import { AgentIcon } from '../../agents/AgentIcon.js';
|
|
39
39
|
import { useModelName } from '../../agents/model-name.js';
|
|
40
40
|
import { ContextMenu, EmptyState, ErrorBoundary, Icon, SectionLabel } from '@adecore/ui';
|
|
41
|
+
import { useHostContextSources, useHostHidesFolder, useHostThreadCards, useHostTimelineFind, useHostWelcome } from '../../host-hooks.js';
|
|
42
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
41
43
|
const ESTIMATED_ROW_PX = 56;
|
|
42
44
|
// How long a jump waits for a thread that is still loading before it calls the item missing.
|
|
43
45
|
const JUMP_THREAD_WAIT_MS = 8000;
|
|
44
46
|
/* A jump puts its row at the top and stops following the end, so the reader stays where they landed. */
|
|
45
47
|
function showRowAtTop(virtualizer, followRef, index) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
followRef.current = false;
|
|
49
|
+
virtualizer.scrollToIndex(index, {
|
|
50
|
+
align: 'start'
|
|
51
|
+
});
|
|
48
52
|
}
|
|
49
53
|
/*
|
|
50
54
|
* A thread before its first message: which model answers, what the lines into this node let it read,
|
|
51
55
|
* and the folder it works in, so a first question can lean on all three.
|
|
52
56
|
*/
|
|
53
|
-
function EmptyThread(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
function EmptyThread(t0) {
|
|
58
|
+
const $ = _c(20);
|
|
59
|
+
const {
|
|
60
|
+
chatId
|
|
61
|
+
} = t0;
|
|
62
|
+
const {
|
|
63
|
+
t
|
|
64
|
+
} = useTranslation("agent-chat");
|
|
65
|
+
const info = useChatRow(chatId, _temp);
|
|
66
|
+
const model = useModelName(info?.provider, info?.selection.model ?? "");
|
|
67
|
+
const sources = useHostContextSources(chatId);
|
|
68
|
+
const hidesFolder = useHostHidesFolder(chatId);
|
|
69
|
+
let t1;
|
|
70
|
+
if ($[0] !== info) {
|
|
71
|
+
t1 = info ? /*#__PURE__*/_jsx(AgentIcon, {
|
|
72
|
+
kind: info.provider
|
|
73
|
+
}) : undefined;
|
|
74
|
+
$[0] = info;
|
|
75
|
+
$[1] = t1;
|
|
76
|
+
} else {
|
|
77
|
+
t1 = $[1];
|
|
78
|
+
}
|
|
79
|
+
let t2;
|
|
80
|
+
if ($[2] !== info || $[3] !== model || $[4] !== t) {
|
|
81
|
+
t2 = info ? t("timeline.empty.ready", {
|
|
82
|
+
model
|
|
83
|
+
}) : t("timeline.empty.ask");
|
|
84
|
+
$[2] = info;
|
|
85
|
+
$[3] = model;
|
|
86
|
+
$[4] = t;
|
|
87
|
+
$[5] = t2;
|
|
88
|
+
} else {
|
|
89
|
+
t2 = $[5];
|
|
90
|
+
}
|
|
91
|
+
let t3;
|
|
92
|
+
if ($[6] !== t1 || $[7] !== t2) {
|
|
93
|
+
t3 = /*#__PURE__*/_jsx(EmptyState, {
|
|
94
|
+
icon: t1,
|
|
95
|
+
className: "p-0",
|
|
96
|
+
children: t2
|
|
97
|
+
});
|
|
98
|
+
$[6] = t1;
|
|
99
|
+
$[7] = t2;
|
|
100
|
+
$[8] = t3;
|
|
101
|
+
} else {
|
|
102
|
+
t3 = $[8];
|
|
103
|
+
}
|
|
104
|
+
let t4;
|
|
105
|
+
if ($[9] !== sources || $[10] !== t) {
|
|
106
|
+
t4 = sources.length > 0 && /*#__PURE__*/_jsxs("div", {
|
|
107
|
+
className: "flex max-w-full flex-col items-center gap-1",
|
|
108
|
+
children: [/*#__PURE__*/_jsx(SectionLabel, {
|
|
109
|
+
children: t("timeline.empty.canRead")
|
|
110
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
111
|
+
className: "max-w-full text-xs text-text-muted",
|
|
112
|
+
children: sources.map(_temp2).join(", ")
|
|
113
|
+
})]
|
|
114
|
+
});
|
|
115
|
+
$[9] = sources;
|
|
116
|
+
$[10] = t;
|
|
117
|
+
$[11] = t4;
|
|
118
|
+
} else {
|
|
119
|
+
t4 = $[11];
|
|
120
|
+
}
|
|
121
|
+
let t5;
|
|
122
|
+
if ($[12] !== hidesFolder || $[13] !== info || $[14] !== t) {
|
|
123
|
+
t5 = info?.cwd && !hidesFolder && /*#__PURE__*/_jsxs("div", {
|
|
124
|
+
className: "flex max-w-full flex-col items-center gap-1",
|
|
125
|
+
children: [/*#__PURE__*/_jsx(SectionLabel, {
|
|
126
|
+
children: t("timeline.empty.worksIn")
|
|
127
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
128
|
+
className: "max-w-full font-mono text-xs break-all text-text-muted",
|
|
129
|
+
children: info.cwd
|
|
130
|
+
})]
|
|
131
|
+
});
|
|
132
|
+
$[12] = hidesFolder;
|
|
133
|
+
$[13] = info;
|
|
134
|
+
$[14] = t;
|
|
135
|
+
$[15] = t5;
|
|
136
|
+
} else {
|
|
137
|
+
t5 = $[15];
|
|
138
|
+
}
|
|
139
|
+
let t6;
|
|
140
|
+
if ($[16] !== t3 || $[17] !== t4 || $[18] !== t5) {
|
|
141
|
+
t6 = /*#__PURE__*/_jsx("div", {
|
|
142
|
+
className: "flex grow items-center justify-center",
|
|
143
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
144
|
+
className: "flex max-w-sm flex-col items-center gap-3 px-6 py-8 text-center",
|
|
145
|
+
children: [t3, t4, t5]
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
$[16] = t3;
|
|
149
|
+
$[17] = t4;
|
|
150
|
+
$[18] = t5;
|
|
151
|
+
$[19] = t6;
|
|
152
|
+
} else {
|
|
153
|
+
t6 = $[19];
|
|
154
|
+
}
|
|
155
|
+
return t6;
|
|
61
156
|
}
|
|
62
157
|
/*
|
|
63
158
|
* A row of the thread behind the chapter line of its bookmark. A message, the only row that takes a
|
|
64
159
|
* bookmark, gets its actions under it; every other row carries a bookmark only for a reply it folds.
|
|
65
160
|
*/
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
161
|
+
function _temp2(source) {
|
|
162
|
+
return source.title;
|
|
163
|
+
}
|
|
164
|
+
function _temp(row) {
|
|
165
|
+
return row?.info ?? null;
|
|
166
|
+
}
|
|
167
|
+
function MarkableRow(t0) {
|
|
168
|
+
const $ = _c(19);
|
|
169
|
+
const {
|
|
170
|
+
row,
|
|
171
|
+
chatId,
|
|
172
|
+
bookmark,
|
|
173
|
+
children
|
|
174
|
+
} = t0;
|
|
175
|
+
if (row.kind !== "user" && row.kind !== "assistant") {
|
|
176
|
+
let t1;
|
|
177
|
+
if ($[0] !== bookmark || $[1] !== chatId) {
|
|
178
|
+
t1 = bookmark !== null && /*#__PURE__*/_jsx(BookmarkMarker, {
|
|
179
|
+
chatId: chatId,
|
|
180
|
+
itemId: bookmark.itemId,
|
|
181
|
+
bookmark: bookmark
|
|
182
|
+
});
|
|
183
|
+
$[0] = bookmark;
|
|
184
|
+
$[1] = chatId;
|
|
185
|
+
$[2] = t1;
|
|
186
|
+
} else {
|
|
187
|
+
t1 = $[2];
|
|
188
|
+
}
|
|
189
|
+
let t2;
|
|
190
|
+
if ($[3] !== children || $[4] !== t1) {
|
|
191
|
+
t2 = /*#__PURE__*/_jsxs(_Fragment, {
|
|
192
|
+
children: [t1, children]
|
|
193
|
+
});
|
|
194
|
+
$[3] = children;
|
|
195
|
+
$[4] = t1;
|
|
196
|
+
$[5] = t2;
|
|
197
|
+
} else {
|
|
198
|
+
t2 = $[5];
|
|
69
199
|
}
|
|
70
|
-
return
|
|
200
|
+
return t2;
|
|
201
|
+
}
|
|
202
|
+
let t1;
|
|
203
|
+
if ($[6] !== bookmark || $[7] !== chatId || $[8] !== row.id) {
|
|
204
|
+
t1 = /*#__PURE__*/_jsx(BookmarkMarker, {
|
|
205
|
+
chatId: chatId,
|
|
206
|
+
itemId: row.id,
|
|
207
|
+
bookmark: bookmark
|
|
208
|
+
});
|
|
209
|
+
$[6] = bookmark;
|
|
210
|
+
$[7] = chatId;
|
|
211
|
+
$[8] = row.id;
|
|
212
|
+
$[9] = t1;
|
|
213
|
+
} else {
|
|
214
|
+
t1 = $[9];
|
|
215
|
+
}
|
|
216
|
+
let t2;
|
|
217
|
+
if ($[10] !== chatId || $[11] !== row) {
|
|
218
|
+
t2 = /*#__PURE__*/_jsx(MessageActions, {
|
|
219
|
+
chatId: chatId,
|
|
220
|
+
row: row
|
|
221
|
+
});
|
|
222
|
+
$[10] = chatId;
|
|
223
|
+
$[11] = row;
|
|
224
|
+
$[12] = t2;
|
|
225
|
+
} else {
|
|
226
|
+
t2 = $[12];
|
|
227
|
+
}
|
|
228
|
+
let t3;
|
|
229
|
+
if ($[13] !== children || $[14] !== t2) {
|
|
230
|
+
t3 = /*#__PURE__*/_jsxs("div", {
|
|
231
|
+
className: "group/message",
|
|
232
|
+
children: [children, t2]
|
|
233
|
+
});
|
|
234
|
+
$[13] = children;
|
|
235
|
+
$[14] = t2;
|
|
236
|
+
$[15] = t3;
|
|
237
|
+
} else {
|
|
238
|
+
t3 = $[15];
|
|
239
|
+
}
|
|
240
|
+
let t4;
|
|
241
|
+
if ($[16] !== t1 || $[17] !== t3) {
|
|
242
|
+
t4 = /*#__PURE__*/_jsxs(_Fragment, {
|
|
243
|
+
children: [t1, t3]
|
|
244
|
+
});
|
|
245
|
+
$[16] = t1;
|
|
246
|
+
$[17] = t3;
|
|
247
|
+
$[18] = t4;
|
|
248
|
+
} else {
|
|
249
|
+
t4 = $[18];
|
|
250
|
+
}
|
|
251
|
+
return t4;
|
|
71
252
|
}
|
|
72
253
|
/* Memoized with stable props, so a streamed word or a scroll draws only the rows it changed. */
|
|
73
|
-
const ThreadRow = memo(function ThreadRow(
|
|
74
|
-
|
|
75
|
-
|
|
254
|
+
const ThreadRow = memo(function ThreadRow(t0) {
|
|
255
|
+
const $ = _c(36);
|
|
256
|
+
let actions;
|
|
257
|
+
let bookmark;
|
|
258
|
+
let chatId;
|
|
259
|
+
let flashNonce;
|
|
260
|
+
let index;
|
|
261
|
+
let measure;
|
|
262
|
+
let previous;
|
|
263
|
+
let row;
|
|
264
|
+
let top;
|
|
265
|
+
if ($[0] !== t0) {
|
|
266
|
+
({
|
|
267
|
+
row,
|
|
268
|
+
previous,
|
|
269
|
+
index,
|
|
270
|
+
top,
|
|
271
|
+
measure,
|
|
272
|
+
chatId,
|
|
273
|
+
flashNonce,
|
|
274
|
+
bookmark,
|
|
275
|
+
...actions
|
|
276
|
+
} = t0);
|
|
277
|
+
$[0] = t0;
|
|
278
|
+
$[1] = actions;
|
|
279
|
+
$[2] = bookmark;
|
|
280
|
+
$[3] = chatId;
|
|
281
|
+
$[4] = flashNonce;
|
|
282
|
+
$[5] = index;
|
|
283
|
+
$[6] = measure;
|
|
284
|
+
$[7] = previous;
|
|
285
|
+
$[8] = row;
|
|
286
|
+
$[9] = top;
|
|
287
|
+
} else {
|
|
288
|
+
actions = $[1];
|
|
289
|
+
bookmark = $[2];
|
|
290
|
+
chatId = $[3];
|
|
291
|
+
flashNonce = $[4];
|
|
292
|
+
index = $[5];
|
|
293
|
+
measure = $[6];
|
|
294
|
+
previous = $[7];
|
|
295
|
+
row = $[8];
|
|
296
|
+
top = $[9];
|
|
297
|
+
}
|
|
298
|
+
let t1;
|
|
299
|
+
if ($[10] !== previous || $[11] !== row) {
|
|
300
|
+
t1 = replyHeader(row, previous);
|
|
301
|
+
$[10] = previous;
|
|
302
|
+
$[11] = row;
|
|
303
|
+
$[12] = t1;
|
|
304
|
+
} else {
|
|
305
|
+
t1 = $[12];
|
|
306
|
+
}
|
|
307
|
+
const header = t1;
|
|
308
|
+
let t2;
|
|
309
|
+
if ($[13] !== flashNonce) {
|
|
310
|
+
t2 = flashNonce !== null && /*#__PURE__*/_jsx("span", {
|
|
311
|
+
className: "chat-flash pointer-events-none absolute inset-0 rounded-lg",
|
|
312
|
+
"aria-hidden": true
|
|
313
|
+
}, flashNonce);
|
|
314
|
+
$[13] = flashNonce;
|
|
315
|
+
$[14] = t2;
|
|
316
|
+
} else {
|
|
317
|
+
t2 = $[14];
|
|
318
|
+
}
|
|
319
|
+
let t3;
|
|
320
|
+
if ($[15] !== chatId || $[16] !== header) {
|
|
321
|
+
t3 = header !== null && /*#__PURE__*/_jsx("div", {
|
|
322
|
+
className: "chat-column-content w-full",
|
|
323
|
+
children: /*#__PURE__*/_jsx(ReplyHeader, {
|
|
324
|
+
chatId: chatId,
|
|
325
|
+
at: header.at
|
|
326
|
+
})
|
|
327
|
+
});
|
|
328
|
+
$[15] = chatId;
|
|
329
|
+
$[16] = header;
|
|
330
|
+
$[17] = t3;
|
|
331
|
+
} else {
|
|
332
|
+
t3 = $[17];
|
|
333
|
+
}
|
|
334
|
+
let t4;
|
|
335
|
+
if ($[18] !== actions || $[19] !== chatId || $[20] !== row) {
|
|
336
|
+
t4 = /*#__PURE__*/_jsx(Row, {
|
|
337
|
+
row: row,
|
|
338
|
+
chatId: chatId,
|
|
339
|
+
...actions
|
|
340
|
+
});
|
|
341
|
+
$[18] = actions;
|
|
342
|
+
$[19] = chatId;
|
|
343
|
+
$[20] = row;
|
|
344
|
+
$[21] = t4;
|
|
345
|
+
} else {
|
|
346
|
+
t4 = $[21];
|
|
347
|
+
}
|
|
348
|
+
let t5;
|
|
349
|
+
if ($[22] !== bookmark || $[23] !== chatId || $[24] !== row || $[25] !== t4) {
|
|
350
|
+
t5 = /*#__PURE__*/_jsx(MarkableRow, {
|
|
351
|
+
row: row,
|
|
352
|
+
chatId: chatId,
|
|
353
|
+
bookmark: bookmark,
|
|
354
|
+
children: t4
|
|
355
|
+
});
|
|
356
|
+
$[22] = bookmark;
|
|
357
|
+
$[23] = chatId;
|
|
358
|
+
$[24] = row;
|
|
359
|
+
$[25] = t4;
|
|
360
|
+
$[26] = t5;
|
|
361
|
+
} else {
|
|
362
|
+
t5 = $[26];
|
|
363
|
+
}
|
|
364
|
+
let t6;
|
|
365
|
+
if ($[27] !== index || $[28] !== measure || $[29] !== previous || $[30] !== row || $[31] !== t2 || $[32] !== t3 || $[33] !== t5 || $[34] !== top) {
|
|
366
|
+
t6 = /*#__PURE__*/_jsxs(RowContainer, {
|
|
367
|
+
row: row,
|
|
368
|
+
previous: previous,
|
|
369
|
+
index: index,
|
|
370
|
+
top: top,
|
|
371
|
+
ref: measure,
|
|
372
|
+
children: [t2, t3, t5]
|
|
373
|
+
});
|
|
374
|
+
$[27] = index;
|
|
375
|
+
$[28] = measure;
|
|
376
|
+
$[29] = previous;
|
|
377
|
+
$[30] = row;
|
|
378
|
+
$[31] = t2;
|
|
379
|
+
$[32] = t3;
|
|
380
|
+
$[33] = t5;
|
|
381
|
+
$[34] = top;
|
|
382
|
+
$[35] = t6;
|
|
383
|
+
} else {
|
|
384
|
+
t6 = $[35];
|
|
385
|
+
}
|
|
386
|
+
return t6;
|
|
76
387
|
});
|
|
77
388
|
/*
|
|
78
389
|
* `overlay` stands in the thread's place and ends where the composer starts, so the thread keeps
|
|
79
390
|
* where it was underneath and the composer stays in reach for the prompts it answers.
|
|
80
391
|
*/
|
|
81
|
-
export function Timeline({
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const rows = useMemo(() => withTimedRows(threadRows, timedRowsOf(cards, drawsVisuals ? (visuals ?? []) : null, heldFrom)), [threadRows, cards, drawsVisuals, visuals, heldFrom]);
|
|
162
|
-
const welcomes = host.useWelcome(chatId);
|
|
163
|
-
const empty = rows.length === 0;
|
|
164
|
-
// A chat that took a turn before is one whose thread has not come in yet, not one to greet.
|
|
165
|
-
const welcome = welcomes && empty && (info === null || (info.usage.turns === 0 && info.activeTurnId === null));
|
|
166
|
-
const marks = useMemo(() => bookmarkRows(rows, bookmarks ?? [], items ?? {}), [rows, bookmarks, items]);
|
|
167
|
-
const ticks = useMemo(() => ticksOf(rows, marks), [rows, marks]);
|
|
168
|
-
// A message a jump is on its way to; it may first have to open the turn its reply folded into.
|
|
169
|
-
const [seeking, setSeeking] = useState(null);
|
|
170
|
-
const [landing, setLanding] = useState(null);
|
|
171
|
-
// A turn a jump is on its way to, which waits for the page its messages are on to come in.
|
|
172
|
-
const [revealing, setRevealing] = useState(null);
|
|
173
|
-
const flash = useTimelineFlash((s) => s.target);
|
|
174
|
-
const revealBlock = useCallback((block) => {
|
|
175
|
-
setLanding(null);
|
|
176
|
-
setSeeking({ itemId: block.itemId, block, key: keyOf(chatId) });
|
|
177
|
-
}, [keyOf, chatId]);
|
|
178
|
-
const uiNavigation = useMemo(() => ({
|
|
179
|
-
chatId,
|
|
180
|
-
reveal: revealBlock,
|
|
181
|
-
flash: flash?.key === keyOf(chatId) && flash.block ? { itemId: flash.rowId, ...flash.block, nonce: flash.nonce } : null
|
|
182
|
-
}), [revealBlock, flash, keyOf, chatId]);
|
|
183
|
-
// Whether the thread's text clears the strip depends on the width of the view, which only the thread's own frame tells.
|
|
184
|
-
useEffect(() => {
|
|
185
|
-
const element = frameRef.current;
|
|
186
|
-
if (element === null || typeof ResizeObserver === 'undefined') {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
const column = element.closest('.chat-column') !== null;
|
|
190
|
-
const measure = () => setFrame((current) => current.width === element.offsetWidth && current.height === element.offsetHeight && current.column === column
|
|
191
|
-
? current
|
|
192
|
-
: { width: element.offsetWidth, height: element.offsetHeight, column });
|
|
193
|
-
const observer = new ResizeObserver(measure);
|
|
194
|
-
observer.observe(element);
|
|
195
|
-
measure();
|
|
196
|
-
return () => observer.disconnect();
|
|
197
|
-
}, []);
|
|
198
|
-
useLayoutEffect(() => {
|
|
199
|
-
const element = composerRef.current;
|
|
200
|
-
if (element === null) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const measure = () => setComposerHeight(element.offsetHeight);
|
|
204
|
-
const observer = new ResizeObserver(measure);
|
|
205
|
-
observer.observe(element);
|
|
206
|
-
measure();
|
|
207
|
-
return () => observer.disconnect();
|
|
208
|
-
}, []);
|
|
209
|
-
// Oversized prompts scroll in full instead of sticking with their top outside the viewport.
|
|
210
|
-
const stickyComposer = !welcome && composerHeight < frame.height - FOLLOW_THRESHOLD_PX;
|
|
211
|
-
const coveredHeight = stickyComposer ? composerHeight : 0;
|
|
212
|
-
const coveredHeightRef = useRef(coveredHeight);
|
|
213
|
-
coveredHeightRef.current = coveredHeight;
|
|
214
|
-
// The composer pages through the thread with PageUp and PageDown; this is the element it moves.
|
|
215
|
-
useEffect(() => registerTimeline(chatId, scrollRef.current, (enabled) => {
|
|
216
|
-
followRef.current = enabled;
|
|
217
|
-
}, () => coveredHeightRef.current), [chatId]);
|
|
218
|
-
// Nothing here builds with the React Compiler, so its memoization rule has nothing to break.
|
|
219
|
-
// oxlint-disable-next-line react/incompatible-library
|
|
220
|
-
const virtualizer = useVirtualizer({
|
|
221
|
-
count: rows.length,
|
|
222
|
-
getScrollElement: () => scrollRef.current,
|
|
223
|
-
estimateSize: () => ESTIMATED_ROW_PX,
|
|
224
|
-
getItemKey: (index) => rows[index].id,
|
|
225
|
-
overscan: 8,
|
|
226
|
-
scrollPaddingEnd: coveredHeight
|
|
392
|
+
export function Timeline({
|
|
393
|
+
chatId,
|
|
394
|
+
composer,
|
|
395
|
+
overlay
|
|
396
|
+
}) {
|
|
397
|
+
const host = useChatHost();
|
|
398
|
+
const {
|
|
399
|
+
fork
|
|
400
|
+
} = host;
|
|
401
|
+
const {
|
|
402
|
+
t
|
|
403
|
+
} = useTranslation('agent-chat');
|
|
404
|
+
const order = useChatRow(chatId, row => row?.order);
|
|
405
|
+
// The structure, not the items. A delta growing a reply must not derive every row again.
|
|
406
|
+
const items = useChatRow(chatId, row_0 => row_0?.structure);
|
|
407
|
+
const activeTurnId = useChatRow(chatId, row_1 => row_1?.info.activeTurnId ?? null);
|
|
408
|
+
const bookmarks = useChatRow(chatId, row_2 => row_2?.bookmarks);
|
|
409
|
+
const visuals = useChatRow(chatId, row_3 => row_3?.visuals);
|
|
410
|
+
const forkedTurns = useForkedTurns(chatId);
|
|
411
|
+
const info = useChatRow(chatId, row_4 => row_4?.info ?? null);
|
|
412
|
+
const cursor = useChatRow(chatId, row_5 => row_5?.history?.cursor ?? null);
|
|
413
|
+
const scope = useChatScope();
|
|
414
|
+
const {
|
|
415
|
+
id: scopeId,
|
|
416
|
+
keyOf
|
|
417
|
+
} = scope;
|
|
418
|
+
// Read when the menu opens rather than subscribed to, for the same reason the rows use the structure.
|
|
419
|
+
const fullItems = () => useChats.getState().byKey[keyOf(chatId)]?.items;
|
|
420
|
+
const scrollRef = useRef(null);
|
|
421
|
+
const threadRef = useRef(null);
|
|
422
|
+
const contentRef = useRef(null);
|
|
423
|
+
const quoteTakerRef = useRef(null);
|
|
424
|
+
const registerQuoteTaker = useCallback(take => {
|
|
425
|
+
quoteTakerRef.current = take;
|
|
426
|
+
return () => {
|
|
427
|
+
if (quoteTakerRef.current === take) {
|
|
428
|
+
quoteTakerRef.current = null;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}, []);
|
|
432
|
+
const followRef = useRef(true);
|
|
433
|
+
const stopFollowing = useCallback(() => {
|
|
434
|
+
followRef.current = false;
|
|
435
|
+
}, []);
|
|
436
|
+
const groups = useToggleSet(stopFollowing);
|
|
437
|
+
const turns = useToggleSet(stopFollowing);
|
|
438
|
+
const subagents = useToggleSet(stopFollowing);
|
|
439
|
+
const scrollGeometry = useRef({
|
|
440
|
+
top: 0,
|
|
441
|
+
height: 0,
|
|
442
|
+
viewport: 0
|
|
443
|
+
});
|
|
444
|
+
const [target, setTarget] = useState(EMPTY_TARGET);
|
|
445
|
+
const {
|
|
446
|
+
trail,
|
|
447
|
+
show
|
|
448
|
+
} = useSubagentTrail(chatId);
|
|
449
|
+
const frameRef = useRef(null);
|
|
450
|
+
const composerRef = useRef(null);
|
|
451
|
+
const [composerHeight, setComposerHeight] = useState(0);
|
|
452
|
+
const [frame, setFrame] = useState({
|
|
453
|
+
width: 0,
|
|
454
|
+
height: 0,
|
|
455
|
+
column: false
|
|
456
|
+
});
|
|
457
|
+
const [loadingEarlier, setLoadingEarlier] = useState(false);
|
|
458
|
+
const loadingEarlierRef = useRef(false);
|
|
459
|
+
// The row being read and where it stood, taken after every render, so a page going in above puts it back.
|
|
460
|
+
const anchorRef = useRef(null);
|
|
461
|
+
const headRef = useRef(undefined);
|
|
462
|
+
const loadEarlier = useCallback(() => {
|
|
463
|
+
if (cursor === null || loadingEarlierRef.current) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
loadingEarlierRef.current = true;
|
|
467
|
+
setLoadingEarlier(true);
|
|
468
|
+
// A page that failed is asked for again on the next scroll; the thread keeps what it has.
|
|
469
|
+
void scope.chats.loadEarlier(chatId, cursor).catch(() => undefined).finally(() => {
|
|
470
|
+
loadingEarlierRef.current = false;
|
|
471
|
+
setLoadingEarlier(false);
|
|
227
472
|
});
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// The composer is in the scroll flow, so its measured height is part of this end position.
|
|
241
|
-
element.scrollTop = element.scrollHeight;
|
|
242
|
-
scrollGeometry.current = { top: element.scrollTop, height: element.scrollHeight, viewport: element.clientHeight };
|
|
243
|
-
}, [rows.length, totalSize, composerHeight, frame.height]);
|
|
244
|
-
// A page that went in above moves every row down by what it measures; the reader stays on the row they were on.
|
|
245
|
-
useLayoutEffect(() => {
|
|
246
|
-
const element = scrollRef.current;
|
|
247
|
-
const head = order?.[0];
|
|
248
|
-
const moved = head !== headRef.current;
|
|
249
|
-
headRef.current = head;
|
|
250
|
-
if (element === null) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const anchor = anchorRef.current;
|
|
254
|
-
const index = moved && anchor !== null && !followRef.current ? rows.findIndex((row) => row.id === anchor.id) : -1;
|
|
255
|
-
if (anchor !== null && index !== -1) {
|
|
256
|
-
// The total measures again what this commit's rows reported; the cache alone lags behind them.
|
|
257
|
-
virtualizer.getTotalSize();
|
|
258
|
-
element.scrollTop = virtualizer.measurementsCache[index].start - anchor.offset;
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
const row = virtualizer.getVirtualItemForOffset(element.scrollTop);
|
|
262
|
-
anchorRef.current = row === undefined ? null : { id: String(row.key), offset: row.start - element.scrollTop };
|
|
473
|
+
}, [scope, chatId, cursor]);
|
|
474
|
+
const threadRows = useMemo(() => {
|
|
475
|
+
if (!order || !items) {
|
|
476
|
+
return [];
|
|
477
|
+
}
|
|
478
|
+
return deriveTimelineRows(order.map(id => items[id]), {
|
|
479
|
+
expandedGroups: groups.ids,
|
|
480
|
+
expandedTurns: turns.ids,
|
|
481
|
+
expandedSubagents: subagents.ids,
|
|
482
|
+
activeTurnId,
|
|
483
|
+
forkedTurns,
|
|
484
|
+
commandLooks: host.commandLooks
|
|
263
485
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
486
|
+
}, [order, items, groups.ids, turns.ids, subagents.ids, activeTurnId, forkedTurns, host.commandLooks]);
|
|
487
|
+
const cards = useHostThreadCards(scopeId, chatId);
|
|
488
|
+
const drawsVisuals = host.visuals !== null;
|
|
489
|
+
// Where the part of the thread this client holds begins, while there is a page before it.
|
|
490
|
+
const heldFrom = cursor === null ? null : items?.[order?.[0] ?? '']?.createdAt ?? null;
|
|
491
|
+
const rows = useMemo(() => withTimedRows(threadRows, timedRowsOf(cards, drawsVisuals ? visuals ?? [] : null, heldFrom)), [threadRows, cards, drawsVisuals, visuals, heldFrom]);
|
|
492
|
+
const welcomes = useHostWelcome(chatId);
|
|
493
|
+
const empty = rows.length === 0;
|
|
494
|
+
// A chat that took a turn before is one whose thread has not come in yet, not one to greet.
|
|
495
|
+
const welcome = welcomes && empty && (info === null || info.usage.turns === 0 && info.activeTurnId === null);
|
|
496
|
+
const marks = useMemo(() => bookmarkRows(rows, bookmarks ?? [], items ?? {}), [rows, bookmarks, items]);
|
|
497
|
+
const ticks = useMemo(() => ticksOf(rows, marks), [rows, marks]);
|
|
498
|
+
// A message a jump is on its way to; it may first have to open the turn its reply folded into.
|
|
499
|
+
const [seeking, setSeeking] = useState(null);
|
|
500
|
+
const [landing, setLanding] = useState(null);
|
|
501
|
+
// A turn a jump is on its way to, which waits for the page its messages are on to come in.
|
|
502
|
+
const [revealing, setRevealing] = useState(null);
|
|
503
|
+
const flash = useTimelineFlash(s => s.target);
|
|
504
|
+
const revealBlock = useCallback(block => {
|
|
505
|
+
setLanding(null);
|
|
506
|
+
setSeeking({
|
|
507
|
+
itemId: block.itemId,
|
|
508
|
+
block,
|
|
509
|
+
key: keyOf(chatId)
|
|
510
|
+
});
|
|
511
|
+
}, [keyOf, chatId]);
|
|
512
|
+
const uiNavigation = useMemo(() => ({
|
|
513
|
+
chatId,
|
|
514
|
+
reveal: revealBlock,
|
|
515
|
+
flash: flash?.key === keyOf(chatId) && flash.block ? {
|
|
516
|
+
itemId: flash.rowId,
|
|
517
|
+
...flash.block,
|
|
518
|
+
nonce: flash.nonce
|
|
519
|
+
} : null
|
|
520
|
+
}), [revealBlock, flash, keyOf, chatId]);
|
|
521
|
+
// Whether the thread's text clears the strip depends on the width of the view, which only the thread's own frame tells.
|
|
522
|
+
useEffect(() => {
|
|
523
|
+
const element = frameRef.current;
|
|
524
|
+
if (element === null || typeof ResizeObserver === 'undefined') {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const column = element.closest('.chat-column') !== null;
|
|
528
|
+
const measure = () => setFrame(current => current.width === element.offsetWidth && current.height === element.offsetHeight && current.column === column ? current : {
|
|
529
|
+
width: element.offsetWidth,
|
|
530
|
+
height: element.offsetHeight,
|
|
531
|
+
column
|
|
532
|
+
});
|
|
533
|
+
const observer = new ResizeObserver(measure);
|
|
534
|
+
observer.observe(element);
|
|
535
|
+
measure();
|
|
536
|
+
return () => observer.disconnect();
|
|
537
|
+
}, []);
|
|
538
|
+
useLayoutEffect(() => {
|
|
539
|
+
const element_0 = composerRef.current;
|
|
540
|
+
if (element_0 === null) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const measure_0 = () => setComposerHeight(element_0.offsetHeight);
|
|
544
|
+
const observer_0 = new ResizeObserver(measure_0);
|
|
545
|
+
observer_0.observe(element_0);
|
|
546
|
+
measure_0();
|
|
547
|
+
return () => observer_0.disconnect();
|
|
548
|
+
}, []);
|
|
549
|
+
// Oversized prompts scroll in full instead of sticking with their top outside the viewport.
|
|
550
|
+
const stickyComposer = !welcome && composerHeight < frame.height - FOLLOW_THRESHOLD_PX;
|
|
551
|
+
const coveredHeight = stickyComposer ? composerHeight : 0;
|
|
552
|
+
const coveredHeightRef = useRef(coveredHeight);
|
|
553
|
+
coveredHeightRef.current = coveredHeight;
|
|
554
|
+
// The composer pages through the thread with PageUp and PageDown; this is the element it moves.
|
|
555
|
+
useEffect(() => registerTimeline(chatId, scrollRef.current, enabled => {
|
|
556
|
+
followRef.current = enabled;
|
|
557
|
+
}, () => coveredHeightRef.current), [chatId]);
|
|
558
|
+
// Nothing here builds with the React Compiler, so its memoization rule has nothing to break.
|
|
559
|
+
// oxlint-disable-next-line react/incompatible-library
|
|
560
|
+
const virtualizer = useVirtualizer({
|
|
561
|
+
count: rows.length,
|
|
562
|
+
getScrollElement: () => scrollRef.current,
|
|
563
|
+
estimateSize: () => ESTIMATED_ROW_PX,
|
|
564
|
+
getItemKey: index => rows[index].id,
|
|
565
|
+
overscan: 8,
|
|
566
|
+
scrollPaddingEnd: coveredHeight
|
|
567
|
+
});
|
|
568
|
+
/*
|
|
569
|
+
* Rows are measured as they render, so the end moves while it is drawn (a streamed line, a diff that
|
|
570
|
+
* highlights, an image that loads). `scrollToIndex` aims at where a row was when asked and fell
|
|
571
|
+
* short; the scroller's own bottom is a fact, and a new total size says a row was measured again.
|
|
572
|
+
* A word that does not add a line moves nothing, so the thread does not listen to the words.
|
|
573
|
+
*/
|
|
574
|
+
const totalSize = virtualizer.getTotalSize();
|
|
575
|
+
useLayoutEffect(() => {
|
|
576
|
+
const element_1 = scrollRef.current;
|
|
577
|
+
if (!followRef.current || element_1 === null) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// The composer is in the scroll flow, so its measured height is part of this end position.
|
|
581
|
+
element_1.scrollTop = element_1.scrollHeight;
|
|
582
|
+
scrollGeometry.current = {
|
|
583
|
+
top: element_1.scrollTop,
|
|
584
|
+
height: element_1.scrollHeight,
|
|
585
|
+
viewport: element_1.clientHeight
|
|
305
586
|
};
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
virtualizer.scrollToIndex(index, { align: 'center' });
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
coveredHeight: () => coveredHeightRef.current,
|
|
332
|
-
stopFollowing
|
|
333
|
-
}), [chatId, onMainAgent, rows, items, order, virtualizer, turns.add, groups.add, subagents.add, stopFollowing]);
|
|
334
|
-
const chatFind = host.useTimelineFind(findOptions);
|
|
335
|
-
const searching = chatFind.open;
|
|
336
|
-
// A find covers the whole thread, so every page before this one comes in while it is open.
|
|
337
|
-
useEffect(() => {
|
|
338
|
-
if (searching) {
|
|
339
|
-
loadEarlier();
|
|
340
|
-
}
|
|
341
|
-
}, [searching, loadEarlier]);
|
|
342
|
-
const found = useMemo(() => (searching ? ticksWithHits(ticks, chatFind.hitRows) : []), [searching, ticks, chatFind.hitRows]);
|
|
343
|
-
const foundCurrent = chatFind.currentRow === null ? null : tickOfRow(ticks, chatFind.currentRow);
|
|
344
|
-
const jumpTo = (index) => {
|
|
345
|
-
const tick = ticks[index];
|
|
346
|
-
if (!tick) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (tick.bookmark !== null) {
|
|
350
|
-
setLanding(null);
|
|
351
|
-
setSeeking({ itemId: tick.bookmark.itemId, key: keyOf(chatId) });
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
showRowAtTop(virtualizer, followRef, tick.rowIndex);
|
|
587
|
+
}, [rows.length, totalSize, composerHeight, frame.height]);
|
|
588
|
+
// A page that went in above moves every row down by what it measures; the reader stays on the row they were on.
|
|
589
|
+
useLayoutEffect(() => {
|
|
590
|
+
const element_2 = scrollRef.current;
|
|
591
|
+
const head = order?.[0];
|
|
592
|
+
const moved = head !== headRef.current;
|
|
593
|
+
headRef.current = head;
|
|
594
|
+
if (element_2 === null) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const anchor = anchorRef.current;
|
|
598
|
+
const index_0 = moved && anchor !== null && !followRef.current ? rows.findIndex(row_6 => row_6.id === anchor.id) : -1;
|
|
599
|
+
if (anchor !== null && index_0 !== -1) {
|
|
600
|
+
// The total measures again what this commit's rows reported; the cache alone lags behind them.
|
|
601
|
+
virtualizer.getTotalSize();
|
|
602
|
+
element_2.scrollTop = virtualizer.measurementsCache[index_0].start - anchor.offset;
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const row_7 = virtualizer.getVirtualItemForOffset(element_2.scrollTop);
|
|
606
|
+
anchorRef.current = row_7 === undefined ? null : {
|
|
607
|
+
id: String(row_7.key),
|
|
608
|
+
offset: row_7.start - element_2.scrollTop
|
|
355
609
|
};
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
610
|
+
});
|
|
611
|
+
// Close to the top, the page before goes in; again after each page that left too little above the reader.
|
|
612
|
+
useEffect(() => {
|
|
613
|
+
const element_3 = scrollRef.current;
|
|
614
|
+
if (element_3 !== null && wantsEarlier(element_3)) {
|
|
615
|
+
loadEarlier();
|
|
616
|
+
}
|
|
617
|
+
}, [rows, loadEarlier, frame.height]);
|
|
618
|
+
// A sub-agent in the thread's place hides the thread, and the strip and the keyboard go with it.
|
|
619
|
+
const onMainAgent = trail.length === 0;
|
|
620
|
+
// A node on a canvas has no strip. It is too narrow to give up a column of its width.
|
|
621
|
+
const showsScrubber = frame.column && onMainAgent && ticks.length >= SCRUBBER_MIN_TICKS;
|
|
622
|
+
const readingWidth = useChatPreferences(preferences => preferences.readingWidth);
|
|
623
|
+
const paddingLeft = threadPaddingLeft(frame.width, showsScrubber, readingMeasure(readingWidth));
|
|
624
|
+
/*
|
|
625
|
+
* Where each message starts, from the virtualizer's measurements (an estimate for a row it never
|
|
626
|
+
* drew), so reading the scroll position costs no layout.
|
|
627
|
+
*/
|
|
628
|
+
const measurements = virtualizer.measurementsCache;
|
|
629
|
+
const starts = ticks.map(tick => measurements[tick.rowIndex]?.start ?? tick.rowIndex * ESTIMATED_ROW_PX);
|
|
630
|
+
const scrollTop = virtualizer.scrollOffset ?? 0;
|
|
631
|
+
const viewport = virtualizer.scrollRect?.height ?? 0;
|
|
632
|
+
const visibleHeight = Math.max(0, viewport - coveredHeight);
|
|
633
|
+
const visibleHeightRef = useRef(visibleHeight);
|
|
634
|
+
visibleHeightRef.current = visibleHeight;
|
|
635
|
+
const inView = showsScrubber ? messagesInView({
|
|
636
|
+
starts,
|
|
637
|
+
scrollTop,
|
|
638
|
+
visibleHeight
|
|
639
|
+
}) : null;
|
|
640
|
+
/*
|
|
641
|
+
* The virtualizer draws again only when its range of rows changes, which a scroll inside one long
|
|
642
|
+
* row never does; the scroll itself redraws the strip once the messages in view are others.
|
|
643
|
+
*/
|
|
644
|
+
const [, redrawStrip] = useReducer(count => count + 1, 0);
|
|
645
|
+
const stripRef = useRef({
|
|
646
|
+
starts,
|
|
647
|
+
inView
|
|
648
|
+
});
|
|
649
|
+
stripRef.current = {
|
|
650
|
+
starts,
|
|
651
|
+
inView
|
|
652
|
+
};
|
|
653
|
+
const followStrip = element_4 => {
|
|
654
|
+
const {
|
|
655
|
+
starts: current_0,
|
|
656
|
+
inView: shown
|
|
657
|
+
} = stripRef.current;
|
|
658
|
+
if (shown === null) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const next = messagesInView({
|
|
662
|
+
starts: current_0,
|
|
663
|
+
scrollTop: element_4.scrollTop,
|
|
664
|
+
visibleHeight: visibleHeightRef.current
|
|
665
|
+
});
|
|
666
|
+
if (next?.first !== shown.first || next?.last !== shown.last) {
|
|
667
|
+
redrawStrip();
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
// One object while what it holds stays, so a find that keys an effect on it does not run it for every scroll.
|
|
671
|
+
const findOptions = useMemo(() => ({
|
|
672
|
+
chatId,
|
|
673
|
+
frame: frameRef,
|
|
674
|
+
thread: threadRef,
|
|
675
|
+
scroller: scrollRef,
|
|
676
|
+
enabled: onMainAgent,
|
|
677
|
+
rows,
|
|
678
|
+
structure: items,
|
|
679
|
+
order,
|
|
680
|
+
firstRowInView: () => {
|
|
681
|
+
const top = scrollRef.current?.scrollTop ?? 0;
|
|
682
|
+
return virtualizer.getVirtualItems().find(virtualRow => virtualRow.end > top)?.index ?? 0;
|
|
683
|
+
},
|
|
684
|
+
openTurn: turns.add,
|
|
685
|
+
openGroup: groups.add,
|
|
686
|
+
openSubagent: subagents.add,
|
|
687
|
+
// A row already on screen stays put; the find scrolls to the match itself once it is drawn.
|
|
688
|
+
scrollToRow: index_1 => {
|
|
689
|
+
const top_0 = scrollRef.current?.scrollTop ?? 0;
|
|
690
|
+
const bottom = top_0 + visibleHeightRef.current;
|
|
691
|
+
if (!virtualizer.getVirtualItems().some(virtualRow_0 => virtualRow_0.index === index_1 && virtualRow_0.end > top_0 && virtualRow_0.start < bottom)) {
|
|
692
|
+
virtualizer.scrollToIndex(index_1, {
|
|
693
|
+
align: 'center'
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
coveredHeight: () => coveredHeightRef.current,
|
|
698
|
+
stopFollowing
|
|
699
|
+
}), [chatId, onMainAgent, rows, items, order, virtualizer, turns.add, groups.add, subagents.add, stopFollowing]);
|
|
700
|
+
const chatFind = useHostTimelineFind(findOptions);
|
|
701
|
+
const searching = chatFind.open;
|
|
702
|
+
// A find covers the whole thread, so every page before this one comes in while it is open.
|
|
703
|
+
useEffect(() => {
|
|
704
|
+
if (searching) {
|
|
705
|
+
loadEarlier();
|
|
706
|
+
}
|
|
707
|
+
}, [searching, loadEarlier]);
|
|
708
|
+
const found = useMemo(() => searching ? ticksWithHits(ticks, chatFind.hitRows) : [], [searching, ticks, chatFind.hitRows]);
|
|
709
|
+
const foundCurrent = chatFind.currentRow === null ? null : tickOfRow(ticks, chatFind.currentRow);
|
|
710
|
+
const jumpTo = index_2 => {
|
|
711
|
+
const tick_0 = ticks[index_2];
|
|
712
|
+
if (!tick_0) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (tick_0.bookmark !== null) {
|
|
716
|
+
setLanding(null);
|
|
717
|
+
setSeeking({
|
|
718
|
+
itemId: tick_0.bookmark.itemId,
|
|
719
|
+
key: keyOf(chatId)
|
|
720
|
+
});
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
showRowAtTop(virtualizer, followRef, tick_0.rowIndex);
|
|
724
|
+
};
|
|
725
|
+
useLayoutEffect(() => {
|
|
726
|
+
if (seeking === null) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (seeking.key !== keyOf(chatId)) {
|
|
730
|
+
setSeeking(null);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (seeking.block) {
|
|
734
|
+
const jump = uiReplyJump(seeking.block, rows, items, turns.ids, cursor !== null);
|
|
735
|
+
if (jump.kind === 'earlier') {
|
|
736
|
+
loadEarlier();
|
|
737
|
+
} else if (jump.kind === 'turn') {
|
|
738
|
+
turns.add(jump.turnId);
|
|
739
|
+
} else {
|
|
404
740
|
setSeeking(null);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
741
|
+
if (jump.kind === 'row') {
|
|
742
|
+
showRowAtTop(virtualizer, followRef, jump.index);
|
|
743
|
+
setLanding({
|
|
744
|
+
target: seeking.block,
|
|
745
|
+
key: seeking.key
|
|
746
|
+
});
|
|
411
747
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
748
|
+
}
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const index_3 = rows.findIndex(row_8 => row_8.id === seeking.itemId);
|
|
752
|
+
if (index_3 !== -1) {
|
|
753
|
+
setSeeking(null);
|
|
754
|
+
showRowAtTop(virtualizer, followRef, index_3);
|
|
755
|
+
if (seeking.options?.flash) {
|
|
756
|
+
useTimelineFlash.getState().flash(seeking.key, seeking.itemId, false);
|
|
757
|
+
}
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
// The thread has not loaded yet, so nothing says the item is missing; the effect below bounds the wait.
|
|
761
|
+
if (items === undefined) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const item = items[seeking.itemId];
|
|
765
|
+
if (item === undefined && cursor !== null) {
|
|
766
|
+
loadEarlier();
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const turnId = item?.turnId ?? null;
|
|
770
|
+
if (turnId !== null && !turns.ids.has(turnId)) {
|
|
771
|
+
turns.add(turnId);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
setSeeking(null);
|
|
775
|
+
seeking.options?.onMissing?.();
|
|
776
|
+
}, [seeking, rows, items, turns, virtualizer, cursor, loadEarlier, keyOf, chatId]);
|
|
777
|
+
const waitingForThread = seeking !== null && items === undefined;
|
|
778
|
+
useEffect(() => {
|
|
779
|
+
if (!waitingForThread) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const timer = window.setTimeout(() => {
|
|
783
|
+
setSeeking(null);
|
|
784
|
+
seeking?.options?.onMissing?.();
|
|
785
|
+
}, JUMP_THREAD_WAIT_MS);
|
|
786
|
+
return () => window.clearTimeout(timer);
|
|
787
|
+
}, [waitingForThread, seeking]);
|
|
788
|
+
useEffect(() => registerItemJumper(keyOf(chatId), (itemId, options) => {
|
|
789
|
+
setLanding(null);
|
|
790
|
+
setSeeking({
|
|
791
|
+
itemId,
|
|
792
|
+
key: keyOf(chatId),
|
|
793
|
+
options
|
|
794
|
+
});
|
|
795
|
+
}), [keyOf, chatId]);
|
|
796
|
+
useLayoutEffect(() => {
|
|
797
|
+
if (!landing || landing.key !== keyOf(chatId)) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
let frame_0 = 0;
|
|
801
|
+
let attempts = 0;
|
|
802
|
+
const reveal = () => {
|
|
803
|
+
const scroller = scrollRef.current;
|
|
804
|
+
if (scroller && revealUiReplyBlock(scroller, landing.target)) {
|
|
805
|
+
useTimelineFlash.getState().flash(keyOf(chatId), landing.target.itemId, false, landing.target);
|
|
419
806
|
setLanding(null);
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
let frame = 0;
|
|
427
|
-
let attempts = 0;
|
|
428
|
-
const reveal = () => {
|
|
429
|
-
const scroller = scrollRef.current;
|
|
430
|
-
if (scroller && revealUiReplyBlock(scroller, landing.target)) {
|
|
431
|
-
useTimelineFlash.getState().flash(keyOf(chatId), landing.target.itemId, false, landing.target);
|
|
432
|
-
setLanding(null);
|
|
433
|
-
}
|
|
434
|
-
else if (++attempts < 30) {
|
|
435
|
-
// Virtual rows mount after scrollToIndex updates the visible range.
|
|
436
|
-
frame = requestAnimationFrame(reveal);
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
setLanding(null);
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
frame = requestAnimationFrame(reveal);
|
|
443
|
-
return () => cancelAnimationFrame(frame);
|
|
444
|
-
}, [landing, keyOf, chatId]);
|
|
445
|
-
useLayoutEffect(() => {
|
|
446
|
-
if (revealing === null) {
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
const { turnId, target } = revealing;
|
|
450
|
-
const changes = target === 'changes' ? rows.findIndex((row) => row.kind === 'changed-files' && row.turnId === turnId) : -1;
|
|
451
|
-
const opening = order?.map((id) => items?.[id]).find((item) => item?.kind === 'user' && item.turnId === turnId);
|
|
452
|
-
const found = changes !== -1 ? changes : rows.findIndex((row) => row.id === opening?.id || row.id === `start-${turnId}` || row.id === `fold-${turnId}`);
|
|
453
|
-
if (found !== -1) {
|
|
454
|
-
setRevealing(null);
|
|
455
|
-
showRowAtTop(virtualizer, followRef, found);
|
|
456
|
-
useTimelineFlash.getState().flash(keyOf(chatId), rows[found].id, changes !== -1);
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
// The turn is before the page this thread holds; a thread that holds it all has no such turn.
|
|
460
|
-
if (cursor === null) {
|
|
461
|
-
setRevealing(null);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
loadEarlier();
|
|
465
|
-
}, [revealing, rows, order, items, virtualizer, cursor, loadEarlier, keyOf, chatId]);
|
|
466
|
-
useEffect(() => registerTurnJumper(keyOf(chatId), (turnId, target) => setRevealing({ turnId, target })), [keyOf, chatId]);
|
|
467
|
-
// The strip is memoized and draws on every scroll, so it gets one callback for its life.
|
|
468
|
-
const jumpRef = useRef(jumpTo);
|
|
469
|
-
jumpRef.current = jumpTo;
|
|
470
|
-
const pick = useCallback((index) => jumpRef.current(index), []);
|
|
471
|
-
// Read when the card asks rather than on every render, so the strip's memo holds while the thread streams.
|
|
472
|
-
const cardChat = useMemo(() => ({
|
|
473
|
-
chatId,
|
|
474
|
-
canFork: (turnId) => {
|
|
475
|
-
if (fork === null) {
|
|
476
|
-
return false;
|
|
477
|
-
}
|
|
478
|
-
const row = useChats.getState().byKey[keyOf(chatId)];
|
|
479
|
-
return forkRefusal(row?.info ?? null, row?.structure[turnId]) === null;
|
|
480
|
-
},
|
|
481
|
-
fork: (turnId) => fork?.(chatId, turnId)
|
|
482
|
-
}), [keyOf, chatId, fork]);
|
|
483
|
-
const stepRef = useRef(() => false);
|
|
484
|
-
stepRef.current = (direction) => {
|
|
485
|
-
if (!onMainAgent) {
|
|
486
|
-
return false;
|
|
487
|
-
}
|
|
488
|
-
const index = stepMessage(starts, (i) => ticks[i].kind === 'person', scrollTop, direction);
|
|
489
|
-
if (index === null) {
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
jumpTo(index);
|
|
493
|
-
return true;
|
|
807
|
+
} else if (++attempts < 30) {
|
|
808
|
+
// Virtual rows mount after scrollToIndex updates the visible range.
|
|
809
|
+
frame_0 = requestAnimationFrame(reveal);
|
|
810
|
+
} else {
|
|
811
|
+
setLanding(null);
|
|
812
|
+
}
|
|
494
813
|
};
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
814
|
+
frame_0 = requestAnimationFrame(reveal);
|
|
815
|
+
return () => cancelAnimationFrame(frame_0);
|
|
816
|
+
}, [landing, keyOf, chatId]);
|
|
817
|
+
useLayoutEffect(() => {
|
|
818
|
+
if (revealing === null) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const {
|
|
822
|
+
turnId: turnId_0,
|
|
823
|
+
target: target_0
|
|
824
|
+
} = revealing;
|
|
825
|
+
const changes = target_0 === 'changes' ? rows.findIndex(row_9 => row_9.kind === 'changed-files' && row_9.turnId === turnId_0) : -1;
|
|
826
|
+
const opening = order?.map(id_0 => items?.[id_0]).find(item_0 => item_0?.kind === 'user' && item_0.turnId === turnId_0);
|
|
827
|
+
const found_0 = changes !== -1 ? changes : rows.findIndex(row_10 => row_10.id === opening?.id || row_10.id === `start-${turnId_0}` || row_10.id === `fold-${turnId_0}`);
|
|
828
|
+
if (found_0 !== -1) {
|
|
829
|
+
setRevealing(null);
|
|
830
|
+
showRowAtTop(virtualizer, followRef, found_0);
|
|
831
|
+
useTimelineFlash.getState().flash(keyOf(chatId), rows[found_0].id, changes !== -1);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
// The turn is before the page this thread holds; a thread that holds it all has no such turn.
|
|
835
|
+
if (cursor === null) {
|
|
836
|
+
setRevealing(null);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
loadEarlier();
|
|
840
|
+
}, [revealing, rows, order, items, virtualizer, cursor, loadEarlier, keyOf, chatId]);
|
|
841
|
+
useEffect(() => registerTurnJumper(keyOf(chatId), (turnId_1, target_1) => setRevealing({
|
|
842
|
+
turnId: turnId_1,
|
|
843
|
+
target: target_1
|
|
844
|
+
})), [keyOf, chatId]);
|
|
845
|
+
// The strip is memoized and draws on every scroll, so it gets one callback for its life.
|
|
846
|
+
const jumpRef = useRef(jumpTo);
|
|
847
|
+
jumpRef.current = jumpTo;
|
|
848
|
+
const pick = useCallback(index_4 => jumpRef.current(index_4), []);
|
|
849
|
+
// Read when the card asks rather than on every render, so the strip's memo holds while the thread streams.
|
|
850
|
+
const cardChat = useMemo(() => ({
|
|
851
|
+
chatId,
|
|
852
|
+
canFork: turnId_2 => {
|
|
853
|
+
if (fork === null) {
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
const row_11 = useChats.getState().byKey[keyOf(chatId)];
|
|
857
|
+
return forkRefusal(row_11?.info ?? null, row_11?.structure[turnId_2]) === null;
|
|
858
|
+
},
|
|
859
|
+
fork: turnId_3 => fork?.(chatId, turnId_3)
|
|
860
|
+
}), [keyOf, chatId, fork]);
|
|
861
|
+
const stepRef = useRef(() => false);
|
|
862
|
+
stepRef.current = direction => {
|
|
863
|
+
if (!onMainAgent) {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
const index_5 = stepMessage(starts, i => ticks[i].kind === 'person', scrollTop, direction);
|
|
867
|
+
if (index_5 === null) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
jumpTo(index_5);
|
|
871
|
+
return true;
|
|
872
|
+
};
|
|
873
|
+
useEffect(() => registerMessageStepper(keyOf(chatId), direction_0 => stepRef.current(direction_0)), [keyOf, chatId]);
|
|
874
|
+
// Read through a ref, so the rows keep one callback for their life and their memo holds.
|
|
875
|
+
const opensRef = useRef({
|
|
876
|
+
rows,
|
|
877
|
+
show
|
|
878
|
+
});
|
|
879
|
+
opensRef.current = {
|
|
880
|
+
rows,
|
|
881
|
+
show
|
|
882
|
+
};
|
|
883
|
+
/* The whole of what a sub-agent did, in the thread's place. Its row only keeps the beginning. */
|
|
884
|
+
const openConversation = useCallback(step => {
|
|
885
|
+
opensRef.current.show(openFromMain(step));
|
|
886
|
+
}, []);
|
|
887
|
+
const addSubagent = subagents.add;
|
|
888
|
+
/* The header of a turn a sub-agent woke. It points at the row that agent worked in, or the one it hangs under. */
|
|
889
|
+
const openSubagent = useCallback(toolUseId => {
|
|
890
|
+
const found_1 = findSubagentBranch(opensRef.current.rows, toolUseId);
|
|
891
|
+
if (!found_1) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
addSubagent(found_1.branch.id);
|
|
895
|
+
showRowAtTop(virtualizer, followRef, found_1.index);
|
|
896
|
+
}, [addSubagent, virtualizer]);
|
|
897
|
+
const flashedRow = flash !== null && flash.key === keyOf(chatId) && !flash.block ? flash.rowId : null;
|
|
898
|
+
return (
|
|
899
|
+
/*#__PURE__*/
|
|
900
|
+
// A file an answer names is relative to the folder this chat runs in, which is a worktree as
|
|
901
|
+
// often as it is the project itself.
|
|
902
|
+
_jsx(FileLinkContext.Provider, {
|
|
903
|
+
value: info?.cwd ?? null,
|
|
904
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
905
|
+
ref: frameRef,
|
|
906
|
+
className: "relative flex min-h-0 grow flex-col",
|
|
907
|
+
style: readingWidthStyle(readingWidth),
|
|
908
|
+
"data-file-cwd": info?.cwd ?? '',
|
|
909
|
+
"data-file-scope-id": scope.id,
|
|
910
|
+
children: [searching && chatFind.bar, /*#__PURE__*/_jsxs(ContextMenu.Root, {
|
|
911
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
912
|
+
ref: scrollRef
|
|
913
|
+
// No CSS scroll-padding here: Chromium counts the sticky composer's caret as hidden behind
|
|
914
|
+
// it and scrolls the thread toward the end on every keystroke.
|
|
915
|
+
,
|
|
916
|
+
className: "chat-scroll min-h-0 grow overflow-auto"
|
|
917
|
+
// The thread keeps its reader in place itself when a page goes in above.
|
|
918
|
+
,
|
|
919
|
+
style: {
|
|
920
|
+
overflowAnchor: 'none'
|
|
921
|
+
},
|
|
922
|
+
onScrollCapture: e => {
|
|
923
|
+
const el = e.currentTarget;
|
|
924
|
+
// Capture the reader's movement before virtualization remeasures rows and shifts the scroll offset.
|
|
925
|
+
const previous = scrollGeometry.current;
|
|
926
|
+
const layoutChanged = previous.height !== el.scrollHeight || previous.viewport !== el.clientHeight;
|
|
927
|
+
const atEnd = el.scrollHeight - el.scrollTop - el.clientHeight < FOLLOW_THRESHOLD_PX;
|
|
928
|
+
if (!layoutChanged && el.scrollTop < previous.top) {
|
|
929
|
+
followRef.current = false;
|
|
930
|
+
} else if (atEnd) {
|
|
931
|
+
followRef.current = true;
|
|
932
|
+
}
|
|
933
|
+
scrollGeometry.current = {
|
|
934
|
+
top: el.scrollTop,
|
|
935
|
+
height: el.scrollHeight,
|
|
936
|
+
viewport: el.clientHeight
|
|
937
|
+
};
|
|
938
|
+
setTimelineAtEnd(chatId, followRef.current);
|
|
939
|
+
if (wantsEarlier(el)) {
|
|
940
|
+
loadEarlier();
|
|
941
|
+
}
|
|
942
|
+
followStrip(el);
|
|
943
|
+
},
|
|
944
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
945
|
+
ref: contentRef,
|
|
946
|
+
className: clsx('relative flex min-h-full flex-col', welcome && 'justify-center'),
|
|
947
|
+
children: [/*#__PURE__*/_jsx(ContextMenu.Trigger, {
|
|
948
|
+
ref: threadRef,
|
|
949
|
+
className: clsx('chat-thread flex flex-col px-4 pt-4', !welcome && 'grow'),
|
|
950
|
+
style: {
|
|
951
|
+
paddingLeft,
|
|
952
|
+
'--chat-wide-inset': `${2 * (showsScrubber ? STRIP_CLEARANCE_PX : paddingLeft) - paddingLeft}px`
|
|
953
|
+
},
|
|
954
|
+
inert: overlay ? true : undefined,
|
|
955
|
+
onContextMenu: e_0 => setTarget(readTimelineTarget(e_0.target, threadRef.current, withCurrentText(rows, fullItems()))),
|
|
956
|
+
children: welcome ? /*#__PURE__*/_jsx(WelcomeGreeting, {
|
|
957
|
+
chatId: chatId
|
|
958
|
+
}) : empty ? /*#__PURE__*/_jsx(EmptyThread, {
|
|
959
|
+
chatId: chatId
|
|
960
|
+
}) : /*#__PURE__*/_jsx("div", {
|
|
961
|
+
className: "relative w-full shrink-0",
|
|
962
|
+
style: {
|
|
963
|
+
height: virtualizer.getTotalSize()
|
|
964
|
+
},
|
|
965
|
+
children: /*#__PURE__*/_jsx(UiReplyNavigationContext.Provider, {
|
|
966
|
+
value: uiNavigation,
|
|
967
|
+
children: /*#__PURE__*/_jsx(FindRevealContext.Provider, {
|
|
968
|
+
value: chatFind.reveal,
|
|
969
|
+
children: virtualizer.getVirtualItems().map(virtualRow_1 => {
|
|
970
|
+
const row_12 = rows[virtualRow_1.index];
|
|
971
|
+
return /*#__PURE__*/_jsx(ThreadRow, {
|
|
972
|
+
row: row_12,
|
|
973
|
+
previous: virtualRow_1.index > 0 ? rows[virtualRow_1.index - 1] : null,
|
|
974
|
+
index: virtualRow_1.index,
|
|
975
|
+
top: virtualRow_1.start,
|
|
976
|
+
measure: virtualizer.measureElement,
|
|
977
|
+
chatId: chatId,
|
|
978
|
+
flashNonce: flash !== null && flashedRow === row_12.id ? flash.nonce : null,
|
|
979
|
+
bookmark: marks.get(row_12.id) ?? null,
|
|
980
|
+
toggleGroup: groups.toggle,
|
|
981
|
+
toggleTurn: turns.toggle,
|
|
982
|
+
toggleSubagent: subagents.toggle,
|
|
983
|
+
openSubagent: openSubagent,
|
|
984
|
+
openConversation: openConversation
|
|
985
|
+
}, row_12.id);
|
|
986
|
+
})
|
|
987
|
+
})
|
|
988
|
+
})
|
|
989
|
+
})
|
|
990
|
+
}), /*#__PURE__*/_jsx(QuoteButton, {
|
|
991
|
+
thread: threadRef,
|
|
992
|
+
frame: contentRef,
|
|
993
|
+
taker: quoteTakerRef
|
|
994
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
995
|
+
ref: composerRef
|
|
996
|
+
// The card is translucent so the thread shows through it, but the pb-3 strip under it must stay opaque.
|
|
997
|
+
,
|
|
998
|
+
className: clsx('relative z-10 shrink-0', composer && 'px-3 pb-3 pt-3 bg-[linear-gradient(to_bottom,transparent_calc(100%-12px),var(--surface)_calc(100%-12px))]'),
|
|
999
|
+
style: {
|
|
1000
|
+
position: stickyComposer ? 'sticky' : 'relative',
|
|
1001
|
+
bottom: 0
|
|
1002
|
+
},
|
|
1003
|
+
children: /*#__PURE__*/_jsx(QuoteTakerContext.Provider, {
|
|
1004
|
+
value: registerQuoteTaker,
|
|
1005
|
+
children: composer
|
|
1006
|
+
})
|
|
1007
|
+
})]
|
|
1008
|
+
})
|
|
1009
|
+
}), /*#__PURE__*/_jsx(TimelineMenuPopup, {
|
|
1010
|
+
target: target,
|
|
1011
|
+
thread: threadRef,
|
|
1012
|
+
chatId: onMainAgent ? chatId : null,
|
|
1013
|
+
column: frame.column
|
|
1014
|
+
})]
|
|
1015
|
+
}), drawsVisuals && /*#__PURE__*/_jsx(VisualDialogs, {
|
|
1016
|
+
chatId: chatId
|
|
1017
|
+
}), loadingEarlier && !overlay && /*#__PURE__*/_jsxs("div", {
|
|
1018
|
+
className: "pointer-events-none absolute inset-x-0 top-3 z-10 flex justify-center",
|
|
1019
|
+
role: "status",
|
|
1020
|
+
children: [/*#__PURE__*/_jsx(Icon, {
|
|
1021
|
+
icon: LoaderCircle,
|
|
1022
|
+
size: 16,
|
|
1023
|
+
className: "animate-spin text-text-faint"
|
|
1024
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
1025
|
+
className: "sr-only",
|
|
1026
|
+
children: t('timeline.loadingEarlier')
|
|
1027
|
+
})]
|
|
1028
|
+
}), showsScrubber && !overlay && /*#__PURE__*/_jsx("div", {
|
|
1029
|
+
className: "absolute top-4",
|
|
1030
|
+
style: {
|
|
1031
|
+
left: STRIP_INSET_PX,
|
|
1032
|
+
width: STRIP_WIDTH_PX,
|
|
1033
|
+
bottom: coveredHeight
|
|
1034
|
+
},
|
|
1035
|
+
children: /*#__PURE__*/_jsx(ErrorBoundary, {
|
|
1036
|
+
label: t('timeline.stripFailed'),
|
|
1037
|
+
resetKeys: [ticks.length],
|
|
1038
|
+
children: /*#__PURE__*/_jsx(Scrubber, {
|
|
1039
|
+
ticks: ticks,
|
|
1040
|
+
firstInView: inView?.first ?? null,
|
|
1041
|
+
lastInView: inView?.last ?? null,
|
|
1042
|
+
onPick: pick,
|
|
1043
|
+
chat: cardChat,
|
|
1044
|
+
found: found,
|
|
1045
|
+
foundCurrent: foundCurrent
|
|
1046
|
+
})
|
|
1047
|
+
})
|
|
1048
|
+
}), overlay && /*#__PURE__*/_jsx("div", {
|
|
1049
|
+
className: "absolute inset-0 z-5 flex flex-col bg-surface",
|
|
1050
|
+
style: {
|
|
1051
|
+
paddingBottom: coveredHeight
|
|
1052
|
+
},
|
|
1053
|
+
children: overlay
|
|
1054
|
+
})]
|
|
1055
|
+
})
|
|
1056
|
+
})
|
|
1057
|
+
);
|
|
1058
|
+
}
|