tacklebox-cli 0.2.0__tar.gz → 0.2.2__tar.gz

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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tacklebox-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A small collection of CLI utilities.
5
5
  Project-URL: Homepage, https://c.csw.im/cswimr/tacklebox
6
6
  Project-URL: Issues, https://c.csw.im/cswimr/tacklebox/issues
7
- Project-URL: source_archive, https://c.csw.im/cswimr/tacklebox/archive/eae45cbaba330c3a410206e3ac4737e1f3f53e27.tar.gz
7
+ Project-URL: source_archive, https://c.csw.im/cswimr/tacklebox/archive/a338ec567b0358a40fa7ca3bd3504309a5046ba1.tar.gz
8
8
  Author-email: cswimr <seaswimmerthefsh@gmail.com>
9
9
  License: The MIT License (MIT)
10
10
  =====================
@@ -54,12 +54,13 @@ tacklebox-cli offers a suite of useful CLI tools.
54
54
 
55
55
  ## Usage
56
56
 
57
- ### tacklebox clip
57
+ ### tacklebox copy / paste
58
58
 
59
- Cross-platform clipboard copying tool. Uses `wl-copy`, `xclip`, or `xsel` on Linux, `pbcopy` on MacOS, `clip` on Windows, and [OSC 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) escape codes when operating over SSH or when no other tools are available.
59
+ Cross-platform clipboard management tool. Uses system tools such as `wl-copy` on Linux Wayland or `clip.exe` on Windows, and [OSC 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) escape codes when **copying** over SSH or when no other tools are available. See [`copy_with_tooling()`](https://c.csw.im/cswimr/tacklebox/src/branch/main/tacklebox/commands/clipboard.py) for all supported tools.
60
60
 
61
61
  ```bash
62
- echo "a" | tr -d '\n' | tacklebox clip
62
+ $ echo "a" | tacklebox copy --trim && tacklebox paste
63
+ a
63
64
  ```
64
65
 
65
66
  ### tacklebox spectacle (Linux only)
@@ -67,10 +68,16 @@ echo "a" | tr -d '\n' | tacklebox clip
67
68
  Uses the [zipline.py](https://pypi.org/project/zipline-py/) library alongside KDE's [Spectacle](https://invent.kde.org/plasma/spectacle) application to take a screenshot or screen recording and automatically upload it to a [Zipline](https://github.com/diced/zipline) instance. This automatically reads Spectacle's configuration files to determine file formats.
68
69
 
69
70
  ```bash
70
- tacklebox spectacle | tr -d '\n' | tacklebox clip
71
+ $ tacklebox spectacle \
72
+ --server "https://zipline.example.com" \
73
+ --token "$(cat /file/containing/zipline/token)" \
74
+ | tacklebox copy --trim
71
75
 
72
76
  # or to record a video
73
- tacklebox spectacle --record | tr -d '\n' | tacklebox clip
77
+ $ tacklebox spectacle --record \
78
+ --server "https://zipline.example.com" \
79
+ --token "$(cat /file/containing/zipline/token)" \
80
+ | tacklebox copy --trim
74
81
  ```
75
82
 
76
83
  ### tacklebox zipline
@@ -8,12 +8,13 @@ tacklebox-cli offers a suite of useful CLI tools.
8
8
 
9
9
  ## Usage
10
10
 
11
- ### tacklebox clip
11
+ ### tacklebox copy / paste
12
12
 
13
- Cross-platform clipboard copying tool. Uses `wl-copy`, `xclip`, or `xsel` on Linux, `pbcopy` on MacOS, `clip` on Windows, and [OSC 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) escape codes when operating over SSH or when no other tools are available.
13
+ Cross-platform clipboard management tool. Uses system tools such as `wl-copy` on Linux Wayland or `clip.exe` on Windows, and [OSC 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) escape codes when **copying** over SSH or when no other tools are available. See [`copy_with_tooling()`](https://c.csw.im/cswimr/tacklebox/src/branch/main/tacklebox/commands/clipboard.py) for all supported tools.
14
14
 
15
15
  ```bash
16
- echo "a" | tr -d '\n' | tacklebox clip
16
+ $ echo "a" | tacklebox copy --trim && tacklebox paste
17
+ a
17
18
  ```
18
19
 
19
20
  ### tacklebox spectacle (Linux only)
@@ -21,10 +22,16 @@ echo "a" | tr -d '\n' | tacklebox clip
21
22
  Uses the [zipline.py](https://pypi.org/project/zipline-py/) library alongside KDE's [Spectacle](https://invent.kde.org/plasma/spectacle) application to take a screenshot or screen recording and automatically upload it to a [Zipline](https://github.com/diced/zipline) instance. This automatically reads Spectacle's configuration files to determine file formats.
22
23
 
23
24
  ```bash
24
- tacklebox spectacle | tr -d '\n' | tacklebox clip
25
+ $ tacklebox spectacle \
26
+ --server "https://zipline.example.com" \
27
+ --token "$(cat /file/containing/zipline/token)" \
28
+ | tacklebox copy --trim
25
29
 
26
30
  # or to record a video
27
- tacklebox spectacle --record | tr -d '\n' | tacklebox clip
31
+ $ tacklebox spectacle --record \
32
+ --server "https://zipline.example.com" \
33
+ --token "$(cat /file/containing/zipline/token)" \
34
+ | tacklebox copy --trim
28
35
  ```
29
36
 
30
37
  ### tacklebox zipline
@@ -0,0 +1,318 @@
1
+ import os
2
+ import platform
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ from base64 import b64encode
7
+ from enum import Enum
8
+ from typing import Annotated, Literal
9
+
10
+ from typer import Argument, Exit, Option, Typer, echo
11
+
12
+ from tacklebox.utils import get_environment
13
+
14
+ app = Typer()
15
+
16
+
17
+ class ClipboardMode(str, Enum):
18
+ COPY = "Copied"
19
+ PASTE = "Pasted"
20
+
21
+
22
+ def _try_command(mode: ClipboardMode, cmd: list[str], verbose: bool = False, data: str | None = None) -> tuple[bool, str | None]:
23
+ """Attempt to run a command using subprocess.
24
+
25
+ Args:
26
+ mode (ClipboardMode): Determines some stdout stuff.
27
+ cmd (list[str]): The command to invoke.
28
+ verbose (bool, optional): Print some additional information during execution. Defaults to False.
29
+ data (str, optional): The data to send the process after it is invoked.
30
+
31
+ Returns:
32
+ bool: Whether or not the command was successful.
33
+ """
34
+ if shutil.which(cmd[0]) is None:
35
+ if verbose:
36
+ echo(f"{cmd[0]} not found in PATH.", err=True)
37
+ return False, None
38
+
39
+ if cmd[0] == "clip.exe":
40
+ encoder = "utf-16le"
41
+ else:
42
+ encoder = "utf-8"
43
+
44
+ try:
45
+ result = subprocess.run(
46
+ cmd,
47
+ input=data.encode(encoder) if data else None,
48
+ stderr=None if verbose else subprocess.DEVNULL,
49
+ env=get_environment(),
50
+ timeout=30,
51
+ )
52
+ if result.returncode == 0:
53
+ if verbose:
54
+ echo(f"{mode.value} using {' '.join(cmd)}", err=True)
55
+ return True, result.stdout.decode(encoder) if result.stdout else None
56
+ except subprocess.TimeoutExpired:
57
+ if verbose:
58
+ echo(f"{cmd[0]} timed out", err=True)
59
+ except OSError as e:
60
+ if verbose:
61
+ echo(f"{cmd[0]} execution failed: {e}", err=True)
62
+ return False, None
63
+
64
+
65
+ def use_tooling(
66
+ mode: ClipboardMode, command: list[str] | None = None, verbose: bool = False, data: str | None = None
67
+ ) -> tuple[bool, str | None]:
68
+ """Attempt to manipulate the system clipboard using system tools.
69
+
70
+ This function uses the following tools, and will try them in the order stated:
71
+ - The content of the `command` argument.
72
+ - If `mode == ClipboardMode.COPY`:
73
+ - Linux (Wayland):
74
+ - `wl-copy`
75
+ - `copyq add -`
76
+ - Linux (X11):
77
+ - `xclip -selection clipboard`
78
+ - `xsel --clipboard --input`
79
+ - `copyq add -`
80
+ - MacOS:
81
+ - `reattach-to-user-namespace pbcopy`
82
+ - `pbcopy`
83
+ - Windows:
84
+ - `win32yank.exe -i`
85
+ - `clip.exe`
86
+ - If `mode == ClipboardMode.PASTE`:
87
+ - Linux (Wayland):
88
+ - `wl-paste --no-newline`
89
+ - `copyq read 0`
90
+ - Linux (X11):
91
+ - `xclip -selection clipboard -o`
92
+ - `xsel --clipboard --output`
93
+ - `copyq read 0`
94
+ - MacOS:
95
+ - `reattach-to-user-namespace pbpaste`
96
+ - `pbpaste`
97
+ - Windows:
98
+ - `win32yank.exe -o`
99
+
100
+ Args:
101
+ mode (ClipboardMode): Whether or not to copy or paste.
102
+ command (list[str] | None): A user-provided command to try before running anything else.
103
+ verbose (bool, optional): Prints some extra information during execution. Defaults to False.
104
+ data (str, optional): The data to copy to the system clipboard, when `mode == ClipboardMode.COPY`.
105
+
106
+ Returns:
107
+ bool: Whether or not copying was successful.
108
+ str: The content that was in the clipboard.
109
+ """
110
+ if command:
111
+ if success := _try_command(mode, command, verbose, data):
112
+ return success
113
+
114
+ system = platform.system().lower()
115
+
116
+ tools: dict[ClipboardMode, dict[str, list[list[str]]]] = {
117
+ ClipboardMode.COPY: {
118
+ "wayland": [["wl-copy"], ["copyq", "add", "-"]],
119
+ "x11": [["xclip", "-selection", "clipboard"], ["xsel", "--clipboard", "--input"], ["copyq", "add", "-"]],
120
+ "darwin": [["reattach-to-user-namespace", "pbcopy"], ["pbcopy"]],
121
+ "windows": [["win32yank.exe", "-i"], ["clip.exe"]],
122
+ },
123
+ ClipboardMode.PASTE: {
124
+ "wayland": [["wl-paste", "--no-newline"], ["copyq", "read", "0"]],
125
+ "x11": [["xclip", "-selection", "clipboard", "-o"], ["xsel", "--clipboard", "--output"], ["copyq", "read", "0"]],
126
+ "darwin": [["reattach-to-user-namespace", "pbpaste"], ["pbpaste"]],
127
+ "windows": [["win32yank.exe", "-o"]],
128
+ },
129
+ }
130
+
131
+ commands: list[list[str]] = []
132
+ match system:
133
+ case "linux":
134
+ in_wsl = False
135
+ try:
136
+ with open("/proc/version", "r") as pv:
137
+ in_wsl = "microsoft" in pv.read().lower()
138
+ except Exception:
139
+ in_wsl = False
140
+
141
+ if in_wsl:
142
+ if verbose:
143
+ echo("Detected Windows Subsystem for Linux.", err=True)
144
+ commands.extend(tools[mode]["windows"])
145
+
146
+ protocol: Literal["wayland", "x11"] | None = (
147
+ "wayland" if "WAYLAND_DISPLAY" in get_environment() else ("x11" if "DISPLAY" in get_environment() else None)
148
+ )
149
+ if verbose:
150
+ echo(f"Detected display protocol: {protocol}", err=True)
151
+
152
+ match protocol:
153
+ case "wayland":
154
+ commands.extend(tools[mode]["wayland"])
155
+ case "x11":
156
+ commands.extend(tools[mode]["x11"])
157
+ case _:
158
+ if not in_wsl:
159
+ if verbose:
160
+ echo(
161
+ "Unknown display protocol: neither WAYLAND_DISPLAY nor DISPLAY set.",
162
+ err=True,
163
+ )
164
+ return False, None
165
+
166
+ case "darwin":
167
+ commands.extend(tools[mode]["darwin"])
168
+ case "windows":
169
+ commands.extend(tools[mode]["windows"])
170
+ case _:
171
+ if verbose:
172
+ echo(f"No suitable clipboard tool known for platform '{system}'", err=True)
173
+ return False, None
174
+
175
+ if verbose:
176
+ echo("\nAttempting commands:\n" + "\n".join(" ".join(cmd) for cmd in commands) + "\n", err=True)
177
+ for cmd in commands:
178
+ if (success := _try_command(mode, cmd, verbose, data))[0]:
179
+ return success
180
+ return False, None
181
+
182
+
183
+ def encode_osc52(data: str, verbose: bool = False) -> str:
184
+ """Encode a string into an [OCS 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) string, supporting tmux and screen as well.
185
+
186
+ Args:
187
+ data (str): The data to encode.
188
+ verbose (bool, optional): Print additional information during execution. Defaults to False.
189
+
190
+ Returns:
191
+ str: The OSC 52 (& base64) encoded data.
192
+ """
193
+ b64_data = b64encode(data.encode("utf-8")).decode("ascii")
194
+ osc_seq = f"\x1b]52;c;{b64_data}\x07"
195
+
196
+ if "TMUX" in os.environ:
197
+ if verbose:
198
+ echo("Wrapping OSC 52 for tmux.")
199
+ return f"\x1bPtmux;\x1b{osc_seq}\x1b\\"
200
+ elif os.environ.get("TERM", "").startswith("screen"):
201
+ if verbose:
202
+ echo("Wrapping OSC 52 for screen.")
203
+ return f"\x1bP{osc_seq}\x1b\\"
204
+ else:
205
+ if verbose:
206
+ echo("Using plain OSC 52.")
207
+ return osc_seq
208
+
209
+
210
+ def _maybe_print_environment_information(verbose: bool) -> None:
211
+ if verbose:
212
+ echo(
213
+ (
214
+ f"Platform: {platform.system()}\n"
215
+ f"TERM: {os.environ.get('TERM')}\n"
216
+ f"TMUX: {'present' if 'TMUX' in os.environ else 'absent'}\n"
217
+ f"SCREEN: {'present' if os.environ.get('TERM', '').startswith('screen') else 'absent'}"
218
+ ),
219
+ err=True,
220
+ )
221
+
222
+
223
+ @app.command("copy")
224
+ @app.command("clip", deprecated=True, hidden=True, epilog="Consider using `tacklebox copy` instead.")
225
+ def copy(
226
+ data: Annotated[
227
+ str | None,
228
+ Argument(
229
+ help="The data to copy to the clipboard. Reads from stdin if this is not provided.",
230
+ ),
231
+ ] = None,
232
+ trim: Annotated[
233
+ bool, Option(..., "--trim", "-t", help="Remove trailing newlines from the input before copying it to the clipboard.")
234
+ ] = False,
235
+ copy_command: Annotated[
236
+ str | None, Option(..., "--copy-command", "-c", help="A command to try first instead of the hardcoded system defaults.")
237
+ ] = None,
238
+ verbose: Annotated[
239
+ bool,
240
+ Option(
241
+ ...,
242
+ "--verbose",
243
+ "-v",
244
+ help="Print some additional information during execution.",
245
+ ),
246
+ ] = False,
247
+ ) -> None:
248
+ """Copy to the system clipboard."""
249
+ if not data:
250
+ data = sys.stdin.read()
251
+
252
+ if not data:
253
+ echo("No input received from stdin.", err=True)
254
+ raise Exit(code=1)
255
+
256
+ if trim:
257
+ data = data.rstrip("\r\n")
258
+
259
+ _maybe_print_environment_information(verbose)
260
+
261
+ command = None
262
+ if copy_command:
263
+ command = copy_command.split(" ")
264
+
265
+ if use_tooling(ClipboardMode.COPY, command, verbose, data):
266
+ return
267
+
268
+ if verbose:
269
+ echo("Clipboard tools failed; trying OSC 52...", err=True)
270
+
271
+ osc = encode_osc52(data, verbose)
272
+ try:
273
+ with open("/dev/tty", "w") as tty:
274
+ tty.write(osc)
275
+ if verbose:
276
+ echo("Copied using OSC 52.")
277
+ except Exception as e:
278
+ echo(f"OSC 52 failed: {e}", err=True)
279
+ raise Exit(code=1)
280
+
281
+
282
+ @app.command("paste")
283
+ def paste(
284
+ paste_command: Annotated[
285
+ str | None, Option(..., "--paste-command", "-c", help="A command to try first instead of the hardcoded system defaults.")
286
+ ] = None,
287
+ trim: Annotated[bool, Option(..., "--trim", "-t", help="Remove trailing newlines from the clipboard entry before pasting it.")] = False,
288
+ verbose: Annotated[
289
+ bool,
290
+ Option(
291
+ ...,
292
+ "--verbose",
293
+ "-v",
294
+ help=("Print some additional information during execution. This WILL mangle the output, so only use this for debugging."),
295
+ ),
296
+ ] = False,
297
+ ) -> None:
298
+ """Paste from the system clipboard."""
299
+ _maybe_print_environment_information(verbose)
300
+
301
+ command = None
302
+ if paste_command:
303
+ command = paste_command.split(" ")
304
+
305
+ if (output := use_tooling(ClipboardMode.PASTE, command, verbose))[0]:
306
+ string = output[1]
307
+ if trim and string is not None:
308
+ string = string.rstrip("\r\n")
309
+
310
+ echo(string)
311
+ return
312
+
313
+ echo(f"Paste command failed!\n{output}", err=True)
314
+ exit(code=1)
315
+
316
+
317
+ if __name__ == "__main__":
318
+ app()
@@ -95,8 +95,8 @@ async def spectacle(
95
95
  default_factory=sys.stdout.isatty,
96
96
  help=(
97
97
  "Choose how to format the output. If --text (or piped),\n"
98
- "youll get a link to the uploaded file; if --object (or on a TTY),\n"
99
- "youll get the raw Python object."
98
+ "you'll get a link to the uploaded file; if --object (or on a TTY),\n"
99
+ "you'll get the raw Python object."
100
100
  ),
101
101
  ),
102
102
  ],
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.0'
21
- __version_tuple__ = version_tuple = (0, 2, 0)
20
+ __version__ = version = '0.2.2'
21
+ __version_tuple__ = version_tuple = (0, 2, 2)
@@ -1,237 +0,0 @@
1
- import os
2
- import platform
3
- import shutil
4
- import subprocess
5
- import sys
6
- from base64 import b64encode
7
- from typing import Annotated, Literal
8
-
9
- from typer import Argument, Exit, Option, Typer, echo
10
-
11
- from tacklebox.utils import get_environment
12
-
13
- app = Typer()
14
-
15
-
16
- def _try_command(data: str, cmd: list[str], verbose: bool = False) -> bool:
17
- """Attempt to run a command using subprocess.
18
-
19
- Args:
20
- data (str): The data to send the process after it is invoked.
21
- cmd (list[str]): The command to invoke.
22
- verbose (bool, optional): Print some additional information during execution. Defaults to False.
23
-
24
- Returns:
25
- bool: Whether or not the command was successful.
26
- """
27
- if shutil.which(cmd[0]) is None:
28
- if verbose:
29
- echo(f"{cmd[0]} not found in PATH.", err=True)
30
- return False
31
-
32
- if cmd[0] == "clip.exe":
33
- encoder = "utf-16le"
34
- else:
35
- encoder = "utf-8"
36
-
37
- try:
38
- result = subprocess.run(
39
- cmd,
40
- input=data.encode(encoder),
41
- stdout=(None if verbose else subprocess.DEVNULL),
42
- stderr=(None if verbose else subprocess.DEVNULL),
43
- env=get_environment(),
44
- timeout=30,
45
- )
46
- if result.returncode == 0:
47
- if verbose:
48
- echo(f"Copied using {' '.join(cmd)}")
49
- return True
50
- except subprocess.TimeoutExpired:
51
- if verbose:
52
- echo(f"{cmd[0]} timed out", err=True)
53
- except OSError as e:
54
- if verbose:
55
- echo(f"{cmd[0]} execution failed: {e}", err=True)
56
- return False
57
-
58
-
59
- def copy_with_tooling(data: str, copy_command: list[str] | None = None, verbose: bool = False) -> bool:
60
- """Attempt to copy the input data to the system clipboard using system tools.
61
-
62
- This function uses the following tools, and will try them in the order stated:
63
- - The content of the `copy_command` argument.
64
- - Linux (Wayland):
65
- - `wl-copy`
66
- - `copyq add -`
67
- - Linux (X11):
68
- - `xclip -selection clipboard`
69
- - `xsel --clipboard --input`
70
- - `copyq add -`
71
- - MacOS:
72
- - `reattach-to-user-namespace pbcopy`
73
- - `pbcopy`
74
- - Windows:
75
- - `clip.exe`
76
-
77
- Args:
78
- data (str): The data to copy to the system clipboard.
79
- copy_command (list[str] | None): A user-provided command to try before running anything else.
80
- verbose (bool, optional): Prints some extra information during execution. Defaults to False.
81
-
82
- Returns:
83
- bool: Whether or not copying was successful.
84
- """
85
- if copy_command:
86
- if success := _try_command(data, copy_command, verbose):
87
- return success
88
-
89
- system = platform.system().lower()
90
-
91
- tools: dict[str, list[list[str]]] = {
92
- "wayland": [["wl-copy"], ["copyq", "add", "-"]],
93
- "x11": [["xclip", "-selection", "clipboard"], ["xsel", "--clipboard", "--input"], ["copyq", "add", "-"]],
94
- "darwin": [["reattach-to-user-namespace", "pbcopy"], ["pbcopy"]],
95
- "windows": [["clip.exe"]],
96
- }
97
-
98
- commands: list[list[str]] = []
99
- match system:
100
- case "linux":
101
- in_wsl = False
102
- try:
103
- with open("/proc/version", "r") as pv:
104
- in_wsl = "microsoft" in pv.read().lower()
105
- except Exception:
106
- in_wsl = False
107
-
108
- if in_wsl:
109
- if verbose:
110
- echo("Detected Windows Subsystem for Linux.")
111
- commands.extend(tools["windows"])
112
-
113
- protocol: Literal["wayland", "x11"] | None = (
114
- "wayland" if "WAYLAND_DISPLAY" in get_environment() else ("x11" if "DISPLAY" in get_environment() else None)
115
- )
116
- if verbose:
117
- echo(f"Detected display protocol: {protocol}")
118
-
119
- match protocol:
120
- case "wayland":
121
- commands.extend(tools["wayland"])
122
- case "x11":
123
- commands.extend(tools["x11"])
124
- case _:
125
- if verbose:
126
- echo(
127
- "Unknown display protocol: neither WAYLAND_DISPLAY nor DISPLAY set.",
128
- err=True,
129
- )
130
- return False
131
- case "darwin":
132
- commands.extend(tools["darwin"])
133
- case "windows":
134
- commands.extend(tools["windows"])
135
- case _:
136
- if verbose:
137
- echo(f"No suitable clipboard tool found for platform '{system}'", err=True)
138
- return False
139
-
140
- for cmd in commands:
141
- if success := _try_command(data, cmd, verbose):
142
- return success
143
- return False
144
-
145
-
146
- def encode_osc52(data: str, verbose: bool = False) -> str:
147
- """Encode a string into an [OCS 52](https://www.reddit.com/r/vim/comments/k1ydpn/a_guide_on_how_to_copy_text_from_anywhere/) string, supporting tmux and screen as well.
148
-
149
- Args:
150
- data (str): The data to encode.
151
- verbose (bool, optional): Print additional information during execution. Defaults to False.
152
-
153
- Returns:
154
- str: The OSC 52 (& base64) encoded data.
155
- """
156
- b64_data = b64encode(data.encode("utf-8")).decode("ascii")
157
- osc_seq = f"\x1b]52;c;{b64_data}\x07"
158
-
159
- if "TMUX" in os.environ:
160
- if verbose:
161
- echo("Wrapping OSC 52 for tmux.")
162
- return f"\x1bPtmux;\x1b{osc_seq}\x1b\\"
163
- elif os.environ.get("TERM", "").startswith("screen"):
164
- if verbose:
165
- echo("Wrapping OSC 52 for screen.")
166
- return f"\x1bP{osc_seq}\x1b\\"
167
- else:
168
- if verbose:
169
- echo("Using plain OSC 52.")
170
- return osc_seq
171
-
172
-
173
- @app.command("clip")
174
- def clipboard(
175
- data: Annotated[
176
- str | None,
177
- Argument(
178
- help="The data to copy to the clipboard. Reads from stdin if this is not provided.",
179
- ),
180
- ] = None,
181
- copy_command: Annotated[str | None, Option(..., "--copy-command", "-c", help="A command to try first instead of the hardcoded system defaults.")] = None,
182
- verbose: Annotated[
183
- bool,
184
- Option(
185
- ...,
186
- "--verbose",
187
- "-v",
188
- help="Print some additional information during execution.",
189
- ),
190
- ] = False,
191
- ) -> None:
192
- """Read from stdin and copy to the system clipboard using wl-copy or OSC 52."""
193
- if not data:
194
- data = sys.stdin.read()
195
-
196
- if not data:
197
- echo("No input received from stdin.", err=True)
198
- raise Exit(code=1)
199
-
200
- if verbose:
201
- echo(
202
- message="\n".join(
203
- (
204
- f"Platform: {platform.system()}",
205
- f"TERM: {os.environ.get('TERM')}",
206
- f"TMUX: {'present' if 'TMUX' in os.environ else 'absent'}",
207
- f"SCREEN: {'present' if os.environ.get('TERM', '').startswith('screen') else 'absent'}",
208
- )
209
- )
210
- )
211
-
212
- command = None
213
- if copy_command:
214
- command = copy_command.split(" ")
215
-
216
- if success := copy_with_tooling(data, command, verbose):
217
- return
218
-
219
- ssh = "SSH_CONNECTION" in os.environ
220
-
221
- if ssh or not success:
222
- if verbose:
223
- echo("Clipboard tools failed; trying OSC 52...", err=True)
224
-
225
- osc = encode_osc52(data, verbose)
226
- try:
227
- with open("/dev/tty", "w") as tty:
228
- tty.write(osc)
229
- if verbose:
230
- echo("Copied using OSC 52.")
231
- except Exception as e:
232
- echo(f"OSC 52 failed: {e}", err=True)
233
- raise Exit(code=1)
234
-
235
-
236
- if __name__ == "__main__":
237
- app()
File without changes
File without changes