Merge pull request #5017 from Dokploy/fix/terminal-mac-option-keys-4297

fix: allow Option/Alt composed characters in terminals on macOS
This commit is contained in:
Mauricio Siu 2026-08-09 01:30:21 -06:00 committed by GitHub
commit 4b90eb0d90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import "@xterm/xterm/css/xterm.css";
import { AttachAddon } from "@xterm/addon-attach";
import { useTheme } from "next-themes";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { fixMacOsAltKeys } from "@/lib/terminal-keyboard";
interface Props {
id: string;
@ -45,6 +46,7 @@ export const DockerTerminal: React.FC<Props> = ({
const ws = new WebSocket(wsUrl);
const addonAttach = new AttachAddon(ws);
fixMacOsAltKeys(term);
// @ts-ignore
term.open(termRef.current);
// @ts-ignore

View File

@ -6,6 +6,7 @@ import "@xterm/xterm/css/xterm.css";
import { AttachAddon } from "@xterm/addon-attach";
import { ClipboardAddon } from "@xterm/addon-clipboard";
import { useTheme } from "next-themes";
import { fixMacOsAltKeys } from "@/lib/terminal-keyboard";
import { getLocalServerData } from "./local-server-config";
interface Props {
@ -58,6 +59,7 @@ export const Terminal: React.FC<Props> = ({ id, serverId }) => {
const addonAttach = new AttachAddon(ws);
const clipboardAddon = new ClipboardAddon();
term.loadAddon(clipboardAddon);
fixMacOsAltKeys(term);
// @ts-ignore
term.open(termRef.current);

View File

@ -0,0 +1,25 @@
import type { Terminal } from "@xterm/xterm";
// xterm's platform detection mistakes the bundled Next.js `process` polyfill
// for Node.js, so it never treats Option/Alt as third-level shift on macOS and
// swallows composed characters like Option+L (@ on German layouts).
// https://github.com/Dokploy/dokploy/issues/4297
export const fixMacOsAltKeys = (term: Terminal) => {
if (!/Mac/.test(navigator.platform)) {
return;
}
term.attachCustomKeyEventHandler((event) => {
if (
event.type === "keydown" &&
event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
event.key.length === 1
) {
event.preventDefault();
term.input(event.key);
return false;
}
return true;
});
};