Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions apps/desktop/src/window/DesktopApplicationMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,37 @@ describe("DesktopApplicationMenu", () => {
}),
);

// Chromium pastes as plain text for the accelerator on its own. Dispatching
// the action as well injects a second paste, which doubles the pasted text.
it.effect("leaves the accelerator to Chromium instead of injecting a paste", () =>
Effect.gen(function* () {
const selectedAction = yield* Deferred.make<string>();
const applicationMenuTemplate =
yield* Deferred.make<readonly Electron.MenuItemConstructorOptions[]>();

yield* configureMenu(selectedAction, applicationMenuTemplate);

const template = yield* Deferred.await(applicationMenuTemplate);
const editMenu = template.find((item) => item.label === "Edit");
if (!Array.isArray(editMenu?.submenu)) {
throw new Error("Expected Edit menu submenu to be an array.");
}
const pasteAsTextItem = editMenu.submenu.find((item) => item.label === "Paste as Text");
if (typeof pasteAsTextItem?.click !== "function") {
throw new Error("Expected Paste as Text menu item to have a click handler.");
}

pasteAsTextItem.click(
{} as Electron.MenuItem,
{} as Electron.BrowserWindow,
{
triggeredByAccelerator: true,
} as unknown as KeyboardEvent,
);
assert.isFalse(yield* Deferred.isDone(selectedAction));
}),
);

// Zoom must route through DesktopWindow.zoomMain instead of the Electron
// zoom roles: the roles zoom whichever webContents has focus, which breaks
// app zoom while an embedded preview WebContentsView holds focus.
Expand Down
12 changes: 11 additions & 1 deletion apps/desktop/src/window/DesktopApplicationMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,17 @@ export const make = Effect.gen(function* () {
const settingsClick = () => {
runMenuEffect("open-settings", dispatchMenuAction("open-settings"));
};
const pasteAsTextClick = () => {
// Chromium already pastes as plain text for this chord, so the accelerator
// needs nothing from the menu: the composer and the terminal each arm
// themselves from the same keydown. Routing it through the renderer anyway
// lands a second, injected paste and doubles the text. Only a menu click,
// which produces no keystroke for them to see, needs that round trip.
const pasteAsTextClick = (
_item: Electron.MenuItem,
_window: Electron.BaseWindow | undefined,
event: Electron.KeyboardEvent,
) => {
if (event.triggeredByAccelerator === true) return;
runMenuEffect("paste-as-text", dispatchMenuAction("paste-as-text"));
};
const zoomClick = (direction: DesktopWindow.MainWindowZoomDirection) => () => {
Expand Down
Loading