{
+ e.preventDefault();
+ const text = e.clipboardData.getData('text/plain');
+ if (text) insertNodes([document.createTextNode(text)]);
+ }}
+ onDrop={(e) => {
+ // 禁止把文件拖成「大图附件」;贴纸只走选择器
+ if (e.dataTransfer?.files?.length) e.preventDefault();
+ }}
+ onKeyDown={(e) => {
+ if (e.nativeEvent.isComposing) return;
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ onSubmit?.();
+ }
+ }}
+ />
+ );
+});
+
+export default PmComposerInput;
diff --git a/frontend/src/pages/MessagesPage.tsx b/frontend/src/pages/MessagesPage.tsx
index b4d914f..dcac5a3 100644
--- a/frontend/src/pages/MessagesPage.tsx
+++ b/frontend/src/pages/MessagesPage.tsx
@@ -31,6 +31,7 @@ import { userPath } from '../utils/userPath';
import { InFlowSiteFooter } from '../components/SiteFooter';
import StickerPicker from '../components/emoji/StickerPicker';
import type { Sticker } from '../data/stickers';
+import PmComposerInput, { isPmStickerSrc, type PmComposerInputHandle } from '../components/PmComposerInput';
import { ArticleImagePickerDialog } from '../components/editor/ArticleImagePickerDialog';
import { Tooltip } from '../components/ui/Tooltip';
import { cn } from '@/lib/utils';
@@ -130,12 +131,13 @@ function PmBubbleContent({ content }: { content: string }) {
{parts.map((part, i) => {
const m = part.match(/^!\[([^\]]*)]\(([^)]+)\)$/);
if (m) {
+ const sticker = isPmStickerSrc(m[2]);
return (
![{m[1]]({m[2]})
);
@@ -273,7 +275,7 @@ export default function MessagesPage() {
const [draftEmbeds, setDraftEmbeds] = useState
([]);
const [showSticker, setShowSticker] = useState(false);
const [imagePickerOpen, setImagePickerOpen] = useState(false);
- const draftRef = useRef(null);
+ const composerRef = useRef(null);
const draftsByPeerRef = useRef