@jini-ai/chat 0.5.7 → 0.6.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/CHANGELOG.md +4 -0
- package/dist/core/ag-ui/entry.js +2 -0
- package/dist/core/assistant-client/durable-run-subscription.d.ts +4 -1
- package/dist/core/assistant-client/durable-run-subscription.js +13 -4
- package/dist/core/events.d.ts +3 -0
- package/dist/core/history-tool-metadata.d.ts +23 -0
- package/dist/core/history-tool-metadata.js +13 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/dist/core/persistence/title.d.ts +24 -0
- package/dist/core/persistence/title.js +60 -9
- package/dist/core/run-activity.d.ts +15 -2
- package/dist/core/run-activity.js +6 -4
- package/dist/core/run-events/entry.js +2 -0
- package/dist/core/tool-call-detail.d.ts +15 -0
- package/dist/core/tool-call-detail.js +18 -0
- package/dist/core/turn-usage.d.ts +42 -3
- package/dist/core/turn-usage.js +101 -4
- package/dist/core/usage-format.d.ts +10 -0
- package/dist/core/usage-format.js +15 -0
- package/dist/embed-widget/browser/lazy-loader.d.ts +34 -0
- package/dist/embed-widget/browser/lazy-loader.js +116 -0
- package/dist/embed-widget/react/dialog.hooks.d.ts +28 -0
- package/dist/embed-widget/react/dialog.hooks.js +54 -0
- package/dist/embed-widget/react/embed.d.ts +1 -0
- package/dist/embed-widget/react/embed.js +5 -3
- package/dist/embed-widget/react/header.d.ts +3 -1
- package/dist/embed-widget/react/header.hooks.d.ts +13 -0
- package/dist/embed-widget/react/header.hooks.js +32 -0
- package/dist/embed-widget/react/header.js +4 -14
- package/dist/embed-widget/vanilla/index.d.ts +63 -0
- package/dist/embed-widget/vanilla/index.js +319 -0
- package/dist/embed-widget/vanilla/state.d.ts +48 -0
- package/dist/embed-widget/vanilla/state.js +120 -0
- package/dist/embed-widget/vanilla/style.css +29 -0
- package/dist/react/components/A2uiSurfaceCard.d.ts +5 -1
- package/dist/react/components/A2uiSurfaceCard.js +9 -9
- package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +7 -0
- package/dist/react/components/AttachmentPreviewModal.hooks.js +1 -1
- package/dist/react/components/ChatFab.d.ts +4 -1
- package/dist/react/components/ChatFab.js +2 -2
- package/dist/react/components/ConversationList.d.ts +8 -1
- package/dist/react/components/ConversationList.hooks.d.ts +18 -0
- package/dist/react/components/ConversationList.hooks.js +25 -0
- package/dist/react/components/ConversationList.js +12 -5
- package/dist/react/components/ConversationList.rules.d.ts +25 -0
- package/dist/react/components/ConversationList.rules.js +19 -0
- package/dist/react/components/McpUiSurfaceCard.d.ts +1 -1
- package/dist/react/components/McpUiSurfaceCard.js +8 -4
- package/dist/react/components/MessageList.d.ts +1 -1
- package/dist/react/components/MessageList.hooks.d.ts +4 -0
- package/dist/react/components/MessageList.hooks.js +6 -1
- package/dist/react/components/MessageList.js +4 -3
- package/dist/react/components/MessageRow.d.ts +17 -0
- package/dist/react/components/MessageRow.js +54 -35
- package/dist/react/components/MessageRow.usage.d.ts +48 -0
- package/dist/react/components/MessageRow.usage.js +47 -0
- package/dist/react/components/MidRunUserMessage.d.ts +2 -12
- package/dist/react/components/MidRunUserMessage.js +4 -2
- package/dist/react/components/SentAttachment.d.ts +19 -0
- package/dist/react/components/SentAttachment.hooks.d.ts +44 -0
- package/dist/react/components/SentAttachment.hooks.js +56 -0
- package/dist/react/components/SentAttachment.js +14 -0
- package/dist/react/components/ToolCallGroup.d.ts +12 -0
- package/dist/react/components/ToolCallGroup.hooks.d.ts +20 -0
- package/dist/react/components/ToolCallGroup.hooks.js +23 -0
- package/dist/react/components/ToolCallGroup.js +13 -0
- package/dist/react/components/ToolCard.js +25 -17
- package/dist/react/components/mcp-ui-surface-state.d.ts +21 -0
- package/dist/react/components/mcp-ui-surface-state.js +26 -0
- package/dist/react/ext-event-renderer-registry.d.ts +7 -0
- package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
- package/dist/react/features/chat-pane/components/ChatPane.js +11 -6
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +1 -0
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +16 -10
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +4 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSuggestion.hooks.d.ts +9 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSuggestion.hooks.js +11 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSurface.hooks.d.ts +17 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneSurface.hooks.js +15 -0
- package/dist/react/features/chat-pane/rules.d.ts +14 -0
- package/dist/react/features/chat-pane/rules.js +18 -0
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +85 -1
- package/dist/react/features/chat-pane/types.d.ts +16 -1
- package/dist/react/hooks/useAnsweredCards.d.ts +20 -0
- package/dist/react/hooks/useAnsweredCards.js +23 -0
- package/dist/react/hooks/useConversation.d.ts +8 -0
- package/dist/react/hooks/useConversation.js +34 -0
- package/dist/react/hooks/useRunActivity.d.ts +5 -3
- package/dist/react/hooks/useRunActivity.js +4 -3
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +3 -0
- package/dist/react/message-blocks.d.ts +22 -3
- package/dist/react/message-blocks.js +25 -3
- package/dist/react/tool-call-display.d.ts +90 -0
- package/dist/react/tool-call-display.js +97 -0
- package/dist/store/ports.d.ts +25 -2
- package/dist/store/runtime/entry.d.ts +1 -0
- package/dist/store/runtime/entry.js +1 -0
- package/dist/store/runtime/history-tools.d.ts +27 -0
- package/dist/store/runtime/history-tools.js +169 -0
- package/dist/store/sql/store.js +52 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +22 -0
- package/package.json +48 -23
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { conversationUsage } from '../../core/turn-usage.js';
|
|
3
|
+
import { formatUsageCost } from '../../core/usage-format.js';
|
|
4
|
+
import { useT } from '../hooks/context.js';
|
|
5
|
+
/** A standalone row has one turn; MessageList supplies the full conversation projection. */
|
|
6
|
+
export function useMessageUsage({ message, summary }, _optional = {}) {
|
|
7
|
+
return useMemo(() => summary ?? conversationUsage({ messages: [message] }).get(message.id), [message, summary]);
|
|
8
|
+
}
|
|
9
|
+
/** Which "Done" lines a message shows. Each finished reply gets its own line where it ends; the reply
|
|
10
|
+
* still running gets none, so a stale line never sits above live work (owner, 2026-10-10). Per-reply
|
|
11
|
+
* lines need the ordered block layout to know where a reply ends; the flat fallback keeps one
|
|
12
|
+
* whole-message line under the message once the run is over. A row still streaming counts as running
|
|
13
|
+
* even when its `runStatus` was never set, so the previous reply's line cannot slide down under the
|
|
14
|
+
* live tool rows above "Running …" (owner bug, 2026-10-10). O(r) for r replies.
|
|
15
|
+
* @example usageLayout({ usage, interleaved: true, runInProgress: true }).footer // undefined */
|
|
16
|
+
export function usageLayout({ usage, interleaved, runInProgress }, { runStreaming = false } = {}) {
|
|
17
|
+
const segments = interleaved ? usage?.segments : undefined;
|
|
18
|
+
const last = segments ? segments.at(-1) : usage;
|
|
19
|
+
return { footer: runInProgress || runStreaming ? undefined : last, replies: segments ? segments.slice(0, -1) : [] };
|
|
20
|
+
}
|
|
21
|
+
/** Whether the footer's "Done" line renders directly above the copy row: a finished turn with a
|
|
22
|
+
* non-empty usage line and no notice between them. Rows get a modifier class from this so a host can
|
|
23
|
+
* lay the two out on one line with a plain class selector — a `:has(> usage + actions)` selector made
|
|
24
|
+
* every keystroke restyle the whole page in long chats (2026-10-11). O(1).
|
|
25
|
+
* @example usageLineMeetsActions({ footer: { kind: 'usage', costUsd: 0.1 }, terminalNotice: null, pending: false, runInProgress: false }) // true */
|
|
26
|
+
export function usageLineMeetsActions({ footer, terminalNotice, pending, runInProgress }, _optional = {}) {
|
|
27
|
+
const hasText = footer !== undefined && (footer.durationMs !== undefined || footer.costUsd !== undefined);
|
|
28
|
+
return hasText && !terminalNotice && !pending && !runInProgress;
|
|
29
|
+
}
|
|
30
|
+
/** Build footer display values outside JSX; token counts remain available in the tooltip and accessible footer label.
|
|
31
|
+
* Each line carries the chat's running total as of that reply, "Done · 16s · $0.39 · total $3.95" (owner,
|
|
32
|
+
* 2026-10-10: keep the total, always shown, even on the first turn). `showTotal: false` turns it off. */
|
|
33
|
+
export function useUsageSummary({ usage }, { showTotal = true } = {}) {
|
|
34
|
+
const t = useT();
|
|
35
|
+
const parts = [];
|
|
36
|
+
if (usage.durationMs !== undefined) {
|
|
37
|
+
const seconds = Math.round(usage.durationMs / 1000);
|
|
38
|
+
parts.push(seconds >= 60 ? `${Math.floor(seconds / 60)}m ${seconds % 60}s` : `${seconds}s`);
|
|
39
|
+
}
|
|
40
|
+
if (usage.costUsd !== undefined) {
|
|
41
|
+
parts.push(formatUsageCost({ costUsd: usage.costUsd, ...(showTotal ? { totalCostUsd: usage.totalCostUsd ?? usage.costUsd } : {}) }));
|
|
42
|
+
}
|
|
43
|
+
const text = parts.join(' · ');
|
|
44
|
+
const title = usage.outputTokens === undefined ? undefined : t('{n} out', { n: usage.outputTokens });
|
|
45
|
+
return { text, title, ariaLabel: title === undefined ? undefined : `${t('Done')} · ${text} · ${title}` };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=MessageRow.usage.js.map
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module MidRunUserMessage
|
|
3
|
-
*
|
|
4
|
-
* Renders a message the human sent while a run was going (`ChatPane`'s `deliverMidRunMessage`) as a
|
|
5
|
-
* user bubble inside the running turn, at the point in the stream where the live agent received it.
|
|
6
|
-
* The daemon records each one as a `user_message` run event (`AgentExecutor.sendUserMessage`), which
|
|
7
|
-
* the run-event translator surfaces as `kind: 'ext'` with that name; this claims it.
|
|
8
|
-
*
|
|
9
|
-
* It is an event of the assistant turn, not a `ChatMessage` of its own, because the agent folded it
|
|
10
|
-
* into the turn it was already running — there is no separate reply to hang off it.
|
|
11
|
-
*/
|
|
12
1
|
import { type ExtEventRenderProps } from '../ext-event-renderer-registry.js';
|
|
13
|
-
/** The ext-event name the daemon's mid-run `user_message` run event arrives under.
|
|
2
|
+
/** The ext-event name the daemon's mid-run `user_message` run event arrives under. One source with
|
|
3
|
+
* the core reply split (`replySegments`), which ends a reply's usage at this same event. */
|
|
14
4
|
export declare const MID_RUN_USER_MESSAGE_EXT_EVENT_NAME = "user_message";
|
|
15
5
|
/** One slot per message id, so each mid-run message renders where it arrived rather than folding into the first. */
|
|
16
6
|
export declare function midRunUserMessageSlotKey({ data }: {
|
|
@@ -10,9 +10,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
10
10
|
* It is an event of the assistant turn, not a `ChatMessage` of its own, because the agent folded it
|
|
11
11
|
* into the turn it was already running — there is no separate reply to hang off it.
|
|
12
12
|
*/
|
|
13
|
+
import { MID_RUN_USER_MESSAGE_EVENT_NAME } from '../../core/turn-usage.js';
|
|
13
14
|
import { registerExtEventRenderer } from '../ext-event-renderer-registry.js';
|
|
14
|
-
/** The ext-event name the daemon's mid-run `user_message` run event arrives under.
|
|
15
|
-
|
|
15
|
+
/** The ext-event name the daemon's mid-run `user_message` run event arrives under. One source with
|
|
16
|
+
* the core reply split (`replySegments`), which ends a reply's usage at this same event. */
|
|
17
|
+
export const MID_RUN_USER_MESSAGE_EXT_EVENT_NAME = MID_RUN_USER_MESSAGE_EVENT_NAME;
|
|
16
18
|
function field(data, key) {
|
|
17
19
|
const value = typeof data === 'object' && data !== null ? data[key] : undefined;
|
|
18
20
|
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ChatAttachment } from '../../core/index.js';
|
|
3
|
+
import { type ObjectUrlPort } from './SentAttachment.hooks.js';
|
|
4
|
+
export interface SentAttachmentButtonProps {
|
|
5
|
+
attachment: ChatAttachment;
|
|
6
|
+
/** Opens the row's preview modal for this attachment. */
|
|
7
|
+
onOpen: (attachment: ChatAttachment) => void;
|
|
8
|
+
/** Host-supplied chip content; when given it wins over the built-in thumbnail/filename. */
|
|
9
|
+
renderAttachment?: ((attachment: ChatAttachment) => ReactNode) | undefined;
|
|
10
|
+
/** Blob-URL port for the thumbnail; defaults to the platform's (see `SentAttachment.hooks.ts`). */
|
|
11
|
+
objectUrls?: ObjectUrlPort | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One attachment on a sent user message: a small image thumbnail when this browser still holds the
|
|
15
|
+
* image's bytes, otherwise the filename chip. Either way a click opens the preview modal, and the
|
|
16
|
+
* accessible name stays "Open {name}".
|
|
17
|
+
*/
|
|
18
|
+
export declare function SentAttachmentButton({ attachment, onOpen, renderAttachment, objectUrls }: SentAttachmentButtonProps): import("react").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=SentAttachment.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatAttachment } from '../../core/index.js';
|
|
2
|
+
/** Blob-URL port, so a test (jsdom has no `URL.createObjectURL`) can supply a fake. */
|
|
3
|
+
export interface ObjectUrlPort {
|
|
4
|
+
create: (file: File) => string;
|
|
5
|
+
revoke: (url: string) => void;
|
|
6
|
+
}
|
|
7
|
+
/** The platform's Blob URLs, or `undefined` where they do not exist (jsdom, non-browser hosts). */
|
|
8
|
+
export declare function platformObjectUrls(): ObjectUrlPort | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The cached file to draw as a thumbnail, or `undefined` when the attachment should stay a chip:
|
|
11
|
+
* not an image (same `looksLikeImageAttachment` rule the preview modal uses), or no bytes cached.
|
|
12
|
+
* @complexity O(1).
|
|
13
|
+
*/
|
|
14
|
+
export declare function sentAttachmentThumbnailSource({ attachment }: {
|
|
15
|
+
attachment: Pick<ChatAttachment, 'kind' | 'name' | 'path'>;
|
|
16
|
+
}, { getSource }?: {
|
|
17
|
+
getSource?: (required: {
|
|
18
|
+
path: string;
|
|
19
|
+
}) => File | undefined;
|
|
20
|
+
}): File | undefined;
|
|
21
|
+
export interface SentAttachmentThumbnail {
|
|
22
|
+
/** Object URL to put in the `<img>`, or `undefined` to render the filename chip. */
|
|
23
|
+
thumbnailUrl: string | undefined;
|
|
24
|
+
/** The `<img>`'s `onError`: an undecodable file falls back to the chip instead of a broken image. */
|
|
25
|
+
onThumbnailError: () => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates the thumbnail's object URL on mount and revokes it on unmount (an effect, not `useMemo`,
|
|
29
|
+
* so a Strict Mode double render cannot leak a URL - same reasoning as the preview modal's hook).
|
|
30
|
+
* @param input.hostRendered - True when a host `renderAttachment` draws this chip.
|
|
31
|
+
* @param options.objectUrls - Blob-URL port; defaults to the platform's, `undefined` where absent.
|
|
32
|
+
* @param options.getSource - Preview-cache lookup; defaults to the shared cache.
|
|
33
|
+
* @complexity O(1) per attachment.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useSentAttachmentThumbnail({ attachment, hostRendered }: {
|
|
36
|
+
attachment: ChatAttachment;
|
|
37
|
+
hostRendered?: boolean;
|
|
38
|
+
}, { objectUrls, getSource }?: {
|
|
39
|
+
objectUrls?: ObjectUrlPort | undefined;
|
|
40
|
+
getSource?: (required: {
|
|
41
|
+
path: string;
|
|
42
|
+
}) => File | undefined;
|
|
43
|
+
}): SentAttachmentThumbnail;
|
|
44
|
+
//# sourceMappingURL=SentAttachment.hooks.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Thumbnail state for one attachment on a SENT user message (`SentAttachment.tsx`).
|
|
3
|
+
*
|
|
4
|
+
* A sent image shows as a small thumbnail instead of a filename chip, so the operator can see at a
|
|
5
|
+
* glance which screenshots went with the turn. The bytes come from the same
|
|
6
|
+
* `attachment-preview-cache.ts` the preview modal reads: only files this browser staged for upload in
|
|
7
|
+
* this page session are there (the server cannot hand them back - see that module's doc). Anything
|
|
8
|
+
* else - a non-image, a reload, the cache's eviction cap, a browser that cannot decode the format -
|
|
9
|
+
* stays the filename chip, which still opens the preview modal's honest fallback.
|
|
10
|
+
*/
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { getAttachmentPreviewSource } from '../hooks/attachment-preview-cache.js';
|
|
13
|
+
import { canCreateObjectUrl, looksLikeImageAttachment } from './AttachmentPreviewModal.hooks.js';
|
|
14
|
+
/** The platform's Blob URLs, or `undefined` where they do not exist (jsdom, non-browser hosts). */
|
|
15
|
+
export function platformObjectUrls() {
|
|
16
|
+
if (!canCreateObjectUrl())
|
|
17
|
+
return undefined;
|
|
18
|
+
return { create: (file) => URL.createObjectURL(file), revoke: (url) => URL.revokeObjectURL(url) };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The cached file to draw as a thumbnail, or `undefined` when the attachment should stay a chip:
|
|
22
|
+
* not an image (same `looksLikeImageAttachment` rule the preview modal uses), or no bytes cached.
|
|
23
|
+
* @complexity O(1).
|
|
24
|
+
*/
|
|
25
|
+
export function sentAttachmentThumbnailSource({ attachment }, { getSource = getAttachmentPreviewSource } = {}) {
|
|
26
|
+
if (!looksLikeImageAttachment({ attachment }))
|
|
27
|
+
return undefined;
|
|
28
|
+
return getSource({ path: attachment.path });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the thumbnail's object URL on mount and revokes it on unmount (an effect, not `useMemo`,
|
|
32
|
+
* so a Strict Mode double render cannot leak a URL - same reasoning as the preview modal's hook).
|
|
33
|
+
* @param input.hostRendered - True when a host `renderAttachment` draws this chip.
|
|
34
|
+
* @param options.objectUrls - Blob-URL port; defaults to the platform's, `undefined` where absent.
|
|
35
|
+
* @param options.getSource - Preview-cache lookup; defaults to the shared cache.
|
|
36
|
+
* @complexity O(1) per attachment.
|
|
37
|
+
*/
|
|
38
|
+
export function useSentAttachmentThumbnail({ attachment, hostRendered = false }, { objectUrls = platformObjectUrls(), getSource = getAttachmentPreviewSource } = {}) {
|
|
39
|
+
const [failed, setFailed] = useState(false);
|
|
40
|
+
const [thumbnailUrl, setThumbnailUrl] = useState(undefined);
|
|
41
|
+
// A host `renderAttachment` owns the chip's content, so no thumbnail URL is made for it.
|
|
42
|
+
const source = failed || hostRendered || !objectUrls ? undefined : sentAttachmentThumbnailSource({ attachment }, { getSource });
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!source || !objectUrls) {
|
|
45
|
+
setThumbnailUrl(undefined);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const url = objectUrls.create(source);
|
|
49
|
+
setThumbnailUrl(url);
|
|
50
|
+
return () => objectUrls.revoke(url);
|
|
51
|
+
// `objectUrls` is a fresh object per render by default; the file is what the URL is made from.
|
|
52
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
+
}, [source]);
|
|
54
|
+
return { thumbnailUrl, onThumbnailError: () => setFailed(true) };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=SentAttachment.hooks.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useT } from '../hooks/context.js';
|
|
3
|
+
import { useSentAttachmentThumbnail } from './SentAttachment.hooks.js';
|
|
4
|
+
/**
|
|
5
|
+
* One attachment on a sent user message: a small image thumbnail when this browser still holds the
|
|
6
|
+
* image's bytes, otherwise the filename chip. Either way a click opens the preview modal, and the
|
|
7
|
+
* accessible name stays "Open {name}".
|
|
8
|
+
*/
|
|
9
|
+
export function SentAttachmentButton({ attachment, onOpen, renderAttachment, objectUrls }) {
|
|
10
|
+
const t = useT();
|
|
11
|
+
const { thumbnailUrl, onThumbnailError } = useSentAttachmentThumbnail({ attachment, hostRendered: renderAttachment !== undefined }, objectUrls === undefined ? {} : { objectUrls });
|
|
12
|
+
return (_jsx("button", { type: "button", className: `jini-message-attachment-chip${thumbnailUrl ? ' is-thumbnail' : ''}`, onClick: () => onOpen(attachment), title: t('Open {name}', { name: attachment.name }), "aria-label": t('Open {name}', { name: attachment.name }), children: renderAttachment ? (renderAttachment(attachment)) : thumbnailUrl ? (_jsx("img", { className: "jini-message-attachment-thumb", src: thumbnailUrl, alt: "", onError: onThumbnailError })) : (attachment.name) }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=SentAttachment.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ToolDisplayRow } from '../tool-call-display.js';
|
|
3
|
+
/**
|
|
4
|
+
* One turn's tool cards folded into a single line ("Used 3 tools ▸") that opens on click — the
|
|
5
|
+
* `toolCallDisplay: 'collapsed'` layout. Props in, JSX out; state and copy are in
|
|
6
|
+
* `useToolCallGroup`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ToolCallGroup<Row extends ToolDisplayRow>({ rows, renderRow }: {
|
|
9
|
+
rows: readonly Row[];
|
|
10
|
+
renderRow: (row: Row) => ReactNode;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=ToolCallGroup.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ToolDisplayRow } from '../tool-call-display.js';
|
|
2
|
+
export interface ToolCallGroupState {
|
|
3
|
+
/** Whether the turn's tool cards are showing under the line. Starts closed. */
|
|
4
|
+
open: boolean;
|
|
5
|
+
toggle: () => void;
|
|
6
|
+
/** "Used 3 tools", "Used 3 tools · 1 failed", or "Running Bash…" while a tool runs. */
|
|
7
|
+
label: string;
|
|
8
|
+
running: boolean;
|
|
9
|
+
failed: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* State and copy for one collapsed tool line (`toolCallDisplay: 'collapsed'`). The open flag lives
|
|
13
|
+
* in the line's own component, so it lasts as long as the message stays in the transcript — the
|
|
14
|
+
* whole time its conversation is open — and resets when the pane remounts.
|
|
15
|
+
* @complexity O(n) in rows, for the summary.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useToolCallGroup({ rows }: {
|
|
18
|
+
rows: readonly ToolDisplayRow[];
|
|
19
|
+
}, _optional?: {}): ToolCallGroupState;
|
|
20
|
+
//# sourceMappingURL=ToolCallGroup.hooks.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { useT } from '../hooks/context.js';
|
|
3
|
+
import { summarizeToolGroup, toolGroupLabel } from '../tool-call-display.js';
|
|
4
|
+
/**
|
|
5
|
+
* State and copy for one collapsed tool line (`toolCallDisplay: 'collapsed'`). The open flag lives
|
|
6
|
+
* in the line's own component, so it lasts as long as the message stays in the transcript — the
|
|
7
|
+
* whole time its conversation is open — and resets when the pane remounts.
|
|
8
|
+
* @complexity O(n) in rows, for the summary.
|
|
9
|
+
*/
|
|
10
|
+
export function useToolCallGroup({ rows }, _optional = {}) {
|
|
11
|
+
const t = useT();
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
const toggle = useCallback(() => setOpen((value) => !value), []);
|
|
14
|
+
const summary = summarizeToolGroup({ rows });
|
|
15
|
+
return {
|
|
16
|
+
open,
|
|
17
|
+
toggle,
|
|
18
|
+
label: toolGroupLabel({ summary, t }),
|
|
19
|
+
running: summary.kind === 'running',
|
|
20
|
+
failed: summary.kind === 'done' && summary.failed > 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ToolCallGroup.hooks.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useToolCallGroup } from './ToolCallGroup.hooks.js';
|
|
3
|
+
import { Icon } from './Icon.js';
|
|
4
|
+
/**
|
|
5
|
+
* One turn's tool cards folded into a single line ("Used 3 tools ▸") that opens on click — the
|
|
6
|
+
* `toolCallDisplay: 'collapsed'` layout. Props in, JSX out; state and copy are in
|
|
7
|
+
* `useToolCallGroup`.
|
|
8
|
+
*/
|
|
9
|
+
export function ToolCallGroup({ rows, renderRow }) {
|
|
10
|
+
const group = useToolCallGroup({ rows });
|
|
11
|
+
return (_jsxs("div", { className: "jini-tool-group", "data-failed": group.failed ? 'true' : undefined, children: [_jsxs("button", { type: "button", className: "jini-tool-group__toggle", "aria-expanded": group.open, onClick: group.toggle, children: [_jsx("span", { className: `jini-tool-group__label${group.running ? ' shimmer-text' : ''}`, children: group.label }), _jsx(Icon, { name: group.open ? 'chevron-down' : 'chevron-right', size: 12 })] }), group.open ? _jsx("div", { className: "jini-message-tools", children: rows.map(renderRow) }) : null] }));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ToolCallGroup.js.map
|
|
@@ -21,7 +21,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
21
21
|
* generic rendering for it.
|
|
22
22
|
*/
|
|
23
23
|
import { useId, useState } from 'react';
|
|
24
|
-
import { describeToolCallDetail, formatToolOutputForDisplay, isDelegatedWrapperToolName, isTodoWriteToolName, parseTodoWriteInput, toRenderProps } from '../../core/index.js';
|
|
24
|
+
import { describeCollapsedToolCall, describeToolCallDetail, formatToolOutputForDisplay, isDelegatedWrapperToolName, isTodoWriteToolName, parseTodoWriteInput, toRenderProps } from '../../core/index.js';
|
|
25
25
|
import { useT } from '../hooks/context.js';
|
|
26
26
|
import { getToolRenderer } from '../tool-renderer-registry.js';
|
|
27
27
|
import { Icon } from './Icon.js';
|
|
@@ -139,19 +139,19 @@ function DelegatedToolCard({ name, input, result, runStreaming, runSucceeded })
|
|
|
139
139
|
const args = isDelegatedWrapperToolName({ name: name }) ? wrapper.input : input;
|
|
140
140
|
const target = primaryTarget(args);
|
|
141
141
|
const isRunning = runStreaming && !result;
|
|
142
|
-
return (_jsx(ExpandableCard, { className: "op-jini-tool", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsxs("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: [t('Tool call'), " \u00B7 ", humanizeToolId(toolId)] }), target ? _jsx("span", { className: "op-meta", children: target }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: args ?? {}, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded, children: _jsx(ToolResultMedia, { result: result }) }) }));
|
|
142
|
+
return (_jsx(ExpandableCard, { className: "op-jini-tool", call: { input: args ?? {}, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsxs("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: [t('Tool call'), " \u00B7 ", humanizeToolId(toolId)] }), target ? _jsx("span", { className: "op-meta", children: target }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: args ?? {}, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded, children: _jsx(ToolResultMedia, { result: result }) }) }));
|
|
143
143
|
}
|
|
144
144
|
function SearchToolsCard({ input, result, runStreaming, runSucceeded }) {
|
|
145
145
|
const t = useT();
|
|
146
146
|
const obj = (input ?? {});
|
|
147
147
|
const isRunning = runStreaming && !result;
|
|
148
|
-
return (_jsx(ExpandableCard, { className: "op-jini-tool", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Search tools') }), obj.query ? _jsx("span", { className: "op-meta", children: obj.query }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
148
|
+
return (_jsx(ExpandableCard, { className: "op-jini-tool", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Search tools') }), obj.query ? _jsx("span", { className: "op-meta", children: obj.query }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
149
149
|
}
|
|
150
150
|
function DescribeToolCard({ input, result, runStreaming, runSucceeded }) {
|
|
151
151
|
const t = useT();
|
|
152
152
|
const obj = (input ?? {});
|
|
153
153
|
const isRunning = runStreaming && !result;
|
|
154
|
-
return (_jsx(ExpandableCard, { className: "op-jini-tool", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Describe tool') }), obj.id ? _jsx("span", { className: "op-meta", children: obj.id }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
154
|
+
return (_jsx(ExpandableCard, { className: "op-jini-tool", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Describe tool') }), obj.id ? _jsx("span", { className: "op-meta", children: obj.id }) : null] }), after: _jsx(FileErrorDetail, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
155
155
|
}
|
|
156
156
|
function OpenInTabButton({ filePath, ctx }) {
|
|
157
157
|
const t = useT();
|
|
@@ -177,7 +177,7 @@ function FileWriteCard({ input, result, runStreaming, runSucceeded, ctx }) {
|
|
|
177
177
|
const baseName = file.split('/').pop();
|
|
178
178
|
const lines = typeof obj.content === 'string' ? obj.content.split('\n').length : null;
|
|
179
179
|
const isRunning = runStreaming && !result;
|
|
180
|
-
return (_jsxs(ExpandableCard, { className: "op-file", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Write') }), _jsxs("span", { className: "op-meta", children: [baseName, lines !== null ? ` · ${t('{n} lines', { n: lines })}` : ''] })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
180
|
+
return (_jsxs(ExpandableCard, { className: "op-file", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Write') }), _jsxs("span", { className: "op-meta", children: [baseName, lines !== null ? ` · ${t('{n} lines', { n: lines })}` : ''] })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
181
181
|
}
|
|
182
182
|
function FileEditCard({ input, result, runStreaming, runSucceeded, ctx }) {
|
|
183
183
|
const t = useT();
|
|
@@ -187,7 +187,7 @@ function FileEditCard({ input, result, runStreaming, runSucceeded, ctx }) {
|
|
|
187
187
|
const baseName = file.split('/').pop();
|
|
188
188
|
const editCount = Array.isArray(obj.edits) ? obj.edits.length : 1;
|
|
189
189
|
const isRunning = runStreaming && !result;
|
|
190
|
-
return (_jsxs(ExpandableCard, { className: "op-file", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Edit') }), _jsxs("span", { className: "op-meta", children: [baseName, " \u00B7 ", editCount, " ", editCount === 1 ? t('change') : t('changes')] })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
190
|
+
return (_jsxs(ExpandableCard, { className: "op-file", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Edit') }), _jsxs("span", { className: "op-meta", children: [baseName, " \u00B7 ", editCount, " ", editCount === 1 ? t('change') : t('changes')] })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
191
191
|
}
|
|
192
192
|
function FileReadCard({ input, result, runStreaming, runSucceeded, ctx }) {
|
|
193
193
|
const t = useT();
|
|
@@ -196,7 +196,7 @@ function FileReadCard({ input, result, runStreaming, runSucceeded, ctx }) {
|
|
|
196
196
|
// See OpenInTabButton's baseName comment: `.split('/').pop()` is never undefined.
|
|
197
197
|
const baseName = file.split('/').pop();
|
|
198
198
|
const isRunning = runStreaming && !result;
|
|
199
|
-
return (_jsxs(ExpandableCard, { className: "op-file", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Read') }), _jsx("span", { className: "op-meta", children: baseName })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
199
|
+
return (_jsxs(ExpandableCard, { className: "op-file", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Read') }), _jsx("span", { className: "op-meta", children: baseName })] }), after: _jsx(FileErrorDetail, { result: result }), children: [_jsxs("div", { className: "op-card-detail op-card-file-detail", children: [_jsx("code", { className: "op-path", children: file }), _jsx(OpenInTabButton, { filePath: file, ctx: ctx })] }), _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded })] }));
|
|
200
200
|
}
|
|
201
201
|
function BashCard({ input, result, runStreaming, runSucceeded }) {
|
|
202
202
|
const t = useT();
|
|
@@ -204,43 +204,51 @@ function BashCard({ input, result, runStreaming, runSucceeded }) {
|
|
|
204
204
|
const command = obj.command ?? '';
|
|
205
205
|
const desc = obj.description;
|
|
206
206
|
const isRunning = runStreaming && !result;
|
|
207
|
-
return (_jsx(ExpandableCard, { className: "op-bash", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Bash') }), desc ? _jsx("span", { className: "op-meta op-desc", children: desc }) : null] }), children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: truncate(command, 400) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 4000) }) : null] }) }));
|
|
207
|
+
return (_jsx(ExpandableCard, { className: "op-bash", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Bash') }), desc ? _jsx("span", { className: "op-meta op-desc", children: desc }) : null] }), children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: truncate(command, 400) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 4000) }) : null] }) }));
|
|
208
208
|
}
|
|
209
209
|
function GlobCard({ input, result, runStreaming, runSucceeded }) {
|
|
210
210
|
const t = useT();
|
|
211
211
|
const obj = (input ?? {});
|
|
212
|
-
return (_jsx(ExpandableCard, { className: "op-search", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search files') }), _jsxs("span", { className: "op-meta", children: [obj.pattern ?? '*', obj.path ? ` in ${obj.path}` : ''] })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
212
|
+
return (_jsx(ExpandableCard, { className: "op-search", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search files') }), _jsxs("span", { className: "op-meta", children: [obj.pattern ?? '*', obj.path ? ` in ${obj.path}` : ''] })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
213
213
|
}
|
|
214
214
|
function GrepCard({ input, result, runStreaming, runSucceeded }) {
|
|
215
215
|
const t = useT();
|
|
216
216
|
const obj = (input ?? {});
|
|
217
|
-
return (_jsx(ExpandableCard, { className: "op-search", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search content') }), _jsxs("span", { className: "op-meta", children: [obj.pattern ?? '', obj.path ? ` in ${obj.path}` : ''] })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
217
|
+
return (_jsx(ExpandableCard, { className: "op-search", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search content') }), _jsxs("span", { className: "op-meta", children: [obj.pattern ?? '', obj.path ? ` in ${obj.path}` : ''] })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
218
218
|
}
|
|
219
219
|
function WebFetchCard({ input, result, runStreaming, runSucceeded }) {
|
|
220
220
|
const t = useT();
|
|
221
221
|
const obj = (input ?? {});
|
|
222
|
-
return (_jsx(ExpandableCard, { className: "op-web", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Fetch') }), _jsx("span", { className: "op-meta", children: obj.url ?? '' })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
222
|
+
return (_jsx(ExpandableCard, { className: "op-web", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Fetch') }), _jsx("span", { className: "op-meta", children: obj.url ?? '' })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
223
223
|
}
|
|
224
224
|
function WebSearchCard({ input, result, runStreaming, runSucceeded }) {
|
|
225
225
|
const t = useT();
|
|
226
226
|
const obj = (input ?? {});
|
|
227
|
-
return (_jsx(ExpandableCard, { className: "op-web", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search') }), _jsx("span", { className: "op-meta", children: obj.query ?? '' })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
227
|
+
return (_jsx(ExpandableCard, { className: "op-web", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: t('Search') }), _jsx("span", { className: "op-meta", children: obj.query ?? '' })] }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
228
228
|
}
|
|
229
229
|
/** Any tool this package has no family card for — an MCP tool, or a card tool such as `assistant_ask_choice` whose form renders elsewhere in the turn. The row still opens to the full call. */
|
|
230
230
|
function GenericCard({ name, input, result, runStreaming, runSucceeded }) {
|
|
231
231
|
const summary = describeInput(input);
|
|
232
|
-
return (_jsx(ExpandableCard, { className: "op-generic", head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: name }), summary ? _jsx("span", { className: "op-meta", children: truncate(summary, 200) }) : null] }), after: _jsx(ToolResultMedia, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
232
|
+
return (_jsx(ExpandableCard, { className: "op-generic", call: { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }, head: _jsxs(_Fragment, { children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: "op-title", children: name }), summary ? _jsx("span", { className: "op-meta", children: truncate(summary, 200) }) : null] }), after: _jsx(ToolResultMedia, { result: result }), children: _jsx(ToolCallDetail, { input: input, result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }) }));
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
235
235
|
* The one row shell every card shares: a real `<button>` head (Enter/Space toggle it) that reports
|
|
236
|
-
* `aria-expanded` and points `aria-controls` at its panel
|
|
237
|
-
*
|
|
236
|
+
* `aria-expanded` and points `aria-controls` at its panel. The panel's `children` mount only while
|
|
237
|
+
* open: a long chat mounts hundreds of collapsed rows, and keeping every full detail mounted made a
|
|
238
|
+
* 38-message chat carry ~13k DOM nodes and 3.45 MB of hidden text. Collapsed, the panel holds one
|
|
239
|
+
* hidden node with the call's capped gist instead, so a `page.*` DOM reader still sees what was
|
|
240
|
+
* called and what came back through `textContent` without anyone clicking.
|
|
238
241
|
* `after` renders outside the panel, for content that must stay visible collapsed (an error, media).
|
|
239
242
|
*/
|
|
240
|
-
function ExpandableCard({ className, head, after, children }) {
|
|
243
|
+
function ExpandableCard({ className, head, after, call, children }) {
|
|
241
244
|
const [open, setOpen] = useState(false);
|
|
242
245
|
const panelId = useId();
|
|
243
|
-
return (_jsxs("div", { className: `op-card ${className}`, children: [_jsxs("button", { type: "button", className: "op-card-head", "aria-expanded": open, "aria-controls": panelId, onClick: () => setOpen((o) => !o), children: [head, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { id: panelId, className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: children }) }), after] }));
|
|
246
|
+
return (_jsxs("div", { className: `op-card ${className}`, children: [_jsxs("button", { type: "button", className: "op-card-head", "aria-expanded": open, "aria-controls": panelId, onClick: () => setOpen((o) => !o), children: [head, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { id: panelId, className: `accordion-collapsible${open ? ' open' : ''}`, children: open ? _jsx("div", { className: "accordion-collapsible-inner", children: children }) : _jsx(CollapsedReaderText, { call: call }) }), after] }));
|
|
247
|
+
}
|
|
248
|
+
/** The collapsed row's stand-in for its panel — see {@link ExpandableCard} and `describeCollapsedToolCall`. */
|
|
249
|
+
function CollapsedReaderText({ call }) {
|
|
250
|
+
const text = describeCollapsedToolCall({ input: call.input, result: call.result }, { runStreaming: call.runStreaming, runSucceeded: call.runSucceeded });
|
|
251
|
+
return _jsx("span", { className: "op-card-reader-text", hidden: true, children: text });
|
|
244
252
|
}
|
|
245
253
|
/** The call's input, its result (or why there is none), and any extra `children` — see `describeToolCallDetail` for the redaction and formatting rules. */
|
|
246
254
|
function ToolCallDetail({ input, result, runStreaming, runSucceeded, children }) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* `McpUiSurfaceCard.tsx` so the component only renders; moved here from that file 2026-10-04 when
|
|
6
6
|
* the card's own deadline (`useSurfaceExpiry`) became a second reason to close it.
|
|
7
7
|
*/
|
|
8
|
+
import type { McpUiToolCallHandler } from '@jini-ai/ui/mcp-ui';
|
|
8
9
|
import type { ExtEventCall } from '../ext-event-renderer-registry.js';
|
|
9
10
|
import type { useT } from '../hooks/context.js';
|
|
10
11
|
type Translate = ReturnType<typeof useT>;
|
|
@@ -23,5 +24,25 @@ export declare function closedSurfaceLabel({ t, call, runStreaming, documentsFor
|
|
|
23
24
|
documentsForUri: number;
|
|
24
25
|
expired: boolean;
|
|
25
26
|
}): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a View's `tools/call` result is a held-open question's delivery receipt: the host route
|
|
29
|
+
* answers `{delivered: true}` (HTTP 202) the moment the click reaches the parked tool call. That
|
|
30
|
+
* call then keeps running the work the answer started, so this receipt — not its later result — is
|
|
31
|
+
* when the card stops waiting on the person.
|
|
32
|
+
*
|
|
33
|
+
* @complexity O(1).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isDeliveredAnswer(result: unknown): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Wraps a card's tool executor so a delivered answer is reported once it resolves. The result is
|
|
38
|
+
* passed through unchanged, and a rejected call reports nothing (the dialog shows its error and can
|
|
39
|
+
* be answered again).
|
|
40
|
+
*
|
|
41
|
+
* @complexity O(1) per call, plus `onToolCall`'s own cost.
|
|
42
|
+
*/
|
|
43
|
+
export declare function reportDeliveredAnswer({ onToolCall, onAnswered }: {
|
|
44
|
+
onToolCall: McpUiToolCallHandler;
|
|
45
|
+
onAnswered: () => void;
|
|
46
|
+
}): McpUiToolCallHandler;
|
|
26
47
|
export {};
|
|
27
48
|
//# sourceMappingURL=mcp-ui-surface-state.d.ts.map
|
|
@@ -30,4 +30,30 @@ function settledLabel(t, call, runStreaming, documentsForUri) {
|
|
|
30
30
|
export function closedSurfaceLabel({ t, call, runStreaming, documentsForUri, expired }) {
|
|
31
31
|
return settledLabel(t, call, runStreaming, documentsForUri) ?? (expired ? t('This question expired') : undefined);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether a View's `tools/call` result is a held-open question's delivery receipt: the host route
|
|
35
|
+
* answers `{delivered: true}` (HTTP 202) the moment the click reaches the parked tool call. That
|
|
36
|
+
* call then keeps running the work the answer started, so this receipt — not its later result — is
|
|
37
|
+
* when the card stops waiting on the person.
|
|
38
|
+
*
|
|
39
|
+
* @complexity O(1).
|
|
40
|
+
*/
|
|
41
|
+
export function isDeliveredAnswer(result) {
|
|
42
|
+
return typeof result === 'object' && result !== null && result.delivered === true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wraps a card's tool executor so a delivered answer is reported once it resolves. The result is
|
|
46
|
+
* passed through unchanged, and a rejected call reports nothing (the dialog shows its error and can
|
|
47
|
+
* be answered again).
|
|
48
|
+
*
|
|
49
|
+
* @complexity O(1) per call, plus `onToolCall`'s own cost.
|
|
50
|
+
*/
|
|
51
|
+
export function reportDeliveredAnswer({ onToolCall, onAnswered }) {
|
|
52
|
+
return async (call) => {
|
|
53
|
+
const result = await onToolCall(call);
|
|
54
|
+
if (isDeliveredAnswer(result))
|
|
55
|
+
onAnswered();
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
33
59
|
//# sourceMappingURL=mcp-ui-surface-state.js.map
|
|
@@ -51,6 +51,13 @@ export interface ExtEventRenderProps {
|
|
|
51
51
|
runId: string | undefined;
|
|
52
52
|
/** See {@link ExtEventCall}. Absent when the group's first event arrived outside any tool call. */
|
|
53
53
|
call?: ExtEventCall;
|
|
54
|
+
/** Whether this card already reported its answer delivered (see {@link onAnswered}). */
|
|
55
|
+
answered?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* A card calls this once the person's answer reached the server. Its tool call may stay open for
|
|
58
|
+
* the work the answer started, and the run must stop reading as "waiting for your answer".
|
|
59
|
+
*/
|
|
60
|
+
onAnswered?: () => void;
|
|
54
61
|
}
|
|
55
62
|
/**
|
|
56
63
|
* Ext-event render callback. Runs inline during `MessageRow`'s render — hook-free, like
|
|
@@ -6,5 +6,5 @@ import type { ChatPaneProps } from '../types.js';
|
|
|
6
6
|
* @complexity Time/space: O(n) in rendered messages, agents, and suggestions.
|
|
7
7
|
* @overallScore 100/100
|
|
8
8
|
*/
|
|
9
|
-
export declare function ChatPane({ transport, agents: injectedAgents, runtimeAccess, runtimeStatusPollMs, title, variant, initialMessages, conversationId, initialSelection, selection: controlledSelection, onSelectionChange, runContext, agentControl, onActivityChange, onMessagesChange, redactUserText, onSecretRedacted, onRescanAgents, scanningAgents, daemonOnline, executionMode, apiModeAvailable, onExecutionModeChange, byokRuntime, onByokModelChange, initialDraft, composerHistoryScope, composerHistoryStorage, validateAttachments, deliverTypedAnswer, deliverMidRunMessage, composerHandle, placeholder, placeholders, suggestions, workingDirectory, initialWorkingDirectory, onChangeWorkingDirectory, workingDirectoryAccess, workingDirectoryControlPlacement, projectFileNames, uploadAttachments, attachmentAccept, disabled, runtimePickerPlacement, composerSlots, header, leadingAccessory, footer, className, style, }: ChatPaneProps): import("react").JSX.Element;
|
|
9
|
+
export declare function ChatPane({ transport, agents: injectedAgents, runtimeAccess, runtimeStatusPollMs, title, variant, initialMessages, refreshSignal, conversationId, initialSelection, selection: controlledSelection, onSelectionChange, runContext, agentControl, onActivityChange, onMessagesChange, redactUserText, onSecretRedacted, onRescanAgents, scanningAgents, daemonOnline, executionMode, apiModeAvailable, onExecutionModeChange, byokRuntime, onByokModelChange, initialDraft, composerHistoryScope, composerHistoryStorage, validateAttachments, deliverTypedAnswer, deliverMidRunMessage, composerHandle, placeholder, placeholders, suggestions, emptyState, workingDirectory, initialWorkingDirectory, onChangeWorkingDirectory, workingDirectoryAccess, workingDirectoryControlPlacement, projectFileNames, toolCallDisplay, showUsageLine, showThinking, uploadAttachments, attachmentAccept, disabled, runtimePickerPlacement, composerSlots, header, leadingAccessory, footer, className, style, }: ChatPaneProps): import("react").JSX.Element;
|
|
10
10
|
//# sourceMappingURL=ChatPane.d.ts.map
|
|
@@ -13,6 +13,8 @@ import { useChatPaneAgentControl } from '../hooks/useChatPaneAgentControl.hooks.
|
|
|
13
13
|
import { useChatPaneComposerPlaceholder } from '../hooks/useChatPaneComposerPlaceholder.hooks.js';
|
|
14
14
|
import { useChatPaneControlsHeight } from '../hooks/useChatPaneControlsHeight.hooks.js';
|
|
15
15
|
import { useChatPaneNewTabLinks } from '../hooks/useChatPaneNewTabLinks.hooks.js';
|
|
16
|
+
import { useChatPaneSurface } from '../hooks/useChatPaneSurface.hooks.js';
|
|
17
|
+
import { useChatPaneSuggestion } from '../hooks/useChatPaneSuggestion.hooks.js';
|
|
16
18
|
import { useChatPaneFileDrop, } from '../hooks/useChatPaneFileDrop.hooks.js';
|
|
17
19
|
import { useChatPaneRuntimeInventory, } from '../hooks/useChatPaneRuntimeInventory.hooks.js';
|
|
18
20
|
import { CHAT_PANE_STYLES } from '../styles.js';
|
|
@@ -180,8 +182,9 @@ function ChatPaneComposerArea({ fileDrop, uploadAttachments, pane, disabled, una
|
|
|
180
182
|
* @complexity Time/space: O(n) in rendered messages, agents, and suggestions.
|
|
181
183
|
* @overallScore 100/100
|
|
182
184
|
*/
|
|
183
|
-
export function ChatPane({ transport, agents: injectedAgents = EMPTY_AGENTS, runtimeAccess, runtimeStatusPollMs = 5_000, title, variant = 'workspace', initialMessages, conversationId, initialSelection, selection: controlledSelection, onSelectionChange, runContext, agentControl, onActivityChange, onMessagesChange, redactUserText, onSecretRedacted, onRescanAgents, scanningAgents = false, daemonOnline = true, executionMode = 'local', apiModeAvailable = false, onExecutionModeChange, byokRuntime, onByokModelChange, initialDraft, composerHistoryScope, composerHistoryStorage, validateAttachments, deliverTypedAnswer, deliverMidRunMessage, composerHandle, placeholder, placeholders, suggestions = [], workingDirectory, initialWorkingDirectory, onChangeWorkingDirectory, workingDirectoryAccess, workingDirectoryControlPlacement = 'below', projectFileNames, uploadAttachments, attachmentAccept, disabled = false, runtimePickerPlacement = 'up', composerSlots, header, leadingAccessory, footer, className, style, }) {
|
|
185
|
+
export function ChatPane({ transport, agents: injectedAgents = EMPTY_AGENTS, runtimeAccess, runtimeStatusPollMs = 5_000, title, variant = 'workspace', initialMessages, refreshSignal, conversationId, initialSelection, selection: controlledSelection, onSelectionChange, runContext, agentControl, onActivityChange, onMessagesChange, redactUserText, onSecretRedacted, onRescanAgents, scanningAgents = false, daemonOnline = true, executionMode = 'local', apiModeAvailable = false, onExecutionModeChange, byokRuntime, onByokModelChange, initialDraft, composerHistoryScope, composerHistoryStorage, validateAttachments, deliverTypedAnswer, deliverMidRunMessage, composerHandle, placeholder, placeholders, suggestions = [], emptyState, workingDirectory, initialWorkingDirectory, onChangeWorkingDirectory, workingDirectoryAccess, workingDirectoryControlPlacement = 'below', projectFileNames, toolCallDisplay = 'show', showUsageLine = true, showThinking = false, uploadAttachments, attachmentAccept, disabled = false, runtimePickerPlacement = 'up', composerSlots, header, leadingAccessory, footer, className, style, }) {
|
|
184
186
|
const t = useT();
|
|
187
|
+
const surface = useChatPaneSurface({ variant, composerSlots, workingDirectoryControlPlacement, uploadAttachments });
|
|
185
188
|
const composerPlaceholder = useChatPaneComposerPlaceholder({ placeholder: placeholder, placeholders: placeholders });
|
|
186
189
|
const inventory = useChatPaneRuntimeInventory(definedProps({ source: {
|
|
187
190
|
access: runtimeAccess,
|
|
@@ -197,6 +200,7 @@ export function ChatPane({ transport, agents: injectedAgents = EMPTY_AGENTS, run
|
|
|
197
200
|
transport,
|
|
198
201
|
agents: runtimeView.agents,
|
|
199
202
|
initialMessages,
|
|
203
|
+
refreshSignal,
|
|
200
204
|
conversationId,
|
|
201
205
|
initialSelection,
|
|
202
206
|
selection: controlledSelection,
|
|
@@ -254,7 +258,7 @@ export function ChatPane({ transport, agents: injectedAgents = EMPTY_AGENTS, run
|
|
|
254
258
|
// the provider directly over HTTP and never touches the CLI inventory.
|
|
255
259
|
const unavailable = pane.selectedAgent === undefined && !apiModeConfigured;
|
|
256
260
|
const fileDrop = useChatPaneFileDrop({
|
|
257
|
-
enabled: uploadAttachments !== undefined
|
|
261
|
+
enabled: surface.uploadAttachments !== undefined
|
|
258
262
|
&& !disabled
|
|
259
263
|
&& !unavailable
|
|
260
264
|
&& !pane.conversation.isStreaming
|
|
@@ -265,17 +269,18 @@ export function ChatPane({ transport, agents: injectedAgents = EMPTY_AGENTS, run
|
|
|
265
269
|
// `ChatPaneProps.composerSlots`'s doc), so the two explicit keys below are never overwriting a
|
|
266
270
|
// caller-supplied value — they are the ONLY source for both.
|
|
267
271
|
const slots = definedProps({ source: {
|
|
268
|
-
...composerSlots,
|
|
269
|
-
leadingAccessories: leadingAccessory,
|
|
270
|
-
footerAccessories: (_jsx(AgentRuntimePicker, { agents: runtimeView.agents, value: pane.selection, onChange: pane.setSelection, ...definedProps({ source: { onRescan: runtimeView.rescanAgents } }), scanning: runtimeView.scanningAgents, daemonOnline: runtimeView.daemonOnline, placement: runtimePickerPlacement, executionMode: executionMode, apiModeAvailable: apiModeAvailable, ...definedProps({ source: { onExecutionModeChange, byokRuntime, onByokModelChange } }) })),
|
|
272
|
+
...surface.composerSlots,
|
|
273
|
+
leadingAccessories: surface.embed ? undefined : leadingAccessory,
|
|
274
|
+
footerAccessories: surface.embed ? undefined : (_jsx(AgentRuntimePicker, { agents: runtimeView.agents, value: pane.selection, onChange: pane.setSelection, ...definedProps({ source: { onRescan: runtimeView.rescanAgents } }), scanning: runtimeView.scanningAgents, daemonOnline: runtimeView.daemonOnline, placement: runtimePickerPlacement, executionMode: executionMode, apiModeAvailable: apiModeAvailable, ...definedProps({ source: { onExecutionModeChange, byokRuntime, onByokModelChange } }) })),
|
|
271
275
|
} });
|
|
272
276
|
const { rootRef, controlsRef } = useChatPaneControlsHeight();
|
|
277
|
+
const selectSuggestion = useChatPaneSuggestion({ rootRef, setDraft: pane.composer.setDraft });
|
|
273
278
|
const openLinkInNewTab = useChatPaneNewTabLinks();
|
|
274
279
|
// `MessageList` is memoized so a composer keystroke does not re-render the transcript; these two
|
|
275
280
|
// props would otherwise be fresh objects on every render and defeat it.
|
|
276
281
|
const { queuedPrompt, cancelQueued } = pane;
|
|
277
282
|
const pendingPrompt = useMemo(() => (queuedPrompt === null ? null : { text: queuedPrompt, onCancel: cancelQueued }), [queuedPrompt, cancelQueued]);
|
|
278
283
|
const projectFileNameSet = useMemo(() => (projectFileNames === undefined ? undefined : new Set(projectFileNames)), [projectFileNames]);
|
|
279
|
-
return (_jsxs("section", { className: chatPaneClassName(variant, className), style: style, "data-activity": pane.activity, ref: rootRef, onClickCapture: openLinkInNewTab, children: [_jsx("style", { "data-jini-chat-pane-styles": "true", children: CHAT_PANE_STYLES }), resolveChatPaneHeader(header, title, pane.reset, t), _jsxs("div", { className: "jini-chat-pane__body", children: [_jsx(MessageList, { messages: pane.conversation.messages, isStreaming: pane.conversation.isStreaming, scrollIntent: pane.conversation.scrollIntent, onScrolled: pane.conversation.acknowledgeScroll, pendingPrompt: pendingPrompt, ...(projectFileNameSet === undefined ? {} : { projectFileNames: projectFileNameSet }) }), _jsxs("div", { className: "jini-chat-pane__controls", ref: controlsRef, children: [_jsx(ChatPaneSuggestionsRow, { suggestions: suggestions, onSelect:
|
|
284
|
+
return (_jsxs("section", { className: chatPaneClassName(variant, className), style: style, "data-activity": pane.activity, ref: rootRef, onClickCapture: openLinkInNewTab, children: [_jsx("style", { "data-jini-chat-pane-styles": "true", children: CHAT_PANE_STYLES }), resolveChatPaneHeader(header, title, pane.reset, t), _jsxs("div", { className: "jini-chat-pane__body", children: [pane.conversation.messages.length === 0 && emptyState ? (_jsxs("div", { className: "jini-message-list jini-message-list--empty", children: [emptyState, surface.embed && _jsx(ChatPaneSuggestionsRow, { suggestions: suggestions, onSelect: selectSuggestion, t: t })] })) : (_jsx(MessageList, { messages: pane.conversation.messages, isStreaming: pane.conversation.isStreaming, scrollIntent: pane.conversation.scrollIntent, onScrolled: pane.conversation.acknowledgeScroll, pendingPrompt: pendingPrompt, toolCallDisplay: toolCallDisplay, showUsageLine: showUsageLine, showThinking: showThinking, ...(projectFileNameSet === undefined ? {} : { projectFileNames: projectFileNameSet }) })), _jsxs("div", { className: "jini-chat-pane__controls", ref: controlsRef, children: [(!surface.embed || (pane.conversation.messages.length === 0 && !emptyState)) && (_jsx(ChatPaneSuggestionsRow, { suggestions: suggestions, onSelect: selectSuggestion, t: t })), pane.secretRedacted ? _jsx("div", { className: "jini-chat-pane__notice", role: "status", children: t(SECRET_REDACTED_NOTICE) }) : null, _jsx(ChatPaneStatusMessages, { unavailable: unavailable, scanningAgents: runtimeView.scanningAgents, connectingAgents: runtimeView.connectingAgents, executionMode: executionMode, apiModeAvailable: apiModeAvailable, onExecutionModeChange: onExecutionModeChange, conversationError: pane.conversation.error, attachmentError: pane.attachmentError, dropReadError: fileDrop.dropReadError, workingDirectoryError: pane.workingDirectoryError, workingDirectoryPending: pane.workingDirectoryPending, workingDirectoryInvalid: pane.workingDirectoryInvalid, runtimeInventoryError: inventory.runtimeInventoryError, typedAnswerNotice: pane.typedAnswerNotice, onSendAsNewMessage: () => void pane.sendAsNewMessage(), t: t }), _jsx(ChatPaneComposerArea, { fileDrop: fileDrop, uploadAttachments: surface.uploadAttachments, pane: pane, disabled: disabled, unavailable: unavailable, placeholder: composerPlaceholder, slots: slots, attachmentAccept: attachmentAccept, workingDirectoryAccess: workingDirectoryAccess, workingDirectoryControlPlacement: surface.workingDirectoryControlPlacement, t: t })] })] }), footer] }));
|
|
280
285
|
}
|
|
281
286
|
//# sourceMappingURL=ChatPane.js.map
|