@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
package/dist/chat/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export { type AccountChoice, useAccountChoice } from './account-choice.js';
|
|
2
2
|
export { transportActions, actionsOf, useChatActions } from './actions.js';
|
|
3
|
-
export { backgroundCounts } from './activity.js';
|
|
4
|
-
export { isImageAttachment, fileBadge, formatBytes, checkAttachmentLimits, filesOf, readAttachments, readStoredAttachments, uploadPreviewUrl, uploadBytes } from './attachments.js';
|
|
5
3
|
export { type ChatSendExtras, ChatClient } from './chat-client.js';
|
|
6
4
|
export type { ProjectChat } from './chat-references.js';
|
|
7
5
|
export { sendFromComposer, type ComposerSend } from './composer-send.js';
|
|
@@ -11,12 +9,12 @@ export { FORKABLE_PROVIDERS, forkRefusal, summaryRefusal, lastSettledTurn, forkP
|
|
|
11
9
|
export { handbackReportOf } from './logic/handback.js';
|
|
12
10
|
export { limitView, type LimitView } from './logic/limit.js';
|
|
13
11
|
export type { TickKind, ScrubberTick } from './logic/scrubber.js';
|
|
14
|
-
export { type TimedRow,
|
|
12
|
+
export { type TimedRow, withTimedRows, timedRowsOf } from './logic/thread-cards.js';
|
|
15
13
|
export { EMPTY_TARGET, readTimelineTarget, type TimelineTarget } from './logic/timeline-target.js';
|
|
16
14
|
export { type TimelineRow, summarizeGroup, deriveTimelineRows, type SubagentBranch, type BaseMoved } from './logic/timeline.js';
|
|
17
|
-
export { toolSummary,
|
|
18
|
-
export { MENTION_DRAG_TYPE, LEGACY_MENTION_DRAG_TYPE,
|
|
19
|
-
export { defaultProvider, selectionFor, startingSelection, accountFor, chatPreferencesPayload, useChatPreferences, readChatPreferences, CHAT_PREFERENCES_KEY,
|
|
15
|
+
export { toolSummary, type FileChange } from './logic/tools.js';
|
|
16
|
+
export { MENTION_DRAG_TYPE, LEGACY_MENTION_DRAG_TYPE, carriesMentions, droppedMentions, writeMentionDrag, chipText, type MentionQuery, type ChipSegment } from './mentions.js';
|
|
17
|
+
export { defaultProvider, selectionFor, startingSelection, accountFor, chatPreferencesPayload, useChatPreferences, readChatPreferences, CHAT_PREFERENCES_KEY, reloadChatPreferences, rememberChatPreferences, rememberReadingWidth, rememberSendKey, rememberChatSelection, type ChatPreferences } from './preferences.js';
|
|
20
18
|
export type { HoldRelease } from './prompt-hold.js';
|
|
21
19
|
export { CHAT_READING_WIDTHS, readingWidthLabel, readingWidthHint, type ChatReadingWidth } from './reading-width.js';
|
|
22
20
|
export { recentChatMessages, recentSubagentMessages, type RecentChatMessage, type RecentChatMessages } from './recent-messages.js';
|
|
@@ -26,6 +24,5 @@ export type { StashedAttachment, StashedPrompt, StashedQuote } from './stash.js'
|
|
|
26
24
|
export { subagentTitle, summaryWordOf, stopOf, type SubagentStop } from './subagent-list.js';
|
|
27
25
|
export { useSubagentSupport } from './subagent-support.js';
|
|
28
26
|
export { crumbOf, openFromMain, composerWrites, canOpenSubagent, useSubagentView, useSubagentTrail, useSubagentItem, type SubagentStep, type SubagentTrail } from './subagent-view.js';
|
|
29
|
-
export { THUMBNAIL_PX, useUploadThumbnail } from './thumbnails.js';
|
|
30
27
|
export { stepTimelineMessage, jumpToTimelineItem, type TurnTarget, registerTurnJumper, jumpToTimelineTurn, type ItemJumpOptions } from './timeline-scroll.js';
|
|
31
28
|
export { type StatusWord, statusLookOf, taskStatusWord, type StatusLook, type TaskState } from '../agents/status-look.js';
|
package/dist/chat/index.js
CHANGED
|
@@ -1,8 +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 { useAccountChoice } from './account-choice.js';
|
|
3
3
|
export { transportActions, actionsOf, useChatActions } from './actions.js';
|
|
4
|
-
export { backgroundCounts } from './activity.js';
|
|
5
|
-
export { isImageAttachment, fileBadge, formatBytes, checkAttachmentLimits, filesOf, readAttachments, readStoredAttachments, uploadPreviewUrl, uploadBytes } from './attachments.js';
|
|
6
4
|
export { ChatClient } from './chat-client.js';
|
|
7
5
|
export { sendFromComposer } from './composer-send.js';
|
|
8
6
|
export { EMPTY_DRAFT, useDrafts, readDraft, writeDraft, offerDraft } from './drafts.js';
|
|
@@ -10,12 +8,12 @@ export { stripAnsi } from './logic/ansi.js';
|
|
|
10
8
|
export { FORKABLE_PROVIDERS, forkRefusal, summaryRefusal, lastSettledTurn, forkPointOf, forkPointLabel, forkShapes, forkPayload, branchRefusal } from './logic/fork.js';
|
|
11
9
|
export { handbackReportOf } from './logic/handback.js';
|
|
12
10
|
export { limitView } from './logic/limit.js';
|
|
13
|
-
export {
|
|
11
|
+
export { withTimedRows, timedRowsOf } from './logic/thread-cards.js';
|
|
14
12
|
export { EMPTY_TARGET, readTimelineTarget } from './logic/timeline-target.js';
|
|
15
13
|
export { summarizeGroup, deriveTimelineRows } from './logic/timeline.js';
|
|
16
|
-
export { toolSummary
|
|
17
|
-
export { MENTION_DRAG_TYPE, LEGACY_MENTION_DRAG_TYPE,
|
|
18
|
-
export { defaultProvider, selectionFor, startingSelection, accountFor, chatPreferencesPayload, useChatPreferences, readChatPreferences, CHAT_PREFERENCES_KEY,
|
|
14
|
+
export { toolSummary } from './logic/tools.js';
|
|
15
|
+
export { MENTION_DRAG_TYPE, LEGACY_MENTION_DRAG_TYPE, carriesMentions, droppedMentions, writeMentionDrag, chipText } from './mentions.js';
|
|
16
|
+
export { defaultProvider, selectionFor, startingSelection, accountFor, chatPreferencesPayload, useChatPreferences, readChatPreferences, CHAT_PREFERENCES_KEY, reloadChatPreferences, rememberChatPreferences, rememberReadingWidth, rememberSendKey, rememberChatSelection } from './preferences.js';
|
|
19
17
|
export { CHAT_READING_WIDTHS, readingWidthLabel, readingWidthHint } from './reading-width.js';
|
|
20
18
|
export { recentChatMessages, recentSubagentMessages } from './recent-messages.js';
|
|
21
19
|
export { RUNTIME_MODES, runtimeModeLabel, runtimeModeHint } from './runtime-modes.js';
|
|
@@ -23,6 +21,5 @@ export { CHAT_SEND_KEYS, SEND_AND_NEW_SHORTCUT, sendKeyLabel, sendKeyHint } from
|
|
|
23
21
|
export { subagentTitle, summaryWordOf, stopOf } from './subagent-list.js';
|
|
24
22
|
export { useSubagentSupport } from './subagent-support.js';
|
|
25
23
|
export { crumbOf, openFromMain, composerWrites, canOpenSubagent, useSubagentView, useSubagentTrail, useSubagentItem } from './subagent-view.js';
|
|
26
|
-
export { THUMBNAIL_PX, useUploadThumbnail } from './thumbnails.js';
|
|
27
24
|
export { stepTimelineMessage, jumpToTimelineItem, registerTurnJumper, jumpToTimelineTurn } from './timeline-scroll.js';
|
|
28
25
|
export { statusLookOf, taskStatusWord } from '../agents/status-look.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { abortedByEnd, abortedByMachine } from '@adecore/agent-contracts';
|
|
2
|
+
/* English like the other prompts the CLI gets, and it reaches the thread as the person's own message, which it is. */
|
|
3
|
+
export const CONTINUE_PROMPT = 'Continue.';
|
|
4
|
+
/*
|
|
5
|
+
* The turn a person stopped last, when nothing has happened since and Continue can pick it up; null otherwise.
|
|
6
|
+
* A paused queue, a limit and a paused goal have their own way out, a turn the machine ended has its own resume,
|
|
7
|
+
* and a request that waits on a person comes first.
|
|
8
|
+
*/
|
|
9
|
+
export function continuableTurn(info, items) {
|
|
10
|
+
if (info.activeTurnId !== null ||
|
|
11
|
+
info.status === 'needs-you' ||
|
|
12
|
+
(info.queue ?? []).length > 0 ||
|
|
13
|
+
info.limit !== undefined ||
|
|
14
|
+
info.goal?.status === 'paused') {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const index = items.findLastIndex((item) => item.kind === 'turn');
|
|
18
|
+
const turn = items[index];
|
|
19
|
+
if (turn?.kind !== 'turn' || turn.state !== 'aborted' || turn.limit !== undefined || abortedByMachine(turn, items) || abortedByEnd(turn, items)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// A message of this turn sits after the turn item too, so only one sent after the turn ended counts.
|
|
23
|
+
const endedAt = turn.endedAt ?? turn.createdAt;
|
|
24
|
+
if (items.slice(index + 1).some((item) => item.kind === 'user' && item.createdAt > endedAt)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return turn.id;
|
|
28
|
+
}
|
|
@@ -9,4 +9,3 @@ export declare function cardRows(cards: readonly ThreadCard[]): TimedRow[];
|
|
|
9
9
|
export declare function visualRows(visuals: readonly ChatVisual[], heldFrom?: number | null): TimedRow[];
|
|
10
10
|
export declare function withTimedRows(rows: TimelineRow[], timed: readonly TimedRow[]): TimelineRow[];
|
|
11
11
|
export declare function timedRowsOf(cards: readonly ThreadCard[], visuals: readonly ChatVisual[] | null, heldFrom?: number | null): TimedRow[];
|
|
12
|
-
export declare function withThreadCards(rows: TimelineRow[], cards: readonly ThreadCard[]): TimelineRow[];
|
|
@@ -77,7 +77,3 @@ export function withTimedRows(rows, timed) {
|
|
|
77
77
|
export function timedRowsOf(cards, visuals, heldFrom = null) {
|
|
78
78
|
return [...(visuals === null ? [] : visualRows(visuals, heldFrom)), ...cardRows(cards)];
|
|
79
79
|
}
|
|
80
|
-
/* The thread's rows with an app's cards among them, the way `withTimedRows` places them. */
|
|
81
|
-
export function withThreadCards(rows, cards) {
|
|
82
|
-
return withTimedRows(rows, cardRows(cards));
|
|
83
|
-
}
|
|
@@ -23,7 +23,6 @@ export declare function chatPreferencesPayload(preferences: ChatPreferences, sco
|
|
|
23
23
|
export declare const useChatPreferences: import("zustand").UseBoundStore<import("zustand").StoreApi<ChatPreferences>>;
|
|
24
24
|
export declare function readChatPreferences(): ChatPreferences;
|
|
25
25
|
export declare let CHAT_PREFERENCES_KEY: string;
|
|
26
|
-
export declare function chatPreferencesKey(): string;
|
|
27
26
|
export declare function reloadChatPreferences(): void;
|
|
28
27
|
export declare function rememberChatPreferences(patch: Partial<ChatPreferences>): void;
|
|
29
28
|
export declare function rememberReadingWidth(readingWidth: ChatReadingWidth): void;
|
package/dist/chat/preferences.js
CHANGED
|
@@ -107,9 +107,6 @@ export let CHAT_PREFERENCES_KEY = storageKey();
|
|
|
107
107
|
onChatStorageConfiguration(() => {
|
|
108
108
|
CHAT_PREFERENCES_KEY = storageKey();
|
|
109
109
|
});
|
|
110
|
-
export function chatPreferencesKey() {
|
|
111
|
-
return storageKey();
|
|
112
|
-
}
|
|
113
110
|
/* Reads the remembered default again, once another window of the same origin wrote it. */
|
|
114
111
|
export function reloadChatPreferences() {
|
|
115
112
|
useChatPreferences.setState(storage.read(), true);
|
package/dist/chat/stash.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface StashedPrompt {
|
|
|
11
11
|
text: string;
|
|
12
12
|
mentions: string[];
|
|
13
13
|
skills: string[];
|
|
14
|
+
chats: string[];
|
|
14
15
|
attachments: StashedAttachment[];
|
|
15
16
|
quote?: StashedQuote;
|
|
16
17
|
createdAt: number;
|
|
@@ -23,6 +24,11 @@ export interface StashedQuote {
|
|
|
23
24
|
export declare function parseStash(raw: string | null): StashedPrompt[];
|
|
24
25
|
export declare function withStashed(prompts: StashedPrompt[], entry: StashedPrompt): StashedPrompt[];
|
|
25
26
|
export declare function stashedFrom(draft: ChatDraft, id: string, now: number, chatId: string): StashedPrompt | null;
|
|
27
|
+
export declare const STASH_CHATS_LIMIT = 16;
|
|
28
|
+
export declare function restorableChats(stashed: readonly string[], referable: ReadonlySet<string>, chatId: string): {
|
|
29
|
+
chats: string[];
|
|
30
|
+
dropped: number;
|
|
31
|
+
};
|
|
26
32
|
export declare const useStash: import("zustand").UseBoundStore<import("zustand").StoreApi<{
|
|
27
33
|
prompts: StashedPrompt[];
|
|
28
34
|
}>>;
|
package/dist/chat/stash.js
CHANGED
|
@@ -51,6 +51,7 @@ export function parseStash(raw) {
|
|
|
51
51
|
text: entry.text,
|
|
52
52
|
mentions: strings(entry.mentions),
|
|
53
53
|
skills: strings(entry.skills),
|
|
54
|
+
chats: strings(entry.chats),
|
|
54
55
|
attachments: attachments(entry.attachments),
|
|
55
56
|
...stashedQuoteField(entry.quote),
|
|
56
57
|
createdAt: typeof entry.createdAt === 'number' ? entry.createdAt : 0
|
|
@@ -80,10 +81,22 @@ export function stashedFrom(draft, id, now, chatId) {
|
|
|
80
81
|
...(kept ? { quote: { text: draft.quote, chatId, itemId: draft.quoteSource } } : {}),
|
|
81
82
|
mentions: [...draft.mentions],
|
|
82
83
|
skills: [...draft.skills],
|
|
84
|
+
chats: [...draft.chats],
|
|
83
85
|
attachments: draft.attachments.map((attachment) => ({ name: attachment.name, mime: attachment.mime, size: uploadBytes(attachment) })),
|
|
84
86
|
createdAt: now
|
|
85
87
|
};
|
|
86
88
|
}
|
|
89
|
+
/* The limit of `chats` in a send. */
|
|
90
|
+
export const STASH_CHATS_LIMIT = 16;
|
|
91
|
+
/*
|
|
92
|
+
* The attached chats a composer may take back: the ones it can still reference, never its own chat, in the
|
|
93
|
+
* order they were picked. `dropped` counts the chats left out because the project no longer has them.
|
|
94
|
+
*/
|
|
95
|
+
export function restorableChats(stashed, referable, chatId) {
|
|
96
|
+
const others = stashed.filter((id) => id !== chatId);
|
|
97
|
+
const chats = others.filter((id) => referable.has(id));
|
|
98
|
+
return { chats: chats.slice(0, STASH_CHATS_LIMIT), dropped: others.length - chats.length };
|
|
99
|
+
}
|
|
87
100
|
const storage = persistedJson(storageKey, parseStash, [], () => chatStorageLegacyKeys('stash'));
|
|
88
101
|
/* Prompts put aside for later can move to another chat, so the shelf is shared across chats. */
|
|
89
102
|
export const useStash = createChatStore(() => ({ prompts: [] }), (store) => store.setState({ prompts: storage.read() }));
|
package/dist/chat/ui/Composer.js
CHANGED
|
@@ -35,6 +35,7 @@ import { useComposerDraft } from './composer/use-composer-draft.js';
|
|
|
35
35
|
import { useComposerMenus } from './composer/use-composer-menus.js';
|
|
36
36
|
import { useComposerSubmit } from './composer/use-composer-submit.js';
|
|
37
37
|
import { LEAVE_DROPS_TO_THE_CARD, useDraftFiles } from './composer/use-draft-files.js';
|
|
38
|
+
import { useContinuableTurn } from './composer/use-continuable-turn.js';
|
|
38
39
|
import { useNotice } from './composer/use-notice.js';
|
|
39
40
|
import { usePendingRequests } from './composer/use-pending-requests.js';
|
|
40
41
|
import { usePromptHistory } from './composer/use-prompt-history.js';
|
|
@@ -73,7 +74,11 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
73
74
|
const files = useDraftFiles({ capabilities, providerName: provider?.name, writable, draft: box, inputRef, setNotice: notice.setNotice });
|
|
74
75
|
const controls = useChatControls({ chatId, info, notice, onRetarget });
|
|
75
76
|
const guard = promptGuard(withQuote(draft.quote, draft.text));
|
|
76
|
-
const
|
|
77
|
+
const continuable = useContinuableTurn(chatId, info);
|
|
78
|
+
// The stopped turn something went out for, since the chat takes a moment to answer and Continue must not stand there meanwhile.
|
|
79
|
+
const [sentFor, setSentFor] = useState(null);
|
|
80
|
+
const extraPrompts = host.prompts.useExtra(scope.id, chatId);
|
|
81
|
+
const { submit, continueTurn, restoreStashed, toggleStash } = useComposerSubmit({
|
|
77
82
|
chatId,
|
|
78
83
|
writable,
|
|
79
84
|
guard,
|
|
@@ -86,7 +91,20 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
86
91
|
hold,
|
|
87
92
|
inputRef,
|
|
88
93
|
setNotice: notice.setNotice,
|
|
89
|
-
onSend
|
|
94
|
+
onSend: (text, extras) => {
|
|
95
|
+
setSentFor(continuable);
|
|
96
|
+
const sent = onSend(text, extras);
|
|
97
|
+
// A send the host did not take leaves Continue where it was; the original promise still reaches its caller.
|
|
98
|
+
void Promise.resolve(sent).then((taken) => {
|
|
99
|
+
if (taken === false) {
|
|
100
|
+
setSentFor(null);
|
|
101
|
+
}
|
|
102
|
+
}, (error) => {
|
|
103
|
+
setSentFor(null);
|
|
104
|
+
throw error;
|
|
105
|
+
});
|
|
106
|
+
return sent;
|
|
107
|
+
}
|
|
90
108
|
});
|
|
91
109
|
const { takingBack, takeBack } = useTakeBack({
|
|
92
110
|
chatId,
|
|
@@ -135,7 +153,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
135
153
|
};
|
|
136
154
|
return (_jsxs("div", { className: "chat-composer-content pointer-events-none relative z-10 w-full", children: [!atEnd && !readOnly && (_jsx("div", { className: "absolute inset-x-0 bottom-full mb-2 flex justify-center", children: _jsx(Tooltip, { label: t('composer.jumpToEnd'), name: true, children: _jsx(Surface, { render: _jsx("button", { onClick: () => scrollTimelineToEnd(chatId) }), className: "pointer-events-auto grid h-8 w-8 place-items-center rounded-full text-text-muted hover:text-text", children: _jsx(Icon, { icon: ChevronDown, size: 16 }) }) }) })), _jsx(ChatActivity, { chatId: chatId }), _jsx("div", { className: clsx('@container/composer pointer-events-auto flex flex-col overflow-hidden rounded-2xl border shadow-float backdrop-blur-[14px] focus-ring-within', files.dragging
|
|
137
155
|
? 'border-accent bg-[color-mix(in_srgb,var(--accent-soft)_60%,var(--surface-raised))]'
|
|
138
|
-
: 'border-border bg-[color-mix(in_srgb,var(--surface-raised)_92%,transparent)]'), ...files.dropTarget, children: _jsx(PromptComposer, { chatId: chatId, pending: pending, focused: focused, onShowElsewhere: answerPromptsElsewhere, disabled: disabled, hasDraft: !empty, denyReason: capabilities?.denyReason === true, onAllAnswered: () => inputRef.current?.focus(), hold: hold, children: _jsxs("div", { className: clsx(writable && 'data-[text-cursor]:cursor-text'), onPointerOver: pointSurface, onMouseDown: pressSurface, children: [_jsx(LimitDock, { chatId: chatId, info: info }), _jsx(GoalDock, { chatId: chatId, info: info, goals: capabilities?.goals }), compactionOffer && (_jsx(ResumeCompactionDock, { tokens: compactionOffer.tokens, onCompact: controls.compact, onDismiss: () => dismissResumeCompaction(scope, chatId, compactionOffer.turnId) })), _jsx(ComposerMenu, { menus: menus }), queue.length > 0 && (_jsx(QueuedMessages, { chatId: chatId, queue: queue, paused: info.queuePaused === true, takingBack: takingBack !== null, onEdit: (message) => void takeBack(message) })), draft.quote !== '' && (_jsx(DraftQuote, { chatId: chatId, quote: draft.quote, source: draft.quoteSource, onRemove: box.removeQuote, onSourceGone: () => notice.setNotice(t('rows.user.quoteGone')) })), draft.chats.length > 0 && _jsx(DraftChats, { chats: draft.chats, onRemove: box.removeChat }), draft.attachments.length > 0 && _jsx(DraftAttachments, { attachments: draft.attachments, onRemove: box.removeAttachment }), _jsx(ComposerInput, { ref: inputRef, dictationToolbar: dictationToolbar, className: "composer-input select-text text-sm leading-normal text-text", value: draft.text, placeholder: placeholder, disabled: !writable, tabbable: focused, extensions: editorExtensions, aria: menus.inputAria, onChange: (value, selection, state) => {
|
|
156
|
+
: 'border-border bg-[color-mix(in_srgb,var(--surface-raised)_92%,transparent)]'), ...files.dropTarget, children: _jsx(PromptComposer, { chatId: chatId, pending: pending, focused: focused, onShowElsewhere: answerPromptsElsewhere, disabled: disabled, hasDraft: !empty, denyReason: capabilities?.denyReason === true, onAllAnswered: () => inputRef.current?.focus(), hold: hold, children: _jsxs("div", { className: clsx(writable && 'data-[text-cursor]:cursor-text'), onPointerOver: pointSurface, onMouseDown: pressSurface, children: [_jsx(LimitDock, { chatId: chatId, info: info }), _jsx(GoalDock, { chatId: chatId, info: info, goals: capabilities?.goals }), compactionOffer && (_jsx(ResumeCompactionDock, { tokens: compactionOffer.tokens, onCompact: controls.compact, onDismiss: () => dismissResumeCompaction(scope, chatId, compactionOffer.turnId) })), _jsx(ComposerMenu, { menus: menus }), queue.length > 0 && (_jsx(QueuedMessages, { chatId: chatId, queue: queue, paused: info.queuePaused === true, takingBack: takingBack !== null, onEdit: (message) => void takeBack(message) })), draft.quote !== '' && (_jsx(DraftQuote, { chatId: chatId, quote: draft.quote, source: draft.quoteSource, onRemove: box.removeQuote, onSourceGone: () => notice.setNotice(t('rows.user.quoteGone'), 'info') })), draft.chats.length > 0 && _jsx(DraftChats, { chats: draft.chats, onRemove: box.removeChat }), draft.attachments.length > 0 && _jsx(DraftAttachments, { attachments: draft.attachments, onRemove: box.removeAttachment }), _jsx(ComposerInput, { ref: inputRef, dictationToolbar: dictationToolbar, className: "composer-input select-text text-sm leading-normal text-text", value: draft.text, placeholder: placeholder, disabled: !writable, tabbable: focused, extensions: editorExtensions, aria: menus.inputAria, onChange: (value, selection, state) => {
|
|
139
157
|
hold.noteInput();
|
|
140
158
|
box.setText(value);
|
|
141
159
|
menus.setMenuIndex(0);
|
|
@@ -144,12 +162,15 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
144
162
|
}, onSelectionChange: menus.trackTriggers, onBlur: () => {
|
|
145
163
|
menus.close();
|
|
146
164
|
hold.release('blur');
|
|
147
|
-
}, onComposing: hold.setComposing, onKeyDown: onKeyDown, onPaste: files.onPaste }), notice.notice && (_jsx("div", { className:
|
|
165
|
+
}, onComposing: hold.setComposing, onKeyDown: onKeyDown, onPaste: files.onPaste }), notice.notice && (_jsx("div", { className: clsx('px-5 pb-1 text-xs @max-md/composer:px-3.5', notice.tone === 'error' ? 'text-status-error' : 'text-text-muted'), role: notice.tone === 'error' ? 'alert' : 'status', children: notice.notice })), guard.visible && (_jsxs("div", { className: clsx('px-5 pb-1 text-right text-xs tabular-nums @max-md/composer:px-3.5', guard.tooLong ? 'text-status-error' : 'text-text-faint'), children: [formatNumber(guard.count), " / ", formatNumber(PROMPT_MAX_CHARS), guard.tooLong && ` ${t('composer.tooLong')}`] })), _jsx(ComposerControls, { chatId: chatId, info: info, providerFixed: providerFixed, writable: writable, attachable: capabilities?.attachments !== false, controls: controls, send: {
|
|
148
166
|
visible: !busy || !empty,
|
|
149
167
|
disabled: empty || !writable || guard.tooLong,
|
|
150
168
|
label: t(sendLabelKey(busy, steerable, sendDelivery)),
|
|
151
169
|
kbd: sendShortcut(sendKey, draft.text.includes('\n')),
|
|
152
|
-
onSend: (alternate) => submit(alternate ? 'alternate' : 'default')
|
|
170
|
+
onSend: (alternate) => submit(alternate ? 'alternate' : 'default'),
|
|
171
|
+
onContinue: continuable !== null && continuable !== sentFor && empty && writable && pending.length === 0 && extraPrompts.length === 0
|
|
172
|
+
? continueTurn
|
|
173
|
+
: null
|
|
153
174
|
}, dictationToolbar: setDictationToolbar, onFiles: (picked) => {
|
|
154
175
|
files.addFiles(picked);
|
|
155
176
|
inputRef.current?.focus();
|
package/dist/chat/ui/EditDiff.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { LazyBoundary } from '@adecore/ui';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
4
5
|
import { lazyNamed } from '../../lazy.js';
|
|
5
6
|
import { diffSourceBudget, legacyDiffBudget } from './diff/diff-source-budget.js';
|
|
@@ -17,7 +18,7 @@ export default function EditDiff({ change, allowEditor = true, maxHeight }) {
|
|
|
17
18
|
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
18
19
|
const legacyAllowed = useMemo(() => legacyMode && legacyDiffBudget(change.before, change.after), [legacyMode, change.before, change.after]);
|
|
19
20
|
const omitted = _jsx(DiffOmitted, { path: change.path });
|
|
20
|
-
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.before, change.after], children: _jsx(
|
|
21
|
+
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.before, change.after], children: _jsx(LazyBoundary, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(LegacyEditDiff, { change: change, maxHeight: maxHeight }) }) })) : (omitted);
|
|
21
22
|
if (!sourceAllowed) {
|
|
22
23
|
return omitted;
|
|
23
24
|
}
|
|
@@ -25,5 +26,5 @@ export default function EditDiff({ change, allowEditor = true, maxHeight }) {
|
|
|
25
26
|
return fallback;
|
|
26
27
|
}
|
|
27
28
|
const unavailable = _jsx(DiffUnavailable, { path: change.path });
|
|
28
|
-
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.before, change.after, loadEngine], children: _jsx(
|
|
29
|
+
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.before, change.after, loadEngine], children: _jsx(LazyBoundary, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(EditorEditDiff, { change: change, loadEngine: loadEngine, fallback: unavailable, maxHeight: maxHeight, sourceChecked: true }) }) }));
|
|
29
30
|
}
|
package/dist/chat/ui/Pickers.js
CHANGED
|
@@ -112,5 +112,5 @@ export function StashPicker({ onRestore }) {
|
|
|
112
112
|
if (prompts.length === 0) {
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
|
-
return (_jsxs(Popover.Root, { children: [_jsx(Tooltip, { label: t('pickers.stash.title'), kbd: STASH_SHORTCUT, children: _jsxs(Popover.Trigger, { className: triggerClass, children: [_jsx(Icon, { icon: Bookmark, size: 12 }), _jsx("span", { className: "tabular-nums", children: prompts.length })] }) }), _jsx(Popover.Popup, { variant: "picker", side: "top", className: "w-80", children: _jsx("div", { className: "max-h-72 overflow-auto p-1", children: prompts.map((prompt) => (_jsxs("div", { className: "group/stash flex items-start gap-1", children: [_jsxs(Popover.Close, { className: "flex min-w-0 grow flex-col gap-0.5 rounded-md px-2 py-1.5 text-left text-xs text-text-muted hover:bg-surface-hover hover:text-text", onClick: () => onRestore(prompt), children: [_jsx("span", { className: "line-clamp-2 whitespace-pre-wrap", children: prompt.text || prompt.quote?.text || t('pickers.stash.noText') }), prompt.attachments.length > 0 && (_jsx("span", { className: "text-text-faint", children: t('pickers.stash.files', { count: prompt.attachments.length }) }))] }), _jsx(IconButton, { icon: Trash2, size: "xs", label: t('common.action.delete'), className: "mt-1 opacity-0 group-hover/stash:opacity-100 focus-visible:opacity-100", onClick: () => forgetStashed(prompt.id) })] }, prompt.id))) }) })] }));
|
|
115
|
+
return (_jsxs(Popover.Root, { children: [_jsx(Tooltip, { label: t('pickers.stash.title'), kbd: STASH_SHORTCUT, children: _jsxs(Popover.Trigger, { className: triggerClass, children: [_jsx(Icon, { icon: Bookmark, size: 12 }), _jsx("span", { className: "tabular-nums", children: prompts.length })] }) }), _jsx(Popover.Popup, { variant: "picker", side: "top", className: "w-80", children: _jsx("div", { className: "max-h-72 overflow-auto p-1", children: prompts.map((prompt) => (_jsxs("div", { className: "group/stash flex items-start gap-1", children: [_jsxs(Popover.Close, { className: "flex min-w-0 grow flex-col gap-0.5 rounded-md px-2 py-1.5 text-left text-xs text-text-muted hover:bg-surface-hover hover:text-text", onClick: () => onRestore(prompt), children: [_jsx("span", { className: "line-clamp-2 whitespace-pre-wrap", children: prompt.text || prompt.quote?.text || t('pickers.stash.noText') }), prompt.attachments.length > 0 && (_jsx("span", { className: "text-text-faint", children: t('pickers.stash.files', { count: prompt.attachments.length }) })), prompt.chats.length > 0 && _jsx("span", { className: "text-text-faint", children: t('pickers.stash.chats', { count: prompt.chats.length }) })] }), _jsx(IconButton, { icon: Trash2, size: "xs", label: t('common.action.delete'), className: "mt-1 opacity-0 group-hover/stash:opacity-100 focus-visible:opacity-100", onClick: () => forgetStashed(prompt.id) })] }, prompt.id))) }) })] }));
|
|
116
116
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { LazyBoundary } from '@adecore/ui';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
4
5
|
import { lazyNamed } from '../../lazy.js';
|
|
5
6
|
import { diffSourceBudget, legacyDiffBudget, patchSourceBudget } from './diff/diff-source-budget.js';
|
|
@@ -23,7 +24,7 @@ export default function UnifiedDiff(props) {
|
|
|
23
24
|
const legacyAllowed = useMemo(() => legacyMode && legacyDiffBudget(oldText, newText, change.diff), [legacyMode, oldText, newText, change.diff]);
|
|
24
25
|
const rawAllowed = useMemo(() => hasHunks || legacyDiffBudget('', '', change.diff), [hasHunks, change.diff]);
|
|
25
26
|
const omitted = _jsx(DiffOmitted, { path: change.path });
|
|
26
|
-
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.diff, oldText, newText, diffStyle], children: _jsx(
|
|
27
|
+
const fallback = legacyAllowed ? (_jsx(DiffFallbackBoundary, { fallback: omitted, resetKeys: [change.path, change.diff, oldText, newText, diffStyle], children: _jsx(LazyBoundary, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(LegacyUnifiedDiff, { ...props }) }) })) : (omitted);
|
|
27
28
|
if (!sourceAllowed) {
|
|
28
29
|
return omitted;
|
|
29
30
|
}
|
|
@@ -34,5 +35,5 @@ export default function UnifiedDiff(props) {
|
|
|
34
35
|
return fallback;
|
|
35
36
|
}
|
|
36
37
|
const unavailable = _jsx(DiffUnavailable, { path: change.path });
|
|
37
|
-
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.diff, oldText, newText, ignoreWhitespace, diffStyle, loadEngine], children: _jsx(
|
|
38
|
+
return (_jsx(DiffFallbackBoundary, { fallback: unavailable, resetKeys: [change.path, change.diff, oldText, newText, ignoreWhitespace, diffStyle, loadEngine], children: _jsx(LazyBoundary, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: _jsx(EditorPatchDiff, { change: change, sourceChecked: true, contents: contents, ignoreWhitespace: ignoreWhitespace, wrap: overflow === 'wrap', fill: fill, maxHeight: maxHeight, layout: diffStyle, loadEngine: loadEngine, fallback: unavailable }) }) }));
|
|
38
39
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { ArrowUp, Plus, Square } from 'lucide-react';
|
|
5
|
-
import { ErrorBoundary, Icon, Tooltip } from '@adecore/ui';
|
|
4
|
+
import { ArrowUp, Play, Plus, Square } from 'lucide-react';
|
|
5
|
+
import { Button, ErrorBoundary, Icon, Tooltip } from '@adecore/ui';
|
|
6
6
|
import { useChatHost } from '../../../host.js';
|
|
7
7
|
import { useChatScope } from '../../../scope.js';
|
|
8
8
|
import { useProviders } from '../../../state/providers.js';
|
|
@@ -30,5 +30,5 @@ export function ComposerControls({ chatId, info, providerFixed, writable, attach
|
|
|
30
30
|
return (_jsxs("div", { className: "flex items-center gap-2 pt-3 pr-3.5 pb-3.5 pl-5 @max-md/composer:gap-1.5 @max-md/composer:p-3 @max-md/composer:pt-2.5", children: [attachable && (_jsx(Tooltip, { label: t('composer.attach'), name: true, children: _jsx("button", { type: "button", className: "grid h-8 w-8 shrink-0 place-items-center rounded-full border border-border text-text-muted hover:bg-surface-hover hover:text-text disabled:opacity-50", disabled: !writable, onClick: () => fileInputRef.current?.click(), children: _jsx(Icon, { icon: Plus, size: 16 }) }) })), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, hidden: true, onChange: (e) => {
|
|
31
31
|
onFiles(Array.from(e.target.files ?? []));
|
|
32
32
|
e.target.value = '';
|
|
33
|
-
} }), _jsx(RunSettings, { disabled: controls.configurationPending || !writable, providers: pickable, provider: info.provider, selection: info.selection, model: model, runtimeMode: info.runtimeMode, effectiveRuntimeMode: info.effectiveRuntimeMode, permissionMode: info.permissionMode, open: controls.modelPickerOpen, onOpenChange: controls.setModelPickerOpen, onModel: controls.chooseModel, onOption: (id, value) => controls.configure({ selection: { ...info.selection, options: { ...info.selection.options, [id]: value } } }), onMode: (runtimeMode) => controls.configure({ runtimeMode }), usage: info.usage, compactDisabled: busy || !writable, onCompact: controls.compact, account: accountChoice === null ? null : { choice: accountChoice, started, onPick: controls.chooseAccount } }), _jsx(StashPicker, { onRestore: onRestore }), Slot !== null && (_jsx(ErrorBoundary, { label: t('composer.slotFailed'), resetKeys: [chatId], compact: true, className: "relative rounded-lg", children: _jsx(Slot, { scopeId: scope.id, chatId: chatId, disabled: !writable, insert: insert }) })), _jsx("span", { className: "grow" }), _jsxs("div", { className: "flex h-9 shrink-0 items-center gap-0.5 rounded-full bg-surface-hover", children: [_jsx("div", { ref: dictationToolbar, className: "flex items-center pl-1 empty:hidden [&_.icon-btn]:rounded-full" }), busy && (_jsx(Tooltip, { label: composerStopLabel(), name: true, children: _jsx("button", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-status-error text-accent-text", onClick: (event) => controls.stop(event.shiftKey), children: _jsx(Icon, { icon: Square, size: 16 }) }) })), send.visible && (_jsx(Tooltip, { label: send.label, kbd: send.kbd, name: true, children: _jsx("button", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent text-accent-text hover:brightness-90 disabled:opacity-50 disabled:hover:brightness-100", disabled: send.disabled, onClick: (event) => send.onSend(event.altKey), children: _jsx(Icon, { icon: ArrowUp, size: 16 }) }) }))] })] }));
|
|
33
|
+
} }), _jsx(RunSettings, { disabled: controls.configurationPending || !writable, providers: pickable, provider: info.provider, selection: info.selection, model: model, runtimeMode: info.runtimeMode, effectiveRuntimeMode: info.effectiveRuntimeMode, permissionMode: info.permissionMode, open: controls.modelPickerOpen, onOpenChange: controls.setModelPickerOpen, onModel: controls.chooseModel, onOption: (id, value) => controls.configure({ selection: { ...info.selection, options: { ...info.selection.options, [id]: value } } }), onMode: (runtimeMode) => controls.configure({ runtimeMode }), usage: info.usage, compactDisabled: busy || !writable, onCompact: controls.compact, account: accountChoice === null ? null : { choice: accountChoice, started, onPick: controls.chooseAccount } }), _jsx(StashPicker, { onRestore: onRestore }), Slot !== null && (_jsx(ErrorBoundary, { label: t('composer.slotFailed'), resetKeys: [chatId], compact: true, className: "relative rounded-lg", children: _jsx(Slot, { scopeId: scope.id, chatId: chatId, disabled: !writable, insert: insert }) })), _jsx("span", { className: "grow" }), _jsxs("div", { className: "flex h-9 shrink-0 items-center gap-0.5 rounded-full bg-surface-hover", children: [_jsx("div", { ref: dictationToolbar, className: "flex items-center pl-1 empty:hidden [&_.icon-btn]:rounded-full" }), busy && (_jsx(Tooltip, { label: composerStopLabel(), name: true, children: _jsx("button", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-status-error text-accent-text", onClick: (event) => controls.stop(event.shiftKey), children: _jsx(Icon, { icon: Square, size: 16 }) }) })), send.onContinue !== null && (_jsx(Tooltip, { label: t('composer.continueHint'), children: _jsxs(Button, { variant: "primary", shape: "pill", disabled: controls.configurationPending, onClick: send.onContinue, children: [_jsx(Icon, { icon: Play, size: 14 }), t('composer.continue')] }) })), send.onContinue === null && send.visible && (_jsx(Tooltip, { label: send.label, kbd: send.kbd, name: true, children: _jsx("button", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent text-accent-text hover:brightness-90 disabled:opacity-50 disabled:hover:brightness-100", disabled: send.disabled, onClick: (event) => send.onSend(event.altKey), children: _jsx(Icon, { icon: ArrowUp, size: 16 }) }) }))] })] }));
|
|
34
34
|
}
|
|
@@ -5,6 +5,7 @@ import type { PromptGuard } from '../../guards.js';
|
|
|
5
5
|
import { type StashedPrompt } from '../../stash.js';
|
|
6
6
|
import type { ComposerInputHandle } from '../ComposerInput.js';
|
|
7
7
|
import type { ChatControls } from './use-chat-controls.js';
|
|
8
|
+
import type { NoticeTone } from './use-notice.js';
|
|
8
9
|
import type { ComposerDraft } from './use-composer-draft.js';
|
|
9
10
|
import type { ComposerMenus } from './use-composer-menus.js';
|
|
10
11
|
import type { PromptHistory } from './use-prompt-history.js';
|
|
@@ -21,11 +22,12 @@ interface SubmitInput {
|
|
|
21
22
|
history: PromptHistory;
|
|
22
23
|
hold: ComposerPromptHold;
|
|
23
24
|
inputRef: RefObject<ComposerInputHandle | null>;
|
|
24
|
-
setNotice(notice: string): void;
|
|
25
|
+
setNotice(notice: string, tone?: NoticeTone): void;
|
|
25
26
|
onSend(text: string, extras: ChatSendExtras): void | Promise<boolean>;
|
|
26
27
|
}
|
|
27
28
|
export declare function useComposerSubmit({ chatId, writable, guard, steerable, sendDelivery, draft: box, menus, controls, history, hold, inputRef, setNotice, onSend }: SubmitInput): {
|
|
28
29
|
submit: (how?: ComposerSend) => void;
|
|
30
|
+
continueTurn: () => void;
|
|
29
31
|
restoreStashed: (prompt: StashedPrompt) => void;
|
|
30
32
|
toggleStash: () => void;
|
|
31
33
|
};
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import { useChatHost } from '../../../host.js';
|
|
3
4
|
import { useChatScope } from '../../../scope.js';
|
|
4
5
|
import { checkAttachmentLimits, uploadBytes } from '../../attachments.js';
|
|
5
6
|
import { registerComposerSender } from '../../composer-send.js';
|
|
6
7
|
import { isEmptyDraft } from '../../drafts.js';
|
|
8
|
+
import { CONTINUE_PROMPT } from '../../logic/continue-after-stop.js';
|
|
7
9
|
import { presentMentions, presentSkills } from '../../mentions.js';
|
|
8
10
|
import { withQuote } from '../../quote.js';
|
|
9
|
-
import { stashDraft, useStash } from '../../stash.js';
|
|
11
|
+
import { restorableChats, stashDraft, useStash } from '../../stash.js';
|
|
10
12
|
import { scrollTimelineToEnd } from '../../timeline-scroll.js';
|
|
11
13
|
/* Sends the draft, or runs the command it names, and puts it away in the stash and back. */
|
|
12
14
|
export function useComposerSubmit({ chatId, writable, guard, steerable, sendDelivery, draft: box, menus, controls, history, hold, inputRef, setNotice, onSend }) {
|
|
15
|
+
const { t } = useTranslation('agent-chat');
|
|
13
16
|
const host = useChatHost();
|
|
14
17
|
const scope = useChatScope();
|
|
15
18
|
const { draft, setDraft } = box;
|
|
19
|
+
const referable = host.useReferableChats();
|
|
16
20
|
/* Opens the host's new chat once the host took the message; a host that answers nothing took it. */
|
|
17
21
|
const afterSend = (sent, andNew) => {
|
|
18
22
|
const openNewChat = host.openNewChat;
|
|
@@ -79,22 +83,36 @@ export function useComposerSubmit({ chatId, writable, guard, steerable, sendDeli
|
|
|
79
83
|
});
|
|
80
84
|
useEffect(() => registerComposerSender(scope.keyOf(chatId), (how) => submitRef.current(how)), [scope, chatId]);
|
|
81
85
|
/*
|
|
82
|
-
* A stashed prompt comes back as text, mentions and
|
|
83
|
-
* keeps leading to its reply in the chat it came from, and is plain
|
|
86
|
+
* A stashed prompt comes back as text, mentions, skills and the chats this project still has; its
|
|
87
|
+
* files were never kept. Its quote keeps leading to its reply in the chat it came from, and is plain
|
|
88
|
+
* text in any other.
|
|
84
89
|
*/
|
|
85
90
|
const restoreStashed = (prompt) => {
|
|
86
91
|
const stashedQuote = prompt.quote;
|
|
87
92
|
const here = stashedQuote !== undefined && stashedQuote.chatId === chatId;
|
|
93
|
+
const { chats, dropped } = restorableChats(prompt.chats, new Set(referable.map((chat) => chat.id)), chatId);
|
|
88
94
|
setDraft((current) => ({
|
|
89
95
|
...current,
|
|
90
96
|
text: stashedQuote === undefined || here ? prompt.text : withQuote(stashedQuote.text, prompt.text),
|
|
91
97
|
mentions: [...prompt.mentions],
|
|
92
98
|
skills: [...prompt.skills],
|
|
99
|
+
chats,
|
|
93
100
|
...(here ? { quote: stashedQuote.text, quoteSource: stashedQuote.itemId } : {})
|
|
94
101
|
}));
|
|
102
|
+
if (dropped > 0) {
|
|
103
|
+
setNotice(t('composer.notice.stashChatsDropped', { count: dropped }), 'info');
|
|
104
|
+
}
|
|
95
105
|
history.forget();
|
|
96
106
|
inputRef.current?.focus();
|
|
97
107
|
};
|
|
108
|
+
/* 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. */
|
|
109
|
+
const continueTurn = () => {
|
|
110
|
+
if (!writable) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
scrollTimelineToEnd(chatId, 'instant');
|
|
114
|
+
afterSend(onSend(CONTINUE_PROMPT, {}), false);
|
|
115
|
+
};
|
|
98
116
|
/* Cmd+S puts the draft away and clears the box; on an empty box the same key takes the last one back. */
|
|
99
117
|
const toggleStash = () => {
|
|
100
118
|
if (isEmptyDraft(draft)) {
|
|
@@ -109,5 +127,5 @@ export function useComposerSubmit({ chatId, writable, guard, steerable, sendDeli
|
|
|
109
127
|
menus.close();
|
|
110
128
|
}
|
|
111
129
|
};
|
|
112
|
-
return { submit, restoreStashed, toggleStash };
|
|
130
|
+
return { submit, continueTurn, restoreStashed, toggleStash };
|
|
113
131
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useChatRow } from '../../../state/chats.js';
|
|
3
|
+
import { continuableTurn } from '../../logic/continue-after-stop.js';
|
|
4
|
+
/* The turn a person stopped that Continue would pick up, or null when there is none. */
|
|
5
|
+
export function useContinuableTurn(chatId, info) {
|
|
6
|
+
const order = useChatRow(chatId, (row) => row?.order);
|
|
7
|
+
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
8
|
+
return useMemo(() => {
|
|
9
|
+
const items = (order ?? []).map((id) => structure?.[id]).filter((item) => item !== undefined);
|
|
10
|
+
return continuableTurn(info, items);
|
|
11
|
+
}, [info, order, structure]);
|
|
12
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
export type NoticeTone = 'error' | 'info';
|
|
1
2
|
export interface ComposerNotice {
|
|
2
3
|
notice: string | null;
|
|
3
|
-
|
|
4
|
+
tone: NoticeTone;
|
|
5
|
+
setNotice(notice: string | null, tone?: NoticeTone): void;
|
|
4
6
|
showError(error: unknown): void;
|
|
5
7
|
}
|
|
6
8
|
export declare function useNotice(): ComposerNotice;
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
const NOTICE_MS = 4000;
|
|
3
3
|
/* A line under the box that says what went wrong, gone again after a few seconds. */
|
|
4
4
|
export function useNotice() {
|
|
5
|
-
const [
|
|
5
|
+
const [shown, setShown] = useState(null);
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
if (!
|
|
7
|
+
if (!shown) {
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
|
-
const timer = setTimeout(() =>
|
|
10
|
+
const timer = setTimeout(() => setShown(null), NOTICE_MS);
|
|
11
11
|
return () => clearTimeout(timer);
|
|
12
|
-
}, [
|
|
12
|
+
}, [shown]);
|
|
13
|
+
const setNotice = useCallback((notice, tone = 'error') => setShown(notice ? { text: notice, tone } : null), []);
|
|
13
14
|
return {
|
|
14
|
-
notice,
|
|
15
|
+
notice: shown?.text ?? null,
|
|
16
|
+
tone: shown?.tone ?? 'error',
|
|
15
17
|
setNotice,
|
|
16
18
|
showError: (error) => setNotice(error instanceof Error ? error.message : String(error))
|
|
17
19
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { LazyBoundary } from '@adecore/ui';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { lazyNamed } from '../../../lazy.js';
|
|
5
5
|
const EditDiff = lazyNamed(() => import('../EditDiff.js'), 'default');
|
|
6
6
|
const UnifiedDiff = lazyNamed(() => import('../UnifiedDiff.js'), 'default');
|
|
7
7
|
export function ReviewDiff({ entry, maxHeight = 480 }) {
|
|
8
8
|
const { t } = useTranslation('agent-chat');
|
|
9
|
-
return (_jsx("div", { "data-review-entry": entry.key, children: _jsx(
|
|
9
|
+
return (_jsx("div", { "data-review-entry": entry.key, children: _jsx(LazyBoundary, { fallback: _jsx("p", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: entry.change.kind === 'edit' ? (_jsx(EditDiff, { change: entry.change.edit, maxHeight: maxHeight })) : (_jsx(UnifiedDiff, { change: entry.change.patch, contents: entry.change.contents, maxHeight: maxHeight })) }) }));
|
|
10
10
|
}
|
package/dist/chat/ui/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { AccountPill } from './AccountPill.js';
|
|
2
2
|
export { AnsiOutput } from './AnsiOutput.js';
|
|
3
|
-
export { BookmarkMarker } from './BookmarkMarker.js';
|
|
4
3
|
export { BookmarkSubmenu } from './BookmarkSubmenu.js';
|
|
5
4
|
export { ChatActivity, StatusIcon } from './ChatActivity.js';
|
|
6
5
|
export { ChatReferenceChip } from './ChatReferenceChip.js';
|
|
@@ -14,54 +13,36 @@ export { GoalDock, GoalPill } from './GoalDock.js';
|
|
|
14
13
|
export { ImageThumb, type ImageThumbProps } from './ImageView.js';
|
|
15
14
|
export { LimitDock, LimitPill } from './LimitState.js';
|
|
16
15
|
export { ReplyMarkdown, MessageMarkdown, Markdown } from './Markdown.js';
|
|
17
|
-
export {
|
|
18
|
-
export { MessageActions } from './MessageActions.js';
|
|
19
|
-
export { ModelPicker, StashPicker } from './Pickers.js';
|
|
20
|
-
export { type PromptHold, PromptComposer } from './PromptComposer.js';
|
|
21
|
-
export { QuoteButton } from './QuoteButton.js';
|
|
16
|
+
export { ModelPicker } from './Pickers.js';
|
|
22
17
|
export { ResumeCompactionDock } from './ResumeCompactionDock.js';
|
|
23
|
-
export {
|
|
24
|
-
export { RunSettings } from './RunSettings.js';
|
|
25
|
-
export { Scrubber, type CardChat } from './Scrubber.js';
|
|
26
|
-
export { SubagentTitleCrumb, SubagentBreadcrumb, SubagentActions } from './SubagentControls.js';
|
|
18
|
+
export { SubagentTitleCrumb, SubagentBreadcrumb } from './SubagentControls.js';
|
|
27
19
|
export { SubagentInfo } from './SubagentInfo.js';
|
|
28
|
-
export { SubagentStopButton } from './SubagentStopButton.js';
|
|
29
20
|
export { SubagentTimeline } from './SubagentTimeline.js';
|
|
30
21
|
export { Timeline } from './Timeline.js';
|
|
31
|
-
export { TimelineMenuPopup
|
|
22
|
+
export { TimelineMenuPopup } from './TimelineMenu.js';
|
|
32
23
|
export { default as UnifiedDiff } from './UnifiedDiff.js';
|
|
33
24
|
export { UploadThumb } from './UploadThumb.js';
|
|
34
|
-
export { VisualDialogs } from './VisualDialogs.js';
|
|
35
|
-
export { type VisualFrameProps, VisualFrame } from './VisualFrame.js';
|
|
36
25
|
export { WelcomeGreeting } from './Welcome.js';
|
|
37
|
-
export {
|
|
38
|
-
export { externalChange, markdownLanguage, composerEditorExtensions } from './composer/editor.js';
|
|
39
|
-
export { insertAtSelection } from './composer/insert.js';
|
|
40
|
-
export { listItemAt, enterAction, inOpenFence, recallDirection, type EnterAction, type EnterKeys, type ListItem, type EnterContext, type RecallContext } from './composer/keys.js';
|
|
26
|
+
export { externalChange, composerEditorExtensions } from './composer/editor.js';
|
|
41
27
|
export { createDiffSyntaxRuntime, diffSyntaxRuntime, type DiffSyntaxRuntime, type DiffSyntaxRuntimeOptions, type DiffSyntaxSession, type DiffSyntaxMapping, type DiffSourceTokenSession, diffSourceTokens } from './diff-syntax.js';
|
|
42
28
|
export { useDiffTheme } from './diff-theme.js';
|
|
43
29
|
export type { UnifiedDiffProps } from './diff/diff-props.js';
|
|
44
|
-
export { FileLinkContext, useFileLinkCwd
|
|
45
|
-
export { type ChatFindField, type FindReveal
|
|
30
|
+
export { FileLinkContext, useFileLinkCwd } from './file-links.js';
|
|
31
|
+
export { type ChatFindField, type FindReveal } from './find-reveal.js';
|
|
46
32
|
export type { DiffContents } from './full-diff.js';
|
|
47
33
|
export { type UiBlockFrameProps, UiBlockFrame, type UiBlockFix, type UiBlockFooterProps } from './intelligent-ui/UiBlockFrame.js';
|
|
48
|
-
export { UiFallbackPart, type UiFallbackProblem } from './intelligent-ui/UiFallbackPart.js';
|
|
49
34
|
export { UiReply } from './intelligent-ui/UiReply.js';
|
|
50
35
|
export { uiChartData, type UiChartSeries, type UiChartValues, type UiChartData } from './intelligent-ui/chart-data.js';
|
|
51
36
|
export { uiNodeLabel } from './intelligent-ui/node-text.js';
|
|
52
37
|
export { UI_RENDERERS } from './intelligent-ui/registry.js';
|
|
53
|
-
export type { UiRenderContext, UiRendererProps, UiLinkTarget, UiLink, UiAnswer, UiLiveStatus, UiLiveValue, UiRenderer, UiRenderers } from './intelligent-ui/render-context.js';
|
|
38
|
+
export type { UiRenderContext, UiRendererProps, UiLinkTarget, UiLink, UiPlacePoint, UiAnswer, UiLiveStatus, UiLiveValue, UiRenderer, UiRenderers } from './intelligent-ui/render-context.js';
|
|
54
39
|
export { uiTableColumns, type UiColumnKind, type UiTableColumn } from './intelligent-ui/table-data.js';
|
|
55
40
|
export type { QuoteTaker } from './quote-selection.js';
|
|
56
41
|
export { ReplyContext } from './reply-context.js';
|
|
57
42
|
export { ForksRow } from './rows/ForksRow.js';
|
|
58
43
|
export { GeneratedImageRow } from './rows/GeneratedImageRow.js';
|
|
59
|
-
export {
|
|
60
|
-
export { type RowContainerProps, RowContainer } from './rows/RowContainer.js';
|
|
61
|
-
export { Row, type RowProps } from './rows/Rows.js';
|
|
62
|
-
export { SubagentRow, SubagentBranchRow } from './rows/SubagentRow.js';
|
|
44
|
+
export { ReportRow, AssistantRow, NoteRow, AgentTurnRow, CompactionRow } from './rows/MessageRows.js';
|
|
63
45
|
export { TaskRow } from './rows/TaskRow.js';
|
|
64
|
-
export {
|
|
65
|
-
export { ToggleLine, WorkRow, RunningFor, WorkLiveRow, WorkGroupRow, TurnFoldRow, ChangedFilesRow, WorkingRow } from './rows/WorkRows.js';
|
|
46
|
+
export { WorkRow, WorkGroupRow, WorkingRow } from './rows/WorkRows.js';
|
|
66
47
|
export { WorkflowRow } from './rows/WorkflowRow.js';
|
|
67
48
|
export { useDiffSourceTokens } from './use-diff-source-tokens.js';
|