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 ? (
-

- ) : (
-
- )}
-
-
-
-
-
- {isImage && (
-
-

-
- )}
-
-
-
- {filename || (isImage ? "Image" : "Attachment")}
-
- {data.mediaType && (
-
- {data.mediaType}
-
- )}
-
+ >
+ ) : (
+ <>
+
+
+
+ {truncateFilename(filename)}
+
-
-
-
+ {/* 关闭按钮 - 右上角 */}
+
{
+ e.stopPropagation();
+ attachments.remove(data.id);
+ }}
+ type="button"
+ >
+
+ Remove
+
+ >
+ )}
+
);
}
@@ -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({
}
>