feat(electron): add global context menu (#13218)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added automatic synchronization of language settings between the
desktop app and the system environment.
* Context menu actions (Cut, Copy, Paste) in the desktop app are now
localized according to the selected language.

* **Improvements**
* Context menu is always available with standard editing actions,
regardless of spell check settings.

* **Localization**
* Added translations for "Cut", "Copy", and "Paste" in the context menu.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-07-16 12:37:38 +08:00
committed by GitHub
parent 45b05f06b3
commit d44771dfe9
11 changed files with 138 additions and 74 deletions

View File

@@ -123,52 +123,57 @@ const WorkbenchView = ({
[tabsHeaderService, workbench.id]
);
const onContextMenu = useAsyncCallback(async () => {
const action = await tabsHeaderService.showContextMenu?.(
workbench.id,
viewIdx
);
switch (action?.type) {
case 'open-in-split-view': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'openInSplitView',
});
break;
}
case 'separate-view': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'separateTabs',
});
break;
}
case 'pin-tab': {
if (action.payload.shouldPin) {
const onContextMenu = useAsyncCallback(
async (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const action = await tabsHeaderService.showContextMenu?.(
workbench.id,
viewIdx
);
switch (action?.type) {
case 'open-in-split-view': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'pin',
});
} else {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'unpin',
action: 'openInSplitView',
});
break;
}
break;
case 'separate-view': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'separateTabs',
});
break;
}
case 'pin-tab': {
if (action.payload.shouldPin) {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'pin',
});
} else {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'unpin',
});
}
break;
}
// fixme: when close tab the view may already be gc'ed
case 'close-tab': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'close',
});
break;
}
default:
break;
}
// fixme: when close tab the view may already be gc'ed
case 'close-tab': {
track.$.appTabsHeader.$.tabAction({
control: 'contextMenu',
action: 'close',
});
break;
}
default:
break;
}
}, [tabsHeaderService, viewIdx, workbench.id]);
},
[tabsHeaderService, viewIdx, workbench.id]
);
const contentNode = useMemo(() => {
return (