@ruimte/agents-react 0.0.0 → 0.7.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/LICENSE +110 -0
- package/README.md +142 -1
- package/dist/agents/AccountDot.d.ts +4 -0
- package/dist/agents/AccountDot.js +8 -0
- package/dist/agents/AgentIcon.d.ts +6 -0
- package/dist/agents/AgentIcon.js +20 -0
- package/dist/agents/ProviderLogo.d.ts +8 -0
- package/dist/agents/ProviderLogo.js +8 -0
- package/dist/agents/account-limits.d.ts +7 -0
- package/dist/agents/account-limits.js +26 -0
- package/dist/agents/accounts.d.ts +22 -0
- package/dist/agents/accounts.js +103 -0
- package/dist/agents/model-name.d.ts +6 -0
- package/dist/agents/model-name.js +44 -0
- package/dist/agents/provider-paths.d.ts +2 -0
- package/dist/agents/provider-paths.js +8 -0
- package/dist/agents/status-look.d.ts +13 -0
- package/dist/agents/status-look.js +22 -0
- package/dist/chat/account-choice.d.ts +11 -0
- package/dist/chat/account-choice.js +30 -0
- package/dist/chat/actions.d.ts +6 -0
- package/dist/chat/actions.js +64 -0
- package/dist/chat/activity.d.ts +5 -0
- package/dist/chat/activity.js +4 -0
- package/dist/chat/attachments.d.ts +23 -0
- package/dist/chat/attachments.js +70 -0
- package/dist/chat/bookmarks.d.ts +14 -0
- package/dist/chat/bookmarks.js +73 -0
- package/dist/chat/chat-client.d.ts +53 -0
- package/dist/chat/chat-client.js +232 -0
- package/dist/chat/chat-references.d.ts +5 -0
- package/dist/chat/chat-references.js +5 -0
- package/dist/chat/drafts.d.ts +29 -0
- package/dist/chat/drafts.js +104 -0
- package/dist/chat/forks.d.ts +2 -0
- package/dist/chat/forks.js +26 -0
- package/dist/chat/guards.d.ts +12 -0
- package/dist/chat/guards.js +63 -0
- package/dist/chat/logic/answers.d.ts +3 -0
- package/dist/chat/logic/answers.js +14 -0
- package/dist/chat/logic/bookmarks.d.ts +5 -0
- package/dist/chat/logic/bookmarks.js +31 -0
- package/dist/chat/logic/context-usage.d.ts +12 -0
- package/dist/chat/logic/context-usage.js +20 -0
- package/dist/chat/logic/fork.d.ts +44 -0
- package/dist/chat/logic/fork.js +138 -0
- package/dist/chat/logic/handback.d.ts +2 -0
- package/dist/chat/logic/handback.js +8 -0
- package/dist/chat/logic/json.d.ts +1 -0
- package/dist/chat/logic/json.js +2 -0
- package/dist/chat/logic/limit.d.ts +7 -0
- package/dist/chat/logic/limit.js +34 -0
- package/dist/chat/logic/resume-compaction.d.ts +14 -0
- package/dist/chat/logic/resume-compaction.js +47 -0
- package/dist/chat/logic/scrubber.d.ts +50 -0
- package/dist/chat/logic/scrubber.js +216 -0
- package/dist/chat/logic/timeline-copy.d.ts +4 -0
- package/dist/chat/logic/timeline-copy.js +51 -0
- package/dist/chat/logic/timeline-target.d.ts +12 -0
- package/dist/chat/logic/timeline-target.js +34 -0
- package/dist/chat/logic/timeline.d.ts +110 -0
- package/dist/chat/logic/timeline.js +339 -0
- package/dist/chat/logic/tool-catalog.d.ts +10 -0
- package/dist/chat/logic/tool-catalog.js +40 -0
- package/dist/chat/logic/tools.d.ts +15 -0
- package/dist/chat/logic/tools.js +79 -0
- package/dist/chat/logic/workflow.d.ts +14 -0
- package/dist/chat/logic/workflow.js +47 -0
- package/dist/chat/mentions.d.ts +46 -0
- package/dist/chat/mentions.js +128 -0
- package/dist/chat/persisted-json.d.ts +5 -0
- package/dist/chat/persisted-json.js +24 -0
- package/dist/chat/preferences.d.ts +23 -0
- package/dist/chat/preferences.js +110 -0
- package/dist/chat/quote.d.ts +2 -0
- package/dist/chat/quote.js +25 -0
- package/dist/chat/recent-messages.d.ts +17 -0
- package/dist/chat/recent-messages.js +28 -0
- package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
- package/dist/chat/resume-compaction-dismissals.js +14 -0
- package/dist/chat/runtime-modes.d.ts +4 -0
- package/dist/chat/runtime-modes.js +11 -0
- package/dist/chat/stash.d.ts +24 -0
- package/dist/chat/stash.js +88 -0
- package/dist/chat/subagent-conversation.d.ts +38 -0
- package/dist/chat/subagent-conversation.js +150 -0
- package/dist/chat/subagent-list.d.ts +16 -0
- package/dist/chat/subagent-list.js +76 -0
- package/dist/chat/subagent-support.d.ts +6 -0
- package/dist/chat/subagent-support.js +14 -0
- package/dist/chat/subagent-view.d.ts +33 -0
- package/dist/chat/subagent-view.js +65 -0
- package/dist/chat/thumbnails.d.ts +10 -0
- package/dist/chat/thumbnails.js +87 -0
- package/dist/chat/timeline-scroll.d.ts +16 -0
- package/dist/chat/timeline-scroll.js +100 -0
- package/dist/chat/ui/AccountPill.d.ts +3 -0
- package/dist/chat/ui/AccountPill.js +24 -0
- package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
- package/dist/chat/ui/BookmarkMarker.js +59 -0
- package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
- package/dist/chat/ui/BookmarkSubmenu.js +44 -0
- package/dist/chat/ui/ChatActivity.d.ts +3 -0
- package/dist/chat/ui/ChatActivity.js +87 -0
- package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
- package/dist/chat/ui/ChatReferenceChip.js +17 -0
- package/dist/chat/ui/CodeBlock.d.ts +4 -0
- package/dist/chat/ui/CodeBlock.js +130 -0
- package/dist/chat/ui/Composer.d.ts +14 -0
- package/dist/chat/ui/Composer.js +821 -0
- package/dist/chat/ui/ComposerInput.d.ts +28 -0
- package/dist/chat/ui/ComposerInput.js +118 -0
- package/dist/chat/ui/DiffPool.d.ts +5 -0
- package/dist/chat/ui/DiffPool.js +26 -0
- package/dist/chat/ui/EditDiff.d.ts +4 -0
- package/dist/chat/ui/EditDiff.js +21 -0
- package/dist/chat/ui/FadingWords.d.ts +3 -0
- package/dist/chat/ui/FadingWords.js +5 -0
- package/dist/chat/ui/ImageView.d.ts +6 -0
- package/dist/chat/ui/ImageView.js +85 -0
- package/dist/chat/ui/LimitState.d.ts +8 -0
- package/dist/chat/ui/LimitState.js +117 -0
- package/dist/chat/ui/Markdown.d.ts +18 -0
- package/dist/chat/ui/Markdown.js +158 -0
- package/dist/chat/ui/MessageActions.d.ts +9 -0
- package/dist/chat/ui/MessageActions.js +56 -0
- package/dist/chat/ui/Pickers.d.ts +15 -0
- package/dist/chat/ui/Pickers.js +110 -0
- package/dist/chat/ui/PromptComposer.d.ts +13 -0
- package/dist/chat/ui/PromptComposer.js +48 -0
- package/dist/chat/ui/QuoteButton.d.ts +7 -0
- package/dist/chat/ui/QuoteButton.js +76 -0
- package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
- package/dist/chat/ui/ResumeCompactionDock.js +15 -0
- package/dist/chat/ui/RunSettings.d.ts +25 -0
- package/dist/chat/ui/RunSettings.js +148 -0
- package/dist/chat/ui/Scrubber.d.ts +17 -0
- package/dist/chat/ui/Scrubber.js +121 -0
- package/dist/chat/ui/SubagentControls.d.ts +11 -0
- package/dist/chat/ui/SubagentControls.js +37 -0
- package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
- package/dist/chat/ui/SubagentStopButton.js +36 -0
- package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
- package/dist/chat/ui/SubagentTimeline.js +92 -0
- package/dist/chat/ui/Timeline.d.ts +5 -0
- package/dist/chat/ui/Timeline.js +318 -0
- package/dist/chat/ui/TimelineMenu.d.ts +12 -0
- package/dist/chat/ui/TimelineMenu.js +61 -0
- package/dist/chat/ui/UnifiedDiff.d.ts +8 -0
- package/dist/chat/ui/UnifiedDiff.js +51 -0
- package/dist/chat/ui/UploadThumb.d.ts +4 -0
- package/dist/chat/ui/UploadThumb.js +11 -0
- package/dist/chat/ui/chips.d.ts +4 -0
- package/dist/chat/ui/chips.js +11 -0
- package/dist/chat/ui/code-lines.d.ts +18 -0
- package/dist/chat/ui/code-lines.js +46 -0
- package/dist/chat/ui/code-streaming.d.ts +1 -0
- package/dist/chat/ui/code-streaming.js +3 -0
- package/dist/chat/ui/code-theme.d.ts +3 -0
- package/dist/chat/ui/code-theme.js +10 -0
- package/dist/chat/ui/composer/chips.d.ts +6 -0
- package/dist/chat/ui/composer/chips.js +29 -0
- package/dist/chat/ui/composer/editor.d.ts +11 -0
- package/dist/chat/ui/composer/editor.js +94 -0
- package/dist/chat/ui/composer/keys.d.ts +30 -0
- package/dist/chat/ui/composer/keys.js +98 -0
- package/dist/chat/ui/diff-theme.d.ts +2 -0
- package/dist/chat/ui/diff-theme.js +20 -0
- package/dist/chat/ui/file-links.d.ts +5 -0
- package/dist/chat/ui/file-links.js +18 -0
- package/dist/chat/ui/find-reveal.d.ts +7 -0
- package/dist/chat/ui/find-reveal.js +13 -0
- package/dist/chat/ui/icons.d.ts +4 -0
- package/dist/chat/ui/icons.js +20 -0
- package/dist/chat/ui/markdown-blocks.d.ts +6 -0
- package/dist/chat/ui/markdown-blocks.js +101 -0
- package/dist/chat/ui/quote-selection.d.ts +3 -0
- package/dist/chat/ui/quote-selection.js +44 -0
- package/dist/chat/ui/rehype-chips.d.ts +23 -0
- package/dist/chat/ui/rehype-chips.js +28 -0
- package/dist/chat/ui/rehype-fade.d.ts +23 -0
- package/dist/chat/ui/rehype-fade.js +30 -0
- package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
- package/dist/chat/ui/remark-html-as-text.js +24 -0
- package/dist/chat/ui/reveal.d.ts +13 -0
- package/dist/chat/ui/reveal.js +112 -0
- package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
- package/dist/chat/ui/rows/ForksRow.js +40 -0
- package/dist/chat/ui/rows/MessageRows.d.ts +36 -0
- package/dist/chat/ui/rows/MessageRows.js +190 -0
- package/dist/chat/ui/rows/Rows.d.ts +12 -0
- package/dist/chat/ui/rows/Rows.js +49 -0
- package/dist/chat/ui/rows/SubagentRow.d.ts +15 -0
- package/dist/chat/ui/rows/SubagentRow.js +98 -0
- package/dist/chat/ui/rows/WorkRows.d.ts +46 -0
- package/dist/chat/ui/rows/WorkRows.js +143 -0
- package/dist/chat/ui/rows/WorkflowRow.d.ts +7 -0
- package/dist/chat/ui/rows/WorkflowRow.js +37 -0
- package/dist/chat/ui/rows/row-rhythm.d.ts +3 -0
- package/dist/chat/ui/rows/row-rhythm.js +14 -0
- package/dist/chat/ui/useToggleSet.d.ts +6 -0
- package/dist/chat/ui/useToggleSet.js +25 -0
- package/dist/host.d.ts +167 -0
- package/dist/host.js +41 -0
- package/dist/lazy.d.ts +5 -0
- package/dist/lazy.js +35 -0
- package/dist/locales/en/agent-chat.json +417 -0
- package/dist/locales/en/agent-prompts.json +42 -0
- package/dist/locales/en/agent-providers.json +131 -0
- package/dist/locales/en/agent-usage.json +131 -0
- package/dist/locales/nl/agent-chat.json +417 -0
- package/dist/locales/nl/agent-prompts.json +42 -0
- package/dist/locales/nl/agent-providers.json +131 -0
- package/dist/locales/nl/agent-usage.json +131 -0
- package/dist/locales.d.ts +5 -0
- package/dist/locales.js +18 -0
- package/dist/mounted-registry.d.ts +7 -0
- package/dist/mounted-registry.js +39 -0
- package/dist/port-transport.d.ts +6 -0
- package/dist/port-transport.js +120 -0
- package/dist/prompts/logic/focus.d.ts +1 -0
- package/dist/prompts/logic/focus.js +17 -0
- package/dist/prompts/logic/keys.d.ts +42 -0
- package/dist/prompts/logic/keys.js +84 -0
- package/dist/prompts/logic/prompts.d.ts +34 -0
- package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
- package/dist/prompts/logic/prompts.fixtures.js +73 -0
- package/dist/prompts/logic/prompts.js +22 -0
- package/dist/prompts/logic/subjects.d.ts +49 -0
- package/dist/prompts/logic/subjects.js +56 -0
- package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
- package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
- package/dist/prompts/logic/usePromptSession.d.ts +24 -0
- package/dist/prompts/logic/usePromptSession.js +87 -0
- package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
- package/dist/prompts/ui/ApprovalBody.js +35 -0
- package/dist/prompts/ui/PromptActions.d.ts +26 -0
- package/dist/prompts/ui/PromptActions.js +19 -0
- package/dist/prompts/ui/PromptCard.d.ts +16 -0
- package/dist/prompts/ui/PromptCard.js +50 -0
- package/dist/prompts/ui/PromptView.d.ts +2 -0
- package/dist/prompts/ui/PromptView.js +47 -0
- package/dist/prompts/ui/QuestionBody.d.ts +9 -0
- package/dist/prompts/ui/QuestionBody.js +87 -0
- package/dist/providers/AccountColors.d.ts +5 -0
- package/dist/providers/AccountColors.js +8 -0
- package/dist/providers/AccountDetail.d.ts +12 -0
- package/dist/providers/AccountDetail.js +76 -0
- package/dist/providers/AccountVariables.d.ts +8 -0
- package/dist/providers/AccountVariables.js +48 -0
- package/dist/providers/AddAccountForm.d.ts +10 -0
- package/dist/providers/AddAccountForm.js +58 -0
- package/dist/providers/CliDetail.d.ts +11 -0
- package/dist/providers/CliDetail.js +62 -0
- package/dist/providers/ProvidersPane.d.ts +8 -0
- package/dist/providers/ProvidersPane.js +90 -0
- package/dist/providers/account-actions.d.ts +7 -0
- package/dist/providers/account-actions.js +53 -0
- package/dist/providers/parts.d.ts +9 -0
- package/dist/providers/parts.js +18 -0
- package/dist/providers/provider-abilities.d.ts +2 -0
- package/dist/providers/provider-abilities.js +7 -0
- package/dist/providers/variables.d.ts +13 -0
- package/dist/providers/variables.js +53 -0
- package/dist/scope.d.ts +11 -0
- package/dist/scope.js +10 -0
- package/dist/settings/sections.d.ts +13 -0
- package/dist/settings/sections.js +30 -0
- package/dist/state/chats.d.ts +32 -0
- package/dist/state/chats.js +124 -0
- package/dist/state/provider-accounts.d.ts +17 -0
- package/dist/state/provider-accounts.js +63 -0
- package/dist/state/providers.d.ts +17 -0
- package/dist/state/providers.js +29 -0
- package/dist/state/usage.d.ts +41 -0
- package/dist/state/usage.js +100 -0
- package/dist/theme.css +186 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.js +16 -0
- package/dist/ui/PlainTextarea.d.ts +2 -0
- package/dist/ui/PlainTextarea.js +5 -0
- package/dist/usage/LimitsList.d.ts +18 -0
- package/dist/usage/LimitsList.js +69 -0
- package/dist/usage/UsageBreakdown.d.ts +9 -0
- package/dist/usage/UsageBreakdown.js +69 -0
- package/dist/usage/UsageChart.d.ts +10 -0
- package/dist/usage/UsageChart.js +49 -0
- package/dist/usage/UsageDialog.d.ts +6 -0
- package/dist/usage/UsageDialog.js +15 -0
- package/dist/usage/UsageLimits.d.ts +1 -0
- package/dist/usage/UsageLimits.js +62 -0
- package/dist/usage/UsageLimitsCard.d.ts +4 -0
- package/dist/usage/UsageLimitsCard.js +27 -0
- package/dist/usage/UsagePage.d.ts +7 -0
- package/dist/usage/UsagePage.js +161 -0
- package/dist/usage/UsagePane.d.ts +3 -0
- package/dist/usage/UsagePane.js +24 -0
- package/dist/usage/UsageSummary.d.ts +12 -0
- package/dist/usage/UsageSummary.js +15 -0
- package/dist/usage/UsageTiles.d.ts +7 -0
- package/dist/usage/UsageTiles.js +19 -0
- package/dist/usage/format.d.ts +14 -0
- package/dist/usage/format.js +50 -0
- package/dist/usage/limit-groups.d.ts +22 -0
- package/dist/usage/limit-groups.js +78 -0
- package/dist/usage/limits.d.ts +4 -0
- package/dist/usage/limits.js +58 -0
- package/dist/usage/money.d.ts +1 -0
- package/dist/usage/money.js +13 -0
- package/dist/usage/summary.d.ts +34 -0
- package/dist/usage/summary.js +90 -0
- package/package.json +639 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, memo, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { ContextMenu } from '@base-ui-components/react/context-menu';
|
|
4
|
+
import { PreviewCard } from '@base-ui-components/react/preview-card';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Bookmark, Copy, GitFork } from 'lucide-react';
|
|
8
|
+
import { layoutTicks, messageAt, slotInView, slotOf, tickWidth, TICK_HEIGHT_PX } from '../logic/scrubber';
|
|
9
|
+
import { BookmarkMenuItems } from './TimelineMenu';
|
|
10
|
+
import { formatMoment } from '@ruimte/ui/format/datetime';
|
|
11
|
+
import { MENU_SEPARATOR } from '@ruimte/ui/classes';
|
|
12
|
+
import { copyText } from '@ruimte/ui/clipboard';
|
|
13
|
+
import { cameThroughPortal } from '@ruimte/ui/floating';
|
|
14
|
+
import { Icon } from '@ruimte/ui/Icon';
|
|
15
|
+
const PREVIEW_CHARS = 280;
|
|
16
|
+
const FOUND_WIDTH_PX = 4;
|
|
17
|
+
const timeOf = (createdAt) => formatMoment(createdAt);
|
|
18
|
+
const CARD_ACTIONS = {
|
|
19
|
+
'copy-message': {
|
|
20
|
+
label: 'timeline.menu.copyMessage',
|
|
21
|
+
icon: Copy,
|
|
22
|
+
// A wake carries the machine's label, not anything a person wrote.
|
|
23
|
+
offers: (tick) => tick.kind === 'person' && tick.text !== '',
|
|
24
|
+
run: (tick) => copyText(tick.text)
|
|
25
|
+
},
|
|
26
|
+
fork: {
|
|
27
|
+
label: 'timeline.menu.forkFromHere',
|
|
28
|
+
icon: GitFork,
|
|
29
|
+
offers: (tick, chat) => chat !== null && tick.turnId !== null && chat.canFork(tick.turnId),
|
|
30
|
+
run: (tick, chat) => {
|
|
31
|
+
if (chat !== null && tick.turnId !== null) {
|
|
32
|
+
chat.fork(tick.turnId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const NOTHING_FOUND = [];
|
|
38
|
+
/*
|
|
39
|
+
* The strip at the left edge of a chat view: a tick per message of the person, per wake by tasks and
|
|
40
|
+
* per other message with a bookmark, the ones on screen bright and the rest dimmed, a bookmark in the accent, growing only under the pointer. A single hover card follows the pointer along the strip, anchored at the tick
|
|
41
|
+
* it points at, so a thousand ticks are a thousand spans and not a thousand popups. While a find is
|
|
42
|
+
* open, a short mark at the strip's right edge stands beside every tick with a hit under it.
|
|
43
|
+
*/
|
|
44
|
+
export const Scrubber = memo(function Scrubber({ ticks, firstInView, lastInView, onPick, chat = null, found = NOTHING_FOUND, foundCurrent = null }) {
|
|
45
|
+
const { t } = useTranslation('agent-chat');
|
|
46
|
+
// State rather than a ref. The card's anchor is built during render and has to change when the strip does.
|
|
47
|
+
const [strip, setStrip] = useState(null);
|
|
48
|
+
const [height, setHeight] = useState(0);
|
|
49
|
+
// The message the card is about; it stays while the card closes, so the card never empties as it fades.
|
|
50
|
+
const [hovered, setHovered] = useState(null);
|
|
51
|
+
// Apart from `hovered`, which outlives the pointer for the card's sake. The ticks shrink back the moment it leaves.
|
|
52
|
+
const [pointing, setPointing] = useState(false);
|
|
53
|
+
const [cardOpen, setCardOpen] = useState(false);
|
|
54
|
+
// The message a right-click was on, kept apart from `hovered`, which moves on with the pointer while the menu is up.
|
|
55
|
+
const [menuIndex, setMenuIndex] = useState(null);
|
|
56
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (strip === null || typeof ResizeObserver === 'undefined') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const observer = new ResizeObserver(() => setHeight(strip.offsetHeight));
|
|
62
|
+
observer.observe(strip);
|
|
63
|
+
return () => observer.disconnect();
|
|
64
|
+
}, [strip]);
|
|
65
|
+
const kinds = useMemo(() => ticks.map((tick) => tick.kind), [ticks]);
|
|
66
|
+
const marked = useMemo(() => ticks.map((tick) => tick.bookmark !== null), [ticks]);
|
|
67
|
+
const layout = useMemo(() => layoutTicks(kinds, height, marked, found), [kinds, height, marked, found]);
|
|
68
|
+
const currentSlot = foundCurrent === null ? null : slotOf(layout, foundCurrent);
|
|
69
|
+
const inView = firstInView === null || lastInView === null ? null : { first: firstInView, last: lastInView };
|
|
70
|
+
const hoveredTick = hovered === null ? null : (ticks[hovered] ?? null);
|
|
71
|
+
const hoveredSlot = hovered === null ? null : slotOf(layout, hovered);
|
|
72
|
+
const anchorY = hoveredSlot === null ? null : layout.slots[hoveredSlot].y;
|
|
73
|
+
const anchor = useMemo(() => {
|
|
74
|
+
const element = strip;
|
|
75
|
+
if (anchorY === null || element === null) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
contextElement: element,
|
|
80
|
+
getBoundingClientRect: () => {
|
|
81
|
+
const rect = element.getBoundingClientRect();
|
|
82
|
+
return DOMRect.fromRect({ x: rect.left, y: rect.top + anchorY + TICK_HEIGHT_PX / 2, width: rect.width, height: 0 });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}, [anchorY, strip]);
|
|
86
|
+
const pointAt = (e) => messageAt(layout, e.clientY - e.currentTarget.getBoundingClientRect().top);
|
|
87
|
+
const actionsFor = (tick) => tick === null ? [] : Object.values(CARD_ACTIONS).filter((action) => action.offers(tick, chat));
|
|
88
|
+
const actions = actionsFor(hoveredTick);
|
|
89
|
+
/* The message a bookmark goes on: the one it is on already, or the person's own. A wake is the machine's and holds none. */
|
|
90
|
+
const markableOf = (tick) => tick === null || chat === null ? null : (tick.bookmark?.itemId ?? (tick.kind === 'person' ? tick.id : null));
|
|
91
|
+
const menuTick = menuIndex === null ? null : (ticks[menuIndex] ?? null);
|
|
92
|
+
const menuMarkable = markableOf(menuTick);
|
|
93
|
+
const onContextMenu = (e) => {
|
|
94
|
+
// The hover card is a child here as well, and a click in it is about no tick.
|
|
95
|
+
const index = strip === null || cameThroughPortal(e) ? null : messageAt(layout, e.clientY - strip.getBoundingClientRect().top);
|
|
96
|
+
const tick = index === null ? null : (ticks[index] ?? null);
|
|
97
|
+
if (tick === null || (actionsFor(tick).length === 0 && markableOf(tick) === null)) {
|
|
98
|
+
e.preventBaseUIHandler();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
setMenuIndex(index);
|
|
102
|
+
};
|
|
103
|
+
return (_jsxs(ContextMenu.Root, { onOpenChange: setMenuOpen, children: [_jsx(ContextMenu.Trigger, { className: "h-full w-full", onContextMenu: onContextMenu, children: _jsxs(PreviewCard.Root, { open: cardOpen && !menuOpen, onOpenChange: setCardOpen, children: [_jsx(PreviewCard.Trigger, { delay: 150, closeDelay: 150, render: _jsx("div", { ref: setStrip, role: "navigation", "aria-label": t('scrubber.strip'), className: "relative h-full w-full cursor-pointer select-none", onPointerMove: (e) => {
|
|
104
|
+
setPointing(true);
|
|
105
|
+
const index = pointAt(e);
|
|
106
|
+
if (index !== hovered) {
|
|
107
|
+
setHovered(index);
|
|
108
|
+
}
|
|
109
|
+
}, onPointerLeave: () => setPointing(false), onClick: (e) => {
|
|
110
|
+
const index = pointAt(e);
|
|
111
|
+
if (index !== null) {
|
|
112
|
+
onPick(index);
|
|
113
|
+
}
|
|
114
|
+
} }), children: layout.slots.map((slot, index) => {
|
|
115
|
+
const distance = pointing && hoveredSlot !== null ? Math.abs(index - hoveredSlot) : null;
|
|
116
|
+
// A bookmark is a place to find again from anywhere, so it never dims with the rest.
|
|
117
|
+
const bright = slot.marked || slotInView(slot, inView) || distance === 0;
|
|
118
|
+
return (_jsxs(Fragment, { children: [_jsx("span", { "aria-hidden": true, className: clsx('absolute left-1 rounded-full transition-[width,opacity] duration-100 ease-out motion-reduce:transition-none', slot.marked ? 'bg-accent' : slot.kind === 'wake' ? 'bg-text-muted' : 'bg-text', bright ? 'opacity-100' : 'opacity-30'), style: { top: slot.y, height: TICK_HEIGHT_PX, width: tickWidth(slot.kind, distance) } }), slot.found && (_jsx("span", { "aria-hidden": true, className: clsx('absolute right-0 rounded-full bg-find-current', index === currentSlot ? 'opacity-100' : 'opacity-50'), style: { top: slot.y, height: TICK_HEIGHT_PX, width: FOUND_WIDTH_PX } }))] }, slot.first));
|
|
119
|
+
}) }), _jsx(PreviewCard.Portal, { children: _jsx(PreviewCard.Positioner, { anchor: anchor, side: "right", align: "center", sideOffset: 4, className: "z-(--z-popup)", children: _jsx(PreviewCard.Popup, { className: "menu-popup w-72", children: hoveredTick !== null && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "px-2.5 pt-1.5 pb-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3 text-xs text-text-faint", children: [hoveredTick.bookmark === null ? (_jsx("span", { children: hoveredTick.kind === 'person' ? t('rows.user.heading') : t('scrubber.tasks') })) : (_jsxs("span", { className: "flex min-w-0 items-center gap-1 self-center text-text-muted", children: [_jsx(Icon, { icon: Bookmark, size: 12, className: "shrink-0 text-accent" }), _jsx("span", { className: "truncate", children: hoveredTick.bookmark.name ?? t('bookmarks.unnamed') })] })), _jsx("time", { className: "tabular-nums", children: timeOf(hoveredTick.createdAt) })] }), _jsx("p", { className: "mt-1 line-clamp-4 text-sm break-words whitespace-pre-line text-text", children: hoveredTick.text.slice(0, PREVIEW_CHARS) || t('pickers.stash.noText') })] }), actions.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: MENU_SEPARATOR }), actions.map((action) => (_jsxs("button", { type: "button", className: "menu-item w-full hover:bg-surface-hover", onClick: () => action.run(hoveredTick, chat), children: [_jsx(Icon, { icon: action.icon, size: 14 }), " ", t(action.label)] }, action.label)))] }))] })) }) }) })] }) }), _jsx(ContextMenu.Portal, { children: _jsx(ContextMenu.Positioner, { className: "z-(--z-popup)", children: _jsxs(ContextMenu.Popup, { className: "menu-popup", children: [menuTick !== null &&
|
|
120
|
+
actionsFor(menuTick).map((action) => (_jsxs(ContextMenu.Item, { className: "menu-item", onClick: () => action.run(menuTick, chat), children: [_jsx(Icon, { icon: action.icon, size: 14 }), " ", t(action.label)] }, action.label))), menuTick !== null && actionsFor(menuTick).length > 0 && menuMarkable !== null && _jsx(ContextMenu.Separator, { className: MENU_SEPARATOR }), chat !== null && menuIndex !== null && menuMarkable !== null && (_jsx(BookmarkMenuItems, { chatId: chat.chatId, itemId: menuMarkable, onName: () => onPick(menuIndex) }))] }) }) })] }));
|
|
121
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare function SubagentTitleCrumb({ chatId, className, children }: {
|
|
3
|
+
chatId: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
export declare function SubagentBreadcrumb({ chatId, title, className }: {
|
|
8
|
+
chatId: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { ChevronRight, X } from 'lucide-react';
|
|
6
|
+
import { breadcrumbOf, MAIN_AGENT, trailTo, useOpenableSubagents, useSubagentTrail } from '../subagent-view';
|
|
7
|
+
import { SubagentStopButton } from './SubagentStopButton';
|
|
8
|
+
import { Icon } from '@ruimte/ui/Icon';
|
|
9
|
+
import { Tooltip } from '@ruimte/ui/Tooltip';
|
|
10
|
+
const CRUMB_LINK = 'truncate text-text-muted hover:text-text';
|
|
11
|
+
/*
|
|
12
|
+
* The chat's own title where the bar draws it. While a sub-agent stands in the chat's place it is the first crumb of the way down, so pressing it goes back to the main agent.
|
|
13
|
+
*/
|
|
14
|
+
export function SubagentTitleCrumb({ chatId, className, children }) {
|
|
15
|
+
const { trail, show } = useSubagentTrail(chatId);
|
|
16
|
+
if (trail.length === 0) {
|
|
17
|
+
return _jsx(_Fragment, { children: children });
|
|
18
|
+
}
|
|
19
|
+
return (_jsx("button", { type: "button", className: clsx('flex min-w-0 items-center', className), onClick: () => show(MAIN_AGENT), children: children }));
|
|
20
|
+
}
|
|
21
|
+
/*
|
|
22
|
+
* The way down after the chat's title, each step a way back up, and a close that goes straight back
|
|
23
|
+
* to the main agent. A bar that does not draw the title itself passes it, and it opens the way.
|
|
24
|
+
* Nothing while the main agent itself is on screen.
|
|
25
|
+
*/
|
|
26
|
+
export function SubagentBreadcrumb({ chatId, title, className }) {
|
|
27
|
+
const { t } = useTranslation('agent-chat');
|
|
28
|
+
const { trail, show } = useSubagentTrail(chatId);
|
|
29
|
+
const subagents = useOpenableSubagents(chatId);
|
|
30
|
+
if (trail.length === 0) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const steps = breadcrumbOf(trail);
|
|
34
|
+
// Only a row of the chat's own thread can be stopped; a grandchild belongs to the sub-agent that opened it.
|
|
35
|
+
const shown = trail.length === 1 ? subagents.find((item) => item.toolUseId === trail[0].toolUseId) : undefined;
|
|
36
|
+
return (_jsxs("nav", { "aria-label": t('subagents.title'), className: clsx('flex min-w-0 items-center gap-1 text-xs', className), children: [title !== undefined && (_jsx("button", { type: "button", className: clsx(CRUMB_LINK, 'min-w-0 shrink'), onClick: () => show(MAIN_AGENT), children: title })), steps.map((step) => (_jsxs(Fragment, { children: [_jsx(Icon, { icon: ChevronRight, size: 12, className: "shrink-0 text-text-faint" }), step.current ? (_jsx("span", { "aria-current": "page", className: "min-w-0 truncate text-text", children: step.label })) : (_jsx("button", { type: "button", className: clsx(CRUMB_LINK, 'min-w-0 shrink'), onClick: () => show(trailTo(trail, step.depth)), children: step.label }))] }, step.depth))), shown !== undefined && _jsx(SubagentStopButton, { chatId: chatId, item: shown, size: "sm" }), _jsx(Tooltip, { label: t('subagents.backToMain'), name: true, children: _jsx("button", { type: "button", className: "icon-btn icon-btn-sm", onClick: () => show(MAIN_AGENT), children: _jsx(Icon, { icon: X, size: 14 }) }) })] }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChatSubagentItem } from '@ruimte/agent-contracts';
|
|
2
|
+
export declare function SubagentStopButton({ chatId, item, size, className }: {
|
|
3
|
+
chatId: string;
|
|
4
|
+
item: ChatSubagentItem;
|
|
5
|
+
size?: 'sm' | 'xs';
|
|
6
|
+
className?: string;
|
|
7
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Square } from 'lucide-react';
|
|
5
|
+
import { chatHost } from '../../host';
|
|
6
|
+
import { useChatScope } from '../../scope';
|
|
7
|
+
import { useChatRow } from '../../state/chats';
|
|
8
|
+
import { useChatActions } from '../actions';
|
|
9
|
+
import { stopLabel, stopOf, subagentTitle } from '../subagent-list';
|
|
10
|
+
import { Icon } from '@ruimte/ui/Icon';
|
|
11
|
+
import { Tooltip } from '@ruimte/ui/Tooltip';
|
|
12
|
+
/* Stops one active sub-agent of a chat, or says nothing when stopping it is not on offer. */
|
|
13
|
+
export function SubagentStopButton({ chatId, item, size = 'xs', className }) {
|
|
14
|
+
const { t } = useTranslation('agent-chat');
|
|
15
|
+
const { id } = useChatScope();
|
|
16
|
+
const actions = useChatActions();
|
|
17
|
+
const turnRunning = useChatRow(chatId, (row) => (row?.info.activeTurnId ?? null) !== null);
|
|
18
|
+
const stop = stopOf(item, turnRunning);
|
|
19
|
+
if (stop === null) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const send = () => {
|
|
23
|
+
void actions.stopSubagent(chatId, item.toolUseId).catch((e) => {
|
|
24
|
+
const description = e instanceof Error ? e.message : t('subagents.noAnswer');
|
|
25
|
+
chatHost().notify({ kind: 'error', title: t('subagents.stopFailed'), description });
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const run = () => {
|
|
29
|
+
if (stop === 'task' && item.childId !== undefined) {
|
|
30
|
+
chatHost().confirm.stopTask(id, item.childId, subagentTitle(item), send);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
send();
|
|
34
|
+
};
|
|
35
|
+
return (_jsx(Tooltip, { label: stopLabel(stop), name: true, children: _jsx("button", { type: "button", className: clsx('icon-btn', size === 'sm' ? 'icon-btn-sm' : 'icon-btn-xs', className), onClick: run, children: _jsx(Icon, { icon: Square, size: size === 'sm' ? 14 : 12 }) }) }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { ContextMenu } from '@base-ui-components/react/context-menu';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Bot } from 'lucide-react';
|
|
6
|
+
import { deriveTimelineRows } from '../logic/timeline';
|
|
7
|
+
import { INITIAL_CONVERSATION, SubagentConversation } from '../subagent-conversation';
|
|
8
|
+
import { useSubagentSupport } from '../subagent-support';
|
|
9
|
+
import { EMPTY_TARGET, readTimelineTarget } from '../logic/timeline-target';
|
|
10
|
+
import { openBelow, useSubagentTrail } from '../subagent-view';
|
|
11
|
+
import { Row } from './rows/Rows';
|
|
12
|
+
import { TimelineMenuPopup } from './TimelineMenu';
|
|
13
|
+
import { FOLLOW_THRESHOLD_PX, rowRhythm } from './rows/row-rhythm';
|
|
14
|
+
import { useToggleSet } from './useToggleSet';
|
|
15
|
+
import { FileLinkContext } from './file-links';
|
|
16
|
+
import { useChatRow } from '../../state/chats';
|
|
17
|
+
import { useChatScope } from '../../scope';
|
|
18
|
+
import { Button } from '@ruimte/ui/Button';
|
|
19
|
+
import { EmptyState } from '@ruimte/ui/EmptyState';
|
|
20
|
+
const NO_TURNS = new Set();
|
|
21
|
+
/*
|
|
22
|
+
* A sub-agent's whole conversation, read back from the host and grown while it is on screen. It
|
|
23
|
+
* stands in the chat's own place, read-only, and a sub-agent it opened goes one level further down.
|
|
24
|
+
*/
|
|
25
|
+
export function SubagentTimeline({ chatId, toolUseId }) {
|
|
26
|
+
const { t } = useTranslation('agent-chat');
|
|
27
|
+
const scope = useChatScope();
|
|
28
|
+
const { trail, show } = useSubagentTrail(chatId);
|
|
29
|
+
const [state, setState] = useState(INITIAL_CONVERSATION);
|
|
30
|
+
const controller = useRef(null);
|
|
31
|
+
const scrollRef = useRef(null);
|
|
32
|
+
const threadRef = useRef(null);
|
|
33
|
+
const [target, setTarget] = useState(EMPTY_TARGET);
|
|
34
|
+
const followRef = useRef(true);
|
|
35
|
+
const stopFollowing = useCallback(() => {
|
|
36
|
+
followRef.current = false;
|
|
37
|
+
}, []);
|
|
38
|
+
const groups = useToggleSet(stopFollowing);
|
|
39
|
+
const subagents = useToggleSet(stopFollowing);
|
|
40
|
+
// The height before older rows went in above, so the rows being read stay where they were.
|
|
41
|
+
const heightBefore = useRef(null);
|
|
42
|
+
const cwd = useChatRow(chatId, (row) => row?.info.cwd ?? null);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const conversation = new SubagentConversation(scope.transport, chatId, toolUseId, setState);
|
|
45
|
+
controller.current = conversation;
|
|
46
|
+
void conversation.start();
|
|
47
|
+
return () => {
|
|
48
|
+
conversation.dispose();
|
|
49
|
+
controller.current = null;
|
|
50
|
+
};
|
|
51
|
+
}, [scope, chatId, toolUseId]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (state.unsupported) {
|
|
54
|
+
useSubagentSupport.getState().markUnsupported(scope.id);
|
|
55
|
+
}
|
|
56
|
+
}, [state.unsupported, scope]);
|
|
57
|
+
const rows = useMemo(() => deriveTimelineRows(state.items, { expandedGroups: groups.ids, expandedTurns: NO_TURNS, expandedSubagents: subagents.ids, activeTurnId: null }), [state.items, groups.ids, subagents.ids]);
|
|
58
|
+
useLayoutEffect(() => {
|
|
59
|
+
const element = scrollRef.current;
|
|
60
|
+
if (element === null) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (heightBefore.current !== null) {
|
|
64
|
+
element.scrollTop += element.scrollHeight - heightBefore.current;
|
|
65
|
+
heightBefore.current = null;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (followRef.current) {
|
|
69
|
+
element.scrollTop = element.scrollHeight;
|
|
70
|
+
}
|
|
71
|
+
}, [rows, state.live]);
|
|
72
|
+
if (state.status === 'loading') {
|
|
73
|
+
return _jsx("div", { className: "chat-column-content px-4 pt-4 text-xs text-text-faint", children: t('subagents.loading') });
|
|
74
|
+
}
|
|
75
|
+
if (state.status === 'failed') {
|
|
76
|
+
return _jsx(EmptyState, { icon: Bot, children: state.unsupported ? t('subagents.unsupported') : (state.error ?? t('subagents.unreadable')) });
|
|
77
|
+
}
|
|
78
|
+
const openChild = (step) => {
|
|
79
|
+
show(openBelow(trail, step));
|
|
80
|
+
};
|
|
81
|
+
const loadEarlier = () => {
|
|
82
|
+
heightBefore.current = scrollRef.current?.scrollHeight ?? null;
|
|
83
|
+
void controller.current?.loadEarlier();
|
|
84
|
+
};
|
|
85
|
+
return (_jsx(FileLinkContext.Provider, { value: cwd, children: _jsx("div", { ref: scrollRef, className: "chat-thread h-full min-h-0 overflow-auto px-4 pt-4 pb-3", onScroll: (event) => {
|
|
86
|
+
const element = event.currentTarget;
|
|
87
|
+
followRef.current = element.scrollHeight - element.scrollTop - element.clientHeight < FOLLOW_THRESHOLD_PX;
|
|
88
|
+
}, children: _jsxs(ContextMenu.Root, { children: [_jsxs(ContextMenu.Trigger, { ref: threadRef, className: "chat-column-content", onContextMenu: (event) => setTarget(readTimelineTarget(event.target, threadRef.current, rows)), children: [state.cursor !== null && (_jsx("div", { className: "flex justify-center pb-2", children: _jsx(Button, { size: "sm", disabled: state.loadingEarlier, onClick: loadEarlier, children: state.loadingEarlier ? t('subagents.loadingEarlier') : t('subagents.loadEarlier') }) })), rows.length === 0 && !state.live && _jsx(EmptyState, { icon: Bot, children: t('rows.subagent.nothingYet') }), rows.map((row, index) => {
|
|
89
|
+
const previous = index > 0 ? rows[index - 1] : null;
|
|
90
|
+
return (_jsx("div", { "data-item-id": row.id, className: rowRhythm(row, previous), children: _jsx(Row, { row: row, chatId: chatId, toggleGroup: groups.toggle, toggleTurn: () => undefined, toggleSubagent: subagents.toggle, openSubagent: () => undefined, openConversation: openChild }) }, row.id));
|
|
91
|
+
}), state.live && _jsx("div", { className: "chat-live-text pt-1 text-xs", children: t('subagents.working') })] }), _jsx(TimelineMenuPopup, { target: target, thread: threadRef })] }) }) }));
|
|
92
|
+
}
|