diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json index 5b245ebae..fcdcda168 100644 --- a/apps/ai-game-creator-shell/package.json +++ b/apps/ai-game-creator-shell/package.json @@ -16,6 +16,7 @@ "dependencies": { "@lexical/react": "^0.47.0", "@lexical/utils": "^0.47.0", + "@tauri-apps/plugin-clipboard-manager": "2.3.2", "@vitejs/plugin-react": "^5.0.4", "lexical": "^0.47.0", "lucide-react": "^0.546.0", diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.lock b/apps/ai-game-creator-shell/src-tauri/Cargo.lock index d71f6667e..2f59b6c29 100644 --- a/apps/ai-game-creator-shell/src-tauri/Cargo.lock +++ b/apps/ai-game-creator-shell/src-tauri/Cargo.lock @@ -65,6 +65,27 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -361,6 +382,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.12.0" @@ -494,6 +521,15 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "combine" version = "4.6.7" @@ -606,6 +642,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -847,13 +889,19 @@ checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" dependencies = [ "bit-set", "cssparser", - "foldhash", + "foldhash 0.2.0", "html5ever", "precomputed-hash", "selectors", "tendril", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.2" @@ -988,6 +1036,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "euclid" version = "0.20.14" @@ -1024,6 +1078,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + [[package]] name = "fdeflate" version = "0.3.7" @@ -1049,6 +1109,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -1065,6 +1131,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1317,6 +1389,7 @@ dependencies = [ "shared-contracts", "tauri", "tauri-build", + "tauri-plugin-clipboard-manager", "tauri-plugin-dialog", "tauri-plugin-opener", "tokio", @@ -1333,6 +1406,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -1534,12 +1617,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -1886,6 +1989,20 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2179,7 +2296,7 @@ dependencies = [ "itoa", "log", "md-5", - "nom", + "nom 7.1.3", "rangemap", "time", "weezl", @@ -2264,6 +2381,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "muda" version = "0.19.3" @@ -2342,6 +2469,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -2399,6 +2535,7 @@ dependencies = [ "block2", "objc2", "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation", ] @@ -2652,6 +2789,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "pango" version = "0.18.3" @@ -2733,6 +2880,17 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap 2.14.0", +] + [[package]] name = "phf" version = "0.13.1" @@ -2980,6 +3138,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.39.4" @@ -4027,6 +4197,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + [[package]] name = "tauri-plugin-dialog" version = "2.7.1" @@ -4254,6 +4439,20 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.51" @@ -4578,6 +4777,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom 8.0.0", + "petgraph", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4886,6 +5096,76 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.13.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.102" @@ -5544,6 +5824,24 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror 2.0.18", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "writeable" version = "0.6.3" @@ -5615,6 +5913,23 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "yoke" version = "0.8.3" @@ -5699,6 +6014,26 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "zerofrom" version = "0.1.8" @@ -5794,6 +6129,21 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "5.12.0" diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.toml b/apps/ai-game-creator-shell/src-tauri/Cargo.toml index 77de932fa..a4226dc45 100644 --- a/apps/ai-game-creator-shell/src-tauri/Cargo.toml +++ b/apps/ai-game-creator-shell/src-tauri/Cargo.toml @@ -19,3 +19,4 @@ tauri-plugin-dialog = "2.7.1" tauri-plugin-opener = "2.5.4" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } zip = { version = "2", default-features = false, features = ["deflate"] } +tauri-plugin-clipboard-manager = "2.3.2" diff --git a/apps/ai-game-creator-shell/src-tauri/capabilities/main.json b/apps/ai-game-creator-shell/src-tauri/capabilities/main.json new file mode 100644 index 000000000..4b2f2af30 --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/capabilities/main.json @@ -0,0 +1,13 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "main", + "description": "AI 游戏创作主窗口允许读取系统剪贴板图片,用于粘贴素材附件。", + "windows": ["client"], + "permissions": [ + "clipboard-manager:allow-read-image", + "clipboard-manager:allow-read-text", + "core:image:allow-rgba", + "core:image:allow-size", + "core:resources:allow-close" + ] +} diff --git a/apps/ai-game-creator-shell/src-tauri/src/main.rs b/apps/ai-game-creator-shell/src-tauri/src/main.rs index 28920a780..3d2b55fc3 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/main.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs @@ -922,6 +922,7 @@ fn main() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_clipboard_manager::init()) .manage(PreviewRegistry::default()) .setup(|app| { configure_game_creator_runtime_config_dir(app.handle())?; diff --git a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx index 24e91140b..8f737440f 100644 --- a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx +++ b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx @@ -4,6 +4,7 @@ import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'; import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; +import { readImage, readText } from '@tauri-apps/plugin-clipboard-manager'; import { $createParagraphNode, $createTextNode, @@ -13,17 +14,16 @@ import { $isRangeSelection, $isTextNode, COMMAND_PRIORITY_EDITOR, + COMMAND_PRIORITY_HIGH, createCommand, type LexicalCommand, type LexicalNode, + PASTE_COMMAND, } from 'lexical'; import { Upload } from 'lucide-react'; import { useEffect, useMemo, useRef } from 'react'; -import type { - HomeAttachmentDraft, - RichText, -} from '../../useHomeDraftStore'; +import type { HomeAttachmentDraft, RichText } from '../../useHomeDraftStore'; import AttachmentContext from './attachmentContext'; import { $createAttachmentNode, @@ -41,11 +41,79 @@ type RichInputAreaProps = { }; const INSERT_ATTACHMENTS_COMMAND: LexicalCommand = createCommand('INSERT_HOME_ATTACHMENTS_COMMAND'); +const INSERT_CLIPBOARD_TEXT_COMMAND: LexicalCommand = createCommand( + 'INSERT_HOME_CLIPBOARD_TEXT_COMMAND', +); + +function createAttachmentDrafts(files: readonly File[]) { + const now = Date.now(); + return files.map((file, index) => ({ + id: `${file.name}:${file.size}:${file.lastModified}:${index}:${now}`, + file, + })); +} + +async function readNativeClipboardImageFile() { + // this used Tauri native api, unavailable in browser env. + let image; + try { + image = await readImage(); + const [{ width, height }, rgba] = await Promise.all([ + image.size(), + image.rgba(), + ]); + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + const context = canvas.getContext('2d'); + if (!context) { + return null; + } + context.putImageData( + new ImageData(new Uint8ClampedArray(rgba), width, height), + 0, + 0, + ); + const blob = await new Promise((resolve) => + canvas.toBlob(resolve, 'image/png'), + ); + if (!blob) { + return null; + } + return new File([blob], `pasted-image-${Date.now()}.png`, { + type: 'image/png', + }); + } catch { + return null; + } finally { + await image?.close(); + } +} + +async function readNativeClipboardText() { + try { + return await readText(); + } catch { + return ''; + } +} + +function selectEditableEndWhenNeeded() { + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + return; + } + const root = $getRoot(); + if (root.getChildrenSize() === 0) { + root.append($createParagraphNode()); + } + root.selectEnd(); +} function EditorPlugins({ - // attachments, onChange, -}: Pick) { + onAttachmentsAdd, +}: Pick) { const [editor] = useLexicalComposerContext(); useEffect( @@ -53,13 +121,7 @@ function EditorPlugins({ editor.registerCommand( INSERT_ATTACHMENTS_COMMAND, (nextAttachments) => { - const selection = $getSelection(); - if (!$isRangeSelection(selection)) { - const root = $getRoot(); - if (root.getChildrenSize() === 0) - root.append($createParagraphNode()); - root.selectEnd(); - } + selectEditableEndWhenNeeded(); const currentSelection = $getSelection(); if ($isRangeSelection(currentSelection)) { currentSelection.insertNodes( @@ -75,6 +137,49 @@ function EditorPlugins({ [editor], ); + useEffect( + () => + editor.registerCommand( + INSERT_CLIPBOARD_TEXT_COMMAND, + (text) => { + selectEditableEndWhenNeeded(); + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + selection.insertText(text); + } + return true; + }, + COMMAND_PRIORITY_EDITOR, + ), + [editor], + ); + + useEffect( + () => + editor.registerCommand( + PASTE_COMMAND, + (event) => { + event.preventDefault(); + void readNativeClipboardImageFile().then(async (file) => { + if (!file) { + const text = await readNativeClipboardText(); + if (text.length === 0) { + return; + } + editor.dispatchCommand(INSERT_CLIPBOARD_TEXT_COMMAND, text); + return; + } + const nextAttachments = createAttachmentDrafts([file]); + editor.dispatchCommand(INSERT_ATTACHMENTS_COMMAND, nextAttachments); + onAttachmentsAdd(nextAttachments); + }); + return true; + }, + COMMAND_PRIORITY_HIGH, + ), + [editor, onAttachmentsAdd], + ); + return ( { @@ -118,11 +223,7 @@ function UploadButton({ const files = Array.from(event.currentTarget.files ?? []); event.currentTarget.value = ''; if (files.length === 0) return; - const now = Date.now(); - const nextAttachments = files.map((file, index) => ({ - id: `${file.name}:${file.size}:${file.lastModified}:${index}:${now}`, - file, - })); + const nextAttachments = createAttachmentDrafts(files); editor.dispatchCommand(INSERT_ATTACHMENTS_COMMAND, nextAttachments); onAttachmentsAdd(nextAttachments); }} @@ -139,7 +240,6 @@ function UploadButton({ ); } - export default function RichInputArea(props: RichInputAreaProps) { const attachmentMap = useMemo( () => @@ -195,8 +295,8 @@ export default function RichInputArea(props: RichInputAreaProps) {