# `Drafter.Clipboard`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/clipboard.ex#L1)

Read and write the user's clipboard.

## Copying

`copy/2` writes an OSC 52 sequence to the terminal the session is attached to,
and additionally to the local machine's clipboard via `pbcopy`, `clip`,
`wl-copy`, `xclip` or `xsel` when one of those is on `PATH`. For a session
served over ssh or telnet the sequence reaches the connected client's terminal,
so the text lands on the client's clipboard.

Terminals supporting OSC 52 include iTerm2, kitty, WezTerm, foot, Alacritty and
xterm; some ship with it disabled. tmux passes the sequence through only with
`set -g set-clipboard on`. The terminal sends no reply, so `copy/2` returns `:ok`
once the sequence and any local write have been issued and never reports whether
the terminal accepted it.

## Pasting

Text the user pastes with their terminal's paste key arrives as a bracketed
paste, delivered as a `{:bracketed_paste, text}` event. A widget receives it by
declaring `handles: [:paste]` and implementing `handle_paste/2` — see
`Drafter.Widget`. This is the only path that carries text from a remote client.

`paste/0` reads the clipboard of the machine the app process runs on, which for a
remote session is the server rather than the connected user. It shells out to a
local clipboard tool and never issues an OSC 52 read.

## Configuration

    config :drafter, clipboard: false

or `clipboard: false` passed to `Drafter.run/2` makes `copy/2` and `paste/0`
no-ops returning `{:error, :disabled}`, and drops bracketed pastes before any
widget sees them. A keyword list enables the two directions separately:
`clipboard: [copy: true, paste: false]`. Key bindings are configured with
`:clipboard_keys` — see `key/1`.

# `target`

```elixir
@type target() :: :clipboard | :primary
```

# `copy`

```elixir
@spec copy(
  String.t(),
  keyword()
) :: :ok | {:error, :disabled | :too_large}
```

Put `text` on the clipboard.

Options:

  * `:target` — `:clipboard` (default) or `:primary`, the X11 primary selection.

Returns `:ok` once the sequence has been issued, `{:error, :disabled}` when copy
is switched off, and `{:error, :too_large}` when `text` exceeds 74994 bytes, the
most an OSC 52 sequence carries.

# `default_keys`

```elixir
@spec default_keys() :: keyword()
```

The bindings used when `:clipboard_keys` says nothing.

# `enabled?`

```elixir
@spec enabled?() :: boolean()
```

Whether this run may write to the user's clipboard.

# `key`

```elixir
@spec key(atom()) :: {term(), [atom()]} | nil
```

The key bound to `action`, as `{key, modifiers}`, or `nil` if unbound.

Actions are `:copy`, `:cut`, `:paste` and `:select_all`, bound by default to
`ctrl+c`, `ctrl+x`, `ctrl+v` and `ctrl+a`. Override them per run with
`config :drafter, clipboard_keys: [copy: {:y, [:ctrl]}]`, or unbind an action by
giving it `false`.

These bindings are not installed framework-wide: a widget must call this function
(or `key?/3`) to act on them, so a widget that wants `ctrl+c` for something else
simply does not ask.

# `key?`

```elixir
@spec key?(atom(), term(), [atom()]) :: boolean()
```

Whether `key` and `mods` are the binding for `action`.

# `osc52`

```elixir
@spec osc52(String.t(), target()) :: binary()
```

The OSC 52 sequence that puts `text` on `target`.

Exposed for callers that manage their own terminal output.

# `paste`

```elixir
@spec paste() :: {:ok, String.t()} | {:error, :disabled | :unavailable}
```

The clipboard contents of the machine this app process is running on.

For a remote session that is the server, not the connected user; the user's own
clipboard arrives as a bracketed paste instead.

Returns `{:ok, text}`, `{:error, :unavailable}` when no clipboard tool is on
`PATH` or the tool fails, and `{:error, :disabled}` when `enabled?/0` is false.

# `paste_enabled?`

```elixir
@spec paste_enabled?() :: boolean()
```

Whether pasted text is delivered to this run at all.

When false, a bracketed paste is dropped before any widget or app sees it. Set
independently of `enabled?/0` via `config :drafter, clipboard: [paste: false]`.

# `sanitize`

```elixir
@spec sanitize(String.t()) :: String.t()
```

Strip control characters from a pasted string.

Carriage returns and CRLF pairs become newlines. Newlines and tabs survive;
every other codepoint below `0x20`, and `DEL` (`0x7F`), is removed. Call this on
any text taken from a paste before acting on it, so that escape sequences the
text carries cannot be interpreted as keystrokes.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
