diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index a5fa0dc2..1b3ccdce 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -132,11 +132,11 @@ export default function ChatPage() {
- + {/* TODO: !!!!必须注释!!!!! */} + {children} diff --git a/frontend/src/components/ai-elements/artifact.tsx b/frontend/src/components/ai-elements/artifact.tsx index 9b5e7697..8d84e9bf 100644 --- a/frontend/src/components/ai-elements/artifact.tsx +++ b/frontend/src/components/ai-elements/artifact.tsx @@ -143,8 +143,10 @@ export const ArtifactContent = ({ className, ...props }: ArtifactContentProps) => ( +
+
); diff --git a/frontend/src/components/ai-elements/chain-of-thought.tsx b/frontend/src/components/ai-elements/chain-of-thought.tsx index 0c4ffff2..752cfc2e 100644 --- a/frontend/src/components/ai-elements/chain-of-thought.tsx +++ b/frontend/src/components/ai-elements/chain-of-thought.tsx @@ -147,7 +147,7 @@ export const ChainOfThoughtStep = memo( {...props} >
- {isValidElement(Icon) ? Icon : } + {isValidElement(Icon) ? Icon : }
diff --git a/frontend/src/components/ai-elements/prompt-input.tsx b/frontend/src/components/ai-elements/prompt-input.tsx index 9bcf46a8..6ff86345 100644 --- a/frontend/src/components/ai-elements/prompt-input.tsx +++ b/frontend/src/components/ai-elements/prompt-input.tsx @@ -295,81 +295,81 @@ export function PromptInputAttachment({ data.mediaType?.startsWith("image/") && data.url ? "image" : "file"; const isImage = mediaType === "image"; - const attachmentLabel = filename || (isImage ? "Image" : "Attachment"); + const truncateFilename = (name: string, maxLen: number = 10) => { + if (name.length <= maxLen) return name; + const ext = name.slice(name.lastIndexOf(".")); + const baseName = name.slice(0, name.lastIndexOf(".")); + const truncated = baseName.slice(0, maxLen - ext.length - 3); + return truncated + "..." + ext; + }; return ( - - -
-
-
- {isImage ? ( - {filename - ) : ( -
- -
- )} -
- + + + + +
- - {attachmentLabel} -
-
- -
- {isImage && ( -
- {filename -
- )} -
-
-

- {filename || (isImage ? "Image" : "Attachment")} -

- {data.mediaType && ( -

- {data.mediaType} -

- )} -
+ + ) : ( + <> +
+ + + {truncateFilename(filename)} +
-
- - + {/* 关闭按钮 - 右上角 */} + + + )} +
); } @@ -393,13 +393,14 @@ export function PromptInputAttachments({ return (
{attachments.files.map((file) => ( - -
{children(file)}
-
+ {children(file)} ))}
); @@ -1032,9 +1033,22 @@ export const PromptInputSubmit = ({ variant = "default", size = "sm", status, + disabled, children, ...props }: PromptInputSubmitProps) => { + const controller = useOptionalPromptInputController(); + + // 判断是否有内容可发送 + const hasContent = controller + ? controller.textInput.value.trim().length > 0 || controller.attachments.files.length > 0 + : false; + + // 正在 streaming 时不允许发送 + const isStreaming = status === "streaming" || status === "submitted"; + + const isDisabled = disabled || !hasContent || isStreaming; + let Icon = ; if (status === "submitted") { @@ -1049,10 +1063,17 @@ export const PromptInputSubmit = ({ {children ?? Icon} diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index 2fcc7f78..62cc3ab2 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -225,7 +225,8 @@ export function ArtifactFileDetail({
- + + {isSupportPreview && viewMode === "preview" && (language === "markdown" || language === "html") && ( @@ -247,7 +248,6 @@ export function ArtifactFileDetail({ src={urlOfArtifact({ filepath, threadId, isMock })} /> )} - {/*
*/} ); @@ -262,7 +262,7 @@ export function ArtifactFilePreview({ }) { if (language === "markdown") { return ( -
+
(null); const textareaRef = useRef(null); + const attachments = usePromptInputAttachments(); const [followups, setFollowups] = useState([]); const [followupsHidden, setFollowupsHidden] = useState(false); @@ -398,10 +400,13 @@ export function InputBox({ return (
{ promptRootRef.current = el; containerRef.current = el; }} className="relative"> + {/* 附件预览区域 - 在输入框上方 */} + + {extraHeader && ( -
+ 0}> {extraHeader} -
+ )} {/* 输入框主容器 */} - - {(attachment) => } - */} {/* Skill 选择按钮 (iframe 与宿主页通信) */} - + {/* [已禁用] 模式选择下拉菜单内容 */} {/* @@ -785,7 +795,7 @@ export function InputBox({ {/* 移动出来 */} {/* 小惊喜等 */} {isNewThread && searchParams.get("mode") !== "skill" && ( -
- -
+ )} {!disabled && @@ -861,11 +869,19 @@ export function InputBox({
); } +// SuggestionList 容器 +function SuggestionListContainer({ sendSelectSkill }: { sendSelectSkill: (skill_id: string) => void }) { + return ( +
+ +
+ ); +} + // 快速选择skillbutton -function SuggestionList() { +function SuggestionList({ sendSelectSkill }: { sendSelectSkill: (skill_id: string) => void }) { const { t } = useI18n(); const { textInput } = usePromptInputController(); - const { sendSelectSkill } = useIframeSkill(); const handleSuggestionClick = useCallback( (suggestion: { prompt: string; skill_id?: string }) => { @@ -958,9 +974,18 @@ function AddAttachmentsButton({ className }: { className?: string }) { ); } // 启动iframeSkillDialog -function IframeSkillDialogButton({ className }: { className?: string }) { +function IframeSkillDialogButton({ + className, + selectedSkill, + openSkillDialog, + clearSkill, +}: { + className?: string; + selectedSkill: { skill_id: string; title: string } | null; + openSkillDialog: () => void; + clearSkill: () => void; +}) { const { t } = useI18n(); - const { selectedSkill, openSkillDialog, clearSkill } = useIframeSkill(); return (
@@ -988,3 +1013,38 @@ function IframeSkillDialogButton({ className }: { className?: string }) {
); } + +// 附件预览栏 - 在输入框上方显示 +function AttachmentPreviewBar() { + const attachments = usePromptInputAttachments(); + + if (!attachments.files.length) { + return null; + } + + return ( +
+ + {(attachment) => } + +
+ ); +} + +// ExtraHeader 容器 - 有附件时上浮 +function ExtraHeaderContainer({ + hasAttachments, + children +}: { + hasAttachments: boolean; + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/frontend/src/components/workspace/messages/message-group.tsx b/frontend/src/components/workspace/messages/message-group.tsx index 45c57211..56268f20 100644 --- a/frontend/src/components/workspace/messages/message-group.tsx +++ b/frontend/src/components/workspace/messages/message-group.tsx @@ -151,7 +151,7 @@ export function MessageGroup({ } >