- {sendError ?
: null}
- {channel === "direct" && !directRecipientId ?
: null}
-
- {replyTo ? (
-
-
- Replying to {agents.find((a) => a.id === replyTo.fromAgentId)?.name ?? "agent"}{replyTo.subject ? `: ${replyTo.subject}` : ""}
-
-
- ) : null}
-
-
-
-
You (Board)
- {!replyTo ?
setSubject(e.target.value)} placeholder="Subject (optional)" aria-label="Message subject" className="min-w-0 flex-1 rounded-lg border border-border bg-card px-2.5 py-1 text-[11px] text-foreground placeholder:text-muted-foreground" /> : null}
-
-
- {attachments.length > 0 && (
-
- {attachments.map((att) => (
-
-
- {att.title}
-
-
- ))}
-
- )}
-
-
-
-
-
void handleSend()} label="Send message" />
-
+ {directMissingNotice}
+
void handleSend()}
+ placeholder="Type a message... (use @ to mention agents)"
+ sending={sending}
+ error={sendError}
+ shortcutHint={`Press ${cmdOrCtrlLabel()}+Enter to send`}
+ sendLabel="Send message"
+ sendDisabled={!body.trim() || (channel === "direct" && !directRecipientId)}
+ leading={}
+ belowHeader={<>{replyBanner}{subjectRow}{attachmentsRow}>}
+ onKeyDownExtra={handleKeyDownExtra}
+ textareaAriaLabel="Message body"
+ />
{mentionState.active && (
)}
-
- Press {cmdOrCtrlLabel()}+Enter to send
)
}
@@ -341,11 +343,16 @@ export function TaskDiscussionComposer({ companyId, taskId, onSent }: { companyI
}, [body, companyId, taskId, onSent])
return (
- {sendError ?
: null}
-
-
+
setBody(v)}
+ onSend={() => void handleSend()}
+ placeholder="Reply in task discussion..."
+ sending={sending}
+ error={sendError}
+ sendLabel="Send task comment"
+ textareaAriaLabel="Task discussion reply"
+ />
)
}
diff --git a/src/renderer/components/communication/composer-shell/ComposerShell.tsx b/src/renderer/components/communication/composer-shell/ComposerShell.tsx
new file mode 100644
index 00000000..177e0dc3
--- /dev/null
+++ b/src/renderer/components/communication/composer-shell/ComposerShell.tsx
@@ -0,0 +1,96 @@
+import { forwardRef, useCallback, type ReactNode, type KeyboardEvent, type ChangeEvent } from "react";
+import { PaperPlaneTilt } from "@phosphor-icons/react";
+import { ActionButton, InlineNotice } from "../../ui";
+
+export interface ComposerShellProps {
+ value: string;
+ onChange: (value: string, event: ChangeEvent