@adecore/agents-react 0.37.3 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/AgentModelLabel.d.ts +10 -0
- package/dist/agents/AgentModelLabel.js +97 -0
- package/dist/chat/logic/visual-height.d.ts +1 -0
- package/dist/chat/logic/visual-height.js +1 -1
- package/dist/chat/ui/Composer.js +385 -360
- package/dist/chat/ui/HostMenuItems.d.ts +4 -0
- package/dist/chat/ui/HostMenuItems.js +15 -0
- package/dist/chat/ui/Pickers.d.ts +2 -1
- package/dist/chat/ui/Pickers.js +126 -123
- package/dist/chat/ui/PromptEditor.d.ts +19 -0
- package/dist/chat/ui/PromptEditor.js +115 -0
- package/dist/chat/ui/Timeline.d.ts +2 -1
- package/dist/chat/ui/Timeline.js +13 -1
- package/dist/chat/ui/VisualFrame.d.ts +8 -0
- package/dist/chat/ui/VisualFrame.js +217 -115
- package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
- package/dist/chat/ui/composer/ComposerControls.js +235 -38
- package/dist/chat/ui/composer/key-handler.d.ts +1 -1
- package/dist/chat/ui/composer/key-handler.js +3 -11
- package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
- package/dist/chat/ui/composer/prompt-picks.js +14 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
- package/dist/chat/ui/composer/use-composer-menus.js +65 -519
- package/dist/chat/ui/composer/use-prompt-menus.d.ts +73 -0
- package/dist/chat/ui/composer/use-prompt-menus.js +536 -0
- package/dist/chat/ui/index.d.ts +4 -0
- package/dist/chat/ui/index.js +3 -0
- package/dist/chat/ui/rows/GeneratedImageRow.js +8 -4
- package/dist/chat/ui/rows/VisualRow.js +184 -142
- package/dist/chat/ui/rows/row-rhythm.js +6 -4
- package/dist/host-hooks.d.ts +2 -1
- package/dist/host-hooks.js +6 -0
- package/dist/host.d.ts +27 -1
- package/dist/index.d.ts +1 -1
- package/dist/locales/en/agent-chat.d.ts +1 -0
- package/dist/locales/en/agent-chat.js +1 -0
- package/dist/locales/nl/agent-chat.d.ts +1 -0
- package/dist/locales/nl/agent-chat.js +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/theme.css +3 -0
- package/package.json +8 -8
- package/src/agents/AgentModelLabel.tsx +40 -0
- package/src/chat/logic/visual-height.ts +1 -1
- package/src/chat/ui/Composer.tsx +12 -3
- package/src/chat/ui/HostMenuItems.tsx +11 -0
- package/src/chat/ui/Pickers.tsx +4 -1
- package/src/chat/ui/PromptEditor.tsx +130 -0
- package/src/chat/ui/Timeline.tsx +22 -1
- package/src/chat/ui/VisualFrame.tsx +68 -21
- package/src/chat/ui/composer/ComposerControls.tsx +65 -15
- package/src/chat/ui/composer/key-handler.ts +3 -9
- package/src/chat/ui/composer/prompt-picks.ts +17 -0
- package/src/chat/ui/composer/use-composer-menus.ts +18 -275
- package/src/chat/ui/composer/use-prompt-menus.ts +341 -0
- package/src/chat/ui/index.ts +4 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +9 -4
- package/src/chat/ui/rows/VisualRow.tsx +5 -1
- package/src/chat/ui/rows/row-rhythm.ts +6 -4
- package/src/host-hooks.ts +9 -1
- package/src/host.ts +45 -1
- package/src/index.ts +3 -0
- package/src/locales/en/agent-chat.ts +1 -0
- package/src/locales/nl/agent-chat.ts +1 -0
- package/src/providers/index.ts +1 -0
- package/src/theme.css +3 -0
|
@@ -13,6 +13,7 @@ import { attachmentAspect, generatedImageView, type GeneratedImageFailure } from
|
|
|
13
13
|
import { toolStartedAt } from '../../logic/tools';
|
|
14
14
|
import { CHIP_IN_MESSAGE, MENTION_TONE } from '../chips';
|
|
15
15
|
import { useFileLinkCwd, useOpenFileLink } from '../file-links';
|
|
16
|
+
import { HostMenuItems } from '../HostMenuItems';
|
|
16
17
|
import { ImageThumb } from '../ImageView';
|
|
17
18
|
import { ROW_GUTTER } from '../icons';
|
|
18
19
|
import { RunningFor, WorkRow } from './WorkRows';
|
|
@@ -104,6 +105,7 @@ function ReadyImage({ chatId, attachment, prompt, transparent }: { chatId: strin
|
|
|
104
105
|
const missing = source.failure !== null;
|
|
105
106
|
const aspect = attachmentAspect(attachment);
|
|
106
107
|
const { open, saveToProject } = attachments;
|
|
108
|
+
const extra = attachments.actions?.(scopeId, chatId, attachment) ?? [];
|
|
107
109
|
|
|
108
110
|
const detail = [
|
|
109
111
|
attachment.width !== undefined && attachment.height !== undefined
|
|
@@ -171,13 +173,16 @@ function ReadyImage({ chatId, attachment, prompt, transparent }: { chatId: strin
|
|
|
171
173
|
>
|
|
172
174
|
<ButtonGroup>
|
|
173
175
|
<IconButton icon={Maximize2} size="sm" label={t('generatedImage.openLarge')} onClick={() => setLightbox(true)} />
|
|
174
|
-
{prompt !== null && (
|
|
176
|
+
{(prompt !== null || extra.length > 0) && (
|
|
175
177
|
<Menu.Root>
|
|
176
178
|
<IconButton icon={Ellipsis} size="sm" label={t('generatedImage.more')} render={<Menu.Trigger />} />
|
|
177
179
|
<Menu.Popup align="end">
|
|
178
|
-
|
|
179
|
-
<
|
|
180
|
-
|
|
180
|
+
{prompt !== null && (
|
|
181
|
+
<Menu.Item onClick={() => copyText(prompt)}>
|
|
182
|
+
<Icon icon={Copy} size={14} /> {t('generatedImage.copyPrompt')}
|
|
183
|
+
</Menu.Item>
|
|
184
|
+
)}
|
|
185
|
+
<HostMenuItems actions={extra} />
|
|
181
186
|
</Menu.Popup>
|
|
182
187
|
</Menu.Root>
|
|
183
188
|
)}
|
|
@@ -5,6 +5,7 @@ import { ButtonGroup, ErrorBoundary, Icon, IconButton, Menu, Surface } from '@ad
|
|
|
5
5
|
import { useChatHost } from '../../../host';
|
|
6
6
|
import { useChatScope } from '../../../scope';
|
|
7
7
|
import { useVisualDialog } from '../../visuals';
|
|
8
|
+
import { HostMenuItems } from '../HostMenuItems';
|
|
8
9
|
import { VisualFrame } from '../VisualFrame';
|
|
9
10
|
|
|
10
11
|
/*
|
|
@@ -16,10 +17,11 @@ import { VisualFrame } from '../VisualFrame';
|
|
|
16
17
|
export function VisualRow({ chatId, visual }: { chatId: string; visual: ChatVisual }) {
|
|
17
18
|
const host = useChatHost();
|
|
18
19
|
const { t } = useTranslation('agent-chat');
|
|
19
|
-
const { keyOf } = useChatScope();
|
|
20
|
+
const { id: scopeId, keyOf } = useChatScope();
|
|
20
21
|
if (host.visuals === null) {
|
|
21
22
|
return null;
|
|
22
23
|
}
|
|
24
|
+
const extra = host.visuals.actions?.(scopeId, chatId, visual) ?? [];
|
|
23
25
|
const open = (kind: 'expand' | 'remove'): void => useVisualDialog.getState().open(keyOf(chatId), visual.id, kind);
|
|
24
26
|
return (
|
|
25
27
|
<ErrorBoundary label={t('visuals.renderFailed')} resetKeys={[visual]} compact className="relative rounded-lg">
|
|
@@ -35,6 +37,8 @@ export function VisualRow({ chatId, visual }: { chatId: string; visual: ChatVisu
|
|
|
35
37
|
<Menu.Root>
|
|
36
38
|
<IconButton icon={Ellipsis} size="sm" label={t('visuals.more')} render={<Menu.Trigger />} />
|
|
37
39
|
<Menu.Popup align="end">
|
|
40
|
+
<HostMenuItems actions={extra} />
|
|
41
|
+
{extra.length > 0 && <Menu.Separator />}
|
|
38
42
|
<Menu.Item onClick={() => open('remove')}>
|
|
39
43
|
<Icon icon={Trash2} size={14} /> {t('visuals.remove')}
|
|
40
44
|
</Menu.Item>
|
|
@@ -7,13 +7,15 @@ export const FOLLOW_THRESHOLD_PX = 40;
|
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
9
|
* The gaps a row wears for where it sits: a question is followed by its answer gap, and a seam where
|
|
10
|
-
* the list rhythm of tool lines meets the block rhythm of prose gets the block gap. A visual
|
|
11
|
-
* margin of
|
|
12
|
-
* already carries the gap before it, and the row after one is the answer, so neither
|
|
10
|
+
* the list rhythm of tool lines meets the block rhythm of prose gets the block gap. A visual and an
|
|
11
|
+
* app's card have no margin of their own like prose does, so the row under one takes the block gap
|
|
12
|
+
* too. A question already carries the gap before it, and the row after one is the answer, so neither
|
|
13
|
+
* takes a seam.
|
|
13
14
|
*/
|
|
14
15
|
export function rowRhythm(row: TimelineRow, previous: TimelineRow | null): string {
|
|
15
16
|
const question = row.kind === 'user';
|
|
16
|
-
const
|
|
17
|
+
const bare = previous?.kind === 'visual' || previous?.kind === 'app-card';
|
|
18
|
+
const seam = !question && previous !== null && previous.kind !== 'user' && (isBlock(row) !== isBlock(previous) || bare);
|
|
17
19
|
return clsx(question && 'pb-(--chat-answer-gap)', seam && 'pt-(--chat-block-gap)');
|
|
18
20
|
}
|
|
19
21
|
|
package/src/host-hooks.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ProviderPlatform } from '@adecore/agent-contracts';
|
|
9
9
|
import { referablesOfChats, type Referables } from './chat/chat-references';
|
|
10
|
-
import { useChatHost, type ChatHost, type CommandLook } from './host';
|
|
10
|
+
import { useChatHost, type ChatHost, type CommandLook, type ComposerSendAction } from './host';
|
|
11
11
|
|
|
12
12
|
export function useHostCodeMode(): 'light' | 'dark' {
|
|
13
13
|
return useChatHost().code.useMode();
|
|
@@ -101,6 +101,14 @@ export function useHostComposerPlaceholder(...args: Parameters<ChatHost['useComp
|
|
|
101
101
|
return useChatHost().useComposerPlaceholder(...args);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
const NO_SEND_ACTIONS: readonly ComposerSendAction[] = [];
|
|
105
|
+
const noSendActions = (): readonly ComposerSendAction[] => NO_SEND_ACTIONS;
|
|
106
|
+
|
|
107
|
+
export function useHostComposerSendActions(scopeId: string, chatId: string): readonly ComposerSendAction[] {
|
|
108
|
+
const useSendActions = useChatHost().useComposerSendActions ?? noSendActions;
|
|
109
|
+
return useSendActions(scopeId, chatId);
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
export function useHostThreadCards(...args: Parameters<ChatHost['useThreadCards']>): ReturnType<ChatHost['useThreadCards']> {
|
|
105
113
|
return useChatHost().useThreadCards(...args);
|
|
106
114
|
}
|
package/src/host.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { createContext, createElement, useContext, useState, type ComponentProps
|
|
|
2
2
|
import type { Extension } from '@codemirror/state';
|
|
3
3
|
import type { EditorView } from '@codemirror/view';
|
|
4
4
|
import type { Highlighter, ThemeRegistration } from 'shiki';
|
|
5
|
+
import type { LucideIcon } from 'lucide-react';
|
|
5
6
|
import type { EditorEngine } from '@adecore/editor';
|
|
6
7
|
import type {
|
|
7
8
|
AgentKind,
|
|
9
|
+
ChatAttachment,
|
|
8
10
|
ChatCheckpointDiff,
|
|
9
11
|
ChatConfigurePayload,
|
|
10
12
|
ChatGoalAction,
|
|
@@ -15,7 +17,10 @@ import type {
|
|
|
15
17
|
ChatUiQueryReading,
|
|
16
18
|
ChatUiLinkPayload,
|
|
17
19
|
ChatUiLinkReading,
|
|
18
|
-
|
|
20
|
+
ChatVisual,
|
|
21
|
+
ModelSelection,
|
|
22
|
+
ProviderPlatform,
|
|
23
|
+
RuntimeMode
|
|
19
24
|
} from '@adecore/agent-contracts';
|
|
20
25
|
import { isApplePlatform } from '@adecore/ui';
|
|
21
26
|
import type { Referables } from './chat/chat-references';
|
|
@@ -165,6 +170,28 @@ export interface ComposerSlotProps {
|
|
|
165
170
|
insert(text: string): void;
|
|
166
171
|
}
|
|
167
172
|
|
|
173
|
+
/* What a send action of the app's gets: the message as it stands when a person picks the action. */
|
|
174
|
+
export interface ComposerSendDraft {
|
|
175
|
+
scopeId: string;
|
|
176
|
+
chatId: string;
|
|
177
|
+
/* The draft as Send would send it, trimmed and with its quote; empty when nothing is written. */
|
|
178
|
+
text: string;
|
|
179
|
+
provider: AgentKind;
|
|
180
|
+
selection: ModelSelection;
|
|
181
|
+
runtimeMode: RuntimeMode;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* An action of the app's own in the menu beside the composer's send button, such as sending the draft later. */
|
|
185
|
+
export interface ComposerSendAction {
|
|
186
|
+
/* Unique among the actions of one composer. */
|
|
187
|
+
id: string;
|
|
188
|
+
label: string;
|
|
189
|
+
icon?: LucideIcon;
|
|
190
|
+
disabled?: boolean;
|
|
191
|
+
/* Runs once a person picks it; the draft stays in the box, and the host shows a failure itself. */
|
|
192
|
+
run(draft: ComposerSendDraft): void;
|
|
193
|
+
}
|
|
194
|
+
|
|
168
195
|
/* What an app's own chips for a chat's sub-agents get, in place of the one flyout over the composer. */
|
|
169
196
|
export interface SubagentSlotProps {
|
|
170
197
|
chatId: string;
|
|
@@ -188,6 +215,19 @@ export interface VisualHost {
|
|
|
188
215
|
frameUrl: string;
|
|
189
216
|
/* Opens an http or https link a person followed in a visual, outside the app. */
|
|
190
217
|
openLink(url: string): void;
|
|
218
|
+
/* Actions of the app's own in a visual's menu, above Remove; left out, the menu holds Remove alone. */
|
|
219
|
+
actions?(scopeId: string, chatId: string, visual: ChatVisual): readonly HostMenuAction[];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* An action of the app's own in the menu of something a chat made, such as keeping a copy somewhere of the app's. */
|
|
223
|
+
export interface HostMenuAction {
|
|
224
|
+
/* Unique among the actions of one menu. */
|
|
225
|
+
id: string;
|
|
226
|
+
label: string;
|
|
227
|
+
icon?: LucideIcon;
|
|
228
|
+
disabled?: boolean;
|
|
229
|
+
/* Runs once a person picks it; the host shows a failure itself. */
|
|
230
|
+
run(): void;
|
|
191
231
|
}
|
|
192
232
|
|
|
193
233
|
/* Who writes a chat's replies, as the header over each one names them. */
|
|
@@ -329,6 +369,8 @@ export interface ChatHost {
|
|
|
329
369
|
* Resolves with the path as a file link opens it, or null when the person cancels; left out, a generated image offers no Save.
|
|
330
370
|
*/
|
|
331
371
|
saveToProject?(scopeId: string, chatId: string, attachmentId: string, suggestedName: string): Promise<string | null>;
|
|
372
|
+
/* Actions of the app's own in a generated image's menu, after the chat's own; left out, the menu holds those alone. */
|
|
373
|
+
actions?(scopeId: string, chatId: string, attachment: ChatAttachment): readonly HostMenuAction[];
|
|
332
374
|
};
|
|
333
375
|
/* The image a tool read, drawn under its row; null draws none. */
|
|
334
376
|
ReadImage: ComponentType<{ path: string }> | null;
|
|
@@ -435,6 +477,8 @@ export interface ChatHost {
|
|
|
435
477
|
useComposerPlaceholder(scopeId: string, chatId: string): string | null;
|
|
436
478
|
/* The app's own control in the composer's row, after the pickers; null draws none. */
|
|
437
479
|
ComposerSlot: ComponentType<ComposerSlotProps> | null;
|
|
480
|
+
/* Actions of the app's own behind a chevron on the send button, in the order given; left out, or none, Send stands alone. */
|
|
481
|
+
useComposerSendActions?(scopeId: string, chatId: string): readonly ComposerSendAction[];
|
|
438
482
|
/* The app's own chips for a chat's sub-agents over the composer; null draws the one flyout. */
|
|
439
483
|
SubagentSlot: ComponentType<SubagentSlotProps> | null;
|
|
440
484
|
/* Cards of the app's own in a thread, in any order. Hand back the same array while none changed, or the thread lays out its rows again on every render. */
|
package/src/index.ts
CHANGED
|
@@ -9,9 +9,12 @@ export {
|
|
|
9
9
|
type TimelineFind,
|
|
10
10
|
type ComposerDictationProps,
|
|
11
11
|
type ComposerSlotProps,
|
|
12
|
+
type ComposerSendAction,
|
|
13
|
+
type ComposerSendDraft,
|
|
12
14
|
type SubagentSlotProps,
|
|
13
15
|
type ThreadCard,
|
|
14
16
|
type VisualHost,
|
|
17
|
+
type HostMenuAction,
|
|
15
18
|
type ReplyAuthor,
|
|
16
19
|
type ChatHost,
|
|
17
20
|
type UiMapSource,
|
package/src/providers/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
2
|
export { AccountDot } from '../agents/AccountDot';
|
|
3
3
|
export { AgentIcon } from '../agents/AgentIcon';
|
|
4
|
+
export { AgentModelLabel } from '../agents/AgentModelLabel';
|
|
4
5
|
export { ModelOptionControl } from '../agents/ModelOptionControl';
|
|
5
6
|
export { ProviderLogo } from '../agents/ProviderLogo';
|
|
6
7
|
export { type AccountEntry, accountsOfKind, ACCOUNT_TONE_CLASSES, accountStatusLine, canContinueOn, type AccountTone } from '../agents/accounts';
|
package/src/theme.css
CHANGED
|
@@ -288,6 +288,9 @@
|
|
|
288
288
|
.composer-input .cm-editor .cm-scroller .cm-content { padding: 18px 14px 6px 20px; caret-color: var(--text); }
|
|
289
289
|
.composer-input .cm-scroller .cm-content .cm-line { padding: 0; }
|
|
290
290
|
.composer-input .cm-scroller .cm-content .cm-placeholder { color: var(--text-faint); }
|
|
291
|
+
/* The same editor as a form field (`PromptEditor`): the field's inset, and room for a few lines before it scrolls. */
|
|
292
|
+
.composer-input.prompt-editor .cm-editor { max-height: 240px; }
|
|
293
|
+
.composer-input.prompt-editor .cm-editor .cm-scroller .cm-content { min-height: 80px; padding: 6px 10px; }
|
|
291
294
|
.composer-hints, .composer-hints-extra { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
|
|
292
295
|
/* The joiner starts with its own comma, so it sits against the lead without the gap. */
|
|
293
296
|
.composer-hints-extra { margin-left: -8px; }
|