@particle-academy/fancy-term 0.2.1 → 0.3.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 +54 -0
- package/dist/index.cjs +288 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -2
- package/dist/index.d.ts +146 -2
- package/dist/index.js +285 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,9 @@ fancy-query's `useFancyStream`.
|
|
|
77
77
|
| `activeShell` | `string` | controlled selected shell id (omit for uncontrolled) |
|
|
78
78
|
| `onShellChange` | `(id, profile) => void` | fired when the user / `setShell` switches |
|
|
79
79
|
| `showShellBar` | `boolean` | render the `<ShellSwitcher>` toolbar above the surface (default `false`) |
|
|
80
|
+
| `clipboard` | `boolean` | enable the copy chord + paste interceptor (default `true`) |
|
|
81
|
+
| `onPaste` | `(payload) => void \| boolean` | every paste; receives `{ text, files, images }` — handle pasted **images** here. Return `false` to suppress the native text paste |
|
|
82
|
+
| `contextMenu` | `false \| Item[] \| (ctx, defaults) => Item[]` | the right-click selection menu (see [Clipboard & context menu](#clipboard--context-menu)) |
|
|
80
83
|
|
|
81
84
|
The ref exposes a `TerminalHandle`:
|
|
82
85
|
|
|
@@ -85,11 +88,56 @@ const term = useRef<TerminalHandle>(null);
|
|
|
85
88
|
// term.current.write / writeln / clear / reset / fit / focus
|
|
86
89
|
// term.current.getBuffer() → the visible buffer as text (what an agent "sees")
|
|
87
90
|
// term.current.getSelection() → current selection
|
|
91
|
+
// term.current.copySelection() → copy the selection to the system clipboard (Promise<boolean>)
|
|
92
|
+
// term.current.paste(text?) → paste text (or the system clipboard) into the terminal
|
|
93
|
+
// term.current.selectAll() / clearSelection()
|
|
88
94
|
// term.current.setShell("pwsh") → switch the active shell (fires onShellChange)
|
|
89
95
|
// term.current.getShell() → the active shell id
|
|
90
96
|
// term.current.xterm → the raw xterm.js instance (escape hatch)
|
|
91
97
|
```
|
|
92
98
|
|
|
99
|
+
## Clipboard & context menu
|
|
100
|
+
|
|
101
|
+
**Copy** — select text, then **Ctrl+Shift+C** (Windows/Linux) or **Cmd+C** (macOS,
|
|
102
|
+
with a selection) copies to the system clipboard. Plain **Ctrl+C** is never
|
|
103
|
+
intercepted — it stays **SIGINT**.
|
|
104
|
+
|
|
105
|
+
**Paste** — text pastes natively. **Images** can't render in a shell, so a pasted
|
|
106
|
+
image is handed to you via `onPaste` to upload / feed an agent / write a path:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Terminal
|
|
110
|
+
output={out}
|
|
111
|
+
onData={(d) => backend.send(d)}
|
|
112
|
+
onPaste={({ text, images }) => {
|
|
113
|
+
for (const img of images) upload(img).then((url) => backend.send(url));
|
|
114
|
+
// return false here to also suppress the native text paste
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Context menu** — right-click shows Copy / Paste / Select all / Clear by default.
|
|
120
|
+
Customize it with the `contextMenu` prop:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
// disable it
|
|
124
|
+
<Terminal contextMenu={false} … />
|
|
125
|
+
|
|
126
|
+
// add to the defaults (the function gets the default items)
|
|
127
|
+
<Terminal
|
|
128
|
+
contextMenu={(ctx, defaults) => [
|
|
129
|
+
...defaults,
|
|
130
|
+
{ id: "sep", separator: true },
|
|
131
|
+
{ id: "send-agent", label: "Send selection to agent", icon: "🤖",
|
|
132
|
+
disabled: !ctx.hasSelection, onSelect: (c) => agent.send(c.selection) },
|
|
133
|
+
]}
|
|
134
|
+
…
|
|
135
|
+
/>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Each item is `{ id, label?, icon?, disabled?, separator?, onSelect?(ctx) }`. Set
|
|
139
|
+
`clipboard={false}` to turn off the copy chord + image-paste interception entirely.
|
|
140
|
+
|
|
93
141
|
## Switching shells
|
|
94
142
|
|
|
95
143
|
fancy-term is a **frontend wrapper** — it never spawns a shell. It owns the
|
|
@@ -220,3 +268,9 @@ mutations so every write broadcasts `AgentActivity`, and supports a staged
|
|
|
220
268
|
## License
|
|
221
269
|
|
|
222
270
|
MIT
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## ⭐ Star Fancy UI
|
|
275
|
+
|
|
276
|
+
If this package is useful to you, a quick ⭐ on the repo really helps us build a better kit. Thank you!
|
package/dist/index.cjs
CHANGED
|
@@ -52,6 +52,77 @@ function useTerminalFit(ref, fit, enabled = true) {
|
|
|
52
52
|
}, [ref, enabled]);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// src/clipboard.ts
|
|
56
|
+
function isImageFile(f) {
|
|
57
|
+
return typeof f.type === "string" && f.type.startsWith("image/");
|
|
58
|
+
}
|
|
59
|
+
function readDataTransfer(dt) {
|
|
60
|
+
const files = [];
|
|
61
|
+
let text = "";
|
|
62
|
+
if (dt) {
|
|
63
|
+
try {
|
|
64
|
+
text = dt.getData("text/plain") ?? "";
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
const items = dt.items;
|
|
68
|
+
if (items) {
|
|
69
|
+
for (let i = 0; i < items.length; i++) {
|
|
70
|
+
const it = items[i];
|
|
71
|
+
if (it && it.kind === "file") {
|
|
72
|
+
const f = it.getAsFile();
|
|
73
|
+
if (f) files.push(f);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (files.length === 0 && dt.files) {
|
|
78
|
+
for (let i = 0; i < dt.files.length; i++) files.push(dt.files[i]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { text, files, images: files.filter(isImageFile) };
|
|
82
|
+
}
|
|
83
|
+
async function writeClipboardText(text) {
|
|
84
|
+
try {
|
|
85
|
+
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
86
|
+
if (nav?.clipboard?.writeText) {
|
|
87
|
+
await nav.clipboard.writeText(text);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
if (typeof document === "undefined") return false;
|
|
94
|
+
const ta = document.createElement("textarea");
|
|
95
|
+
ta.value = text;
|
|
96
|
+
ta.style.position = "fixed";
|
|
97
|
+
ta.style.left = "-9999px";
|
|
98
|
+
ta.style.opacity = "0";
|
|
99
|
+
document.body.appendChild(ta);
|
|
100
|
+
ta.select();
|
|
101
|
+
const ok = document.execCommand("copy");
|
|
102
|
+
document.body.removeChild(ta);
|
|
103
|
+
return ok;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function readClipboardText() {
|
|
109
|
+
try {
|
|
110
|
+
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
111
|
+
if (nav?.clipboard?.readText) return await nav.clipboard.readText();
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/copy-keybinding.ts
|
|
118
|
+
function shouldCopyEvent(e, hasSelection) {
|
|
119
|
+
if (e.type !== "keydown") return false;
|
|
120
|
+
if (e.key !== "c" && e.key !== "C") return false;
|
|
121
|
+
if (e.ctrlKey && e.shiftKey && !e.metaKey) return true;
|
|
122
|
+
if (e.metaKey && !e.ctrlKey && hasSelection) return true;
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
55
126
|
// src/hooks/use-terminal.ts
|
|
56
127
|
var DEFAULT_FONT = 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace';
|
|
57
128
|
function readBuffer(term) {
|
|
@@ -94,6 +165,17 @@ function useTerminal(containerRef, options = {}) {
|
|
|
94
165
|
focus: () => xtermRef.current?.focus(),
|
|
95
166
|
getBuffer: () => readBuffer(xtermRef.current),
|
|
96
167
|
getSelection: () => xtermRef.current?.getSelection() ?? "",
|
|
168
|
+
copySelection: async () => {
|
|
169
|
+
const sel = xtermRef.current?.getSelection() ?? "";
|
|
170
|
+
if (!sel) return false;
|
|
171
|
+
return writeClipboardText(sel);
|
|
172
|
+
},
|
|
173
|
+
paste: async (text) => {
|
|
174
|
+
const data = text ?? await readClipboardText();
|
|
175
|
+
if (data) xtermRef.current?.paste(data);
|
|
176
|
+
},
|
|
177
|
+
selectAll: () => xtermRef.current?.selectAll(),
|
|
178
|
+
clearSelection: () => xtermRef.current?.clearSelection(),
|
|
97
179
|
// Shell selection is owned by the <Terminal> component layer (it needs the
|
|
98
180
|
// `shells` list + onShellChange). The headless engine is shell-agnostic, so
|
|
99
181
|
// these are no-ops here and get overridden when <Terminal> composes them.
|
|
@@ -109,8 +191,11 @@ function useTerminal(containerRef, options = {}) {
|
|
|
109
191
|
const o = optsRef.current;
|
|
110
192
|
const term = new xterm.Terminal({
|
|
111
193
|
theme: o.theme ?? fancyDarkTheme,
|
|
112
|
-
rows
|
|
113
|
-
cols
|
|
194
|
+
// Only pass rows/cols when explicitly set. xterm validates these and
|
|
195
|
+
// console-errors "rows/cols must be numeric" if handed `undefined`, rather
|
|
196
|
+
// than falling back to its 80×24 default. Omit the keys instead.
|
|
197
|
+
...typeof o.rows === "number" ? { rows: o.rows } : {},
|
|
198
|
+
...typeof o.cols === "number" ? { cols: o.cols } : {},
|
|
114
199
|
disableStdin: o.readOnly ?? false,
|
|
115
200
|
cursorBlink: o.cursorBlink ?? true,
|
|
116
201
|
cursorStyle: o.cursorStyle ?? "block",
|
|
@@ -128,9 +213,29 @@ function useTerminal(containerRef, options = {}) {
|
|
|
128
213
|
const resizeSub = term.onResize(
|
|
129
214
|
({ cols, rows }) => optsRef.current.onResize?.({ cols, rows })
|
|
130
215
|
);
|
|
216
|
+
term.attachCustomKeyEventHandler((e) => {
|
|
217
|
+
if (optsRef.current.clipboard === false) return true;
|
|
218
|
+
if (shouldCopyEvent(e, term.hasSelection())) {
|
|
219
|
+
void writeClipboardText(term.getSelection());
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
});
|
|
224
|
+
const onPasteEvent = (e) => {
|
|
225
|
+
const o2 = optsRef.current;
|
|
226
|
+
if (o2.clipboard === false) return;
|
|
227
|
+
if (o2.readOnly) {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const payload = readDataTransfer(e.clipboardData);
|
|
232
|
+
if (o2.onPaste?.(payload) === false) e.preventDefault();
|
|
233
|
+
};
|
|
234
|
+
el.addEventListener("paste", onPasteEvent, true);
|
|
131
235
|
if (o.initialOutput) term.write(o.initialOutput);
|
|
132
236
|
if (o.fit ?? true) handle.fit();
|
|
133
237
|
return () => {
|
|
238
|
+
el.removeEventListener("paste", onPasteEvent, true);
|
|
134
239
|
dataSub.dispose();
|
|
135
240
|
resizeSub.dispose();
|
|
136
241
|
term.dispose();
|
|
@@ -361,6 +466,122 @@ function ShellSwitcher({
|
|
|
361
466
|
);
|
|
362
467
|
}
|
|
363
468
|
|
|
469
|
+
// src/menu-position.ts
|
|
470
|
+
function clampMenuPosition(at, menu, viewport, margin = 8) {
|
|
471
|
+
const maxX = Math.max(margin, viewport.width - menu.width - margin);
|
|
472
|
+
const maxY = Math.max(margin, viewport.height - menu.height - margin);
|
|
473
|
+
return {
|
|
474
|
+
x: Math.min(Math.max(margin, at.x), maxX),
|
|
475
|
+
y: Math.min(Math.max(margin, at.y), maxY)
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function TerminalContextMenu({
|
|
479
|
+
at,
|
|
480
|
+
items,
|
|
481
|
+
ctx,
|
|
482
|
+
onClose
|
|
483
|
+
}) {
|
|
484
|
+
const ref = react.useRef(null);
|
|
485
|
+
const [pos, setPos] = react.useState(at);
|
|
486
|
+
react.useLayoutEffect(() => {
|
|
487
|
+
const el = ref.current;
|
|
488
|
+
if (!el || typeof window === "undefined") return;
|
|
489
|
+
const r = el.getBoundingClientRect();
|
|
490
|
+
setPos(
|
|
491
|
+
clampMenuPosition(
|
|
492
|
+
at,
|
|
493
|
+
{ width: r.width, height: r.height },
|
|
494
|
+
{ width: window.innerWidth, height: window.innerHeight }
|
|
495
|
+
)
|
|
496
|
+
);
|
|
497
|
+
}, [at]);
|
|
498
|
+
react.useEffect(() => {
|
|
499
|
+
const onDown = (e) => {
|
|
500
|
+
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
501
|
+
};
|
|
502
|
+
const onKey = (e) => {
|
|
503
|
+
if (e.key === "Escape") onClose();
|
|
504
|
+
};
|
|
505
|
+
document.addEventListener("mousedown", onDown, true);
|
|
506
|
+
document.addEventListener("keydown", onKey, true);
|
|
507
|
+
return () => {
|
|
508
|
+
document.removeEventListener("mousedown", onDown, true);
|
|
509
|
+
document.removeEventListener("keydown", onKey, true);
|
|
510
|
+
};
|
|
511
|
+
}, [onClose]);
|
|
512
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
513
|
+
"div",
|
|
514
|
+
{
|
|
515
|
+
ref,
|
|
516
|
+
"data-fancy-terminal-menu": "",
|
|
517
|
+
role: "menu",
|
|
518
|
+
style: {
|
|
519
|
+
position: "fixed",
|
|
520
|
+
left: pos.x,
|
|
521
|
+
top: pos.y,
|
|
522
|
+
zIndex: 2147483e3,
|
|
523
|
+
minWidth: 176,
|
|
524
|
+
padding: 4,
|
|
525
|
+
background: "#18181b",
|
|
526
|
+
border: "1px solid #3f3f46",
|
|
527
|
+
borderRadius: 8,
|
|
528
|
+
boxShadow: "0 10px 32px -8px rgba(0,0,0,.6)",
|
|
529
|
+
color: "#e4e4e7",
|
|
530
|
+
font: '13px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
531
|
+
userSelect: "none"
|
|
532
|
+
},
|
|
533
|
+
children: items.map(
|
|
534
|
+
(it) => it.separator ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
535
|
+
"div",
|
|
536
|
+
{
|
|
537
|
+
role: "separator",
|
|
538
|
+
style: { height: 1, margin: "4px 6px", background: "#3f3f46" }
|
|
539
|
+
},
|
|
540
|
+
it.id
|
|
541
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
542
|
+
"button",
|
|
543
|
+
{
|
|
544
|
+
type: "button",
|
|
545
|
+
role: "menuitem",
|
|
546
|
+
"data-menu-item": it.id,
|
|
547
|
+
disabled: it.disabled,
|
|
548
|
+
onClick: () => {
|
|
549
|
+
if (it.disabled) return;
|
|
550
|
+
it.onSelect?.(ctx);
|
|
551
|
+
onClose();
|
|
552
|
+
},
|
|
553
|
+
onMouseEnter: (e) => {
|
|
554
|
+
if (!it.disabled) e.currentTarget.style.background = "#27272a";
|
|
555
|
+
},
|
|
556
|
+
onMouseLeave: (e) => {
|
|
557
|
+
e.currentTarget.style.background = "transparent";
|
|
558
|
+
},
|
|
559
|
+
style: {
|
|
560
|
+
display: "flex",
|
|
561
|
+
alignItems: "center",
|
|
562
|
+
gap: 9,
|
|
563
|
+
width: "100%",
|
|
564
|
+
textAlign: "left",
|
|
565
|
+
padding: "6px 9px",
|
|
566
|
+
border: "none",
|
|
567
|
+
borderRadius: 5,
|
|
568
|
+
background: "transparent",
|
|
569
|
+
color: it.disabled ? "#52525b" : "inherit",
|
|
570
|
+
cursor: it.disabled ? "default" : "pointer",
|
|
571
|
+
font: "inherit"
|
|
572
|
+
},
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 16, textAlign: "center", opacity: 0.8 }, children: it.icon ?? "" }),
|
|
575
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 }, children: it.label })
|
|
576
|
+
]
|
|
577
|
+
},
|
|
578
|
+
it.id
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
364
585
|
// src/output-diff.ts
|
|
365
586
|
function diffOutput(written, next) {
|
|
366
587
|
if (next === written) return null;
|
|
@@ -377,6 +598,28 @@ function decideShellSelect(shells, current, id) {
|
|
|
377
598
|
function decideShellSwitch(current, next) {
|
|
378
599
|
return { changed: current !== next };
|
|
379
600
|
}
|
|
601
|
+
|
|
602
|
+
// src/context-menu.ts
|
|
603
|
+
function defaultMenuItems(ctx, actions) {
|
|
604
|
+
const items = [
|
|
605
|
+
{ id: "copy", label: "Copy", icon: "\u29C9", disabled: !ctx.hasSelection, onSelect: actions.copy }
|
|
606
|
+
];
|
|
607
|
+
if (!ctx.readOnly) {
|
|
608
|
+
items.push({ id: "paste", label: "Paste", icon: "\u2398", onSelect: actions.paste });
|
|
609
|
+
}
|
|
610
|
+
items.push({ id: "select-all", label: "Select all", onSelect: actions.selectAll });
|
|
611
|
+
if (!ctx.readOnly) {
|
|
612
|
+
items.push({ id: "sep-default", separator: true });
|
|
613
|
+
items.push({ id: "clear", label: "Clear", onSelect: actions.clear });
|
|
614
|
+
}
|
|
615
|
+
return items;
|
|
616
|
+
}
|
|
617
|
+
function resolveMenuItems(config, ctx, actions) {
|
|
618
|
+
if (config === false) return null;
|
|
619
|
+
const defaults = defaultMenuItems(ctx, actions);
|
|
620
|
+
const items = config === void 0 || config === true ? defaults : Array.isArray(config) ? config : config(ctx, defaults);
|
|
621
|
+
return items.length > 0 ? items : null;
|
|
622
|
+
}
|
|
380
623
|
var Terminal = react.forwardRef(function Terminal2({
|
|
381
624
|
output,
|
|
382
625
|
theme,
|
|
@@ -396,6 +639,9 @@ var Terminal = react.forwardRef(function Terminal2({
|
|
|
396
639
|
activeShell,
|
|
397
640
|
onShellChange,
|
|
398
641
|
showShellBar = false,
|
|
642
|
+
clipboard,
|
|
643
|
+
onPaste,
|
|
644
|
+
contextMenu,
|
|
399
645
|
className,
|
|
400
646
|
style,
|
|
401
647
|
...rest
|
|
@@ -428,7 +674,9 @@ var Terminal = react.forwardRef(function Terminal2({
|
|
|
428
674
|
// diffing effect below owns the buffer, so don't double-write.
|
|
429
675
|
initialOutput: output === void 0 ? initialOutput : void 0,
|
|
430
676
|
onData,
|
|
431
|
-
onResize
|
|
677
|
+
onResize,
|
|
678
|
+
clipboard,
|
|
679
|
+
onPaste
|
|
432
680
|
});
|
|
433
681
|
react.useImperativeHandle(
|
|
434
682
|
ref,
|
|
@@ -448,18 +696,46 @@ var Terminal = react.forwardRef(function Terminal2({
|
|
|
448
696
|
handle.write(change.write);
|
|
449
697
|
written.current = output;
|
|
450
698
|
}, [output, handle]);
|
|
699
|
+
const [menu, setMenu] = react.useState(null);
|
|
700
|
+
const openMenu = react.useCallback(
|
|
701
|
+
(e) => {
|
|
702
|
+
if (contextMenu === false) return;
|
|
703
|
+
e.preventDefault();
|
|
704
|
+
const selection = handle.getSelection();
|
|
705
|
+
const ctx = {
|
|
706
|
+
hasSelection: selection.length > 0,
|
|
707
|
+
selection,
|
|
708
|
+
readOnly: !!readOnly
|
|
709
|
+
};
|
|
710
|
+
const items = resolveMenuItems(contextMenu, ctx, {
|
|
711
|
+
copy: () => void handle.copySelection(),
|
|
712
|
+
paste: () => void handle.paste(),
|
|
713
|
+
selectAll: () => handle.selectAll(),
|
|
714
|
+
clear: () => handle.clear()
|
|
715
|
+
});
|
|
716
|
+
if (items) setMenu({ at: { x: e.clientX, y: e.clientY }, items, ctx });
|
|
717
|
+
},
|
|
718
|
+
[contextMenu, handle, readOnly]
|
|
719
|
+
);
|
|
720
|
+
const menuNode = menu ? /* @__PURE__ */ jsxRuntime.jsx(TerminalContextMenu, { at: menu.at, items: menu.items, ctx: menu.ctx, onClose: () => setMenu(null) }) : null;
|
|
451
721
|
const surface = /* @__PURE__ */ jsxRuntime.jsx(
|
|
452
722
|
"div",
|
|
453
723
|
{
|
|
454
724
|
ref: containerRef,
|
|
455
725
|
"data-fancy-terminal": "",
|
|
726
|
+
onContextMenu: openMenu,
|
|
456
727
|
"data-readonly": readOnly ? "" : void 0,
|
|
457
728
|
"data-fancy-terminal-shell": shellId ?? void 0,
|
|
458
729
|
style: showShellBar && shells ? { width: "100%", flex: 1, minHeight: 0 } : { width: "100%", height: "100%", ...style },
|
|
459
730
|
...showShellBar && shells ? {} : rest
|
|
460
731
|
}
|
|
461
732
|
);
|
|
462
|
-
if (!showShellBar || !shells)
|
|
733
|
+
if (!showShellBar || !shells) {
|
|
734
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
735
|
+
surface,
|
|
736
|
+
menuNode
|
|
737
|
+
] });
|
|
738
|
+
}
|
|
463
739
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
464
740
|
"div",
|
|
465
741
|
{
|
|
@@ -490,7 +766,8 @@ var Terminal = react.forwardRef(function Terminal2({
|
|
|
490
766
|
)
|
|
491
767
|
}
|
|
492
768
|
),
|
|
493
|
-
surface
|
|
769
|
+
surface,
|
|
770
|
+
menuNode
|
|
494
771
|
]
|
|
495
772
|
}
|
|
496
773
|
);
|
|
@@ -542,8 +819,14 @@ function useTerminalSession(options) {
|
|
|
542
819
|
exports.BUILTIN_SHELLS = BUILTIN_SHELLS;
|
|
543
820
|
exports.ShellSwitcher = ShellSwitcher;
|
|
544
821
|
exports.Terminal = Terminal;
|
|
822
|
+
exports.TerminalContextMenu = TerminalContextMenu;
|
|
823
|
+
exports.clampMenuPosition = clampMenuPosition;
|
|
824
|
+
exports.defaultMenuItems = defaultMenuItems;
|
|
545
825
|
exports.fancyDarkTheme = fancyDarkTheme;
|
|
826
|
+
exports.isImageFile = isImageFile;
|
|
827
|
+
exports.resolveMenuItems = resolveMenuItems;
|
|
546
828
|
exports.resolveShell = resolveShell;
|
|
829
|
+
exports.shouldCopyEvent = shouldCopyEvent;
|
|
547
830
|
exports.useTerminal = useTerminal;
|
|
548
831
|
exports.useTerminalFit = useTerminalFit;
|
|
549
832
|
exports.useTerminalSession = useTerminalSession;
|