mirror of
https://seed.radicle.dev/rad:z4D5UCArafTzTQpDZNQRuqswh3ury.git
synced 2026-10-18 19:10:45 +08:00
Fix pasted images not rendering
Pasting a single image went through save_embed_by_clipboard, which stored the clipboard's raw RGBA pixels instead of an encoded image. The result could not be decoded, so the embed showed as a broken image. Always store the pasted file's own bytes instead. Nothing else uses save_embed_by_clipboard, so it's removed. Co-Authored-By: Rūdolfs Ošiņš <rudolfs@osins.org>
This commit is contained in:
committed by
Rūdolfs Ošiņš
co-authored by
Rūdolfs Ošiņš
parent
118abffdea
commit
8175823ab8
Generated
-1
@@ -4374,7 +4374,6 @@ dependencies = [
|
||||
"sqlite",
|
||||
"ssh-key",
|
||||
"tauri",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-fs",
|
||||
"tempfile",
|
||||
"thiserror 2.0.19",
|
||||
|
||||
@@ -5,7 +5,6 @@ use radicle::identity;
|
||||
use radicle_types as types;
|
||||
use radicle_types::error::Error;
|
||||
use radicle_types::traits::thread::Thread;
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
|
||||
use crate::AppState;
|
||||
@@ -33,21 +32,6 @@ pub async fn save_embed_by_path(
|
||||
ctx.save_embed_by_path(rid, path)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_embed_by_clipboard(
|
||||
app_handle: tauri::AppHandle,
|
||||
ctx: tauri::State<'_, AppState>,
|
||||
rid: identity::RepoId,
|
||||
name: String,
|
||||
) -> Result<git::Oid, Error> {
|
||||
let content = app_handle
|
||||
.clipboard()
|
||||
.read_image()
|
||||
.map(|i| i.rgba().to_vec())?;
|
||||
|
||||
ctx.save_embed_by_bytes(rid, name, content)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_embed_by_bytes(
|
||||
ctx: tauri::State<'_, AppState>,
|
||||
|
||||
@@ -88,7 +88,6 @@ pub fn run() {
|
||||
cob::patch::rebuild_patch_cache,
|
||||
cob::patch::revisions_by_patch,
|
||||
cob::save_embed_by_bytes,
|
||||
cob::save_embed_by_clipboard,
|
||||
cob::save_embed_by_path,
|
||||
cob::save_embed_to_disk,
|
||||
diff::get_diff,
|
||||
|
||||
@@ -23,7 +23,6 @@ serde_json = { version = "1.0.0" }
|
||||
sqlite = { version = "0.37.0", features = ["bundled"] }
|
||||
ssh-key = { version = "0.6.3" }
|
||||
tauri = { version = "2.11.1", features = ["isolation"] }
|
||||
tauri-plugin-clipboard-manager = { version = "2.3.2" }
|
||||
tauri-plugin-fs = { version = "2.5.0" }
|
||||
tempfile = { version = "3.19.0" }
|
||||
thiserror = { version = "2.0.12" }
|
||||
|
||||
@@ -54,10 +54,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
AliasError(#[from] radicle::node::AliasError),
|
||||
|
||||
/// Tauri Plugin Clipboard error.
|
||||
#[error(transparent)]
|
||||
TauriPluginClipboard(#[from] tauri_plugin_clipboard_manager::Error),
|
||||
|
||||
/// Tauri Plugin Fs error.
|
||||
#[error(transparent)]
|
||||
TauriPluginFs(#[from] tauri_plugin_fs::Error),
|
||||
|
||||
@@ -6,10 +6,7 @@ use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Tauri commands with no HTTP route, and why.
|
||||
const TAURI_ONLY: &[(&str, &str)] = &[(
|
||||
"save_embed_by_clipboard",
|
||||
"reads the native clipboard, which the HTTP server has no access to",
|
||||
)];
|
||||
const TAURI_ONLY: &[(&str, &str)] = &[];
|
||||
|
||||
/// HTTP routes with no Tauri command, and why.
|
||||
const HTTP_ONLY: &[(&str, &str)] = &[(
|
||||
|
||||
@@ -242,19 +242,11 @@
|
||||
|
||||
if (e.clipboardData?.files && e.clipboardData.files.length > 0) {
|
||||
e.preventDefault();
|
||||
const files = Array.from(e.clipboardData.files);
|
||||
// A single item may be an image the webview can't hand over, so the
|
||||
// backend reads it from the clipboard itself.
|
||||
if (e.clipboardData.items.length === 1) {
|
||||
await uploadEmbeds(
|
||||
files.slice(0, 1),
|
||||
file => file.name,
|
||||
file =>
|
||||
invoke<string>("save_embed_by_clipboard", { name: file.name, rid }),
|
||||
);
|
||||
} else {
|
||||
await uploadEmbeds(files, file => file.name, saveFileBytes);
|
||||
}
|
||||
await uploadEmbeds(
|
||||
Array.from(e.clipboardData.files),
|
||||
file => file.name,
|
||||
saveFileBytes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import type { EmbedWithMimeType } from "@bindings/cob/EmbedWithMimeType";
|
||||
import type { JSHandle, Locator, Page } from "@playwright/test";
|
||||
import type { RadiclePeer } from "@tests/support/peerManager.js";
|
||||
|
||||
import { createProject } from "@tests/support/collaboration.js";
|
||||
import { expect, test, waitForCommand } from "@tests/support/fixtures.js";
|
||||
|
||||
const onePixelPng =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
|
||||
|
||||
async function openIssue(page: Page, peer: RadiclePeer) {
|
||||
const { rid, issueId } = await createProject(peer);
|
||||
await page.goto(`/repos/${rid}/issues/${issueId}`);
|
||||
await expect(page.getByText("Nobody reads them.")).toBeVisible();
|
||||
return rid;
|
||||
}
|
||||
|
||||
function fileTransfer(
|
||||
@@ -86,3 +91,37 @@ test("the drop highlight clears once the file leaves", async ({
|
||||
await highlight.dispatchEvent("dragleave", { dataTransfer: transfer });
|
||||
await expect(highlight).toBeHidden();
|
||||
});
|
||||
|
||||
test("a pasted image is stored as the image itself", async ({ page, peer }) => {
|
||||
const rid = await openIssue(page, peer);
|
||||
const composer = page.getByPlaceholder("Leave a comment");
|
||||
|
||||
await waitForCommand(page, "save_embed_by_bytes", () =>
|
||||
composer.evaluate((textarea, png) => {
|
||||
const clipboardData = new DataTransfer();
|
||||
const bytes = Uint8Array.from(atob(png), c => c.charCodeAt(0));
|
||||
clipboardData.items.add(
|
||||
new File([bytes], "image.png", { type: "image/png" }),
|
||||
);
|
||||
textarea.dispatchEvent(
|
||||
new ClipboardEvent("paste", {
|
||||
clipboardData,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
}, onePixelPng),
|
||||
);
|
||||
await expect(composer).toHaveValue(/\[image\.png\]\(\w+\)/);
|
||||
|
||||
const oid = /\[image\.png\]\((\w+)\)/.exec(await composer.inputValue())?.[1];
|
||||
const response = await page.request.post(
|
||||
`http://127.0.0.1:${peer.httpdBaseUrl.port}/get_embed`,
|
||||
{ data: { rid, oid, name: "image.png" } },
|
||||
);
|
||||
const embed = (await response.json()) as EmbedWithMimeType;
|
||||
expect(embed.mimeType).toBe("image/png");
|
||||
expect(Buffer.from(embed.content)).toEqual(
|
||||
Buffer.from(onePixelPng, "base64"),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user