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:
@@ -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,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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user