fix(shell): use c to copy for claude

This commit is contained in:
Haileyesus
2026-07-02 22:39:10 +03:00
parent cc2811126b
commit 84fe5758fc

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import type { MutableRefObject, RefObject } from 'react';
import { ClipboardAddon, type IClipboardProvider } from '@xterm/addon-clipboard';
import { FitAddon } from '@xterm/addon-fit';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { WebglAddon } from '@xterm/addon-webgl';
@@ -19,6 +20,43 @@ import {
import { sendSocketMessage } from '../utils/socket';
import { ensureXtermFocusStyles } from '../utils/terminalStyles';
// CLIs running inside the pty (e.g. `claude auth login`'s "press c to copy"
// device-flow prompt) write to the clipboard via an OSC 52 escape sequence,
// not a browser event — xterm.js ignores OSC 52 unless a clipboard addon is
// loaded. Routes writes through the same fallback-aware helper the terminal's
// own selection-copy shortcut uses, since `navigator.clipboard` is often
// unavailable on self-hosted, non-HTTPS deployments.
// `ClipboardSelectionType.SYSTEM` is `'c'` (vs. `'p'` for the X11 primary
// selection) — compared as a literal since the addon ships it as a const
// enum, which isolatedModules builds (esbuild/Vite) can't import as a value.
const oscClipboardProvider: IClipboardProvider = {
readText: async (selection) => {
if (selection !== 'c') {
return '';
}
try {
return (await navigator.clipboard?.readText?.()) || '';
} catch {
return '';
}
},
writeText: async (selection, text) => {
if (selection !== 'c') {
return;
}
await copyTextToClipboard(text);
},
};
// The addon's published typings declare a single `(provider?)` constructor
// param, but the shipped runtime actually takes `(base64?, provider?)` — see
// node_modules/@xterm/addon-clipboard/lib/addon-clipboard.js. Cast to call it
// the way it's really implemented.
const ClipboardAddonCtor = ClipboardAddon as unknown as new (
base64?: unknown,
provider?: IClipboardProvider,
) => ClipboardAddon;
type UseShellTerminalOptions = {
terminalContainerRef: RefObject<HTMLDivElement>;
terminalRef: MutableRefObject<Terminal | null>;
@@ -93,6 +131,8 @@ export function useShellTerminal({
fitAddonRef.current = nextFitAddon;
nextTerminal.loadAddon(nextFitAddon);
nextTerminal.loadAddon(new ClipboardAddonCtor(undefined, oscClipboardProvider));
// Avoid wrapped partial links in compact login flows.
if (!minimal) {
nextTerminal.loadAddon(new WebLinksAddon());