Metadata-Version: 2.5
Name: jupyterlab_terminal_clipboard_extension
Version: 1.0.9
Summary: Jupyterlab extension to handle copy to clipboard actions in a terminal to allow copy to clipboard of the browser/user
Project-URL: Homepage, https://github.com/stellarshenson/jupyterlab_terminal_clipboard_extension
Project-URL: Bug Tracker, https://github.com/stellarshenson/jupyterlab_terminal_clipboard_extension/issues
Project-URL: Repository, https://github.com/stellarshenson/jupyterlab_terminal_clipboard_extension.git
Author-email: Stellars Henson <konrad.jelen@gmail.com>
License-Expression: BSD-3-Clause
License-File: LICENSE
Keywords: jupyter,jupyterlab,jupyterlab-extension
Classifier: Framework :: Jupyter
Classifier: Framework :: Jupyter :: JupyterLab
Classifier: Framework :: Jupyter :: JupyterLab :: 4
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: jupyter-builder>=1.2.0; extra == 'dev'
Requires-Dist: jupyterlab>=4; extra == 'dev'
Description-Content-Type: text/markdown

# jupyterlab_terminal_clipboard_extension

[![GitHub Actions](https://github.com/stellarshenson/jupyterlab_terminal_clipboard_extension/actions/workflows/build.yml/badge.svg)](https://github.com/stellarshenson/jupyterlab_terminal_clipboard_extension/actions/workflows/build.yml)
[![npm version](https://img.shields.io/npm/v/jupyterlab_terminal_clipboard_extension.svg)](https://www.npmjs.com/package/jupyterlab_terminal_clipboard_extension)
[![PyPI version](https://img.shields.io/pypi/v/jupyterlab-terminal-clipboard-extension.svg)](https://pypi.org/project/jupyterlab-terminal-clipboard-extension/)
[![Total PyPI downloads](https://static.pepy.tech/badge/jupyterlab-terminal-clipboard-extension)](https://pepy.tech/project/jupyterlab-terminal-clipboard-extension)
[![JupyterLab 4](https://img.shields.io/badge/JupyterLab-4-orange.svg)](https://jupyterlab.readthedocs.io/en/stable/)
[![Brought To You By KOLOMOLO](https://img.shields.io/badge/Brought%20To%20You%20By-KOLOMOLO-00ffff?style=flat)](https://kolomolo.com)
[![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-blue?style=flat)](https://www.paypal.com/donate/?hosted_button_id=B4KPBJDLLXTSA)

> [!TIP]
> This extension is part of the [stellars_jupyterlab_extensions](https://github.com/stellarshenson/stellars_jupyterlab_extensions) metapackage. Install all Stellars extensions at once: `pip install stellars_jupyterlab_extensions`

Terminal programs running inside JupyterLab normally cannot copy anything to your clipboard. Tools like password managers, vim, tmux, and other TUI applications that rely on `xclip` or `xsel` fail silently because there is no X11 display in a browser-based terminal. This extension fixes that - when a terminal program copies something, it lands in your browser clipboard so you can paste it anywhere.

## Features

- **OSC 52 clipboard interception** - captures clipboard escape sequences from terminal output
- **Auto-installing shims** - deploys `xclip`, `xsel`, `wl-copy` replacements to `~/.local/bin/` on startup when real tools are not functional (no display server)
- **Non-destructive install** - only installs shims when the real tools cannot work, and never overwrites a clipboard tool it did not create

## Installation

Requires JupyterLab 4.0.0 or higher.

```bash
pip install jupyterlab_terminal_clipboard_extension
```

Ensure `~/.local/bin` is in your PATH for the shims to work:

```bash
export PATH="$HOME/.local/bin:$PATH"
```

## Uninstall

To remove the extension, execute:

```bash
pip uninstall jupyterlab_terminal_clipboard_extension
```

## Technical Details

The extension has two components. The **frontend plugin** registers an OSC 52 parser handler on each terminal's xterm.js instance. When a terminal application writes `\033]52;c;<base64>\a`, the handler decodes the payload and writes it to the browser clipboard via `navigator.clipboard.writeText()`, falling back to `document.execCommand('copy')` for HTTP (non-secure) contexts.

Both of those paths need a focused document, and a terminal program emits OSC 52 whenever it likes - routinely while you are in another window. A copy that arrives unfocused is therefore held rather than attempted, and written when focus returns. One copy is held at a time, the newest wins, and a copy that waits longer than 15 seconds is discarded rather than landing on top of something you have copied elsewhere in the meantime. Every outcome is logged, so a copy is never lost silently.

The held copy is written from a `focus` or `visibilitychange` event, which Chrome accepts for a focused tab. Firefox requires a genuine user gesture for clipboard writes and refuses this one, logging the refusal - re-run the command with the tab focused to copy there.

The **server plugin** runs on JupyterLab startup and installs lightweight POSIX shell shims into `~/.local/bin/`. These shims replace `xclip`, `xsel`, and `wl-copy` - the system clipboard tools that terminal programs call. Instead of talking to an X11 display, the shims base64-encode the input and emit an OSC 52 escape sequence to `/dev/tty`, which the frontend plugin then intercepts.

```
terminal app -> xclip/xsel (shim) -> OSC 52 escape sequence -> extension -> browser clipboard
```

Shims are only installed when the real tools cannot function (no `DISPLAY` or `WAYLAND_DISPLAY` environment variable set). If a real X11 or Wayland clipboard is available, the shims are not deployed and the original tools are preserved. An existing file in `~/.local/bin` that is not one of our shims is never overwritten - the installer logs a warning and skips it, so a clipboard tool you placed there yourself stays intact.

Because OSC 52 only reaches the browser through the terminal, the shims write the escape sequence exclusively to `/dev/tty`. When a program calls them without a controlling terminal, they exit non-zero with a message on stderr rather than emitting the sequence to stdout, where it would corrupt the caller's output.

## Contributing

If you would like to contribute to this extension, please refer to the [Contributing Guide](CONTRIBUTING.md).
