Suppress the webview's default right-click menu

Reload/Inspect Element no longer appear on uncontrolled right-clicks
in any window; text fields keep the system text menu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-13 23:30:23 +02:00
co-authored by Claude Fable 5
parent ab59c785b5
commit 14b92b33a4
3 changed files with 30 additions and 4 deletions
@@ -5,7 +5,7 @@ import { ConnectionsService } from '../connections.service';
import { ConfirmDialog } from '../dialogs/confirm-dialog'; import { ConfirmDialog } from '../dialogs/confirm-dialog';
import { PromptDialog } from '../dialogs/prompt-dialog'; import { PromptDialog } from '../dialogs/prompt-dialog';
import { DialogsService } from '../dialogs.service'; import { DialogsService } from '../dialogs.service';
import { IS_MAC_CHROME } from '../platform'; import { blockNativeContextMenu, IS_MAC_CHROME } from '../platform';
import { RpcService } from '../rpc.service'; import { RpcService } from '../rpc.service';
import { SettingsService } from '../settings.service'; import { SettingsService } from '../settings.service';
import { Toasts } from '../toasts/toasts'; import { Toasts } from '../toasts/toasts';
@@ -73,7 +73,10 @@ function fromDraft(d: Draft): SavedConnection {
selector: 'app-root', selector: 'app-root',
imports: [NgIcon, ConfirmDialog, PromptDialog, Toasts], imports: [NgIcon, ConfirmDialog, PromptDialog, Toasts],
templateUrl: './connection-manager.html', templateUrl: './connection-manager.html',
host: { '(window:keydown.escape)': 'onEscape()' }, host: {
'(window:keydown.escape)': 'onEscape()',
'(window:contextmenu)': 'blockContextMenu($event)',
},
}) })
export class ConnectionManager { export class ConnectionManager {
readonly store = inject(ConnectionsService); readonly store = inject(ConnectionsService);
@@ -138,6 +141,10 @@ export class ConnectionManager {
if (!this.dialogs.anyOpen) this.close(); if (!this.dialogs.anyOpen) this.close();
} }
blockContextMenu(event: Event): void {
blockNativeContextMenu(event);
}
async save(): Promise<string | null> { async save(): Promise<string | null> {
const d = this.draft(); const d = this.draft();
if (!d || !this.valid()) return null; if (!d || !this.valid()) return null;
+10 -2
View File
@@ -10,7 +10,7 @@ import {
viewChild, viewChild,
} from '@angular/core'; } from '@angular/core';
import { NgIcon } from '@ng-icons/core'; import { NgIcon } from '@ng-icons/core';
import { EDITOR_CONTEXT, IS_MAC_CHROME } from '../platform'; import { blockNativeContextMenu, EDITOR_CONTEXT, IS_MAC_CHROME } from '../platform';
import { RpcService } from '../rpc.service'; import { RpcService } from '../rpc.service';
import { SettingsService } from '../settings.service'; import { SettingsService } from '../settings.service';
import { ToastService } from '../toast.service'; import { ToastService } from '../toast.service';
@@ -31,7 +31,11 @@ const DRAFT_DEBOUNCE_MS = 400;
selector: 'app-root', selector: 'app-root',
imports: [NgIcon, Toasts], imports: [NgIcon, Toasts],
templateUrl: './editor-window.html', templateUrl: './editor-window.html',
host: { '(window:keydown)': 'onKeydown($event)', '(window:blur)': 'flushDraft()' }, host: {
'(window:keydown)': 'onKeydown($event)',
'(window:blur)': 'flushDraft()',
'(window:contextmenu)': 'blockContextMenu($event)',
},
}) })
export class EditorWindow { export class EditorWindow {
readonly ctx = EDITOR_CONTEXT; readonly ctx = EDITOR_CONTEXT;
@@ -126,6 +130,10 @@ export class EditorWindow {
queueMicrotask(() => this.box()?.nativeElement.focus()); queueMicrotask(() => this.box()?.nativeElement.focus());
} }
blockContextMenu(event: Event): void {
blockNativeContextMenu(event);
}
onInput(value: string): void { onInput(value: string): void {
this.text.set(value); this.text.set(value);
this.scheduleDraft(); this.scheduleDraft();
+11
View File
@@ -23,3 +23,14 @@ export const APP_MODE: 'main' | 'editor' | 'manager' =
export const EDITOR_CONTEXT = export const EDITOR_CONTEXT =
typeof window !== 'undefined' ? (window.__APP_CTX as EditorOpenContext | undefined) : undefined; typeof window !== 'undefined' ? (window.__APP_CTX as EditorOpenContext | undefined) : undefined;
/**
* Kill the webview's default right-click menu (Reload, Inspect Element…)
* everywhere the app doesn't show its own native menu. Text fields keep the
* system text menu (cut/copy/paste, spelling).
*/
export function blockNativeContextMenu(event: Event): void {
const target = event.target as HTMLElement | null;
if (target?.closest('input, textarea, [contenteditable="true"]')) return;
event.preventDefault();
}