Skip to content

Enable mac clipboard via pngpaste (Support unseekable attachments) #971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tjltjl opened this issue May 4, 2025 · 1 comment
Open

Comments

@tjltjl
Copy link

tjltjl commented May 4, 2025

It would be so lovely if this would just work (on macos):

llm -m gemma3:27b "Transcribe to markdown" -a <(pngpaste -)

but unfortunately it doesn't (see below for the error output).

Background: pngpaste outputs what is currently on the clipboard, so pressing shift-cmd-4 and outlining an area (shortcut for "copy part of screen to clipboard as an image") and running the above command would be a pretty neat hack.

The reason for the exception is that the process substitution input file "<()" is not seekable. My proposal: if the input file for -a is not seekable, read it into a buffer for up to 1GB, then use that buffer with bytesio.

I can make a PR if this sounds ok.

Another alternative: read and save the first bytes, use those to do the magic check, and then stream all the rest of the bytes in. If that works for all filetypes, that could even just be the default.

Traceback (most recent call last):
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/puremagic/main.py", line 214, in _file_details
    fin.seek(-max_foot, os.SEEK_END)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
io.UnsupportedOperation: File or stream is not seekable.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/tjl/.local/bin/llm", line 8, in <module>
    sys.exit(cli())
             ~~~^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 1161, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 1082, in main
    rv = self.invoke(ctx)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 1695, in invoke
    sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 949, in make_context
    self.parse_args(ctx, args)
    ~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 1417, in parse_args
    value, args = param.handle_parse_result(ctx, opts, args)
                  ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 2403, in handle_parse_result
    value = self.process_value(ctx, value)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 2359, in process_value
    value = self.type_cast_value(ctx, value)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 2345, in type_cast_value
    return tuple(convert(x) for x in check_iter(value))
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 2345, in <genexpr>
    return tuple(convert(x) for x in check_iter(value))
                 ~~~~~~~^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/core.py", line 2319, in convert
    return self.type(value, param=self, ctx=ctx)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/click/types.py", line 84, in __call__
    return self.convert(value, param, ctx)
           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/llm/cli.py", line 207, in convert
    return resolve_attachment(value)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/llm/cli.py", line 195, in resolve_attachment
    mimetype = mimetype_from_path(str(path))
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/llm/utils.py", line 40, in mimetype_from_path
    type_ = puremagic.from_file(path, mime=True)
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/puremagic/main.py", line 270, in from_file
    head, foot = _file_details(filename)
                 ~~~~~~~~~~~~~^^^^^^^^^^
  File "/Users/tjl/.local/pipx/venvs/llm/lib/python3.13/site-packages/puremagic/main.py", line 216, in _file_details
    fin.seek(0)
    ~~~~~~~~^^^
io.UnsupportedOperation: File or stream is not seekable.
@tjltjl tjltjl changed the title Support unseekable attachments Enable mac clipboard via pngpaste (Support unseekable attachments) May 5, 2025
@tidbeck
Copy link

tidbeck commented May 6, 2025

Piping the output from pngpaste seems to work as expected

pngpaste - | llm -m openai/o1 "Transcribe to markdown" -a -

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants