@parallelworks/ui 0.51.0 → 0.52.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/README.md +155 -14
- package/dist/ai/agent/toolStrings.d.ts +2 -0
- package/dist/ai/agent/toolStrings.js +20 -18
- package/dist/ai/ai.css +12 -0
- package/dist/ai/components/AllocationSelector.js +5 -1
- package/dist/ai/components/AttachmentManager.js +51 -42
- package/dist/ai/components/AttachmentUpload.d.ts +4 -2
- package/dist/ai/components/AttachmentUpload.js +9 -9
- package/dist/ai/components/BlockedGroupBanner.js +8 -14
- package/dist/ai/components/BranchNavigator.js +11 -12
- package/dist/ai/components/ChatEmptyState.js +12 -19
- package/dist/ai/components/ChatInput.d.ts +4 -3
- package/dist/ai/components/ChatInput.js +27 -13
- package/dist/ai/components/ChatMessage.js +45 -121
- package/dist/ai/components/ChatMessageList.d.ts +5 -2
- package/dist/ai/components/ChatMessageList.js +89 -51
- package/dist/ai/components/ChatSidebar.js +11 -7
- package/dist/ai/components/ChatThread.js +1 -13
- package/dist/ai/components/DragOverlay.js +3 -3
- package/dist/ai/components/MessageAttachments.d.ts +10 -0
- package/dist/ai/components/MessageAttachments.js +78 -0
- package/dist/ai/components/ModelSelector.js +3 -3
- package/dist/ai/components/PastedText.js +2 -2
- package/dist/ai/components/ProviderIssueBanner.js +19 -23
- package/dist/ai/components/ShareDialog.js +2 -2
- package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
- package/dist/ai/components/agent/ActivityLine.js +8 -51
- package/dist/ai/components/agent/AgentMessageParts.js +114 -53
- package/dist/ai/core/ChatProvider.d.ts +16 -5
- package/dist/ai/core/ChatProvider.js +41 -8
- package/dist/ai/core/config.js +4 -3
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +3 -3
- package/dist/ai/strings.d.ts +29 -3
- package/dist/ai/strings.js +81 -26
- package/dist/ai/types.d.ts +4 -0
- package/dist/ai/ui/Notice.d.ts +2 -2
- package/dist/ai/ui/Notice.js +29 -62
- package/dist/ai/ui/sidebar.d.ts +5 -3
- package/dist/ai/ui/sidebar.js +21 -9
- package/dist/components/ActionMenu.d.ts +1 -0
- package/dist/components/ActionMenu.js +6 -1
- package/dist/components/AppLink.d.ts +26 -0
- package/dist/components/AppLink.js +51 -0
- package/dist/components/Avatar.d.ts +2 -1
- package/dist/components/Avatar.js +4 -2
- package/dist/components/Button.d.ts +15 -5
- package/dist/components/Button.js +47 -40
- package/dist/components/Callout.d.ts +31 -7
- package/dist/components/Callout.js +50 -19
- package/dist/components/Card.d.ts +11 -6
- package/dist/components/Card.js +14 -8
- package/dist/components/Checkbox.js +2 -1
- package/dist/components/ColorInput.js +5 -3
- package/dist/components/Combobox.js +49 -19
- package/dist/components/ConfirmModal.d.ts +19 -8
- package/dist/components/ConfirmModal.js +31 -53
- package/dist/components/CopyButton.d.ts +38 -0
- package/dist/components/CopyButton.js +68 -0
- package/dist/components/CreateModal.js +118 -111
- package/dist/components/DatePicker.js +12 -8
- package/dist/components/DescriptionList.d.ts +25 -5
- package/dist/components/DescriptionList.js +56 -22
- package/dist/components/Disclosure.js +1 -1
- package/dist/components/Drawer.js +10 -16
- package/dist/components/Field.d.ts +15 -5
- package/dist/components/Field.js +40 -8
- package/dist/components/IconButton.d.ts +14 -5
- package/dist/components/IconButton.js +25 -14
- package/dist/components/InfoTip.d.ts +4 -1
- package/dist/components/InfoTip.js +2 -1
- package/dist/components/Input.d.ts +14 -8
- package/dist/components/Input.js +46 -31
- package/dist/components/Loader.js +1 -1
- package/dist/components/MenuEntries.d.ts +4 -0
- package/dist/components/MenuEntries.js +11 -12
- package/dist/components/Meter.js +5 -4
- package/dist/components/Modal.d.ts +28 -4
- package/dist/components/Modal.js +37 -14
- package/dist/components/Overview.d.ts +20 -12
- package/dist/components/Overview.js +14 -9
- package/dist/components/PageHeader.d.ts +47 -65
- package/dist/components/PageHeader.js +275 -153
- package/dist/components/PageHeaderAction.d.ts +67 -0
- package/dist/components/PageHeaderAction.js +107 -0
- package/dist/components/Pagination.js +6 -16
- package/dist/components/Popover.js +3 -2
- package/dist/components/Provider.d.ts +77 -19
- package/dist/components/Provider.js +139 -130
- package/dist/components/Radio.js +2 -1
- package/dist/components/RefreshButton.js +7 -8
- package/dist/components/RequiredMark.js +1 -1
- package/dist/components/Section.js +1 -1
- package/dist/components/Select.d.ts +4 -9
- package/dist/components/Select.js +24 -11
- package/dist/components/SettingModeOption.d.ts +1 -1
- package/dist/components/SettingModeOption.js +7 -1
- package/dist/components/SettingsCard.d.ts +5 -3
- package/dist/components/SettingsCard.js +27 -20
- package/dist/components/Slider.js +1 -1
- package/dist/components/StatusBadge.js +2 -2
- package/dist/components/Switch.js +5 -4
- package/dist/components/Table.d.ts +30 -20
- package/dist/components/Table.js +50 -73
- package/dist/components/Tabs.d.ts +3 -7
- package/dist/components/Tabs.js +6 -10
- package/dist/components/ThemeEditor.js +18 -8
- package/dist/components/Toast.d.ts +11 -3
- package/dist/components/Toast.js +45 -20
- package/dist/components/buttonClasses.js +1 -1
- package/dist/components/comboboxItems.d.ts +4 -0
- package/dist/components/comboboxItems.js +6 -1
- package/dist/components/dismiss.d.ts +12 -0
- package/dist/components/dismiss.js +19 -0
- package/dist/components/fieldBox.d.ts +4 -0
- package/dist/components/fieldBox.js +7 -3
- package/dist/components/floating.d.ts +3 -3
- package/dist/components/floating.js +1 -1
- package/dist/components/markdownCodeBreaks.js +3 -4
- package/dist/components/pageHeaderStore.d.ts +41 -0
- package/dist/components/pageHeaderStore.js +67 -0
- package/dist/components/pathBreaks.d.ts +8 -0
- package/dist/components/pathBreaks.js +28 -0
- package/dist/components/segmented.js +1 -1
- package/dist/components/shortcutKey.d.ts +2 -1
- package/dist/components/shortcutKey.js +19 -2
- package/dist/components/splitter.js +1 -1
- package/dist/components/useCssIsDark.js +40 -18
- package/dist/components/useTheme.js +9 -10
- package/dist/dev.d.ts +9 -0
- package/dist/dev.js +14 -0
- package/dist/editor/Editor.d.ts +3 -2
- package/dist/editor/Editor.js +11 -2
- package/dist/editor/EditorField.d.ts +2 -1
- package/dist/editor/EditorField.js +11 -29
- package/dist/editor/FormCodePanel.d.ts +7 -3
- package/dist/editor/FormCodePanel.js +14 -7
- package/dist/editor/Monaco.d.ts +14 -5
- package/dist/editor/Monaco.js +163 -112
- package/dist/editor/index.d.ts +5 -5
- package/dist/editor/index.js +3 -3
- package/dist/editor/themes.d.ts +8 -5
- package/dist/editor/themes.js +58 -30
- package/dist/editor/yaml.d.ts +6 -2
- package/dist/editor/yaml.js +11 -2
- package/dist/file-explorer/FileExplorer.js +143 -124
- package/dist/file-explorer/FileExplorerContext.js +1 -1
- package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
- package/dist/file-explorer/FilePreview/Viewer.js +2 -2
- package/dist/file-explorer/FilePreview/index.js +4 -4
- package/dist/file-explorer/FileRow.js +1 -1
- package/dist/file-explorer/FileRowsWindow.js +34 -3
- package/dist/file-explorer/FileTable.js +3 -1
- package/dist/file-explorer/TreeView.js +37 -14
- package/dist/file-explorer/helpers.d.ts +2 -1
- package/dist/file-explorer/helpers.js +21 -20
- package/dist/file-explorer/toasts.js +27 -19
- package/dist/file-explorer/treeRows.d.ts +6 -0
- package/dist/file-explorer/treeRows.js +19 -1
- package/dist/file-explorer/useNearEnd.js +3 -6
- package/dist/form/Fields/CheckboxGroupField.js +12 -5
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Fields/LabelField.js +1 -1
- package/dist/form/Fields/ObjectField.d.ts +1 -1
- package/dist/form/Fields/ObjectField.js +3 -2
- package/dist/form/Fields/StringField.js +16 -29
- package/dist/form/Fields/TextAreaField.js +3 -4
- package/dist/form/Form.js +3 -8
- package/dist/form/Wizard/WizardNavigation.js +0 -3
- package/dist/form/Wizard/utils.js +1 -1
- package/dist/form/fieldFrame.d.ts +1 -1
- package/dist/form/fieldFrame.js +2 -2
- package/dist/form/types/fieldComponentTypes.d.ts +2 -1
- package/dist/graph/AnnotationBanner.d.ts +1 -2
- package/dist/graph/AnnotationBanner.js +30 -32
- package/dist/graph/Connectors.js +4 -1
- package/dist/graph/DependencyGraph.js +2 -2
- package/dist/graph/GraphEditor.js +11 -9
- package/dist/graph/GraphEditorDialogs.js +1 -1
- package/dist/graph/GraphNode.js +7 -4
- package/dist/graph/InputDialog.js +1 -1
- package/dist/graph/InputsEditor.js +14 -11
- package/dist/graph/MatrixGroup.js +9 -3
- package/dist/graph/Reveal.js +18 -10
- package/dist/graph/ShortcutsButton.d.ts +11 -2
- package/dist/graph/ShortcutsButton.js +14 -3
- package/dist/graph/TreeView.js +35 -18
- package/dist/graph/WorkflowSettingsDialog.js +1 -1
- package/dist/graph/editorFields.js +1 -1
- package/dist/graph/editorRows.js +16 -10
- package/dist/graph/editorStrings.d.ts +7 -0
- package/dist/graph/editorStrings.js +8 -1
- package/dist/graph/inputRefs.js +1 -1
- package/dist/graph/settingsViews.js +11 -18
- package/dist/graph/stepWith.js +1 -1
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +16 -16
- package/dist/list/ActionToolbar.d.ts +42 -6
- package/dist/list/ActionToolbar.js +297 -101
- package/dist/list/ListPage.d.ts +15 -23
- package/dist/list/ListPage.js +42 -43
- package/dist/list/ListTable.d.ts +32 -7
- package/dist/list/ListTable.js +48 -12
- package/dist/list/ListViewControls.js +1 -1
- package/dist/list/UserHoverCard.js +7 -15
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +2 -2
- package/dist/list/listSearch.js +12 -10
- package/dist/list/listView.d.ts +10 -1
- package/dist/list/listView.js +15 -1
- package/dist/logviewer/LogViewer.d.ts +4 -2
- package/dist/logviewer/LogViewer.js +87 -69
- package/dist/logviewer/logviewer.css +3 -2
- package/dist/reducedMotion.d.ts +5 -0
- package/dist/reducedMotion.js +20 -0
- package/dist/safeUrl.d.ts +1 -1
- package/dist/safeUrl.js +4 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/components/Breadcrumbs.d.ts +0 -37
- package/dist/components/Breadcrumbs.js +0 -90
- package/dist/components/CopyToClipboard.d.ts +0 -17
- package/dist/components/CopyToClipboard.js +0 -66
- package/dist/components/Sort.d.ts +0 -9
- package/dist/components/Sort.js +0 -42
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChevronLeftIcon, ChevronRightIcon } from "../../icons.js";
|
|
2
|
+
import { IconButton } from "../../components/IconButton.js";
|
|
2
3
|
import { useChatConfig } from "../core/config.js";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { useMemo } from "react";
|
|
@@ -26,14 +27,13 @@ function BranchNavigator({ messages, currentMessageId, onNavigate }) {
|
|
|
26
27
|
return /* @__PURE__ */ jsxs("div", {
|
|
27
28
|
className: "inline-flex items-center text-xs theme-muted-text",
|
|
28
29
|
children: [
|
|
29
|
-
/* @__PURE__ */ jsx(
|
|
30
|
-
|
|
30
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
31
|
+
label: t.previous,
|
|
31
32
|
onClick: handlePrev,
|
|
32
33
|
disabled: !hasPrev,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-3.5 h-3.5" })
|
|
34
|
+
size: "md",
|
|
35
|
+
className: "size-7 hover:not-disabled:not-aria-disabled:chat-tint disabled:opacity-30",
|
|
36
|
+
icon: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
|
|
37
37
|
}),
|
|
38
38
|
/* @__PURE__ */ jsxs("span", {
|
|
39
39
|
className: "min-w-[3ch] text-center tabular-nums",
|
|
@@ -43,14 +43,13 @@ function BranchNavigator({ messages, currentMessageId, onNavigate }) {
|
|
|
43
43
|
siblings.length
|
|
44
44
|
]
|
|
45
45
|
}),
|
|
46
|
-
/* @__PURE__ */ jsx(
|
|
47
|
-
|
|
46
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
47
|
+
label: t.next,
|
|
48
48
|
onClick: handleNext,
|
|
49
49
|
disabled: !hasNext,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3.5 h-3.5" })
|
|
50
|
+
size: "md",
|
|
51
|
+
className: "size-7 hover:not-disabled:not-aria-disabled:chat-tint disabled:opacity-30",
|
|
52
|
+
icon: /* @__PURE__ */ jsx(ChevronRightIcon, {})
|
|
54
53
|
})
|
|
55
54
|
]
|
|
56
55
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { AddIcon, CloudIcon } from "../../icons.js";
|
|
2
2
|
import { keyedByContent } from "../../components/keys.js";
|
|
3
3
|
import { useStrings } from "../../components/Provider.js";
|
|
4
|
+
import { Button } from "../../components/Button.js";
|
|
5
|
+
import { Callout } from "../../components/Callout.js";
|
|
4
6
|
import { useChatConfig } from "../core/config.js";
|
|
5
7
|
import { useChat } from "../core/ChatProvider.js";
|
|
6
8
|
import BlockedGroupBanner from "./BlockedGroupBanner.js";
|
|
7
9
|
import { getGreeting } from "../core/greeting.js";
|
|
8
10
|
import useDragDrop from "../core/useDragDrop.js";
|
|
9
|
-
import { NoticeCard } from "../ui/Notice.js";
|
|
10
11
|
import { ComposerControls, ConnectToolsLink } from "./ComposerChrome.js";
|
|
11
12
|
import DragOverlay from "./DragOverlay.js";
|
|
12
13
|
import ChatInput from "./ChatInput.js";
|
|
@@ -79,26 +80,18 @@ function ChatEmptyState({ targetSession }) {
|
|
|
79
80
|
/* @__PURE__ */ jsx(ProviderIssueBanner, {}),
|
|
80
81
|
sessionUnreachable && !sessionBannerDismissed && /* @__PURE__ */ jsx("div", {
|
|
81
82
|
className: "mx-4 mt-2",
|
|
82
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ jsx(Callout, {
|
|
83
84
|
tone: "warning",
|
|
85
|
+
size: "sm",
|
|
86
|
+
title: t.sessionUnreachableTitle(targetSession?.split(":").pop() ?? ""),
|
|
87
|
+
action: /* @__PURE__ */ jsx(Button, {
|
|
88
|
+
size: "sm",
|
|
89
|
+
variant: "outline",
|
|
90
|
+
onClick: () => refreshModels(),
|
|
91
|
+
children: common.retry
|
|
92
|
+
}),
|
|
84
93
|
onDismiss: () => setSessionBannerDismissed(true),
|
|
85
|
-
|
|
86
|
-
children: [
|
|
87
|
-
/* @__PURE__ */ jsx("span", {
|
|
88
|
-
className: "block font-medium",
|
|
89
|
-
children: t.sessionUnreachableTitle(targetSession?.split(":").pop() ?? "")
|
|
90
|
-
}),
|
|
91
|
-
/* @__PURE__ */ jsx("span", {
|
|
92
|
-
className: "mt-0.5 block",
|
|
93
|
-
children: t.sessionUnreachableBody
|
|
94
|
-
}),
|
|
95
|
-
/* @__PURE__ */ jsx("button", {
|
|
96
|
-
type: "button",
|
|
97
|
-
onClick: () => refreshModels(),
|
|
98
|
-
className: "mt-1 font-medium underline-offset-2 hover:underline",
|
|
99
|
-
children: common.retry
|
|
100
|
-
})
|
|
101
|
-
]
|
|
94
|
+
children: t.sessionUnreachableBody
|
|
102
95
|
})
|
|
103
96
|
}),
|
|
104
97
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
1
|
+
import { type ReactNode, type Ref } from 'react';
|
|
2
2
|
import type { AttachmentMeta, MessagePaste } from '../types.js';
|
|
3
3
|
import { type SlashMenuConfig } from './SlashMenu.js';
|
|
4
4
|
import { type ComposerPastes } from './usePasteCards.js';
|
|
@@ -43,10 +43,11 @@ interface ChatInputProps {
|
|
|
43
43
|
stopping?: boolean;
|
|
44
44
|
};
|
|
45
45
|
pastes?: ComposerPastes | undefined;
|
|
46
|
+
ref?: Ref<ChatInputHandle> | undefined;
|
|
46
47
|
}
|
|
47
48
|
export interface ChatInputHandle {
|
|
48
49
|
addFiles: (files: FileList | File[]) => void;
|
|
49
50
|
focus: () => void;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
export
|
|
52
|
+
export default function ChatInput({ onSend, disabled, placeholder, conversationId, onEditLastMessage, allowEmpty, attachments: attachmentsWanted, requireModel, inputLabel, sendLabel, sendTestId, slash, notices, activity, context, reserveContext, settingsLeft, settingsRight, hint, turn, pastes, ref, }: ChatInputProps): import("react").JSX.Element;
|
|
53
|
+
export {};
|
|
@@ -11,7 +11,7 @@ import { PasteFileCard } from "./PastedText.js";
|
|
|
11
11
|
import { SlashMenu, useSlashMenu } from "./SlashMenu.js";
|
|
12
12
|
import { usePasteCards } from "./usePasteCards.js";
|
|
13
13
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
-
import {
|
|
14
|
+
import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
15
15
|
//#region src/ai/components/ChatInput.tsx
|
|
16
16
|
/** An optional box that is still empty should not wear the filled look of
|
|
17
17
|
* one waiting to send something. */
|
|
@@ -19,7 +19,7 @@ function sendability({ hasText, canFlushQueue, allowEmpty, disabled, modelReady,
|
|
|
19
19
|
const canSend = (hasText || canFlushQueue || allowEmpty) && !disabled && modelReady && pendingCount === 0;
|
|
20
20
|
return {
|
|
21
21
|
canSend,
|
|
22
|
-
|
|
22
|
+
emphasized: canSend && (hasText || !allowEmpty)
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
function keyAction(e, empty, canEditLast) {
|
|
@@ -39,13 +39,13 @@ function AttachButton({ open, disabled, label, onClick }) {
|
|
|
39
39
|
children: /* @__PURE__ */ jsx(AddIcon, { className: cx("h-5 w-5 transition-transform duration-200", open && "rotate-45") })
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
function SendButton({ onClick, enabled,
|
|
42
|
+
function SendButton({ onClick, enabled, emphasized, label, testId }) {
|
|
43
43
|
return /* @__PURE__ */ jsx("button", {
|
|
44
44
|
type: "button",
|
|
45
45
|
onClick,
|
|
46
46
|
disabled: !enabled,
|
|
47
47
|
"data-testid": testId,
|
|
48
|
-
className: cx("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-[background-color,color,transform] duration-150 active:scale-95",
|
|
48
|
+
className: cx("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-[background-color,color,transform] duration-150 active:scale-95", emphasized ? "theme-accent hover:opacity-90" : "chat-send-idle", !enabled && "cursor-not-allowed active:scale-100"),
|
|
49
49
|
"aria-label": label,
|
|
50
50
|
title: label,
|
|
51
51
|
children: /* @__PURE__ */ jsx(ArrowUpIcon, { className: "h-4 w-4" })
|
|
@@ -64,8 +64,12 @@ function streamControls(chat, turn) {
|
|
|
64
64
|
queuedCount: chat.queuedMessages.length
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
function fitToContent(el) {
|
|
68
|
+
el.style.height = "auto";
|
|
69
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
70
|
+
}
|
|
67
71
|
var draftsSwept = false;
|
|
68
|
-
|
|
72
|
+
function ChatInput({ onSend, disabled = false, placeholder, conversationId, onEditLastMessage, allowEmpty = false, attachments: attachmentsWanted = true, requireModel = true, inputLabel, sendLabel, sendTestId, slash, notices, activity, context, reserveContext = false, settingsLeft, settingsRight = /* @__PURE__ */ jsx(ComposerControls, {}), hint, turn, pastes, ref }) {
|
|
69
73
|
const [input, setInput] = useState(() => readDraft(conversationId));
|
|
70
74
|
useEffect(() => {
|
|
71
75
|
if (!draftsSwept) {
|
|
@@ -98,12 +102,22 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
98
102
|
textareaRef.current?.focus();
|
|
99
103
|
}
|
|
100
104
|
}), []);
|
|
101
|
-
|
|
102
|
-
if (textareaRef.current)
|
|
103
|
-
textareaRef.current.style.height = "auto";
|
|
104
|
-
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
|
105
|
-
}
|
|
105
|
+
useLayoutEffect(() => {
|
|
106
|
+
if (textareaRef.current) fitToContent(textareaRef.current);
|
|
106
107
|
}, [input]);
|
|
108
|
+
useLayoutEffect(() => {
|
|
109
|
+
const el = textareaRef.current;
|
|
110
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
111
|
+
let width = el.clientWidth;
|
|
112
|
+
const observer = new ResizeObserver(() => {
|
|
113
|
+
if (el.clientWidth !== width) {
|
|
114
|
+
width = el.clientWidth;
|
|
115
|
+
fitToContent(el);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
observer.observe(el);
|
|
119
|
+
return () => observer.disconnect();
|
|
120
|
+
}, []);
|
|
107
121
|
const previousConversationRef = useRef(conversationId);
|
|
108
122
|
useEffect(() => {
|
|
109
123
|
if (previousConversationRef.current !== conversationId) {
|
|
@@ -119,7 +133,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
119
133
|
const uploadedCount = attachments.filter((a) => a.uploaded).length;
|
|
120
134
|
const pendingCount = attachments.length - uploadedCount;
|
|
121
135
|
const canFlushQueue = !isStreaming && queuedCount > 0 && !disabled;
|
|
122
|
-
const { canSend,
|
|
136
|
+
const { canSend, emphasized } = sendability({
|
|
123
137
|
hasText: input.trim().length > 0 || cards.length > 0,
|
|
124
138
|
canFlushQueue,
|
|
125
139
|
allowEmpty,
|
|
@@ -195,7 +209,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
195
209
|
}) : /* @__PURE__ */ jsx(SendButton, {
|
|
196
210
|
onClick: handleSubmit,
|
|
197
211
|
enabled: canSend,
|
|
198
|
-
|
|
212
|
+
emphasized,
|
|
199
213
|
label: sendLabel ?? t.sendMessage,
|
|
200
214
|
testId: sendTestId
|
|
201
215
|
});
|
|
@@ -297,6 +311,6 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
297
311
|
] })]
|
|
298
312
|
})]
|
|
299
313
|
});
|
|
300
|
-
}
|
|
314
|
+
}
|
|
301
315
|
//#endregion
|
|
302
316
|
export { ChatInput as default };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { cx } from "../../cx.js";
|
|
2
|
-
import {
|
|
3
|
-
import { safeUrl } from "../../safeUrl.js";
|
|
2
|
+
import { EditIcon, RetryIcon } from "../../icons.js";
|
|
4
3
|
import { useStrings } from "../../components/Provider.js";
|
|
5
|
-
import { useCopy } from "../../components/useCopy.js";
|
|
6
4
|
import { Avatar } from "../../components/Avatar.js";
|
|
7
|
-
import {
|
|
5
|
+
import { Button } from "../../components/Button.js";
|
|
6
|
+
import { IconButton } from "../../components/IconButton.js";
|
|
7
|
+
import { Callout } from "../../components/Callout.js";
|
|
8
|
+
import { CopyButton } from "../../components/CopyButton.js";
|
|
9
|
+
import { formatDuration } from "../../format.js";
|
|
8
10
|
import { RelativeTime } from "../../components/relativeTime.js";
|
|
9
11
|
import { useChatConfig } from "../core/config.js";
|
|
10
12
|
import Markdown from "../ui/Markdown.js";
|
|
@@ -13,26 +15,19 @@ import { Reasoning } from "./Reasoning.js";
|
|
|
13
15
|
import AgentMessageParts from "./agent/AgentMessageParts.js";
|
|
14
16
|
import BranchNavigator from "./BranchNavigator.js";
|
|
15
17
|
import { UserMessageText } from "./PastedText.js";
|
|
18
|
+
import MessageAttachments from "./MessageAttachments.js";
|
|
16
19
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
20
|
import { memo, useEffect, useRef, useState } from "react";
|
|
18
21
|
import { DateTime } from "luxon";
|
|
19
22
|
//#region src/ai/components/ChatMessage.tsx
|
|
23
|
+
/** A message action: the chat's square, tinted from the ink on hover. */
|
|
24
|
+
var actionClasses = "size-7 hover:not-disabled:not-aria-disabled:chat-tint";
|
|
20
25
|
var COLLAPSE_THRESHOLD = 680;
|
|
21
26
|
function parseTimestamp(iso) {
|
|
22
27
|
if (!iso) return null;
|
|
23
28
|
const dt = DateTime.fromISO(iso);
|
|
24
29
|
return dt.isValid ? dt : null;
|
|
25
30
|
}
|
|
26
|
-
function triggerDownload(url, filename) {
|
|
27
|
-
const href = safeUrl(url);
|
|
28
|
-
if (!href) return;
|
|
29
|
-
const a = document.createElement("a");
|
|
30
|
-
a.href = href;
|
|
31
|
-
a.download = filename;
|
|
32
|
-
document.body.appendChild(a);
|
|
33
|
-
a.click();
|
|
34
|
-
document.body.removeChild(a);
|
|
35
|
-
}
|
|
36
31
|
function ChatMessage({ message, allMessages, isStreaming = false, latest = false, isEditing = false, showAuthor = false, currentUsername, attachmentDownloadUrl, onOpenAttachment, onNavigateBranch, onEdit, onSaveEdit, onCancelEdit, onRegenerate, onRetry, onOpenReasoning, onApprovalAnswer, flush }) {
|
|
37
32
|
const isUser = message.role === "user";
|
|
38
33
|
const isTool = message.role === "tool";
|
|
@@ -47,8 +42,6 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
47
42
|
const showTurnDurationLabel = !isUser && !isStreaming && !hasReasoning && (!!message.reasoningDuration || toolCallCount > 0);
|
|
48
43
|
const { messageMeta: t, thinking: tThinking } = useChatConfig().strings;
|
|
49
44
|
const { common } = useStrings();
|
|
50
|
-
const { copy, copied } = useCopy();
|
|
51
|
-
const [showActions, setShowActions] = useState(false);
|
|
52
45
|
const [editContent, setEditContent] = useState(message.content || "");
|
|
53
46
|
const textareaRef = useRef(null);
|
|
54
47
|
const contentRef = useRef(null);
|
|
@@ -106,16 +99,9 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
106
99
|
const timestamp = parseTimestamp(message.timestamp);
|
|
107
100
|
const totalTokens = message.tokensUsed?.total_tokens;
|
|
108
101
|
const collapsed = !isUser && collapsible && !expanded && !isStreaming && !latest;
|
|
109
|
-
const copyToClipboard = async () => {
|
|
110
|
-
if (message.content) await copy(message.content, { quiet: true });
|
|
111
|
-
};
|
|
112
102
|
return /* @__PURE__ */ jsx("div", {
|
|
113
103
|
role: "none",
|
|
114
104
|
className: cx("group w-full", isUser ? "pt-8 pb-5" : "py-3", !flush && "px-4"),
|
|
115
|
-
onMouseEnter: () => setShowActions(true),
|
|
116
|
-
onMouseLeave: () => setShowActions(false),
|
|
117
|
-
onFocus: () => setShowActions(true),
|
|
118
|
-
onBlur: () => setShowActions(false),
|
|
119
105
|
children: isEditing ? /* @__PURE__ */ jsxs("div", {
|
|
120
106
|
className: "w-full",
|
|
121
107
|
children: [/* @__PURE__ */ jsx("textarea", {
|
|
@@ -124,7 +110,8 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
124
110
|
onChange: handleTextareaChange,
|
|
125
111
|
onKeyDown: handleKeyDown,
|
|
126
112
|
className: "chat-composer block w-full min-h-[100px] max-h-[200px] overflow-y-auto px-4 py-3.5 rounded-[1.25rem] bg-(--theme-panel-bg) theme-text text-[1rem] leading-relaxed resize-none focus:outline-none",
|
|
127
|
-
placeholder: t.editPlaceholder
|
|
113
|
+
placeholder: t.editPlaceholder,
|
|
114
|
+
"aria-label": t.edit
|
|
128
115
|
}), /* @__PURE__ */ jsxs("div", {
|
|
129
116
|
className: "flex items-center justify-end gap-2 mt-2.5",
|
|
130
117
|
children: [/* @__PURE__ */ jsx("button", {
|
|
@@ -212,110 +199,47 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
212
199
|
className: "inline-flex items-center mt-1 text-[11px] italic theme-muted-text",
|
|
213
200
|
children: t.stopped
|
|
214
201
|
}),
|
|
215
|
-
hasError && /* @__PURE__ */
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
className: "flex items-center gap-1 px-2 py-1 text-xs font-medium text-red-700 hover:bg-red-500/15 rounded transition-colors",
|
|
227
|
-
children: [/* @__PURE__ */ jsx(RetryIcon, { className: "w-3 h-3" }), common.retry]
|
|
228
|
-
})
|
|
229
|
-
]
|
|
202
|
+
hasError && /* @__PURE__ */ jsx(Callout, {
|
|
203
|
+
tone: "error",
|
|
204
|
+
size: "sm",
|
|
205
|
+
className: "mt-2",
|
|
206
|
+
action: onRetry && /* @__PURE__ */ jsxs(Button, {
|
|
207
|
+
size: "sm",
|
|
208
|
+
variant: "outline",
|
|
209
|
+
onClick: () => onRetry(message.id),
|
|
210
|
+
children: [/* @__PURE__ */ jsx(RetryIcon, {}), common.retry]
|
|
211
|
+
}),
|
|
212
|
+
children: message.error
|
|
230
213
|
}),
|
|
231
|
-
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (isImage && downloadUrl) return /* @__PURE__ */ jsx("div", {
|
|
237
|
-
className: "block rounded-lg overflow-hidden border theme-border",
|
|
238
|
-
...onOpenAttachment ? {
|
|
239
|
-
onClick: () => onOpenAttachment(att),
|
|
240
|
-
role: "button",
|
|
241
|
-
style: { cursor: "pointer" }
|
|
242
|
-
} : {},
|
|
243
|
-
children: /* @__PURE__ */ jsx("img", {
|
|
244
|
-
src: downloadUrl,
|
|
245
|
-
alt: att.filename || "Image attachment",
|
|
246
|
-
className: "max-w-[200px] max-h-[200px] object-cover",
|
|
247
|
-
loading: "lazy"
|
|
248
|
-
})
|
|
249
|
-
}, att.id);
|
|
250
|
-
if (downloadUrl) return /* @__PURE__ */ jsxs("button", {
|
|
251
|
-
type: "button",
|
|
252
|
-
onClick: () => onOpenAttachment ? onOpenAttachment(att) : triggerDownload(downloadUrl, att.filename || "File"),
|
|
253
|
-
className: "inline-flex items-center gap-1.5 text-xs px-2.5 py-1.5 rounded-lg bg-(--theme-muted-panel-bg) hover:theme-hover transition-colors cursor-pointer",
|
|
254
|
-
children: [
|
|
255
|
-
/* @__PURE__ */ jsx(FileIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
|
|
256
|
-
/* @__PURE__ */ jsx("span", {
|
|
257
|
-
className: "truncate max-w-[150px]",
|
|
258
|
-
children: att.filename || "File"
|
|
259
|
-
}),
|
|
260
|
-
att.size ? /* @__PURE__ */ jsxs("span", {
|
|
261
|
-
className: "theme-muted-text",
|
|
262
|
-
children: [
|
|
263
|
-
"(",
|
|
264
|
-
formatBytes(att.size),
|
|
265
|
-
")"
|
|
266
|
-
]
|
|
267
|
-
}) : null,
|
|
268
|
-
/* @__PURE__ */ jsx(DownloadIcon, { className: "w-3 h-3 flex-shrink-0" })
|
|
269
|
-
]
|
|
270
|
-
}, att.id);
|
|
271
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
272
|
-
className: "inline-flex items-center gap-1.5 text-xs px-2.5 py-1.5 rounded-lg bg-(--theme-muted-panel-bg) theme-muted-text",
|
|
273
|
-
...onOpenAttachment ? {
|
|
274
|
-
onClick: () => onOpenAttachment(att),
|
|
275
|
-
role: "button",
|
|
276
|
-
style: { cursor: "pointer" }
|
|
277
|
-
} : {},
|
|
278
|
-
children: [
|
|
279
|
-
/* @__PURE__ */ jsx(FileIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
|
|
280
|
-
/* @__PURE__ */ jsx("span", {
|
|
281
|
-
className: "truncate max-w-[150px]",
|
|
282
|
-
children: att.filename || "File"
|
|
283
|
-
}),
|
|
284
|
-
att.size ? /* @__PURE__ */ jsxs("span", {
|
|
285
|
-
className: "theme-muted-text",
|
|
286
|
-
children: [
|
|
287
|
-
"(",
|
|
288
|
-
formatBytes(att.size),
|
|
289
|
-
")"
|
|
290
|
-
]
|
|
291
|
-
}) : null
|
|
292
|
-
]
|
|
293
|
-
}, att.id);
|
|
294
|
-
})
|
|
214
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx(MessageAttachments, {
|
|
215
|
+
attachments: message.attachments,
|
|
216
|
+
downloadUrl: attachmentDownloadUrl,
|
|
217
|
+
onOpen: onOpenAttachment,
|
|
218
|
+
className: isRightAligned ? "justify-end" : void 0
|
|
295
219
|
}),
|
|
296
220
|
!isStreaming && !isEditing && /* @__PURE__ */ jsxs("div", {
|
|
297
|
-
className: cx("chat-actions flex items-center gap-0.5 transition-opacity duration-150", isRightAligned ? "absolute right-0 top-full -mr-1.5 mt-0.5" : "-ml-1.5 mt-2", isRightAligned &&
|
|
221
|
+
className: cx("chat-actions flex items-center gap-0.5 transition-opacity duration-150", isRightAligned ? "absolute right-0 top-full -mr-1.5 mt-0.5" : "-ml-1.5 mt-2", isRightAligned && "pointer-events-none opacity-0 group-focus-within:pointer-events-auto group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:opacity-100"),
|
|
298
222
|
children: [
|
|
299
|
-
/* @__PURE__ */ jsx(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
223
|
+
message.content && /* @__PURE__ */ jsx(CopyButton, {
|
|
224
|
+
text: message.content ?? "",
|
|
225
|
+
label: t.copyLabel,
|
|
226
|
+
quiet: true,
|
|
227
|
+
size: "md",
|
|
228
|
+
className: actionClasses
|
|
305
229
|
}),
|
|
306
|
-
isUser && !isOtherUser && onEdit && /* @__PURE__ */ jsx(
|
|
307
|
-
|
|
230
|
+
isUser && !isOtherUser && onEdit && /* @__PURE__ */ jsx(IconButton, {
|
|
231
|
+
label: t.edit,
|
|
308
232
|
onClick: () => onEdit(message.id),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
233
|
+
size: "md",
|
|
234
|
+
className: actionClasses,
|
|
235
|
+
icon: /* @__PURE__ */ jsx(EditIcon, {})
|
|
312
236
|
}),
|
|
313
|
-
!isUser && onRegenerate && /* @__PURE__ */ jsx(
|
|
314
|
-
|
|
237
|
+
!isUser && onRegenerate && /* @__PURE__ */ jsx(IconButton, {
|
|
238
|
+
label: t.regenerate,
|
|
315
239
|
onClick: () => onRegenerate(message.id),
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
240
|
+
size: "md",
|
|
241
|
+
className: actionClasses,
|
|
242
|
+
icon: /* @__PURE__ */ jsx(RetryIcon, {})
|
|
319
243
|
}),
|
|
320
244
|
!isUser && onNavigateBranch && /* @__PURE__ */ jsx(BranchNavigator, {
|
|
321
245
|
messages: allMessages,
|
|
@@ -323,7 +247,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, latest = false
|
|
|
323
247
|
onNavigate: onNavigateBranch
|
|
324
248
|
}),
|
|
325
249
|
(timestamp || !isUser && (message.model || totalTokens)) && /* @__PURE__ */ jsxs("span", {
|
|
326
|
-
className: cx("chat-meta flex items-center gap-2 ml-2 text-xs tabular-nums theme-muted-text transition-opacity duration-150 select-none",
|
|
250
|
+
className: cx("chat-meta flex items-center gap-2 ml-2 text-xs tabular-nums theme-muted-text transition-opacity duration-150 select-none", "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"),
|
|
327
251
|
children: [
|
|
328
252
|
/* @__PURE__ */ jsx(RelativeTime, { value: timestamp }),
|
|
329
253
|
!isUser && message.model && /* @__PURE__ */ jsx("span", { children: message.model }),
|
|
@@ -3,6 +3,8 @@ import type { ApprovalAnswerValue, ChatMessage as Message } from '../types.js';
|
|
|
3
3
|
interface ChatMessageListProps {
|
|
4
4
|
messages: Message[];
|
|
5
5
|
allMessages: Message[];
|
|
6
|
+
/** The reply being written. Left unset, the list shows the chat provider's
|
|
7
|
+
* own stream; a host that streams elsewhere passes its reply, or null. */
|
|
6
8
|
streamingMessage?: Message | null;
|
|
7
9
|
queuedMessages?: Message[];
|
|
8
10
|
onRemoveQueued?: (id: string) => void;
|
|
@@ -30,7 +32,8 @@ interface ChatMessageListProps {
|
|
|
30
32
|
isStreaming?: boolean;
|
|
31
33
|
/** Rows without their own side padding, for a host that pads the column. */
|
|
32
34
|
flush?: boolean;
|
|
33
|
-
/** Names the list
|
|
35
|
+
/** Names the list, a live log, for assistive technology. Defaults to the
|
|
36
|
+
* chat strings' `thread.label`. */
|
|
34
37
|
label?: string;
|
|
35
38
|
/** Rendered after the last message, inside the scrolling column. */
|
|
36
39
|
footer?: ReactNode;
|
|
@@ -38,5 +41,5 @@ interface ChatMessageListProps {
|
|
|
38
41
|
* for the first frame is not announced twice. */
|
|
39
42
|
workingIndicator?: boolean;
|
|
40
43
|
}
|
|
41
|
-
export default function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessages, onRemoveQueued, canRemoveQueued, queueDelivery, editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator, }: ChatMessageListProps): import("react").JSX.Element;
|
|
44
|
+
export default function ChatMessageList({ messages, allMessages, streamingMessage: streamingMessageProp, queuedMessages, onRemoveQueued, canRemoveQueued, queueDelivery, editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator, }: ChatMessageListProps): import("react").JSX.Element;
|
|
42
45
|
export {};
|