@particle-academy/fancy-term 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -1
- package/dist/index.cjs +474 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +283 -3
- package/dist/index.d.ts +283 -3
- package/dist/index.js +462 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,170 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, CSSProperties, RefObject } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { Terminal as Terminal$1, IDisposable, ITheme } from '@xterm/xterm';
|
|
4
|
+
|
|
5
|
+
/** What a paste / drop carried: plain text plus any files (e.g. pasted images). */
|
|
6
|
+
interface ClipboardPayload {
|
|
7
|
+
/** Plain text from the clipboard (or pasted selection). May be "". */
|
|
8
|
+
text: string;
|
|
9
|
+
/** Files present on the clipboard / drop — typically pasted/dropped images. */
|
|
10
|
+
files: File[];
|
|
11
|
+
/** Convenience: the subset of `files` whose MIME type is `image/*`. */
|
|
12
|
+
images: File[];
|
|
13
|
+
}
|
|
14
|
+
/** True when a file looks like an image (by MIME type). */
|
|
15
|
+
declare function isImageFile(f: {
|
|
16
|
+
type?: string;
|
|
17
|
+
}): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A host-supplied clipboard. Every copy/paste path in fancy-term — the copy
|
|
20
|
+
* chord, the context menu, OSC 52, `handle.copySelection` / `handle.paste` —
|
|
21
|
+
* routes through this, so an Electron host (where `navigator.clipboard` silently
|
|
22
|
+
* no-ops in a sandboxed renderer) can bridge to its main-process clipboard over
|
|
23
|
+
* IPC. `writeText` may return anything (a `Promise<void>` from IPC is fine);
|
|
24
|
+
* `readText` returns the clipboard text.
|
|
25
|
+
*/
|
|
26
|
+
interface ClipboardProvider {
|
|
27
|
+
writeText: (text: string) => void | Promise<unknown>;
|
|
28
|
+
readText: () => string | Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
/** The default provider — `navigator.clipboard` with the legacy `execCommand` fallback. */
|
|
31
|
+
declare const navigatorClipboard: ClipboardProvider;
|
|
32
|
+
/** The `clipboard` prop: `false` disables clipboard wiring; a provider object injects a host clipboard; `true`/omitted uses {@link navigatorClipboard}. */
|
|
33
|
+
type ClipboardOption = boolean | ClipboardProvider;
|
|
34
|
+
/** Normalize the `clipboard` prop into `{ enabled, provider }`, read live per call. */
|
|
35
|
+
declare function resolveClipboard(clipboard: ClipboardOption | undefined): {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
provider: ClipboardProvider;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The copy/paste convention the terminal follows:
|
|
42
|
+
* - `contextmenu` — copy/paste via the right-click menu only (plus the always-on
|
|
43
|
+
* Ctrl+Shift+C copy chord).
|
|
44
|
+
* - `linux` — highlight-to-copy (X11 primary-selection style) + middle-click
|
|
45
|
+
* paste. Right-click still opens the context menu.
|
|
46
|
+
* - `winmac` — Ctrl/Cmd+C copies the selection (falling back to SIGINT when
|
|
47
|
+
* nothing is selected), Ctrl/Cmd+V pastes.
|
|
48
|
+
*
|
|
49
|
+
* Omitting the prop keeps the historical default: the Ctrl+Shift+C chord and
|
|
50
|
+
* Cmd+C-with-selection, no auto-copy, no mouse paste.
|
|
51
|
+
*/
|
|
52
|
+
type CopyPasteMode = "contextmenu" | "linux" | "winmac";
|
|
53
|
+
/** The resolved, event-agnostic behavior flags a {@link CopyPasteMode} implies. */
|
|
54
|
+
interface CopyPasteBehavior {
|
|
55
|
+
/** Auto-copy the selection to the clipboard as soon as it changes (linux). */
|
|
56
|
+
selectToCopy: boolean;
|
|
57
|
+
/** Paste on middle-mouse-button (linux). */
|
|
58
|
+
middleClickPaste: boolean;
|
|
59
|
+
/** (Ctrl|Cmd)+C copies the selection when one exists (winmac). */
|
|
60
|
+
keyCopy: boolean;
|
|
61
|
+
/** (Ctrl|Cmd)+V — and Ctrl+Shift+V — paste (winmac). */
|
|
62
|
+
keyPaste: boolean;
|
|
63
|
+
}
|
|
64
|
+
declare function copyPasteBehavior(mode: CopyPasteMode | undefined): CopyPasteBehavior;
|
|
65
|
+
/** The subset of a KeyboardEvent the copy/paste decision reads. */
|
|
66
|
+
interface KeyEventLike {
|
|
67
|
+
type: string;
|
|
68
|
+
key: string;
|
|
69
|
+
ctrlKey: boolean;
|
|
70
|
+
shiftKey: boolean;
|
|
71
|
+
metaKey: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a key event to a clipboard action, given the current selection state
|
|
75
|
+
* and the active behavior. Returns `"copy"`, `"paste"`, or `null` (do nothing —
|
|
76
|
+
* let the byte reach the shell). Only fires on `keydown`.
|
|
77
|
+
*
|
|
78
|
+
* Copy is honored across every mode for the terminal-safe **Ctrl+Shift+C** chord
|
|
79
|
+
* and macOS **Cmd+C-with-selection**; `keyCopy` adds plain (Ctrl|Cmd)+C. Plain
|
|
80
|
+
* Ctrl+C with no selection is never a copy — it stays SIGINT.
|
|
81
|
+
*/
|
|
82
|
+
declare function resolveKeyAction(e: KeyEventLike, hasSelection: boolean, behavior: CopyPasteBehavior): "copy" | "paste" | null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* OSC 52 policy: `copy` allows programs to WRITE the clipboard (the common TUI
|
|
86
|
+
* case), `read` allows a program to READ it back (a `?` request), `both` allows
|
|
87
|
+
* either, `false` disables OSC 52 entirely. Read is the real exfiltration risk
|
|
88
|
+
* (arbitrary terminal output could siphon your clipboard), so it must be opted
|
|
89
|
+
* into explicitly.
|
|
90
|
+
*/
|
|
91
|
+
type Osc52Mode = "copy" | "read" | "both" | false;
|
|
92
|
+
/** A parsed OSC 52 request: which selection buffer, and write-a-payload vs read-request. */
|
|
93
|
+
interface Osc52Request {
|
|
94
|
+
/** The `Pc` field — clipboard selection(s), e.g. `"c"` (clipboard), `"p"` (primary). */
|
|
95
|
+
selection: string;
|
|
96
|
+
kind: "write" | "read";
|
|
97
|
+
/** Decoded text for a write; `""` for a read request. */
|
|
98
|
+
data: string;
|
|
99
|
+
}
|
|
100
|
+
/** UTF-8-safe base64 encode (browser `btoa` operates on binary strings). */
|
|
101
|
+
declare function encodeBase64(text: string): string;
|
|
102
|
+
/** UTF-8-safe base64 decode; returns `""` on malformed input. */
|
|
103
|
+
declare function decodeBase64(b64: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Parse an OSC 52 payload (the part after `ESC ] 52 ;`, i.e. `<Pc> ; <Pd>`).
|
|
106
|
+
* `Pd === "?"` is a read request; otherwise `Pd` is base64 to write. Returns
|
|
107
|
+
* `null` for a malformed payload (no `;`).
|
|
108
|
+
*/
|
|
109
|
+
declare function parseOsc52(payload: string): Osc52Request | null;
|
|
110
|
+
/** Build the OSC 52 response a `read` request expects: `ESC ] 52 ; <Pc> ; <base64> BEL`. */
|
|
111
|
+
declare function osc52Response(selection: string, text: string): string;
|
|
112
|
+
/**
|
|
113
|
+
* Register an OSC 52 handler on an xterm instance, routing writes/reads through
|
|
114
|
+
* `provider` per `mode`. Returns the {@link IDisposable} (or `null` when
|
|
115
|
+
* `mode === false`). The handler always returns `true` (consumed) so a
|
|
116
|
+
* disallowed direction is swallowed rather than echoed as garbage.
|
|
117
|
+
*/
|
|
118
|
+
declare function registerOsc52(term: Terminal$1, provider: () => ClipboardProvider, mode: () => Osc52Mode): IDisposable | null;
|
|
119
|
+
|
|
120
|
+
/** Context handed to the menu at right-click time + to custom item builders. */
|
|
121
|
+
interface TerminalContextMenuContext {
|
|
122
|
+
/** Whether there is a non-empty text selection. */
|
|
123
|
+
hasSelection: boolean;
|
|
124
|
+
/** The selected text (or ""). */
|
|
125
|
+
selection: string;
|
|
126
|
+
/** Whether the terminal is read-only (paste / clear are dropped by default). */
|
|
127
|
+
readOnly: boolean;
|
|
128
|
+
}
|
|
129
|
+
/** A single menu entry. `onSelect` runs on click; set `separator` for a divider. */
|
|
130
|
+
interface TerminalContextMenuItem {
|
|
131
|
+
/** Stable id (used as the React key + a `data-menu-item` handle). */
|
|
132
|
+
id: string;
|
|
133
|
+
/** Visible label. Ignored for separators. */
|
|
134
|
+
label?: string;
|
|
135
|
+
/** Optional leading glyph / emoji / short string (JSON-friendly — no ReactNode). */
|
|
136
|
+
icon?: string;
|
|
137
|
+
/** Greyed-out + non-interactive. */
|
|
138
|
+
disabled?: boolean;
|
|
139
|
+
/** Render a divider instead of a clickable item. */
|
|
140
|
+
separator?: boolean;
|
|
141
|
+
/** Invoked on click with the menu context. */
|
|
142
|
+
onSelect?: (ctx: TerminalContextMenuContext) => void;
|
|
143
|
+
}
|
|
144
|
+
/** The actions the built-in items dispatch to (wired by the component). */
|
|
145
|
+
interface TerminalMenuActions {
|
|
146
|
+
copy: () => void;
|
|
147
|
+
paste: () => void;
|
|
148
|
+
selectAll: () => void;
|
|
149
|
+
clear: () => void;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The `contextMenu` prop shape:
|
|
153
|
+
* - `false` — disable the menu entirely.
|
|
154
|
+
* - `true` / omitted — the default Copy / Paste / Select all / Clear menu.
|
|
155
|
+
* - an array — replace the default items wholesale.
|
|
156
|
+
* - a function — receives the context **and the default items** so you can
|
|
157
|
+
* add/remove/reorder (e.g. append a "Send to agent" item).
|
|
158
|
+
*/
|
|
159
|
+
type TerminalContextMenuConfig = boolean | TerminalContextMenuItem[] | ((ctx: TerminalContextMenuContext, defaults: TerminalContextMenuItem[]) => TerminalContextMenuItem[]);
|
|
160
|
+
/** The built-in Copy / Paste / Select all / Clear items, contextual to selection + readOnly. */
|
|
161
|
+
declare function defaultMenuItems(ctx: TerminalContextMenuContext, actions: TerminalMenuActions): TerminalContextMenuItem[];
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the configured menu into the final item list, or `null` when disabled.
|
|
164
|
+
* Empty results (e.g. a function that filtered everything out) also resolve to
|
|
165
|
+
* `null` so the caller can skip opening an empty menu.
|
|
166
|
+
*/
|
|
167
|
+
declare function resolveMenuItems(config: TerminalContextMenuConfig | undefined, ctx: TerminalContextMenuContext, actions: TerminalMenuActions): TerminalContextMenuItem[] | null;
|
|
4
168
|
|
|
5
169
|
/** xterm.js color theme (re-exported for ergonomic typing). */
|
|
6
170
|
type TerminalTheme = ITheme;
|
|
@@ -33,6 +197,13 @@ interface ShellProfile {
|
|
|
33
197
|
interface TerminalHandle {
|
|
34
198
|
/** The underlying xterm.js instance — escape hatch for addons / advanced use. Null before mount. */
|
|
35
199
|
readonly xterm: Terminal$1 | null;
|
|
200
|
+
/**
|
|
201
|
+
* Resolves with the xterm instance once it's opened + measured. Because
|
|
202
|
+
* `xterm` is null until the container lays out, a consumer that must wire an
|
|
203
|
+
* addon can `await handle.ready` instead of polling. Re-armed if the terminal
|
|
204
|
+
* is torn down + recreated (e.g. a container change).
|
|
205
|
+
*/
|
|
206
|
+
readonly ready: Promise<Terminal$1>;
|
|
36
207
|
/** Write raw data (ANSI escape sequences honored) to the terminal. */
|
|
37
208
|
write: (data: string) => void;
|
|
38
209
|
/** Write data followed by CRLF. */
|
|
@@ -49,6 +220,14 @@ interface TerminalHandle {
|
|
|
49
220
|
getBuffer: () => string;
|
|
50
221
|
/** The current text selection, or "" when nothing is selected. */
|
|
51
222
|
getSelection: () => string;
|
|
223
|
+
/** Copy the current selection to the system clipboard. Resolves false when there's nothing selected or the clipboard is unavailable. */
|
|
224
|
+
copySelection: () => Promise<boolean>;
|
|
225
|
+
/** Paste text into the terminal. With no argument, reads the system clipboard. Honors xterm's bracketed-paste mode. */
|
|
226
|
+
paste: (text?: string) => Promise<void>;
|
|
227
|
+
/** Select the entire buffer. */
|
|
228
|
+
selectAll: () => void;
|
|
229
|
+
/** Clear the current selection. */
|
|
230
|
+
clearSelection: () => void;
|
|
52
231
|
/**
|
|
53
232
|
* Switch the active shell by id. Resolves the matching {@link ShellProfile}
|
|
54
233
|
* from `shells` and fires `onShellChange`; in uncontrolled mode it also updates
|
|
@@ -88,8 +267,50 @@ interface TerminalOptions {
|
|
|
88
267
|
cols: number;
|
|
89
268
|
rows: number;
|
|
90
269
|
}) => void;
|
|
270
|
+
/**
|
|
271
|
+
* Clipboard wiring — gates the Ctrl+Shift+C / Cmd+C copy chord, the paste
|
|
272
|
+
* interceptor ({@link onPaste}), the context-menu copy/paste, and OSC 52.
|
|
273
|
+
*
|
|
274
|
+
* - `true` / omitted — enabled, backed by `navigator.clipboard`.
|
|
275
|
+
* - `false` — disabled (no copy chord / OSC 52; native text paste still works).
|
|
276
|
+
* - a `{ writeText, readText }` **provider** — every copy/paste path routes
|
|
277
|
+
* through it. Supply this in a sandboxed Electron renderer, where
|
|
278
|
+
* `navigator.clipboard` silently no-ops, to bridge to the main-process
|
|
279
|
+
* clipboard over IPC.
|
|
280
|
+
*/
|
|
281
|
+
clipboard?: ClipboardOption;
|
|
282
|
+
/**
|
|
283
|
+
* OSC 52 clipboard policy — lets terminal programs (Claude Code, tmux, vim)
|
|
284
|
+
* set/read the system clipboard via `ESC ] 52`. `"copy"` (default) allows
|
|
285
|
+
* writes only; `"read"` / `"both"` also answer read requests (an exfiltration
|
|
286
|
+
* risk — opt in deliberately); `false` disables it. Routed through the same
|
|
287
|
+
* clipboard provider as {@link clipboard}; a no-op when clipboard is `false`.
|
|
288
|
+
* Default `"copy"`.
|
|
289
|
+
*/
|
|
290
|
+
osc52?: Osc52Mode;
|
|
291
|
+
/**
|
|
292
|
+
* Copy/paste UX convention: `"contextmenu"` (menu + Ctrl+Shift+C),
|
|
293
|
+
* `"linux"` (highlight-to-copy + middle-click paste), or `"winmac"`
|
|
294
|
+
* (Ctrl/Cmd+C copies the selection, Ctrl/Cmd+V pastes). Omit for the historical
|
|
295
|
+
* default (Ctrl+Shift+C + Cmd+C-with-selection). Ctrl+Shift+C always copies.
|
|
296
|
+
*/
|
|
297
|
+
copyPaste?: CopyPasteMode;
|
|
298
|
+
/**
|
|
299
|
+
* Called once the xterm instance is opened + attached — the imperative twin of
|
|
300
|
+
* {@link TerminalHandle.ready}. Use it to load an addon without racing the
|
|
301
|
+
* container layout (where `handle.xterm` is still null).
|
|
302
|
+
*/
|
|
303
|
+
onReady?: (xterm: Terminal$1) => void;
|
|
304
|
+
/**
|
|
305
|
+
* Fired on every paste with the clipboard payload — `{ text, files, images }`.
|
|
306
|
+
* Plain text still pastes into the terminal natively; this is where a host
|
|
307
|
+
* receives pasted **images** (a shell can't render them) to upload / hand to an
|
|
308
|
+
* agent / write a path. Return `false` to fully consume the paste (suppress the
|
|
309
|
+
* default text paste — e.g. to transform it, then call `handle.paste(...)`).
|
|
310
|
+
*/
|
|
311
|
+
onPaste?: (payload: ClipboardPayload) => void | boolean;
|
|
91
312
|
}
|
|
92
|
-
interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElement>, "onInput" | "onResize" | "children"> {
|
|
313
|
+
interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElement>, "onInput" | "onResize" | "onPaste" | "contextMenu" | "children"> {
|
|
93
314
|
/**
|
|
94
315
|
* The controlled output buffer. The component diffs against the previous value
|
|
95
316
|
* and writes only the appended delta, so a host can drive the terminal from
|
|
@@ -112,6 +333,13 @@ interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElem
|
|
|
112
333
|
* `shells` is provided. Default `false` — opt-in so existing layout is unchanged.
|
|
113
334
|
*/
|
|
114
335
|
showShellBar?: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* The right-click selection context menu. `false` disables it; `true` / omitted
|
|
338
|
+
* gives the default Copy / Paste / Select all / Clear menu; an array replaces
|
|
339
|
+
* the items; a `(ctx, defaults) => items` function lets you add/remove/reorder
|
|
340
|
+
* (e.g. append a "Send to agent" item). See {@link TerminalContextMenuConfig}.
|
|
341
|
+
*/
|
|
342
|
+
contextMenu?: TerminalContextMenuConfig;
|
|
115
343
|
}
|
|
116
344
|
/**
|
|
117
345
|
* Resolve a shell id to its {@link ShellProfile} within a list. Pure helper —
|
|
@@ -169,6 +397,22 @@ interface ShellSwitcherProps {
|
|
|
169
397
|
*/
|
|
170
398
|
declare function ShellSwitcher({ shells, value, onChange, className, style, disabled, }: ShellSwitcherProps): react.JSX.Element;
|
|
171
399
|
|
|
400
|
+
/**
|
|
401
|
+
* The terminal selection context menu. Deliberately plain React + inline styles
|
|
402
|
+
* (fancy-term has no react-fancy dependency) styled to match the shell bar. It
|
|
403
|
+
* closes on click-outside + Escape, and clamps itself inside the viewport on
|
|
404
|
+
* mount so a right-click near an edge never spills off-screen.
|
|
405
|
+
*/
|
|
406
|
+
declare function TerminalContextMenu({ at, items, ctx, onClose, }: {
|
|
407
|
+
at: {
|
|
408
|
+
x: number;
|
|
409
|
+
y: number;
|
|
410
|
+
};
|
|
411
|
+
items: TerminalContextMenuItem[];
|
|
412
|
+
ctx: TerminalContextMenuContext;
|
|
413
|
+
onClose: () => void;
|
|
414
|
+
}): react.JSX.Element;
|
|
415
|
+
|
|
172
416
|
/**
|
|
173
417
|
* Create + own an xterm.js instance for a container, returning a stable
|
|
174
418
|
* {@link TerminalHandle}. The headless engine layer behind `<Terminal>` — use it
|
|
@@ -257,4 +501,40 @@ declare function useTerminalSession(options: UseTerminalSessionOptions): Termina
|
|
|
257
501
|
*/
|
|
258
502
|
declare const fancyDarkTheme: TerminalTheme;
|
|
259
503
|
|
|
260
|
-
|
|
504
|
+
/** The subset of a KeyboardEvent the copy decision reads. */
|
|
505
|
+
interface CopyKeyEvent {
|
|
506
|
+
type: string;
|
|
507
|
+
key: string;
|
|
508
|
+
ctrlKey: boolean;
|
|
509
|
+
shiftKey: boolean;
|
|
510
|
+
metaKey: boolean;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Whether a key event should copy the current selection to the clipboard.
|
|
514
|
+
*
|
|
515
|
+
* - **Ctrl+Shift+C** (Windows / Linux terminal convention) — always.
|
|
516
|
+
* - **Cmd / Meta+C** (macOS) — only when there is a selection.
|
|
517
|
+
* - **Plain Ctrl+C is never a copy** — it must stay SIGINT.
|
|
518
|
+
*
|
|
519
|
+
* Only fires on `keydown`. When this returns true the caller copies the
|
|
520
|
+
* selection and consumes the event (returns `false` from xterm's
|
|
521
|
+
* `attachCustomKeyEventHandler` so no control byte is sent).
|
|
522
|
+
*/
|
|
523
|
+
declare function shouldCopyEvent(e: CopyKeyEvent, hasSelection: boolean): boolean;
|
|
524
|
+
|
|
525
|
+
interface MenuPoint {
|
|
526
|
+
x: number;
|
|
527
|
+
y: number;
|
|
528
|
+
}
|
|
529
|
+
interface MenuSize {
|
|
530
|
+
width: number;
|
|
531
|
+
height: number;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Clamp a menu's top-left so the whole menu stays inside the viewport (minus a
|
|
535
|
+
* margin). Pushes left/up when it would overflow the right/bottom edge, and never
|
|
536
|
+
* goes past the top-left margin. Pure.
|
|
537
|
+
*/
|
|
538
|
+
declare function clampMenuPosition(at: MenuPoint, menu: MenuSize, viewport: MenuSize, margin?: number): MenuPoint;
|
|
539
|
+
|
|
540
|
+
export { BUILTIN_SHELLS, type ClipboardOption, type ClipboardPayload, type ClipboardProvider, type CopyKeyEvent, type CopyPasteBehavior, type CopyPasteMode, type CursorStyle, type KeyEventLike, type Osc52Mode, type Osc52Request, type ShellProfile, ShellSwitcher, type ShellSwitcherProps, Terminal, TerminalContextMenu, type TerminalContextMenuConfig, type TerminalContextMenuContext, type TerminalContextMenuItem, type TerminalHandle, type TerminalMenuActions, type TerminalOptions, type TerminalProps, type TerminalSessionApi, type TerminalSessionTransport, type TerminalTheme, type UseTerminalSessionOptions, clampMenuPosition, copyPasteBehavior, decodeBase64, defaultMenuItems, encodeBase64, fancyDarkTheme, isImageFile, navigatorClipboard, osc52Response, parseOsc52, registerOsc52, resolveClipboard, resolveKeyAction, resolveMenuItems, resolveShell, shouldCopyEvent, useTerminal, useTerminalFit, useTerminalSession };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,170 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, CSSProperties, RefObject } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { Terminal as Terminal$1, IDisposable, ITheme } from '@xterm/xterm';
|
|
4
|
+
|
|
5
|
+
/** What a paste / drop carried: plain text plus any files (e.g. pasted images). */
|
|
6
|
+
interface ClipboardPayload {
|
|
7
|
+
/** Plain text from the clipboard (or pasted selection). May be "". */
|
|
8
|
+
text: string;
|
|
9
|
+
/** Files present on the clipboard / drop — typically pasted/dropped images. */
|
|
10
|
+
files: File[];
|
|
11
|
+
/** Convenience: the subset of `files` whose MIME type is `image/*`. */
|
|
12
|
+
images: File[];
|
|
13
|
+
}
|
|
14
|
+
/** True when a file looks like an image (by MIME type). */
|
|
15
|
+
declare function isImageFile(f: {
|
|
16
|
+
type?: string;
|
|
17
|
+
}): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A host-supplied clipboard. Every copy/paste path in fancy-term — the copy
|
|
20
|
+
* chord, the context menu, OSC 52, `handle.copySelection` / `handle.paste` —
|
|
21
|
+
* routes through this, so an Electron host (where `navigator.clipboard` silently
|
|
22
|
+
* no-ops in a sandboxed renderer) can bridge to its main-process clipboard over
|
|
23
|
+
* IPC. `writeText` may return anything (a `Promise<void>` from IPC is fine);
|
|
24
|
+
* `readText` returns the clipboard text.
|
|
25
|
+
*/
|
|
26
|
+
interface ClipboardProvider {
|
|
27
|
+
writeText: (text: string) => void | Promise<unknown>;
|
|
28
|
+
readText: () => string | Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
/** The default provider — `navigator.clipboard` with the legacy `execCommand` fallback. */
|
|
31
|
+
declare const navigatorClipboard: ClipboardProvider;
|
|
32
|
+
/** The `clipboard` prop: `false` disables clipboard wiring; a provider object injects a host clipboard; `true`/omitted uses {@link navigatorClipboard}. */
|
|
33
|
+
type ClipboardOption = boolean | ClipboardProvider;
|
|
34
|
+
/** Normalize the `clipboard` prop into `{ enabled, provider }`, read live per call. */
|
|
35
|
+
declare function resolveClipboard(clipboard: ClipboardOption | undefined): {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
provider: ClipboardProvider;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The copy/paste convention the terminal follows:
|
|
42
|
+
* - `contextmenu` — copy/paste via the right-click menu only (plus the always-on
|
|
43
|
+
* Ctrl+Shift+C copy chord).
|
|
44
|
+
* - `linux` — highlight-to-copy (X11 primary-selection style) + middle-click
|
|
45
|
+
* paste. Right-click still opens the context menu.
|
|
46
|
+
* - `winmac` — Ctrl/Cmd+C copies the selection (falling back to SIGINT when
|
|
47
|
+
* nothing is selected), Ctrl/Cmd+V pastes.
|
|
48
|
+
*
|
|
49
|
+
* Omitting the prop keeps the historical default: the Ctrl+Shift+C chord and
|
|
50
|
+
* Cmd+C-with-selection, no auto-copy, no mouse paste.
|
|
51
|
+
*/
|
|
52
|
+
type CopyPasteMode = "contextmenu" | "linux" | "winmac";
|
|
53
|
+
/** The resolved, event-agnostic behavior flags a {@link CopyPasteMode} implies. */
|
|
54
|
+
interface CopyPasteBehavior {
|
|
55
|
+
/** Auto-copy the selection to the clipboard as soon as it changes (linux). */
|
|
56
|
+
selectToCopy: boolean;
|
|
57
|
+
/** Paste on middle-mouse-button (linux). */
|
|
58
|
+
middleClickPaste: boolean;
|
|
59
|
+
/** (Ctrl|Cmd)+C copies the selection when one exists (winmac). */
|
|
60
|
+
keyCopy: boolean;
|
|
61
|
+
/** (Ctrl|Cmd)+V — and Ctrl+Shift+V — paste (winmac). */
|
|
62
|
+
keyPaste: boolean;
|
|
63
|
+
}
|
|
64
|
+
declare function copyPasteBehavior(mode: CopyPasteMode | undefined): CopyPasteBehavior;
|
|
65
|
+
/** The subset of a KeyboardEvent the copy/paste decision reads. */
|
|
66
|
+
interface KeyEventLike {
|
|
67
|
+
type: string;
|
|
68
|
+
key: string;
|
|
69
|
+
ctrlKey: boolean;
|
|
70
|
+
shiftKey: boolean;
|
|
71
|
+
metaKey: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a key event to a clipboard action, given the current selection state
|
|
75
|
+
* and the active behavior. Returns `"copy"`, `"paste"`, or `null` (do nothing —
|
|
76
|
+
* let the byte reach the shell). Only fires on `keydown`.
|
|
77
|
+
*
|
|
78
|
+
* Copy is honored across every mode for the terminal-safe **Ctrl+Shift+C** chord
|
|
79
|
+
* and macOS **Cmd+C-with-selection**; `keyCopy` adds plain (Ctrl|Cmd)+C. Plain
|
|
80
|
+
* Ctrl+C with no selection is never a copy — it stays SIGINT.
|
|
81
|
+
*/
|
|
82
|
+
declare function resolveKeyAction(e: KeyEventLike, hasSelection: boolean, behavior: CopyPasteBehavior): "copy" | "paste" | null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* OSC 52 policy: `copy` allows programs to WRITE the clipboard (the common TUI
|
|
86
|
+
* case), `read` allows a program to READ it back (a `?` request), `both` allows
|
|
87
|
+
* either, `false` disables OSC 52 entirely. Read is the real exfiltration risk
|
|
88
|
+
* (arbitrary terminal output could siphon your clipboard), so it must be opted
|
|
89
|
+
* into explicitly.
|
|
90
|
+
*/
|
|
91
|
+
type Osc52Mode = "copy" | "read" | "both" | false;
|
|
92
|
+
/** A parsed OSC 52 request: which selection buffer, and write-a-payload vs read-request. */
|
|
93
|
+
interface Osc52Request {
|
|
94
|
+
/** The `Pc` field — clipboard selection(s), e.g. `"c"` (clipboard), `"p"` (primary). */
|
|
95
|
+
selection: string;
|
|
96
|
+
kind: "write" | "read";
|
|
97
|
+
/** Decoded text for a write; `""` for a read request. */
|
|
98
|
+
data: string;
|
|
99
|
+
}
|
|
100
|
+
/** UTF-8-safe base64 encode (browser `btoa` operates on binary strings). */
|
|
101
|
+
declare function encodeBase64(text: string): string;
|
|
102
|
+
/** UTF-8-safe base64 decode; returns `""` on malformed input. */
|
|
103
|
+
declare function decodeBase64(b64: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Parse an OSC 52 payload (the part after `ESC ] 52 ;`, i.e. `<Pc> ; <Pd>`).
|
|
106
|
+
* `Pd === "?"` is a read request; otherwise `Pd` is base64 to write. Returns
|
|
107
|
+
* `null` for a malformed payload (no `;`).
|
|
108
|
+
*/
|
|
109
|
+
declare function parseOsc52(payload: string): Osc52Request | null;
|
|
110
|
+
/** Build the OSC 52 response a `read` request expects: `ESC ] 52 ; <Pc> ; <base64> BEL`. */
|
|
111
|
+
declare function osc52Response(selection: string, text: string): string;
|
|
112
|
+
/**
|
|
113
|
+
* Register an OSC 52 handler on an xterm instance, routing writes/reads through
|
|
114
|
+
* `provider` per `mode`. Returns the {@link IDisposable} (or `null` when
|
|
115
|
+
* `mode === false`). The handler always returns `true` (consumed) so a
|
|
116
|
+
* disallowed direction is swallowed rather than echoed as garbage.
|
|
117
|
+
*/
|
|
118
|
+
declare function registerOsc52(term: Terminal$1, provider: () => ClipboardProvider, mode: () => Osc52Mode): IDisposable | null;
|
|
119
|
+
|
|
120
|
+
/** Context handed to the menu at right-click time + to custom item builders. */
|
|
121
|
+
interface TerminalContextMenuContext {
|
|
122
|
+
/** Whether there is a non-empty text selection. */
|
|
123
|
+
hasSelection: boolean;
|
|
124
|
+
/** The selected text (or ""). */
|
|
125
|
+
selection: string;
|
|
126
|
+
/** Whether the terminal is read-only (paste / clear are dropped by default). */
|
|
127
|
+
readOnly: boolean;
|
|
128
|
+
}
|
|
129
|
+
/** A single menu entry. `onSelect` runs on click; set `separator` for a divider. */
|
|
130
|
+
interface TerminalContextMenuItem {
|
|
131
|
+
/** Stable id (used as the React key + a `data-menu-item` handle). */
|
|
132
|
+
id: string;
|
|
133
|
+
/** Visible label. Ignored for separators. */
|
|
134
|
+
label?: string;
|
|
135
|
+
/** Optional leading glyph / emoji / short string (JSON-friendly — no ReactNode). */
|
|
136
|
+
icon?: string;
|
|
137
|
+
/** Greyed-out + non-interactive. */
|
|
138
|
+
disabled?: boolean;
|
|
139
|
+
/** Render a divider instead of a clickable item. */
|
|
140
|
+
separator?: boolean;
|
|
141
|
+
/** Invoked on click with the menu context. */
|
|
142
|
+
onSelect?: (ctx: TerminalContextMenuContext) => void;
|
|
143
|
+
}
|
|
144
|
+
/** The actions the built-in items dispatch to (wired by the component). */
|
|
145
|
+
interface TerminalMenuActions {
|
|
146
|
+
copy: () => void;
|
|
147
|
+
paste: () => void;
|
|
148
|
+
selectAll: () => void;
|
|
149
|
+
clear: () => void;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The `contextMenu` prop shape:
|
|
153
|
+
* - `false` — disable the menu entirely.
|
|
154
|
+
* - `true` / omitted — the default Copy / Paste / Select all / Clear menu.
|
|
155
|
+
* - an array — replace the default items wholesale.
|
|
156
|
+
* - a function — receives the context **and the default items** so you can
|
|
157
|
+
* add/remove/reorder (e.g. append a "Send to agent" item).
|
|
158
|
+
*/
|
|
159
|
+
type TerminalContextMenuConfig = boolean | TerminalContextMenuItem[] | ((ctx: TerminalContextMenuContext, defaults: TerminalContextMenuItem[]) => TerminalContextMenuItem[]);
|
|
160
|
+
/** The built-in Copy / Paste / Select all / Clear items, contextual to selection + readOnly. */
|
|
161
|
+
declare function defaultMenuItems(ctx: TerminalContextMenuContext, actions: TerminalMenuActions): TerminalContextMenuItem[];
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the configured menu into the final item list, or `null` when disabled.
|
|
164
|
+
* Empty results (e.g. a function that filtered everything out) also resolve to
|
|
165
|
+
* `null` so the caller can skip opening an empty menu.
|
|
166
|
+
*/
|
|
167
|
+
declare function resolveMenuItems(config: TerminalContextMenuConfig | undefined, ctx: TerminalContextMenuContext, actions: TerminalMenuActions): TerminalContextMenuItem[] | null;
|
|
4
168
|
|
|
5
169
|
/** xterm.js color theme (re-exported for ergonomic typing). */
|
|
6
170
|
type TerminalTheme = ITheme;
|
|
@@ -33,6 +197,13 @@ interface ShellProfile {
|
|
|
33
197
|
interface TerminalHandle {
|
|
34
198
|
/** The underlying xterm.js instance — escape hatch for addons / advanced use. Null before mount. */
|
|
35
199
|
readonly xterm: Terminal$1 | null;
|
|
200
|
+
/**
|
|
201
|
+
* Resolves with the xterm instance once it's opened + measured. Because
|
|
202
|
+
* `xterm` is null until the container lays out, a consumer that must wire an
|
|
203
|
+
* addon can `await handle.ready` instead of polling. Re-armed if the terminal
|
|
204
|
+
* is torn down + recreated (e.g. a container change).
|
|
205
|
+
*/
|
|
206
|
+
readonly ready: Promise<Terminal$1>;
|
|
36
207
|
/** Write raw data (ANSI escape sequences honored) to the terminal. */
|
|
37
208
|
write: (data: string) => void;
|
|
38
209
|
/** Write data followed by CRLF. */
|
|
@@ -49,6 +220,14 @@ interface TerminalHandle {
|
|
|
49
220
|
getBuffer: () => string;
|
|
50
221
|
/** The current text selection, or "" when nothing is selected. */
|
|
51
222
|
getSelection: () => string;
|
|
223
|
+
/** Copy the current selection to the system clipboard. Resolves false when there's nothing selected or the clipboard is unavailable. */
|
|
224
|
+
copySelection: () => Promise<boolean>;
|
|
225
|
+
/** Paste text into the terminal. With no argument, reads the system clipboard. Honors xterm's bracketed-paste mode. */
|
|
226
|
+
paste: (text?: string) => Promise<void>;
|
|
227
|
+
/** Select the entire buffer. */
|
|
228
|
+
selectAll: () => void;
|
|
229
|
+
/** Clear the current selection. */
|
|
230
|
+
clearSelection: () => void;
|
|
52
231
|
/**
|
|
53
232
|
* Switch the active shell by id. Resolves the matching {@link ShellProfile}
|
|
54
233
|
* from `shells` and fires `onShellChange`; in uncontrolled mode it also updates
|
|
@@ -88,8 +267,50 @@ interface TerminalOptions {
|
|
|
88
267
|
cols: number;
|
|
89
268
|
rows: number;
|
|
90
269
|
}) => void;
|
|
270
|
+
/**
|
|
271
|
+
* Clipboard wiring — gates the Ctrl+Shift+C / Cmd+C copy chord, the paste
|
|
272
|
+
* interceptor ({@link onPaste}), the context-menu copy/paste, and OSC 52.
|
|
273
|
+
*
|
|
274
|
+
* - `true` / omitted — enabled, backed by `navigator.clipboard`.
|
|
275
|
+
* - `false` — disabled (no copy chord / OSC 52; native text paste still works).
|
|
276
|
+
* - a `{ writeText, readText }` **provider** — every copy/paste path routes
|
|
277
|
+
* through it. Supply this in a sandboxed Electron renderer, where
|
|
278
|
+
* `navigator.clipboard` silently no-ops, to bridge to the main-process
|
|
279
|
+
* clipboard over IPC.
|
|
280
|
+
*/
|
|
281
|
+
clipboard?: ClipboardOption;
|
|
282
|
+
/**
|
|
283
|
+
* OSC 52 clipboard policy — lets terminal programs (Claude Code, tmux, vim)
|
|
284
|
+
* set/read the system clipboard via `ESC ] 52`. `"copy"` (default) allows
|
|
285
|
+
* writes only; `"read"` / `"both"` also answer read requests (an exfiltration
|
|
286
|
+
* risk — opt in deliberately); `false` disables it. Routed through the same
|
|
287
|
+
* clipboard provider as {@link clipboard}; a no-op when clipboard is `false`.
|
|
288
|
+
* Default `"copy"`.
|
|
289
|
+
*/
|
|
290
|
+
osc52?: Osc52Mode;
|
|
291
|
+
/**
|
|
292
|
+
* Copy/paste UX convention: `"contextmenu"` (menu + Ctrl+Shift+C),
|
|
293
|
+
* `"linux"` (highlight-to-copy + middle-click paste), or `"winmac"`
|
|
294
|
+
* (Ctrl/Cmd+C copies the selection, Ctrl/Cmd+V pastes). Omit for the historical
|
|
295
|
+
* default (Ctrl+Shift+C + Cmd+C-with-selection). Ctrl+Shift+C always copies.
|
|
296
|
+
*/
|
|
297
|
+
copyPaste?: CopyPasteMode;
|
|
298
|
+
/**
|
|
299
|
+
* Called once the xterm instance is opened + attached — the imperative twin of
|
|
300
|
+
* {@link TerminalHandle.ready}. Use it to load an addon without racing the
|
|
301
|
+
* container layout (where `handle.xterm` is still null).
|
|
302
|
+
*/
|
|
303
|
+
onReady?: (xterm: Terminal$1) => void;
|
|
304
|
+
/**
|
|
305
|
+
* Fired on every paste with the clipboard payload — `{ text, files, images }`.
|
|
306
|
+
* Plain text still pastes into the terminal natively; this is where a host
|
|
307
|
+
* receives pasted **images** (a shell can't render them) to upload / hand to an
|
|
308
|
+
* agent / write a path. Return `false` to fully consume the paste (suppress the
|
|
309
|
+
* default text paste — e.g. to transform it, then call `handle.paste(...)`).
|
|
310
|
+
*/
|
|
311
|
+
onPaste?: (payload: ClipboardPayload) => void | boolean;
|
|
91
312
|
}
|
|
92
|
-
interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElement>, "onInput" | "onResize" | "children"> {
|
|
313
|
+
interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElement>, "onInput" | "onResize" | "onPaste" | "contextMenu" | "children"> {
|
|
93
314
|
/**
|
|
94
315
|
* The controlled output buffer. The component diffs against the previous value
|
|
95
316
|
* and writes only the appended delta, so a host can drive the terminal from
|
|
@@ -112,6 +333,13 @@ interface TerminalProps extends TerminalOptions, Omit<HTMLAttributes<HTMLDivElem
|
|
|
112
333
|
* `shells` is provided. Default `false` — opt-in so existing layout is unchanged.
|
|
113
334
|
*/
|
|
114
335
|
showShellBar?: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* The right-click selection context menu. `false` disables it; `true` / omitted
|
|
338
|
+
* gives the default Copy / Paste / Select all / Clear menu; an array replaces
|
|
339
|
+
* the items; a `(ctx, defaults) => items` function lets you add/remove/reorder
|
|
340
|
+
* (e.g. append a "Send to agent" item). See {@link TerminalContextMenuConfig}.
|
|
341
|
+
*/
|
|
342
|
+
contextMenu?: TerminalContextMenuConfig;
|
|
115
343
|
}
|
|
116
344
|
/**
|
|
117
345
|
* Resolve a shell id to its {@link ShellProfile} within a list. Pure helper —
|
|
@@ -169,6 +397,22 @@ interface ShellSwitcherProps {
|
|
|
169
397
|
*/
|
|
170
398
|
declare function ShellSwitcher({ shells, value, onChange, className, style, disabled, }: ShellSwitcherProps): react.JSX.Element;
|
|
171
399
|
|
|
400
|
+
/**
|
|
401
|
+
* The terminal selection context menu. Deliberately plain React + inline styles
|
|
402
|
+
* (fancy-term has no react-fancy dependency) styled to match the shell bar. It
|
|
403
|
+
* closes on click-outside + Escape, and clamps itself inside the viewport on
|
|
404
|
+
* mount so a right-click near an edge never spills off-screen.
|
|
405
|
+
*/
|
|
406
|
+
declare function TerminalContextMenu({ at, items, ctx, onClose, }: {
|
|
407
|
+
at: {
|
|
408
|
+
x: number;
|
|
409
|
+
y: number;
|
|
410
|
+
};
|
|
411
|
+
items: TerminalContextMenuItem[];
|
|
412
|
+
ctx: TerminalContextMenuContext;
|
|
413
|
+
onClose: () => void;
|
|
414
|
+
}): react.JSX.Element;
|
|
415
|
+
|
|
172
416
|
/**
|
|
173
417
|
* Create + own an xterm.js instance for a container, returning a stable
|
|
174
418
|
* {@link TerminalHandle}. The headless engine layer behind `<Terminal>` — use it
|
|
@@ -257,4 +501,40 @@ declare function useTerminalSession(options: UseTerminalSessionOptions): Termina
|
|
|
257
501
|
*/
|
|
258
502
|
declare const fancyDarkTheme: TerminalTheme;
|
|
259
503
|
|
|
260
|
-
|
|
504
|
+
/** The subset of a KeyboardEvent the copy decision reads. */
|
|
505
|
+
interface CopyKeyEvent {
|
|
506
|
+
type: string;
|
|
507
|
+
key: string;
|
|
508
|
+
ctrlKey: boolean;
|
|
509
|
+
shiftKey: boolean;
|
|
510
|
+
metaKey: boolean;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Whether a key event should copy the current selection to the clipboard.
|
|
514
|
+
*
|
|
515
|
+
* - **Ctrl+Shift+C** (Windows / Linux terminal convention) — always.
|
|
516
|
+
* - **Cmd / Meta+C** (macOS) — only when there is a selection.
|
|
517
|
+
* - **Plain Ctrl+C is never a copy** — it must stay SIGINT.
|
|
518
|
+
*
|
|
519
|
+
* Only fires on `keydown`. When this returns true the caller copies the
|
|
520
|
+
* selection and consumes the event (returns `false` from xterm's
|
|
521
|
+
* `attachCustomKeyEventHandler` so no control byte is sent).
|
|
522
|
+
*/
|
|
523
|
+
declare function shouldCopyEvent(e: CopyKeyEvent, hasSelection: boolean): boolean;
|
|
524
|
+
|
|
525
|
+
interface MenuPoint {
|
|
526
|
+
x: number;
|
|
527
|
+
y: number;
|
|
528
|
+
}
|
|
529
|
+
interface MenuSize {
|
|
530
|
+
width: number;
|
|
531
|
+
height: number;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Clamp a menu's top-left so the whole menu stays inside the viewport (minus a
|
|
535
|
+
* margin). Pushes left/up when it would overflow the right/bottom edge, and never
|
|
536
|
+
* goes past the top-left margin. Pure.
|
|
537
|
+
*/
|
|
538
|
+
declare function clampMenuPosition(at: MenuPoint, menu: MenuSize, viewport: MenuSize, margin?: number): MenuPoint;
|
|
539
|
+
|
|
540
|
+
export { BUILTIN_SHELLS, type ClipboardOption, type ClipboardPayload, type ClipboardProvider, type CopyKeyEvent, type CopyPasteBehavior, type CopyPasteMode, type CursorStyle, type KeyEventLike, type Osc52Mode, type Osc52Request, type ShellProfile, ShellSwitcher, type ShellSwitcherProps, Terminal, TerminalContextMenu, type TerminalContextMenuConfig, type TerminalContextMenuContext, type TerminalContextMenuItem, type TerminalHandle, type TerminalMenuActions, type TerminalOptions, type TerminalProps, type TerminalSessionApi, type TerminalSessionTransport, type TerminalTheme, type UseTerminalSessionOptions, clampMenuPosition, copyPasteBehavior, decodeBase64, defaultMenuItems, encodeBase64, fancyDarkTheme, isImageFile, navigatorClipboard, osc52Response, parseOsc52, registerOsc52, resolveClipboard, resolveKeyAction, resolveMenuItems, resolveShell, shouldCopyEvent, useTerminal, useTerminalFit, useTerminalSession };
|