fix(desktop): show the text context menu on password fields (#3285)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 17:30:11 +02:00
1 parent e7859fefb1
commit 510cc1b916
4 files changed
+17 -71

No files matched your search

@@ -217,6 +217,7 @@ export const TextareaContextMenu = observer(
);
onClose();
};
const isPasswordTarget = targetElement instanceof HTMLInputElement && targetElement.type === 'password';
const spellcheckEnabled = Spellcheck.enabled;
const hasMisspelling = spellcheckEnabled && misspelledWord && suggestions.length > 0;
return (
@@ -307,19 +308,21 @@ export const TextareaContextMenu = observer(
{i18n._(SELECT_ALL_DESCRIPTOR)}
</MenuItem>
</MenuGroup>
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--5">
<MenuItemSubmenu
label={i18n._(SPELLCHECK_DESCRIPTOR)}
render={() => (
<SpellcheckSubmenu
isElectron={isElectron()}
onOpenSpellcheckSettings={handleOpenSpellcheckSettings}
data-flx="channel.textarea.textarea-context-menu.spellcheck-submenu"
/>
)}
data-flx="channel.textarea.textarea-context-menu.menu-item-submenu"
/>
</MenuGroup>
{!isPasswordTarget && (
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--5">
<MenuItemSubmenu
label={i18n._(SPELLCHECK_DESCRIPTOR)}
render={() => (
<SpellcheckSubmenu
isElectron={isElectron()}
onOpenSpellcheckSettings={handleOpenSpellcheckSettings}
data-flx="channel.textarea.textarea-context-menu.spellcheck-submenu"
/>
)}
data-flx="channel.textarea.textarea-context-menu.menu-item-submenu"
/>
</MenuGroup>
)}
{showSendButtonToggle && (
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--6">
<CheckboxItem
@@ -43,7 +43,6 @@ const DISALLOWED_INPUT_TYPES = new Set([
'submit',
'time',
'week',
'password',
]);
const CONTEXT_TARGET_MAX_AGE_MS = 5000;
const createSyntheticEvent = (
+1 -47
View File
@@ -140,13 +140,6 @@ let factoryPromise: Promise<HunspellFactory> | null = null;
let ipcRegistered = false;
let launchModeLogged = false;
const contextSourceByWebContents = new WeakMap<
WebContents,
{
isTextarea: boolean;
ts: number;
}
>();
const norm = (code: string): string => code.toLowerCase();
const beginSessionApply = (session: Session): number => {
const generation = (sessionApplyGeneration.get(session) ?? 0) + 1;
@@ -971,20 +964,6 @@ const suggestWord = (session: Session, word: string): Array<string> => {
const ensureSharedIpc = () => {
if (ipcRegistered) return;
ipcRegistered = true;
ipcMain.on(
'spellcheck-context-target',
(
event,
payload: {
isTextarea?: boolean;
},
) => {
contextSourceByWebContents.set(event.sender, {
isTextarea: Boolean(payload?.isTextarea),
ts: Date.now(),
});
},
);
ipcMain.on(
'spellcheck:update-autodetect-text',
(
@@ -1016,31 +995,6 @@ const ensureSharedIpc = () => {
return suggestWord(event.sender.session, word);
});
};
const shouldHandleContextMenu = (webContents: WebContents, params: Electron.ContextMenuParams): boolean => {
if (!params['isEditable']) return false;
const inputFieldType = (
params as {
inputFieldType?: string;
}
).inputFieldType;
const isPassword =
(
params as {
isPassword?: boolean;
}
).isPassword === true ||
inputFieldType === 'password' ||
(
params as {
formControlType?: string;
}
).formControlType === 'password';
if (isPassword) return false;
const target = contextSourceByWebContents.get(webContents);
const targetRecent = target && Date.now() - target.ts < 5000;
const isTextLike = inputFieldType === 'plainText' || inputFieldType === 'textarea' || inputFieldType === undefined;
return Boolean((targetRecent && target.isTextarea) || isTextLike);
};
let rendererSpellcheckHandlersRegistered = false;
export const registerSpellcheck = (webContents: WebContents): void => {
@@ -1117,7 +1071,7 @@ export const registerSpellcheck = (webContents: WebContents): void => {
});
}
webContents.on('context-menu', (event, params) => {
if (!shouldHandleContextMenu(webContents, params)) return;
if (!params.isEditable) return;
event.preventDefault();
const cur = sessionState.get(session) ?? {...defaultState};
const resolved = sessionResolvedEngine.get(session) ?? resolveEngine(cur, session);
-10
View File
@@ -870,16 +870,6 @@ const api: ElectronAPI = {
} satisfies VoiceEngineV2BridgeHardwareEncoderApi,
};
window.addEventListener(
'contextmenu',
(event) => {
const target = event.target as HTMLElement | null;
const isTextarea = Boolean(target?.closest?.('textarea'));
ipcRenderer.send('spellcheck-context-target', {isTextarea});
},
true,
);
let spellcheckAutodetectTimer: NodeJS.Timeout | null = null;
let spellcheckAutodetectContextSequence = 0;
const SPELLCHECK_AUTODETECT_DEBOUNCE_MS = 750;