@adecore/agents-react 0.24.1 → 0.25.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/chat/index.d.ts +4 -7
- package/dist/chat/index.js +4 -7
- package/dist/chat/logic/continue-after-stop.d.ts +3 -0
- package/dist/chat/logic/continue-after-stop.js +28 -0
- package/dist/chat/logic/thread-cards.d.ts +0 -1
- package/dist/chat/logic/thread-cards.js +0 -4
- package/dist/chat/preferences.d.ts +0 -1
- package/dist/chat/preferences.js +0 -3
- package/dist/chat/stash.d.ts +6 -0
- package/dist/chat/stash.js +13 -0
- package/dist/chat/ui/Composer.js +26 -5
- package/dist/chat/ui/EditDiff.js +4 -3
- package/dist/chat/ui/Pickers.js +1 -1
- package/dist/chat/ui/UnifiedDiff.js +4 -3
- package/dist/chat/ui/composer/ComposerControls.d.ts +1 -0
- package/dist/chat/ui/composer/ComposerControls.js +3 -3
- package/dist/chat/ui/composer/use-composer-submit.d.ts +3 -1
- package/dist/chat/ui/composer/use-composer-submit.js +22 -4
- package/dist/chat/ui/composer/use-continuable-turn.d.ts +2 -0
- package/dist/chat/ui/composer/use-continuable-turn.js +12 -0
- package/dist/chat/ui/composer/use-notice.d.ts +3 -1
- package/dist/chat/ui/composer/use-notice.js +8 -6
- package/dist/chat/ui/diff/ReviewDiff.js +2 -2
- package/dist/chat/ui/index.d.ts +9 -28
- package/dist/chat/ui/index.js +8 -27
- package/dist/chat/ui/intelligent-ui/UiReply.js +20 -2
- package/dist/chat/ui/intelligent-ui/node-text.d.ts +3 -1
- package/dist/chat/ui/intelligent-ui/node-text.js +32 -0
- package/dist/chat/ui/intelligent-ui/registry.js +15 -5
- package/dist/chat/ui/intelligent-ui/render-context.d.ts +6 -0
- package/dist/chat/ui/intelligent-ui/renderers/base-land.d.ts +1 -0
- package/dist/chat/ui/intelligent-ui/renderers/base-land.js +6 -0
- package/dist/chat/ui/intelligent-ui/renderers/binding.d.ts +6 -0
- package/dist/chat/ui/intelligent-ui/renderers/binding.js +10 -0
- package/dist/chat/ui/intelligent-ui/renderers/content.d.ts +2 -0
- package/dist/chat/ui/intelligent-ui/renderers/content.js +68 -6
- package/dist/chat/ui/intelligent-ui/renderers/inputs.d.ts +2 -0
- package/dist/chat/ui/intelligent-ui/renderers/inputs.js +40 -13
- package/dist/chat/ui/intelligent-ui/renderers/map-slots.d.ts +20 -0
- package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +87 -0
- package/dist/chat/ui/intelligent-ui/renderers/map-style.d.ts +18 -0
- package/dist/chat/ui/intelligent-ui/renderers/map-style.js +72 -0
- package/dist/chat/ui/intelligent-ui/renderers/map-view.d.ts +15 -0
- package/dist/chat/ui/intelligent-ui/renderers/map-view.js +213 -0
- package/dist/chat/ui/intelligent-ui/renderers/map.d.ts +3 -0
- package/dist/chat/ui/intelligent-ui/renderers/map.js +113 -0
- package/dist/chat/ui/intelligent-ui/renderers/place-point.d.ts +3 -0
- package/dist/chat/ui/intelligent-ui/renderers/place-point.js +13 -0
- package/dist/chat/ui/intelligent-ui/renderers/text.d.ts +2 -0
- package/dist/chat/ui/intelligent-ui/renderers/text.js +14 -1
- package/dist/chat/ui/intelligent-ui/use-in-view.d.ts +1 -0
- package/dist/chat/ui/intelligent-ui/use-in-view.js +17 -0
- package/dist/chat/ui/rows/WorkRows.js +3 -3
- package/dist/host.d.ts +16 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/lazy.js +5 -7
- package/dist/locales/en/agent-chat.d.ts +47 -0
- package/dist/locales/en/agent-chat.js +48 -1
- package/dist/locales/nl/agent-chat.d.ts +47 -0
- package/dist/locales/nl/agent-chat.js +48 -1
- package/dist/prompts/index.d.ts +3 -5
- package/dist/prompts/index.js +3 -5
- package/dist/providers/index.d.ts +3 -9
- package/dist/providers/index.js +3 -9
- package/dist/state/index.d.ts +2 -2
- package/dist/state/index.js +2 -2
- package/dist/state/usage.d.ts +0 -1
- package/dist/state/usage.js +0 -3
- package/dist/theme.css +10 -0
- package/package.json +9 -8
- package/src/chat/index.ts +2 -23
- package/src/chat/logic/continue-after-stop.ts +32 -0
- package/src/chat/logic/thread-cards.ts +0 -5
- package/src/chat/preferences.ts +0 -4
- package/src/chat/stash.ts +17 -0
- package/src/chat/ui/Composer.tsx +33 -5
- package/src/chat/ui/EditDiff.tsx +6 -5
- package/src/chat/ui/Pickers.tsx +1 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -5
- package/src/chat/ui/composer/ComposerControls.tsx +13 -3
- package/src/chat/ui/composer/use-composer-submit.ts +25 -5
- package/src/chat/ui/composer/use-continuable-turn.ts +14 -0
- package/src/chat/ui/composer/use-notice.ts +14 -7
- package/src/chat/ui/diff/ReviewDiff.tsx +3 -3
- package/src/chat/ui/index.ts +9 -49
- package/src/chat/ui/intelligent-ui/UiReply.tsx +24 -1
- package/src/chat/ui/intelligent-ui/node-text.ts +34 -1
- package/src/chat/ui/intelligent-ui/registry.ts +33 -5
- package/src/chat/ui/intelligent-ui/render-context.ts +14 -0
- package/src/chat/ui/intelligent-ui/renderers/base-land.ts +7 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +14 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +144 -9
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +79 -17
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +105 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +96 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +251 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +226 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +16 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +28 -1
- package/src/chat/ui/intelligent-ui/use-in-view.ts +18 -0
- package/src/chat/ui/rows/WorkRows.tsx +3 -3
- package/src/host.ts +28 -0
- package/src/index.ts +2 -12
- package/src/lazy.tsx +5 -7
- package/src/locales/en/agent-chat.ts +48 -1
- package/src/locales/nl/agent-chat.ts +48 -1
- package/src/prompts/index.ts +3 -18
- package/src/providers/index.ts +3 -9
- package/src/state/index.ts +1 -16
- package/src/state/usage.ts +0 -4
- package/src/stylesheets.d.ts +2 -0
- package/src/theme.css +10 -0
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { useEffect, useRef, type RefObject } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import { useChatHost } from '../../../host';
|
|
3
4
|
import { useChatScope } from '../../../scope';
|
|
4
5
|
import { checkAttachmentLimits, uploadBytes } from '../../attachments';
|
|
5
6
|
import type { ChatSendExtras } from '../../chat-client';
|
|
6
7
|
import { registerComposerSender, type ComposerSend } from '../../composer-send';
|
|
7
8
|
import { isEmptyDraft } from '../../drafts';
|
|
9
|
+
import { CONTINUE_PROMPT } from '../../logic/continue-after-stop';
|
|
8
10
|
import type { PromptGuard } from '../../guards';
|
|
9
11
|
import { presentMentions, presentSkills } from '../../mentions';
|
|
10
12
|
import { withQuote } from '../../quote';
|
|
11
|
-
import { stashDraft, useStash, type StashedPrompt } from '../../stash';
|
|
13
|
+
import { restorableChats, stashDraft, useStash, type StashedPrompt } from '../../stash';
|
|
12
14
|
import { scrollTimelineToEnd } from '../../timeline-scroll';
|
|
13
15
|
import type { ComposerInputHandle } from '../ComposerInput';
|
|
14
16
|
import type { ChatControls } from './use-chat-controls';
|
|
17
|
+
import type { NoticeTone } from './use-notice';
|
|
15
18
|
import type { ComposerDraft } from './use-composer-draft';
|
|
16
19
|
import type { ComposerMenus } from './use-composer-menus';
|
|
17
20
|
import type { PromptHistory } from './use-prompt-history';
|
|
@@ -31,7 +34,7 @@ interface SubmitInput {
|
|
|
31
34
|
history: PromptHistory;
|
|
32
35
|
hold: ComposerPromptHold;
|
|
33
36
|
inputRef: RefObject<ComposerInputHandle | null>;
|
|
34
|
-
setNotice(notice: string): void;
|
|
37
|
+
setNotice(notice: string, tone?: NoticeTone): void;
|
|
35
38
|
onSend(text: string, extras: ChatSendExtras): void | Promise<boolean>;
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -51,9 +54,11 @@ export function useComposerSubmit({
|
|
|
51
54
|
setNotice,
|
|
52
55
|
onSend
|
|
53
56
|
}: SubmitInput) {
|
|
57
|
+
const { t } = useTranslation('agent-chat');
|
|
54
58
|
const host = useChatHost();
|
|
55
59
|
const scope = useChatScope();
|
|
56
60
|
const { draft, setDraft } = box;
|
|
61
|
+
const referable = host.useReferableChats();
|
|
57
62
|
|
|
58
63
|
/* Opens the host's new chat once the host took the message; a host that answers nothing took it. */
|
|
59
64
|
const afterSend = (sent: void | Promise<boolean>, andNew: boolean): void => {
|
|
@@ -127,23 +132,38 @@ export function useComposerSubmit({
|
|
|
127
132
|
useEffect(() => registerComposerSender(scope.keyOf(chatId), (how) => submitRef.current(how)), [scope, chatId]);
|
|
128
133
|
|
|
129
134
|
/*
|
|
130
|
-
* A stashed prompt comes back as text, mentions and
|
|
131
|
-
* keeps leading to its reply in the chat it came from, and is plain
|
|
135
|
+
* A stashed prompt comes back as text, mentions, skills and the chats this project still has; its
|
|
136
|
+
* files were never kept. Its quote keeps leading to its reply in the chat it came from, and is plain
|
|
137
|
+
* text in any other.
|
|
132
138
|
*/
|
|
133
139
|
const restoreStashed = (prompt: StashedPrompt): void => {
|
|
134
140
|
const stashedQuote = prompt.quote;
|
|
135
141
|
const here = stashedQuote !== undefined && stashedQuote.chatId === chatId;
|
|
142
|
+
const { chats, dropped } = restorableChats(prompt.chats, new Set(referable.map((chat) => chat.id)), chatId);
|
|
136
143
|
setDraft((current) => ({
|
|
137
144
|
...current,
|
|
138
145
|
text: stashedQuote === undefined || here ? prompt.text : withQuote(stashedQuote.text, prompt.text),
|
|
139
146
|
mentions: [...prompt.mentions],
|
|
140
147
|
skills: [...prompt.skills],
|
|
148
|
+
chats,
|
|
141
149
|
...(here ? { quote: stashedQuote.text, quoteSource: stashedQuote.itemId } : {})
|
|
142
150
|
}));
|
|
151
|
+
if (dropped > 0) {
|
|
152
|
+
setNotice(t('composer.notice.stashChatsDropped', { count: dropped }), 'info');
|
|
153
|
+
}
|
|
143
154
|
history.forget();
|
|
144
155
|
inputRef.current?.focus();
|
|
145
156
|
};
|
|
146
157
|
|
|
158
|
+
/* Sends the prompt that goes on after a stop through the same door as a typed one, so the host sees a person's message. */
|
|
159
|
+
const continueTurn = (): void => {
|
|
160
|
+
if (!writable) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
scrollTimelineToEnd(chatId, 'instant');
|
|
164
|
+
afterSend(onSend(CONTINUE_PROMPT, {}), false);
|
|
165
|
+
};
|
|
166
|
+
|
|
147
167
|
/* Cmd+S puts the draft away and clears the box; on an empty box the same key takes the last one back. */
|
|
148
168
|
const toggleStash = (): void => {
|
|
149
169
|
if (isEmptyDraft(draft)) {
|
|
@@ -159,5 +179,5 @@ export function useComposerSubmit({
|
|
|
159
179
|
}
|
|
160
180
|
};
|
|
161
181
|
|
|
162
|
-
return { submit, restoreStashed, toggleStash };
|
|
182
|
+
return { submit, continueTurn, restoreStashed, toggleStash };
|
|
163
183
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import type { ChatInfo, ChatItem } from '@adecore/agent-contracts';
|
|
3
|
+
import { useChatRow } from '../../../state/chats';
|
|
4
|
+
import { continuableTurn } from '../../logic/continue-after-stop';
|
|
5
|
+
|
|
6
|
+
/* The turn a person stopped that Continue would pick up, or null when there is none. */
|
|
7
|
+
export function useContinuableTurn(chatId: string, info: ChatInfo): string | null {
|
|
8
|
+
const order = useChatRow(chatId, (row) => row?.order);
|
|
9
|
+
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
const items = (order ?? []).map((id) => structure?.[id]).filter((item): item is ChatItem => item !== undefined);
|
|
12
|
+
return continuableTurn(info, items);
|
|
13
|
+
}, [info, order, structure]);
|
|
14
|
+
}
|
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
const NOTICE_MS = 4000;
|
|
4
4
|
|
|
5
|
+
/* An error is what went wrong; info is something the composer did that the person should know. */
|
|
6
|
+
export type NoticeTone = 'error' | 'info';
|
|
7
|
+
|
|
5
8
|
export interface ComposerNotice {
|
|
6
9
|
notice: string | null;
|
|
7
|
-
|
|
10
|
+
tone: NoticeTone;
|
|
11
|
+
setNotice(notice: string | null, tone?: NoticeTone): void;
|
|
8
12
|
showError(error: unknown): void;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
/* A line under the box that says what went wrong, gone again after a few seconds. */
|
|
12
16
|
export function useNotice(): ComposerNotice {
|
|
13
|
-
const [
|
|
17
|
+
const [shown, setShown] = useState<{ text: string; tone: NoticeTone } | null>(null);
|
|
14
18
|
|
|
15
19
|
useEffect(() => {
|
|
16
|
-
if (!
|
|
20
|
+
if (!shown) {
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
|
-
const timer = setTimeout(() =>
|
|
23
|
+
const timer = setTimeout(() => setShown(null), NOTICE_MS);
|
|
20
24
|
return () => clearTimeout(timer);
|
|
21
|
-
}, [
|
|
25
|
+
}, [shown]);
|
|
26
|
+
|
|
27
|
+
const setNotice = useCallback((notice: string | null, tone: NoticeTone = 'error'): void => setShown(notice ? { text: notice, tone } : null), []);
|
|
22
28
|
|
|
23
29
|
return {
|
|
24
|
-
notice,
|
|
30
|
+
notice: shown?.text ?? null,
|
|
31
|
+
tone: shown?.tone ?? 'error',
|
|
25
32
|
setNotice,
|
|
26
33
|
showError: (error) => setNotice(error instanceof Error ? error.message : String(error))
|
|
27
34
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LazyBoundary } from '@adecore/ui';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { lazyNamed } from '../../../lazy';
|
|
4
4
|
import type { ReviewEntry } from './review-plan';
|
|
@@ -10,13 +10,13 @@ export function ReviewDiff({ entry, maxHeight = 480 }: { entry: ReviewEntry; max
|
|
|
10
10
|
const { t } = useTranslation('agent-chat');
|
|
11
11
|
return (
|
|
12
12
|
<div data-review-entry={entry.key}>
|
|
13
|
-
<
|
|
13
|
+
<LazyBoundary fallback={<p className="px-3 py-2 text-xs text-text-faint">{t('work.loadingDiff')}</p>}>
|
|
14
14
|
{entry.change.kind === 'edit' ? (
|
|
15
15
|
<EditDiff change={entry.change.edit} maxHeight={maxHeight} />
|
|
16
16
|
) : (
|
|
17
17
|
<UnifiedDiff change={entry.change.patch} contents={entry.change.contents} maxHeight={maxHeight} />
|
|
18
18
|
)}
|
|
19
|
-
</
|
|
19
|
+
</LazyBoundary>
|
|
20
20
|
</div>
|
|
21
21
|
);
|
|
22
22
|
}
|
package/src/chat/ui/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
2
|
export { AccountPill } from './AccountPill';
|
|
3
3
|
export { AnsiOutput } from './AnsiOutput';
|
|
4
|
-
export { BookmarkMarker } from './BookmarkMarker';
|
|
5
4
|
export { BookmarkSubmenu } from './BookmarkSubmenu';
|
|
6
5
|
export { ChatActivity, StatusIcon } from './ChatActivity';
|
|
7
6
|
export { ChatReferenceChip } from './ChatReferenceChip';
|
|
@@ -15,40 +14,17 @@ export { GoalDock, GoalPill } from './GoalDock';
|
|
|
15
14
|
export { ImageThumb, type ImageThumbProps } from './ImageView';
|
|
16
15
|
export { LimitDock, LimitPill } from './LimitState';
|
|
17
16
|
export { ReplyMarkdown, MessageMarkdown, Markdown } from './Markdown';
|
|
18
|
-
export {
|
|
19
|
-
export { MessageActions } from './MessageActions';
|
|
20
|
-
export { ModelPicker, StashPicker } from './Pickers';
|
|
21
|
-
export { type PromptHold, PromptComposer } from './PromptComposer';
|
|
22
|
-
export { QuoteButton } from './QuoteButton';
|
|
17
|
+
export { ModelPicker } from './Pickers';
|
|
23
18
|
export { ResumeCompactionDock } from './ResumeCompactionDock';
|
|
24
|
-
export {
|
|
25
|
-
export { RunSettings } from './RunSettings';
|
|
26
|
-
export { Scrubber, type CardChat } from './Scrubber';
|
|
27
|
-
export { SubagentTitleCrumb, SubagentBreadcrumb, SubagentActions } from './SubagentControls';
|
|
19
|
+
export { SubagentTitleCrumb, SubagentBreadcrumb } from './SubagentControls';
|
|
28
20
|
export { SubagentInfo } from './SubagentInfo';
|
|
29
|
-
export { SubagentStopButton } from './SubagentStopButton';
|
|
30
21
|
export { SubagentTimeline } from './SubagentTimeline';
|
|
31
22
|
export { Timeline } from './Timeline';
|
|
32
|
-
export { TimelineMenuPopup
|
|
23
|
+
export { TimelineMenuPopup } from './TimelineMenu';
|
|
33
24
|
export { default as UnifiedDiff } from './UnifiedDiff';
|
|
34
25
|
export { UploadThumb } from './UploadThumb';
|
|
35
|
-
export { VisualDialogs } from './VisualDialogs';
|
|
36
|
-
export { type VisualFrameProps, VisualFrame } from './VisualFrame';
|
|
37
26
|
export { WelcomeGreeting } from './Welcome';
|
|
38
|
-
export {
|
|
39
|
-
export { externalChange, markdownLanguage, composerEditorExtensions } from './composer/editor';
|
|
40
|
-
export { insertAtSelection } from './composer/insert';
|
|
41
|
-
export {
|
|
42
|
-
listItemAt,
|
|
43
|
-
enterAction,
|
|
44
|
-
inOpenFence,
|
|
45
|
-
recallDirection,
|
|
46
|
-
type EnterAction,
|
|
47
|
-
type EnterKeys,
|
|
48
|
-
type ListItem,
|
|
49
|
-
type EnterContext,
|
|
50
|
-
type RecallContext
|
|
51
|
-
} from './composer/keys';
|
|
27
|
+
export { externalChange, composerEditorExtensions } from './composer/editor';
|
|
52
28
|
export {
|
|
53
29
|
createDiffSyntaxRuntime,
|
|
54
30
|
diffSyntaxRuntime,
|
|
@@ -61,11 +37,10 @@ export {
|
|
|
61
37
|
} from './diff-syntax';
|
|
62
38
|
export { useDiffTheme } from './diff-theme';
|
|
63
39
|
export type { UnifiedDiffProps } from './diff/diff-props';
|
|
64
|
-
export { FileLinkContext, useFileLinkCwd
|
|
65
|
-
export { type ChatFindField, type FindReveal
|
|
40
|
+
export { FileLinkContext, useFileLinkCwd } from './file-links';
|
|
41
|
+
export { type ChatFindField, type FindReveal } from './find-reveal';
|
|
66
42
|
export type { DiffContents } from './full-diff';
|
|
67
43
|
export { type UiBlockFrameProps, UiBlockFrame, type UiBlockFix, type UiBlockFooterProps } from './intelligent-ui/UiBlockFrame';
|
|
68
|
-
export { UiFallbackPart, type UiFallbackProblem } from './intelligent-ui/UiFallbackPart';
|
|
69
44
|
export { UiReply } from './intelligent-ui/UiReply';
|
|
70
45
|
export { uiChartData, type UiChartSeries, type UiChartValues, type UiChartData } from './intelligent-ui/chart-data';
|
|
71
46
|
export { uiNodeLabel } from './intelligent-ui/node-text';
|
|
@@ -75,6 +50,7 @@ export type {
|
|
|
75
50
|
UiRendererProps,
|
|
76
51
|
UiLinkTarget,
|
|
77
52
|
UiLink,
|
|
53
|
+
UiPlacePoint,
|
|
78
54
|
UiAnswer,
|
|
79
55
|
UiLiveStatus,
|
|
80
56
|
UiLiveValue,
|
|
@@ -86,24 +62,8 @@ export type { QuoteTaker } from './quote-selection';
|
|
|
86
62
|
export { ReplyContext } from './reply-context';
|
|
87
63
|
export { ForksRow } from './rows/ForksRow';
|
|
88
64
|
export { GeneratedImageRow } from './rows/GeneratedImageRow';
|
|
89
|
-
export {
|
|
90
|
-
UserRow,
|
|
91
|
-
ReportRow,
|
|
92
|
-
ReplyHeader,
|
|
93
|
-
AssistantRow,
|
|
94
|
-
ThinkingRow,
|
|
95
|
-
LiveThoughtRow,
|
|
96
|
-
NoteRow,
|
|
97
|
-
AgentTurnRow,
|
|
98
|
-
CompactionRow,
|
|
99
|
-
ApprovalHistoryRow,
|
|
100
|
-
QuestionHistoryRow
|
|
101
|
-
} from './rows/MessageRows';
|
|
102
|
-
export { type RowContainerProps, RowContainer } from './rows/RowContainer';
|
|
103
|
-
export { Row, type RowProps } from './rows/Rows';
|
|
104
|
-
export { SubagentRow, SubagentBranchRow } from './rows/SubagentRow';
|
|
65
|
+
export { ReportRow, AssistantRow, NoteRow, AgentTurnRow, CompactionRow } from './rows/MessageRows';
|
|
105
66
|
export { TaskRow } from './rows/TaskRow';
|
|
106
|
-
export {
|
|
107
|
-
export { ToggleLine, WorkRow, RunningFor, WorkLiveRow, WorkGroupRow, TurnFoldRow, ChangedFilesRow, WorkingRow } from './rows/WorkRows';
|
|
67
|
+
export { WorkRow, WorkGroupRow, WorkingRow } from './rows/WorkRows';
|
|
108
68
|
export { WorkflowRow } from './rows/WorkflowRow';
|
|
109
69
|
export { useDiffSourceTokens } from './use-diff-source-tokens';
|
|
@@ -365,6 +365,23 @@ function UiNodeBody({
|
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
+
/*
|
|
369
|
+
* The points the machine froze beside a block, as a renderer asks for them. Until the block is frozen
|
|
370
|
+
* nothing is looked up yet, unless the machine keeps no readings for this reply at all; once it is, a place
|
|
371
|
+
* with no reading for its current address is one the machine did not look up.
|
|
372
|
+
*/
|
|
373
|
+
function placeReader(frozen: ChatUiQueryState['blocks'][string] | undefined, revision: string | undefined, unrecorded: boolean): UiRenderContext['place'] {
|
|
374
|
+
if (frozen === undefined || frozen.revision !== revision) {
|
|
375
|
+
return () => (unrecorded ? null : undefined);
|
|
376
|
+
}
|
|
377
|
+
return (nodeId, address) => {
|
|
378
|
+
const reading = Object.hasOwn(frozen.places ?? {}, nodeId) ? frozen.places![nodeId] : undefined;
|
|
379
|
+
return reading?.address === address.trim() && reading.state === 'found' && reading.lat !== undefined && reading.lon !== undefined
|
|
380
|
+
? { lat: reading.lat, lon: reading.lon, label: reading.label }
|
|
381
|
+
: null;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
368
385
|
/* What names a block and whether it still streams; the rest of its render context the block works out itself. */
|
|
369
386
|
type UiBlockIdentity = Pick<UiRenderContext, 'scopeId' | 'chatId' | 'itemId' | 'blockId' | 'phase'>;
|
|
370
387
|
|
|
@@ -372,12 +389,15 @@ function UiBlockBody({
|
|
|
372
389
|
block,
|
|
373
390
|
context,
|
|
374
391
|
answered,
|
|
375
|
-
frozen
|
|
392
|
+
frozen,
|
|
393
|
+
unrecorded
|
|
376
394
|
}: {
|
|
377
395
|
block: UiBlock;
|
|
378
396
|
context: UiBlockIdentity;
|
|
379
397
|
answered?: ChatUiAnswer;
|
|
380
398
|
frozen?: ChatUiQueryState['blocks'][string];
|
|
399
|
+
/* Set for a finished reply the machine keeps no readings for, which nothing will ever look up. */
|
|
400
|
+
unrecorded: boolean;
|
|
381
401
|
}): ReactNode {
|
|
382
402
|
const host = useChatHost();
|
|
383
403
|
const { t } = useTranslation('agent-chat');
|
|
@@ -430,9 +450,11 @@ function UiBlockBody({
|
|
|
430
450
|
);
|
|
431
451
|
const liveValue = useUiLiveValues(block, evaluation.nodes, queries.currentReadings);
|
|
432
452
|
const sendChoice = host.intelligentUi?.sendChoice;
|
|
453
|
+
const place = useMemo(() => placeReader(frozen, block.revision, unrecorded), [frozen, block.revision, unrecorded]);
|
|
433
454
|
const rendering: UiRenderContext = {
|
|
434
455
|
...context,
|
|
435
456
|
...links,
|
|
457
|
+
place,
|
|
436
458
|
answer,
|
|
437
459
|
failedChoiceId,
|
|
438
460
|
live: queries.live,
|
|
@@ -531,6 +553,7 @@ export function UiReply({
|
|
|
531
553
|
<UiBlockBody
|
|
532
554
|
block={part.block}
|
|
533
555
|
frozen={queries?.blocks[part.block.id]}
|
|
556
|
+
unrecorded={context.phase === 'final' && queries === undefined}
|
|
534
557
|
answered={answers?.[part.block.id]}
|
|
535
558
|
context={{ scopeId: context.scopeId, chatId: context.chatId, itemId: context.itemId, phase: context.phase, blockId: part.block.id }}
|
|
536
559
|
/>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TFunction } from 'i18next';
|
|
2
|
-
import type
|
|
2
|
+
import { parseUiEventTime, type UiComponentName, type UiProps, type UiViewNode } from '@adecore/intelligent-ui';
|
|
3
|
+
import { formatClock, formatDay, formatDayClock, formatDayClockWithYear, formatDayWithYear } from '@adecore/ui/format';
|
|
3
4
|
import type { UiLiveStatus } from './render-context';
|
|
4
5
|
|
|
5
6
|
/* The text a node holds, every `$text` below it joined as written, such as the code of a CodeBlock. */
|
|
@@ -12,6 +13,16 @@ export function uiNodeLabel(node: UiViewNode): string {
|
|
|
12
13
|
return uiNodeText(node).replace(/\s+/g, ' ').trim();
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/* The text written directly in a node, beside its component children: the label of a Select, whose children are mostly its options. */
|
|
17
|
+
export function uiOwnLabel(node: UiViewNode): string {
|
|
18
|
+
return node.children
|
|
19
|
+
.filter((child) => child.type === '$text')
|
|
20
|
+
.map((child) => String(child.props.text ?? ''))
|
|
21
|
+
.join('')
|
|
22
|
+
.replace(/\s+/g, ' ')
|
|
23
|
+
.trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
/* The valid children of one kind, which a parent reads as metadata: the columns of a table, the tabs of a strip. */
|
|
16
27
|
export function uiChildrenOf<Name extends UiComponentName>(node: UiViewNode, type: Name): UiViewNode<UiProps<Name>>[] {
|
|
17
28
|
return node.children.filter((child) => child.type === type && child.error === undefined) as UiViewNode<UiProps<Name>>[];
|
|
@@ -117,3 +128,25 @@ export function uiUrlDomain(url: string): string {
|
|
|
117
128
|
return url;
|
|
118
129
|
}
|
|
119
130
|
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* An event's `at` the way the person's language and region write it. The year is written once it is not the
|
|
134
|
+
* one we are in; a text without an offset reads as written, whatever zone or clock change the reader is in.
|
|
135
|
+
* A text the catalog would not accept stays as written.
|
|
136
|
+
*/
|
|
137
|
+
export function uiEventTime(at: string, now: Date | number = Date.now()): string {
|
|
138
|
+
const moment = parseUiEventTime(at);
|
|
139
|
+
if (moment === null) {
|
|
140
|
+
return at;
|
|
141
|
+
}
|
|
142
|
+
const zone = moment.written ? 'UTC' : undefined;
|
|
143
|
+
const year = moment.written ? moment.date.getUTCFullYear() : moment.date.getFullYear();
|
|
144
|
+
const withYear = year !== new Date(now).getFullYear();
|
|
145
|
+
if (moment.kind === 'time') {
|
|
146
|
+
return formatClock(moment.date, zone);
|
|
147
|
+
}
|
|
148
|
+
if (moment.kind === 'date') {
|
|
149
|
+
return withYear ? formatDayWithYear(moment.date, zone) : formatDay(moment.date, zone);
|
|
150
|
+
}
|
|
151
|
+
return withYear ? formatDayClockWithYear(moment.date, zone) : formatDayClock(moment.date, zone);
|
|
152
|
+
}
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
import type { UiRenderers } from './render-context';
|
|
2
2
|
import { ChartRenderer } from './renderers/chart';
|
|
3
3
|
import { ChoiceRenderer, ChoicesRenderer } from './renderers/choices';
|
|
4
|
-
import { CodeBlockRenderer, ImageRenderer, SourceRenderer, SourcesRenderer } from './renderers/content';
|
|
4
|
+
import { CodeBlockRenderer, CompareRenderer, GalleryRenderer, ImageRenderer, SourceRenderer, SourcesRenderer } from './renderers/content';
|
|
5
5
|
import { EntityListRenderer, EntryRenderer, StatRenderer, StatsRenderer, TableRenderer } from './renderers/data';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ButtonRenderer,
|
|
8
|
+
ChecklistRenderer,
|
|
9
|
+
ItemRenderer,
|
|
10
|
+
SegmentedRenderer,
|
|
11
|
+
SelectRenderer,
|
|
12
|
+
SliderRenderer,
|
|
13
|
+
SwitchRenderer,
|
|
14
|
+
TextFieldRenderer
|
|
15
|
+
} from './renderers/inputs';
|
|
7
16
|
import { CommitRenderer, DiffRenderer, FileRenderer, NodeRenderer } from './renderers/links';
|
|
17
|
+
import { MapRenderer } from './renderers/map';
|
|
8
18
|
import { SectionRenderer, SectionsRenderer, TabRenderer, TabsRenderer } from './renderers/structure';
|
|
9
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
CalloutRenderer,
|
|
21
|
+
EventRenderer,
|
|
22
|
+
ProgressRenderer,
|
|
23
|
+
StepRenderer,
|
|
24
|
+
StepsRenderer,
|
|
25
|
+
SummaryRenderer,
|
|
26
|
+
TagRenderer,
|
|
27
|
+
TimelineRenderer
|
|
28
|
+
} from './renderers/text';
|
|
10
29
|
|
|
11
30
|
/*
|
|
12
31
|
* A renderer for every component of the catalog; a name missing here fails the typecheck. Null draws
|
|
13
|
-
* nothing for the node itself: the runtime resolves Show and Each before rendering, and Column
|
|
14
|
-
* Option are metadata their parent reads from its node (a table its columns, Segmented
|
|
32
|
+
* nothing for the node itself: the runtime resolves Show and Each before rendering, and Column,
|
|
33
|
+
* Option and Place are metadata their parent reads from its node (a table its columns, Segmented and
|
|
34
|
+
* Select their options, a map its places).
|
|
15
35
|
*/
|
|
16
36
|
export const UI_RENDERERS: UiRenderers = {
|
|
17
37
|
Summary: SummaryRenderer,
|
|
@@ -20,6 +40,8 @@ export const UI_RENDERERS: UiRenderers = {
|
|
|
20
40
|
Progress: ProgressRenderer,
|
|
21
41
|
Steps: StepsRenderer,
|
|
22
42
|
Step: StepRenderer,
|
|
43
|
+
Timeline: TimelineRenderer,
|
|
44
|
+
Event: EventRenderer,
|
|
23
45
|
Stats: StatsRenderer,
|
|
24
46
|
Stat: StatRenderer,
|
|
25
47
|
EntityList: EntityListRenderer,
|
|
@@ -33,6 +55,10 @@ export const UI_RENDERERS: UiRenderers = {
|
|
|
33
55
|
Section: SectionRenderer,
|
|
34
56
|
CodeBlock: CodeBlockRenderer,
|
|
35
57
|
Image: ImageRenderer,
|
|
58
|
+
Gallery: GalleryRenderer,
|
|
59
|
+
Compare: CompareRenderer,
|
|
60
|
+
Map: MapRenderer,
|
|
61
|
+
Place: null,
|
|
36
62
|
Sources: SourcesRenderer,
|
|
37
63
|
Source: SourceRenderer,
|
|
38
64
|
File: FileRenderer,
|
|
@@ -44,7 +70,9 @@ export const UI_RENDERERS: UiRenderers = {
|
|
|
44
70
|
Switch: SwitchRenderer,
|
|
45
71
|
Slider: SliderRenderer,
|
|
46
72
|
Segmented: SegmentedRenderer,
|
|
73
|
+
Select: SelectRenderer,
|
|
47
74
|
Option: null,
|
|
75
|
+
TextField: TextFieldRenderer,
|
|
48
76
|
Button: ButtonRenderer,
|
|
49
77
|
Show: null,
|
|
50
78
|
Each: null,
|
|
@@ -15,6 +15,14 @@ export interface UiLink {
|
|
|
15
15
|
code?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/* The point a Place stands at, in decimal degrees. */
|
|
19
|
+
export interface UiPlacePoint {
|
|
20
|
+
lat: number;
|
|
21
|
+
lon: number;
|
|
22
|
+
/* What the host called the point it found, such as the street and town. */
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
/* The Choice that answered a block. `sending` already reads as sent, so a second click does nothing. */
|
|
19
27
|
export interface UiAnswer {
|
|
20
28
|
choiceId: string;
|
|
@@ -63,6 +71,12 @@ export interface UiRenderContext {
|
|
|
63
71
|
onChoose?(nodeId: string): void;
|
|
64
72
|
/* Why the choices are closed while `onChoose` is absent, such as live data still being read; without it they say they are not available. */
|
|
65
73
|
choiceReason?: string | null;
|
|
74
|
+
/*
|
|
75
|
+
* The point the host found for the `address` of the Place `nodeId`: undefined while it is not looked up
|
|
76
|
+
* yet, null when none was found. A reading counts only for the address it was made for, so a place that
|
|
77
|
+
* moved to another node, or whose address changed, does not wear another place's point.
|
|
78
|
+
*/
|
|
79
|
+
place?(nodeId: string, address: string): UiPlacePoint | null | undefined;
|
|
66
80
|
/* Table cells use the evaluated rows index and column key. */
|
|
67
81
|
liveValue?(nodeId: string, prop: string, path?: readonly [row: number, key: string]): UiLiveValue | undefined;
|
|
68
82
|
}
|