fix(desktop): bypass parsing for bare URL paste

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-04 21:31:55 -07:00
committed by Carl
parent 10998681b4
commit 2e339c0d09
2 changed files with 57 additions and 47 deletions

View File

@@ -135,8 +135,13 @@ function shouldAppendSpaceAfterPaste(text: string): boolean {
return PASTED_LINK_AT_END_RE.test(trimmedEnd);
}
function unwrapExactHttpAutolink(text: string): string | null {
return /^<(https?:\/\/[^\s<>]+)>$/i.exec(text)?.[1] ?? null;
function unwrapExactHttpLink(text: string): string | null {
return (
/^(?:<(https?:\/\/[^\s<>]+)>|(https?:\/\/\S+))$/i
.exec(text)
?.slice(1)
.find(Boolean) ?? null
);
}
const LinkPasteTrailingSpace = Extension.create({
@@ -493,7 +498,7 @@ export function useRichTextEditor({
editorProps: {
handleDOMEvents: {
paste: (view, event) => {
const url = unwrapExactHttpAutolink(
const url = unwrapExactHttpLink(
(event as ClipboardEvent).clipboardData?.getData("text/plain") ??
"",
);

View File

@@ -480,52 +480,57 @@ test("link preview style defaults to compact and Rich unfurls descriptions", asy
await page.getByTestId("link-preview-style-compact").click();
});
test("angle-bracket link preview paste paints before cold resolver work", async ({
page,
}) => {
const previewUrl = "https://github.com/block/buzz/pull/3246?paste=async";
await page.goto("/");
await page.getByTestId("channel-general").click();
const input = page.getByTestId("message-input");
await input.focus();
for (const [pasteShape, wrapUrl] of [
["bare", (url: string) => url],
["angle-bracket", (url: string) => `<${url}>`],
] as const) {
test(`${pasteShape} link preview paste paints before cold resolver work`, async ({
page,
}) => {
const previewUrl = `https://github.com/block/buzz/pull/3246?paste=${pasteShape}`;
await page.goto("/");
await page.getByTestId("channel-general").click();
const input = page.getByTestId("message-input");
await input.focus();
const pasteText = `<${previewUrl}>`;
const firstPaint = await input.evaluate(async (element, pasteText) => {
const clipboardData = new DataTransfer();
clipboardData.setData("text/plain", pasteText);
const startedAt = performance.now();
element.dispatchEvent(
new ClipboardEvent("paste", {
bubbles: true,
cancelable: true,
clipboardData,
}),
const pasteText = wrapUrl(previewUrl);
const firstPaint = await input.evaluate(async (element, pasteText) => {
const clipboardData = new DataTransfer();
clipboardData.setData("text/plain", pasteText);
const startedAt = performance.now();
element.dispatchEvent(
new ClipboardEvent("paste", {
bubbles: true,
cancelable: true,
clipboardData,
}),
);
await new Promise<void>((resolve) =>
requestAnimationFrame(() => resolve()),
);
return {
elapsedMs: performance.now() - startedAt,
resolverStarted: (window.__BUZZ_E2E_COMMAND_PAYLOADS__ ?? []).some(
(entry) => entry.command === "fetch_link_preview_metadata",
),
text: element.textContent,
};
}, pasteText);
expect(firstPaint.text).toContain(previewUrl);
expect(firstPaint.resolverStarted).toBe(false);
expect(firstPaint.elapsedMs).toBeLessThan(100);
const composerPreview = page
.locator("[data-composer-link-previews]")
.locator('[data-link-preview="github-pull-request"]');
await expect(input).toContainText(previewUrl, { timeout: 1_000 });
await expect(composerPreview).toHaveAttribute(
"data-state",
/^(processing|done)$/,
{ timeout: 1_000 },
);
await new Promise<void>((resolve) =>
requestAnimationFrame(() => resolve()),
);
return {
elapsedMs: performance.now() - startedAt,
resolverStarted: (window.__BUZZ_E2E_COMMAND_PAYLOADS__ ?? []).some(
(entry) => entry.command === "fetch_link_preview_metadata",
),
text: element.textContent,
};
}, pasteText);
expect(firstPaint.text).toContain(previewUrl);
expect(firstPaint.resolverStarted).toBe(false);
expect(firstPaint.elapsedMs).toBeLessThan(100);
const composerPreview = page
.locator("[data-composer-link-previews]")
.locator('[data-link-preview="github-pull-request"]');
await expect(input).toContainText(previewUrl, { timeout: 1_000 });
await expect(composerPreview).toHaveAttribute(
"data-state",
/^(processing|done)$/,
{ timeout: 1_000 },
);
await expect(page.getByTestId("send-message")).toBeEnabled();
});
await expect(page.getByTestId("send-message")).toBeEnabled();
});
}
test("completed link preview sends once and leaves the composer cleared", async ({
page,