(sendResult)
? await sendResult
: sendResult;
@@ -244,8 +259,8 @@ export function ChatInput({
canSend,
clearAttachments,
dictation,
- expandSkillSlashCommand,
onSend,
+ resolveSkillSlashCommand,
selectedPersonaId,
selectedSkills,
setSelectedSkills,
@@ -347,9 +362,12 @@ export function ChatInput({
);
return selectedModel?.displayName ?? selectedModel?.name ?? currentModelId;
}, [availableModels, currentModel, currentModelId]);
- const effectivePlaceholder = t("input.placeholder", {
- agent: agentDisplayName,
- });
+ const inputPlaceholder = getChatInputPlaceholder(
+ t,
+ agentDisplayName,
+ dictation.isRecording,
+ dictation.isTranscribing,
+ );
const handleClearStickyPersona = useCallback(() => {
onPersonaChange?.(null);
@@ -437,13 +455,7 @@ export function ChatInput({
onChange={handleInput}
onKeyDown={handleKeyDown}
onPaste={handlePaste}
- placeholder={
- dictation.isRecording
- ? t("toolbar.voiceInputRecording")
- : dictation.isTranscribing
- ? t("toolbar.voiceInputTranscribing")
- : effectivePlaceholder
- }
+ placeholder={inputPlaceholder}
disabled={disabled}
rows={1}
className="mb-3 min-h-[36px] max-h-[200px] w-full resize-none bg-transparent px-1 text-[14px] leading-relaxed text-foreground placeholder:font-light placeholder:text-muted-foreground/60 focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:opacity-60"
diff --git a/ui/goose2/src/features/chat/ui/MessageBubble.tsx b/ui/goose2/src/features/chat/ui/MessageBubble.tsx
index a72af28a2d6b..ab521e87d118 100644
--- a/ui/goose2/src/features/chat/ui/MessageBubble.tsx
+++ b/ui/goose2/src/features/chat/ui/MessageBubble.tsx
@@ -1,13 +1,6 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
-import {
- Copy,
- Check,
- RotateCcw,
- Pencil,
- FileText,
- FolderClosed,
-} from "lucide-react";
+import { Check, FileText, FolderClosed } from "lucide-react";
import { IconRobot } from "@tabler/icons-react";
import { openPath } from "@tauri-apps/plugin-opener";
import { cn } from "@/shared/lib/cn";
@@ -20,11 +13,7 @@ import {
formatProviderLabel,
} from "@/shared/ui/icons/ProviderIcons";
import { useAvatarSrc } from "@/shared/hooks/useAvatarSrc";
-import {
- MessageActions,
- MessageAction,
- MessageResponse,
-} from "@/shared/ui/ai-elements/message";
+import { MessageResponse } from "@/shared/ui/ai-elements/message";
import {
Reasoning,
ReasoningTrigger,
@@ -45,6 +34,8 @@ import type {
ReasoningContent as ReasoningContentType,
SystemNotificationContent,
} from "@/shared/types/messages";
+import { MessageBubbleActions } from "./MessageBubbleActions";
+import { MessageMetadataChip } from "./MessageMetadataChip";
function MessageAttachmentRow({
attachment,
@@ -202,6 +193,9 @@ function renderContentBlock(
case "text": {
const tc = content as TextContent;
if (isUserMessage) {
+ if (!tc.text.trim()) {
+ return null;
+ }
return (
{tc.text}
@@ -283,31 +277,6 @@ function renderContentBlock(
}
}
-function CopyAction({
- copied,
- onCopy,
-}: {
- copied: boolean;
- onCopy: () => void;
-}) {
- const { t } = useTranslation(["chat", "common"]);
-
- return (
-
- {copied ? : }
-
- );
-}
-
export const MessageBubble = memo(function MessageBubble({
message,
isStreaming,
@@ -369,6 +338,7 @@ export const MessageBubble = memo(function MessageBubble({
(assistantDisplayName || personaAvatarUrl || assistantProviderIcon),
);
const messageAttachments = message.metadata?.attachments ?? [];
+ const messageChips = message.metadata?.chips ?? [];
const timestamp = (
+ {isUser && messageChips.length > 0 && (
+
+ {messageChips.map((chip) => (
+
+ ))}
+
+ )}
{messageAttachments.length > 0 && (
{messageAttachments.map((attachment) => (
@@ -480,38 +460,16 @@ export const MessageBubble = memo(function MessageBubble({
isUser ? "right-0" : "left-0",
)}
>
-
- {isUser && timestamp}
- {textContent && (
- copyToClipboard(textContent)}
- />
- )}
- {!isUser && onRetryMessage && (
- onRetryMessage(message.id)}
- >
-
-
- )}
- {isUser && onEditMessage && (
- onEditMessage(message.id)}
- >
-
-
- )}
- {!isUser && timestamp}
-
+
copyToClipboard(textContent)}
+ onRetryMessage={onRetryMessage}
+ onEditMessage={onEditMessage}
+ />