diff --git a/src/pages/views/workspace/ChatStream.tsx b/src/pages/views/workspace/ChatStream.tsx
index 1146aab..c8295c1 100644
--- a/src/pages/views/workspace/ChatStream.tsx
+++ b/src/pages/views/workspace/ChatStream.tsx
@@ -33,6 +33,108 @@ interface ChatStreamProps {
onOpenEditor?: () => void;
}
+const LONG_MESSAGE_WORD_LIMIT = 1000;
+const COLLAPSED_PREVIEW_WORDS = 200;
+
+function _countWords(text: string): number {
+ const trimmed = text.trim();
+ if (!trimmed) return 0;
+ return trimmed.split(/\s+/).length;
+}
+
+function _truncateToWords(text: string, maxWords: number): string {
+ const words = text.trim().split(/\s+/);
+ if (words.length <= maxWords) return text;
+ return `${words.slice(0, maxWords).join(' ')}…`;
+}
+
+const _MARKDOWN_COMPONENTS = {
+ code: _CodeBlock,
+ table: ({ children }: { children?: React.ReactNode }) => (
+
+ ),
+ th: ({ children }: { children?: React.ReactNode }) => (
+
+ {children}
+ |
+ ),
+ td: ({ children }: { children?: React.ReactNode }) => (
+
+ {children}
+ |
+ ),
+ a: ({ href, children }: { href?: string; children?: React.ReactNode }) => (
+
+ {children}
+
+ ),
+ img: ({ src, alt, ...rest }: React.ImgHTMLAttributes) =>
+ src ?
: null,
+};
+
+function _CollapsibleMessageMarkdown({
+ text,
+ allowCollapse,
+}: {
+ text: string;
+ allowCollapse: boolean;
+}) {
+ const { t } = useLanguage();
+ const [expanded, setExpanded] = useState(false);
+ const wordCount = _countWords(text);
+ const isLong = wordCount > LONG_MESSAGE_WORD_LIMIT;
+ const shouldTruncate = isLong && allowCollapse && !expanded;
+ const displayText = shouldTruncate ? _truncateToWords(text, COLLAPSED_PREVIEW_WORDS) : text;
+
+ return (
+ <>
+
+ {displayText}
+
+ {isLong && allowCollapse && (
+
+ )}
+ >
+ );
+}
+
export const ChatStream: React.FC = ({ messages,
agentProgress,
isProcessing,
@@ -49,6 +151,8 @@ export const ChatStream: React.FC = ({ messages,
const enqueuedIdsRef = useRef>(new Set());
const [copiedId, setCopiedId] = useState(null);
+ const lastMessageId = messages.length > 0 ? messages[messages.length - 1].id : null;
+
const _handleCopy = useCallback((msgId: string, text: string) => {
navigator.clipboard.writeText(text).then(() => {
setCopiedId(msgId);
@@ -157,53 +261,10 @@ export const ChatStream: React.FC = ({ messages,
)}
{msg.message && (
- (
-
- ),
- th: ({ children }) => (
-
- {children}
- |
- ),
- td: ({ children }) => (
-
- {children}
- |
- ),
- a: ({ href, children }) => (
-
- {children}
-
- ),
- img: ({ src, alt, ...rest }) =>
- src ?
: null,
- }}
- >
- {msg.message}
-
+ <_CollapsibleMessageMarkdown
+ text={msg.message}
+ allowCollapse={!(isProcessing && msg.id === lastMessageId && msg.role === 'assistant')}
+ />
)}
{msg.documents && msg.documents.length > 0 && (
diff --git a/src/pages/views/workspace/WorkspaceInput.tsx b/src/pages/views/workspace/WorkspaceInput.tsx
index 843a3bf..e953015 100644
--- a/src/pages/views/workspace/WorkspaceInput.tsx
+++ b/src/pages/views/workspace/WorkspaceInput.tsx
@@ -68,7 +68,6 @@ interface WorkspaceInputProps {
onPendingAttachDsConsumed?: () => void;
pendingAttachFdsId?: string;
onPendingAttachFdsConsumed?: () => void;
- onPasteAsFile?: (file: File) => void;
draftAppend?: string;
onDraftAppendConsumed?: () => void;
workflowId?: string | null;
@@ -136,7 +135,6 @@ export const WorkspaceInput = forwardRef) => {
- if (!onPasteAsFile) return;
- const text = e.clipboardData.getData('text/plain');
- if (text && text.length >= 1000) {
- e.preventDefault();
- const blob = new Blob([text], { type: 'text/plain' });
- const file = new File([blob], `pasted-text-${Date.now()}.txt`, { type: 'text/plain' });
- onPasteAsFile(file);
- }
- }, [onPasteAsFile]);
-
const _isTreeMimeDrag = useCallback((e: React.DragEvent) => {
const types = e.dataTransfer.types;
return (
@@ -768,7 +755,6 @@ export const WorkspaceInput = forwardRef = ({ persistentInstance
onPendingAttachDsConsumed={() => { setPendingAttachDsId(''); setPendingAttachDsLabel(''); }}
pendingAttachFdsId={pendingAttachFdsId}
onPendingAttachFdsConsumed={() => setPendingAttachFdsId('')}
- onPasteAsFile={_uploadAndAttach}
draftAppend={draftAppend}
onDraftAppendConsumed={() => setDraftAppend('')}
workflowId={workspace.workflowId}