feat(P4A.T4): wire ImageAnnotator into composer attachment preview
Hover an image attachment in the composer to reveal a pencil-edit button; clicking it opens ImageAnnotator and Save replaces the File in attachments[]. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
EyeOffIcon,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
PollIcon,
|
||||
ReplyIcon,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
SpinnerIcon,
|
||||
XIcon,
|
||||
} from '../components/icons';
|
||||
import { ImageAnnotator } from '../components/ImageAnnotator';
|
||||
import { InCallPanel } from '../components/InCallPanel';
|
||||
import { IncomingCallPanel } from '../components/IncomingCallPanel';
|
||||
import { CallPreviewPanel } from '../components/CallPreviewPanel';
|
||||
@@ -154,6 +156,7 @@ export function ConversationPage() {
|
||||
const [sendError, setSendError] = useState<string | null>(null);
|
||||
const [stickToBottom, setStickToBottom] = useState(true);
|
||||
const [attachments, setAttachments] = useState<File[]>([]);
|
||||
const [annotatingIndex, setAnnotatingIndex] = useState<number | null>(null);
|
||||
const [infoPanelOpen, setInfoPanelOpen] = useState(false);
|
||||
const [mediaDrawerOpen, setMediaDrawerOpen] = useState(false);
|
||||
const [pollDialogOpen, setPollDialogOpen] = useState(false);
|
||||
@@ -917,6 +920,9 @@ export function ConversationPage() {
|
||||
key={idx}
|
||||
file={file}
|
||||
onRemove={() => setAttachments((prev) => prev.filter((_, i) => i !== idx))}
|
||||
{...(file.type.startsWith('image/')
|
||||
? { onEdit: () => setAnnotatingIndex(idx) }
|
||||
: {})}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -1163,6 +1169,17 @@ export function ConversationPage() {
|
||||
}}
|
||||
onUnpin={(messageId) => void handleTogglePin(messageId)}
|
||||
/>
|
||||
|
||||
{annotatingIndex !== null && attachments[annotatingIndex] && (
|
||||
<ImageAnnotator
|
||||
file={attachments[annotatingIndex]!}
|
||||
onCancel={() => setAnnotatingIndex(null)}
|
||||
onSave={(next) => {
|
||||
setAttachments((prev) => prev.map((f, i) => (i === annotatingIndex ? next : f)));
|
||||
setAnnotatingIndex(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1390,7 +1407,15 @@ function Banner({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
function AttachmentPreview({ file, onRemove }: { file: File; onRemove: () => void }) {
|
||||
function AttachmentPreview({
|
||||
file,
|
||||
onRemove,
|
||||
onEdit,
|
||||
}: {
|
||||
file: File;
|
||||
onRemove: () => void;
|
||||
onEdit?: () => void;
|
||||
}) {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const [url, setUrl] = useState<string | null>(null);
|
||||
useEffect(() => {
|
||||
@@ -1400,7 +1425,7 @@ function AttachmentPreview({ file, onRemove }: { file: File; onRemove: () => voi
|
||||
return () => URL.revokeObjectURL(u);
|
||||
}, [file, isImage]);
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-lg border border-line bg-surface-2 dark:bg-[#2b2d31]">
|
||||
<div className="group relative overflow-hidden rounded-lg border border-line bg-surface-2 dark:bg-[#2b2d31]">
|
||||
{isImage && url ? (
|
||||
<img src={url} alt={file.name} className="block h-20 w-20 object-cover" />
|
||||
) : (
|
||||
@@ -1412,6 +1437,17 @@ function AttachmentPreview({ file, onRemove }: { file: File; onRemove: () => voi
|
||||
<span className="text-fg-muted">{(file.size / 1024).toFixed(0)} KB</span>
|
||||
</div>
|
||||
)}
|
||||
{isImage && onEdit && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onEdit}
|
||||
aria-label="Bearbeiten"
|
||||
title="Bearbeiten"
|
||||
className="absolute bottom-1 left-1 flex h-5 w-5 cursor-pointer items-center justify-center rounded-full bg-black/70 text-white opacity-0 transition group-hover:opacity-100 hover:bg-accent/80"
|
||||
>
|
||||
<PencilIcon className="h-3 w-3" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
|
||||
Reference in New Issue
Block a user