Giter Club home page Giter Club logo

pymux's People

Contributors

bobotig avatar chriskuehl avatar dslackw avatar dzosz avatar eas604 avatar fuzion24 avatar haridsv avatar jonathanslenders avatar jvantuyl avatar kseistrup avatar sethwoodworth avatar tony avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pymux's Issues

Traceback upon opening: unexpected keyword argument 'errors'

I installed pymux from github clone taken at 11:55am ET April 30th 2016.

Traceback (most recent call last):
  File "/anaconda/bin/pymux", line 9, in <module>
    load_entry_point('pymux==0.9', 'console_scripts', 'pymux')()
  File "/anaconda/lib/python2.7/site-packages/pymux/entry_points/run_pymux.py", line 135, in run
    Client(socket_name).attach(true_color=true_color)
  File "/anaconda/lib/python2.7/site-packages/pymux/client.py", line 47, in __init__
    self._stdin_reader = PosixStdinReader(sys.stdin.fileno(), errors='replace')
TypeError: __init__() got an unexpected keyword argument 'errors'

Paste in original terminal fails after pymux exit

$ cd /tmp
$ ls
testfile.txt
$ pymux  # now start pymux
$ ^D     # and exit again
$ ls     # now double-click on testfile.txt, then paste the name
testfile.txt
$ cat 0~testfile.txt1~
/bin/ls: cannot access 0~testfile.txt1~: No such file or directory
$ 

The terminal can be restored with a reset.

becomes unresponsive after `C-b .`

I typed C-b . and rather than asking me which window position to move it to, giving an error, or doing nothing, it became unresponsive.

Freeze after a mouse click

Not sure this is a problem on pymux or prompt-toolkit. After I upgrade prompt-toolkit from 1.0.9 to 1.0.10 or 1.0.13, pymux just freeze after I use a mouse click on my terminal, which I must run killall pymux on another terminal. Finally I have to revert prompt-toolkit back to 1.0.9.

edit: after upgrade prompt-toolkit to 1.0.14, it still freeze after a mouse click.

Environment:

  • OS: Fedora 25 x64
  • Desktop: Gnome 3.22.2 on Wayland
  • Terminal: Gnome Terminal 3.22.1
  • Python: PyPy 5.4.0 and Python 3.6.0
  • pymux: 0.13
  • prompt-toolkit (work): 1.0.9
  • prompt-toolkit (freeze): 1.0.10, 1.0.13, 1.0.14

README: In comparison to tmux, Unicode support might be noteworthy

Hi,

after a quick test run I came to notice that (for me) a serious advantage of pymux over tmux is that it seems to more properly support unicode.

tmux has serious problems with non-ascii characters in the command line or window titles. To me, this mostly seems to stem from a lack of awareness on the part of the developers. I had a dive into the code once intending to fix this in tmux, but I quickly got discouraged by the homebrewn UTF-8 implementation they're using everywhere (no trace of other encodings) and the inconsistent use of that (some parts handle UTF-8, some just don't).

On pymux, I could not find any problems entering fullwidth characters encoded as multibyte UTF-8 sequences into command line or window titles.

Best regards

Library support?

You mention in the readme that you may create a library from pymux. How far in the future is that plan?
I'm currently working on a shell ui for jenkins:
https://github.com/dothebart/jsh
and would need something to have parallel builds browseable in a smart way - like a multi window less.
The copy buffer is mentioned to be searcheable with hilighting - can it als work / tail a file?

Exiting a terminal crashes

The minute I enter a command in a terminal, I get the following error when I enter exit (there is no error if I exit right after creating the tab):

Traceback (most recent call last):
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/cache.py", line 32, in get
    return self._data[key]
KeyError: 14

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/main.py", line 517, in run_server
    PipeInput(), DummyCallbacks())
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/eventloop/posix.py", line 162, in run
    t()
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/interface.py", line 324, in redraw
    self._redraw()
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/interface.py", line 344, in _redraw
    self.renderer.render(self, self.layout, is_done=self.is_done)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/renderer.py", line 422, in render
    extended_height=size.rows,
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 398, in write_to_screen
    self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 157, in write_to_screen
    c.write_to_screen(cli, screen, mouse_handlers, WritePosition(xpos, ypos, width, s))
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 885, in write_to_screen
    _ContainerProxy.write_to_screen(self, cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 844, in write_to_screen
    self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 512, in write_to_screen
    fl.content.write_to_screen(cli, screen, mouse_handlers, wp)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 989, in write_to_screen
    _ContainerProxy.write_to_screen(self, cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 844, in write_to_screen
    self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 541, in write_to_screen
    body.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 157, in write_to_screen
    c.write_to_screen(cli, screen, mouse_handlers, WritePosition(xpos, ypos, width, s))
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 977, in write_to_screen
    _ContainerProxy.write_to_screen(self, cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 844, in write_to_screen
    self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 142, in write_to_screen
    sizes = self._divide_heigths(cli, write_position)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 177, in _divide_heigths
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 177, in <listcomp>
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 175, in get_dimension_for_child
    return c.preferred_height(cli, write_position.width, write_position.extended_height)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 254, in preferred_height
    sizes = self._divide_widths(cli, width)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 283, in _divide_widths
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 283, in <listcomp>
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 281, in get_dimension_for_child
    return c.preferred_width(cli, width)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/containers.py", line 977, in preferred_width
    cli, max_available_width - total_margin_width)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/controls.py", line 249, in preferred_width
    text = token_list_to_text(self._get_tokens_cached(cli))
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/controls.py", line 239, in _get_tokens_cached
    cli.render_counter, lambda: self.get_tokens(cli))
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/cache.py", line 35, in get
    value = getter_func()
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/prompt_toolkit/layout/controls.py", line 239, in <lambda>
    cli.render_counter, lambda: self.get_tokens(cli))
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/layout.py", line 695, in get_title_tokens
    if arrangement_pane.name:
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/arrangement.py", line 91, in name
    name = self.process.get_name()
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/process.py", line 336, in get_name
    return get_name_for_fd(self.master)
  File "/remote/vgrnd5/davidbr/soft/anaconda3/lib/python3.4/site-packages/pymux/process.py", line 429, in get_name_for_fd
    pgrp = os.tcgetpgrp(fd)
OSError: [Errno 0] Error

Using in a third party software

Note: This is not a bug, but an inquire on how to use this as a lib.

I'm building a software where I would like to run two commands in a terminal, which should be split in two vertically, and each command output should be displayed in one of the sides.

Would you say it is possible to do that with pymux? Which moving parts of your software should I use? Could you share some pseudo-code please? Thanks!

Backport BetterScreen into pyte

Currently BetterScreen duplicates a lot of pyte.Screen functionality. Clearly duplication is no good when it comes to software, as the bugs need to be fixed independently in multiple copies of the code. The same goes for other maintenance changes like performance optimizations.

I would very much like to make Screen extensible so there is no need for duplication. Would you be interested in such a PR?

AttributeError: 'BetterStream' object has no attribute 'percent'

I get this traceback when pymux is installed with python2.7 and python3.5. This is on Debian Stretch.

It looks like pyte's version 0.6.0 release removed Stream.percent.

Traceback (most recent call last):
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/main.py", line 538, in run_server
    PipeInput(), DummyCallbacks())
  File "/home/seth/.local/lib/python3.5/site-packages/prompt_toolkit/eventloop/posix.py", line 164, in run
    t()
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/server.py", line 70, in _recv
    self._process(self._recv_buffer[:pos])
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/server.py", line 113, in _process
    self._create_cli(true_color=true_color, ansi_colors_only=ansi_colors_only, term=term)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/server.py", line 157, in _create_cli
    self.cli = self.pymux.create_cli(self, output, input)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/main.py", line 459, in create_cli
    self.create_window(cli, command=self.startup_command)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/main.py", line 311, in create_window
    pane = self._create_pane(None, command, start_directory=start_directory)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/main.py", line 283, in _create_pane
    has_priority=has_priority)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/process.py", line 119, in from_command
    has_priority=has_priority)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/process.py", line 85, in __init__
    self.stream = BetterStream(self.screen)
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/stream.py", line 36, in __init__
    self._validate_screen()
  File "/home/seth/.local/lib/python3.5/site-packages/pymux/stream.py", line 56, in _validate_screen
    for d in [self.basic, self.escape, self.sharp, self.percent, self.csi]:
AttributeError: 'BetterStream' object has no attribute 'percent'

Sharing sessions between users (feature suggestion)

Already mentioned this on reddit (https://www.reddit.com/r/Python/comments/3z9rwt/pymux_a_tmux_clone_in_pure_python/cyklk1q), but I think a really awesome feature for this would be support for sharing session while maintaining unique conf settings for each connected user, something tmux doesn't currently do, even with rogue mode. This would allow users to use different shortcut keys from the host who started the session.

What would be even more awesome (but this may be getting too complex) is keeping track of which user initiated the action or opened the pane. That way the individual panes could inherit the config of the user who opened them (useful to know which .vimrc to load, for example), prevent guests from navigating to areas they shouldn't have permission to access, or even mark the panes themselves as read-only.

FreeBSD breaks on startup

❯ freebsd-version -ku; uname -apKU                    
11.0-CURRENT
11.0-CURRENT
FreeBSD z600 11.0-CURRENT FreeBSD 11.0-CURRENT #40: Sun Jan 10 11:00:46 CST 2016     root@z600:/usr/obj/usr/src/sys/MYKERNEL  amd64 amd64 1100093 1100093
Traceback (most recent call last):
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/main.py", line 500, in run_server
    PipeInput(), DummyCallbacks())
  File "/usr/home/u/.local/lib/python3.5/site-packages/prompt_toolkit/eventloop/posix.py", line 150, in run
    t()
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/server.py", line 56, in _recv
    self._process(self._recv_buffer[:pos])
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/server.py", line 91, in _process
    self._create_cli(true_color=true_color)
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/server.py", line 132, in _create_cli
    self.cli = self.pymux.create_cli(self, output, input)
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/main.py", line 428, in create_cli
    self.create_window(cli, command=self.startup_command)
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/main.py", line 296, in create_window
    pane = self._create_pane(None, command, start_directory=start_directory)
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/main.py", line 277, in _create_pane
    process.start()
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/process.py", line 95, in start
    self._start()
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/process.py", line 130, in _start
    self._in_child()
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/process.py", line 194, in _in_child
    pty_make_controlling_tty(self.slave)
  File "/usr/home/u/.local/lib/python3.5/site-packages/pymux/utils.py", line 66, in pty_make_controlling_tty
    fd = os.open("/dev/tty", os.O_WRONLY)
OSError: [Errno 6] Device not configured: '/dev/tty'

Tag releases

Can you tag (and if possible sign) releases so you make it easier for distribution maintainers download, package and update your software (I have Debian in mind in this case).

Thanks.

Pymux doesn't honor the $TMP or $TMPDIR variables

I use pam_tmpdir in order to have a more secure directory for storing temporary files on a per user basis. The description reads:

Many programs use $TMPDIR for storing temporary files. Not all of them are good at securing the permissions of those files. libpam-tmpdir sets $TMPDIR and $TMP for PAM sessions and sets the permissions quite tight. This helps system security by having an extra layer of security, making such symlink attacks and other /tmp based attacks harder or impossible.

Pymux creates its socket in /tmp, thus ignoring the TMP and TMPDIR variables.

In addition, pymux seems to give this socket permissions 0755. Perhaps it would be more prudent to restrict it to e.g. 0750 (umask 0027)?

Selecting Panes by Number

Hello,

How can I select a pane by number? display-panes doesn't seem to work for that purpose.

Thanks

Missing asyncio_amp.

(venv_pymux) ✓ ~/projects/python/pymux [master|…19] 
09:36 $ python --version && pip list && pymux
Python 3.4.3
asyncio (3.4.3)
docopt (0.6.2)
libpymux (0.1)
pip (6.1.1)
pymux (0.1)
pyte (0.4.9)
setuptools (15.0)
Traceback (most recent call last):
  File "/usr/local/bin/pymux", line 4, in <module>
    __import__('pkg_resources').run_script('pymux==0.1', 'pymux')
  File "/usr/local/lib/python3.4/site-packages/setuptools-15.2-py3.4.egg/pkg_resources/__init__.py", line 729, in run_script
  File "/usr/local/lib/python3.4/site-packages/setuptools-15.2-py3.4.egg/pkg_resources/__init__.py", line 1649, in run_script
  File "/usr/local/lib/python3.4/site-packages/pymux-0.1-py3.4.egg/EGG-INFO/scripts/pymux", line 21, in <module>
  File "/usr/local/lib/python3.4/site-packages/pymux-0.1-py3.4.egg/pymux/socket_client.py", line 5, in <module>
  File "/usr/local/lib/python3.4/site-packages/pymux-0.1-py3.4.egg/pymux/amp_commands.py", line 1, in <module>
ImportError: No module named 'asyncio_amp'

What else do I need to do to use pymux?

Thanks. :-)

No Pane Tilebars?

Hello,

None of my panes have a titlebar. Am I missing a configuration setting?

Thanks

Return key doesn't work in vimwiki

Not sure whether this issue is related to #18

In vimwiki, enter key opens links in new buffer. When running vim inside pymux, enter key instead causes the cursor to jump to the line below (as if j is pressed).

Bug: AttributeError: 'Key' object has no attribute 'encode'

Traceback (most recent call last):
  File "/home/jonathan/git/pymux/pymux/main.py", line 538, in run_server
    PipeInput(), DummyCallbacks())
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/eventloop/posix.py", line 164, in run
    t()
  File "/home/jonathan/git/pymux/pymux/server.py", line 70, in _recv
    self._process(self._recv_buffer[:pos])
  File "/home/jonathan/git/pymux/pymux/server.py", line 91, in _process
    self._inputstream.feed(packet['data'])
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/terminal/vt100_input.py", line 398, in feed
    self._input_parser.send(c)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/terminal/vt100_input.py", line 307, in _input_parser_generator
    self._call_handler(match, prefix)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/terminal/vt100_input.py", line 340, in _call_handler
    self.feed_key_callback(KeyPress(key, insert_text))
  File "/home/jonathan/git/pymux/pymux/server.py", line 36, in feed_key
    self.cli.input_processor.process_keys()
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/key_binding/input_processor.py", line 219, in process_keys
    self._process_coroutine.send(key_press)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/key_binding/input_processor.py", line 176, in _process
    self._call_handler(matches[-1], key_sequence=buffer)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/key_binding/input_processor.py", line 247, in _call_handler
    handler.call(event)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/key_binding/registry.py", line 61, in call
    return self.handler(event)
  File "/home/jonathan/git/pymux/pymux/key_bindings.py", line 134, in _
    p.process.write_key(event.key_sequence[0].key)
  File "/home/jonathan/git/pymux/pymux/process.py", line 257, in write_key
    self.write_input(data)
  File "/home/jonathan/git/pymux/pymux/process.py", line 238, in write_input
    self.write_bytes(data.encode('utf-8'))
AttributeError: 'Key' object has no attribute 'encode'

C-j as prefix causes <Enter> key to be treated as prefix

In tmux, you can set C-j as the prefix, e.g.:

set-option prefix C-j
unbind C-b
bind C-j send-prefix

Somehow tmux is able to distinguish Ctrl+j and <Enter>, but pymux is unable to. In tmux, <Enter> continues to work just fine and Ctrl+j immediately gets recognized as prefix. In pymux, both are treated equally, so to enter a new line, I have to press Ctrl+j or <Enter> twice.

README: In comparison to tmux, entry on multiple clients/single session is inaccurate

Mostly FYI, but an update of the corresponding point in the README might be warranted.

When several clients are attached to the same session, each client can watch a different window. When clients are watching different windows, every client uses the full terminal size.

This is possible in tmux. You attach a second client via tmux new -t $sessionname instead of tmux attach -t $sessionname and set -g aggressive-resize on from your tmux config. This slightly differs in semantics from what is proposed in the README but works to the same effect.

UnicodeDecodeError in vim

$ pymux
$ vim newfile
$ ctrl+b
$ shift+% (vertical split)
$ mouse click on second pane, then click back again
$ crash

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8c in position 4: invalid start byte
Crash occurs on mate-terminal with both pyvim and vim, only when using mouse click to switch the panes. Default configuration.
screenshot
Sorry, pymux did not create a crash file in /tmp, nor in /var/tmp. The only files that were left there were open sockets pymux.sock.<user>.1

how does it work on mac

I've tried my best, but didn't make it yet.
The problem is I create a config file and try to use the key to split my window, it does't work.
By the way, I have test it in iterm and termial that osx provided

Double Exec?

Line 102 of process.py

def execv():
    if before_exec_func:
        before_exec_func()

    for p in os.environ['PATH'].split(':'):
        path = os.path.join(p, command[0])
        if os.path.exists(path) and os.access(path, os.X_OK):
            os.execv(path, command)

Should this not return the os.execv? it seems like it will call it for multiple hits in the path.

Pyte 0.5.1: ImportError: cannot import name ctrl

It seems there is some changes in Pyte, using 70d9901.

Traceback (most recent call last):
  File "/tmp/tpy/bin/pymux", line 9, in <module>
    load_entry_point('pymux==0.5', 'console_scripts', 'pymux')()
  File "/tmp/tpy/lib/python2.7/site-packages/pkg_resources/__init__.py", line 474, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/tmp/tpy/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2582, in load_entry_point
    return ep.load()
  File "/tmp/tpy/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2265, in load
    return self._load()
  File "/tmp/tpy/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2268, in _load
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/tmp/tpy/lib/python2.7/site-packages/pymux/entry_points/run_pymux.py", line 28, in <module>
    from pymux.main import Pymux
  File "/tmp/tpy/lib/python2.7/site-packages/pymux/main.py", line 17, in <module>
    from .arrangement import Arrangement, Pane, Window
  File "/tmp/tpy/lib/python2.7/site-packages/pymux/arrangement.py", line 11, in <module>
    from .process import Process
  File "/tmp/tpy/lib/python2.7/site-packages/pymux/process.py", line 14, in <module>
    from .stream import BetterStream
  File "/tmp/tpy/lib/python2.7/site-packages/pymux/stream.py", line 7, in <module>
    from pyte import ctrl
ImportError: cannot import name ctrl

OS X 10.11 crash on startup

4c56c84

I haven't ruled out if this is due to 10.11's new features.

See http://stackoverflow.com/a/32906318 (OSError: dlopen(libSystem.dylib, 6): image not found)

INFO:pymux:Listening on u'/var/folders/kx/gmc5nl8x2hg370c3nktpc7vm0000gn/T/pymux.sock.me.4'.
INFO:pymux:Client attached.
INFO:pymux:handle command:  <type 'unicode'>.
INFO:pymux:handle command: bind-key '"' split-window -v <type 'unicode'>.
INFO:pymux:handle command: bind-key % split-window -h <type 'unicode'>.
INFO:pymux:handle command: bind-key c new-window <type 'unicode'>.
INFO:pymux:handle command: bind-key Right select-pane -R <type 'unicode'>.
INFO:pymux:handle command: bind-key Left select-pane -L <type 'unicode'>.
INFO:pymux:handle command: bind-key Up select-pane -U <type 'unicode'>.
INFO:pymux:handle command: bind-key Down select-pane -D <type 'unicode'>.
INFO:pymux:handle command: bind-key C-l select-pane -R <type 'unicode'>.
INFO:pymux:handle command: bind-key C-h select-pane -L <type 'unicode'>.
INFO:pymux:handle command: bind-key C-j select-pane -D <type 'unicode'>.
INFO:pymux:handle command: bind-key C-k select-pane -U <type 'unicode'>.
INFO:pymux:handle command: bind-key ; last-pane <type 'unicode'>.
INFO:pymux:handle command: bind-key ! break-pane <type 'unicode'>.
INFO:pymux:handle command: bind-key d detach-client <type 'unicode'>.
INFO:pymux:handle command: bind-key t clock-mode <type 'unicode'>.
INFO:pymux:handle command: bind-key Space next-layout <type 'unicode'>.
INFO:pymux:handle command: bind-key C-z suspend-client <type 'unicode'>.
INFO:pymux:handle command:  <type 'unicode'>.
INFO:pymux:handle command: bind-key z resize-pane -Z <type 'unicode'>.
INFO:pymux:handle command: bind-key k resize-pane -U 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key j resize-pane -D 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key h resize-pane -L 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key l resize-pane -R 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key q display-panes <type 'unicode'>.
INFO:pymux:handle command: bind-key C-Up resize-pane -U 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key C-Down resize-pane -D 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key C-Left resize-pane -L 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key C-Right resize-pane -R 2 <type 'unicode'>.
INFO:pymux:handle command: bind-key M-Up resize-pane -U 5 <type 'unicode'>.
INFO:pymux:handle command: bind-key M-Down resize-pane -D 5 <type 'unicode'>.
INFO:pymux:handle command: bind-key M-Left resize-pane -L 5 <type 'unicode'>.
INFO:pymux:handle command: bind-key M-Right resize-pane -R 5 <type 'unicode'>.
INFO:pymux:handle command:  <type 'unicode'>.
INFO:pymux:handle command: bind-key : command-prompt <type 'unicode'>.
INFO:pymux:handle command: bind-key 0 select-window -t :0 <type 'unicode'>.
INFO:pymux:handle command: bind-key 1 select-window -t :1 <type 'unicode'>.
INFO:pymux:handle command: bind-key 2 select-window -t :2 <type 'unicode'>.
INFO:pymux:handle command: bind-key 3 select-window -t :3 <type 'unicode'>.
INFO:pymux:handle command: bind-key 4 select-window -t :4 <type 'unicode'>.
INFO:pymux:handle command: bind-key 5 select-window -t :5 <type 'unicode'>.
INFO:pymux:handle command: bind-key 6 select-window -t :6 <type 'unicode'>.
INFO:pymux:handle command: bind-key 7 select-window -t :7 <type 'unicode'>.
INFO:pymux:handle command: bind-key 8 select-window -t :8 <type 'unicode'>.
INFO:pymux:handle command: bind-key 9 select-window -t :9 <type 'unicode'>.
INFO:pymux:handle command: bind-key n next-window <type 'unicode'>.
INFO:pymux:handle command: bind-key p previous-window <type 'unicode'>.
INFO:pymux:handle command: bind-key o select-pane -t :.+ <type 'unicode'>.
INFO:pymux:handle command: bind-key { swap-pane -U <type 'unicode'>.
INFO:pymux:handle command: bind-key } swap-pane -D <type 'unicode'>.
INFO:pymux:handle command: bind-key x confirm-before -p "kill-pane #P?" kill-pane <type 'unicode'>.
INFO:pymux:handle command: bind-key & confirm-before -p "kill-window #W?" kill-window <type 'unicode'>.
INFO:pymux:handle command: bind-key C-o rotate-window <type 'unicode'>.
INFO:pymux:handle command: bind-key M-o rotate-window -D <type 'unicode'>.
INFO:pymux:handle command: bind-key C-b send-prefix <type 'unicode'>.
INFO:pymux:handle command: bind-key . command-prompt "move-window -t '%%'" <type 'unicode'>.
INFO:pymux:handle command: bind-key [ copy-mode <type 'unicode'>.
INFO:pymux:handle command: bind-key ] paste-buffer <type 'unicode'>.
INFO:pymux:handle command: bind-key ? list-keys <type 'unicode'>.
INFO:pymux:handle command: bind-key PPage copy-mode -u <type 'unicode'>.
INFO:pymux:handle command:  <type 'unicode'>.
INFO:pymux:handle command: # Layouts. <type 'unicode'>.
INFO:pymux:handle command: bind-key M-1 select-layout even-horizontal <type 'unicode'>.
INFO:pymux:handle command: bind-key M-2 select-layout even-vertical <type 'unicode'>.
INFO:pymux:handle command: bind-key M-3 select-layout main-horizontal <type 'unicode'>.
INFO:pymux:handle command: bind-key M-4 select-layout main-vertical <type 'unicode'>.
INFO:pymux:handle command: bind-key M-5 select-layout tiled <type 'unicode'>.
INFO:pymux:handle command:  <type 'unicode'>.
INFO:pymux:handle command: # Renaming stuff. <type 'unicode'>.
INFO:pymux:handle command: bind-key , command-prompt -I #W "rename-window '%%'" <type 'unicode'>.
INFO:pymux:handle command: #bind-key "'" command-prompt -I #W "rename-pane '%%'" <type 'unicode'>.
INFO:pymux:handle command: bind-key "'" command-prompt -p index "select-window -t ':%%'" <type 'unicode'>.
INFO:pymux:handle command: bind-key . command-prompt "move-window -t '%%'" <type 'unicode'>.
INFO:pymux:Created process ['/bin/zsh'].
❯ cat /var/folders/kx/gmc5nl8x2hg370c3nktpc7vm0000gn/T/pymux.crash-3X4asm
Traceback (most recent call last):
  File "/Users/me/study/python/pymux/pymux/main.py", line 517, in run_server
    PipeInput(), DummyCallbacks())
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/eventloop/posix.py", line 154, in run
    t()
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/interface.py", line 324, in redraw
    self._redraw()
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/interface.py", line 344, in _redraw
    self.renderer.render(self, self.layout, is_done=self.is_done)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/renderer.py", line 422, in render
    extended_height=size.rows,
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 398, in write_to_screen
    self.content.write_to_screen(cli, screen, mouse_handlers, write_position)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 142, in write_to_screen
    sizes = self._divide_heigths(cli, write_position)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 177, in _divide_heigths
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 175, in get_dimension_for_child
    return c.preferred_height(cli, write_position.width, write_position.extended_height)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 1614, in preferred_height
    return self.content.preferred_height(cli, width, max_available_height)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 254, in preferred_height
    sizes = self._divide_widths(cli, width)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 283, in _divide_widths
    dimensions = [get_dimension_for_child(c, index) for index, c in enumerate(self.children)]
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 281, in get_dimension_for_child
    return c.preferred_width(cli, width)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/containers.py", line 977, in preferred_width
    cli, max_available_width - total_margin_width)
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/controls.py", line 249, in preferred_width
    text = token_list_to_text(self._get_tokens_cached(cli))
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/controls.py", line 239, in _get_tokens_cached
    cli.render_counter, lambda: self.get_tokens(cli))
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/cache.py", line 35, in get
    value = getter_func()
  File "/Users/me/Library/Python/2.7/lib/python/site-packages/prompt_toolkit/layout/controls.py", line 239, in <lambda>
    cli.render_counter, lambda: self.get_tokens(cli))
  File "/Users/me/study/python/pymux/pymux/layout.py", line 338, in _get_status_tokens
    format_pymux_string(self.pymux, cli, format_str, window=w),
  File "/Users/me/study/python/pymux/pymux/format.py", line 96, in format_pymux_string
    string = string.replace(symbol, f())
  File "/Users/me/study/python/pymux/pymux/format.py", line 47, in name_of_window
    return window.name or '(noname)'
  File "/Users/me/study/python/pymux/pymux/arrangement.py", line 270, in name
    return pane.name
  File "/Users/me/study/python/pymux/pymux/arrangement.py", line 91, in name
    name = self.process.get_name()
  File "/Users/me/study/python/pymux/pymux/process.py", line 336, in get_name
    return get_name_for_fd(self.master)
  File "/Users/me/study/python/pymux/pymux/process.py", line 446, in get_name_for_fd
    return get_proc_name(pgrp)
  File "/Users/me/study/python/pymux/pymux/darwin.py", line 80, in get_proc_name
    proc_kinfo = get_proc_info(pid)
  File "/Users/me/study/python/pymux/pymux/darwin.py", line 54, in get_proc_info
    _init()
  File "/Users/me/study/python/pymux/pymux/darwin.py", line 46, in _init
    LIBC = cdll.LoadLibrary('libc.dylib')
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ctypes/__init__.py", line 443, in LoadLibrary
    return self._dlltype(name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ctypes/__init__.py", line 365, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: dlopen(libc.dylib, 6): image not found

Auto-update status bar

It seems like the lower status bar gets updated only when the user is typing into the terminal or clicks the mouse inside. So when it shows e.g. the current time this can be at least misleading. I've just checked, tmux does perform an auto-update.

Now turning this into a feature request: ideally it would be very useful to have hooks implemented as simple functions by the user, referenced by name/location from the config file that return arbitrary strings to be shown in the lower status bar. If these would be called in some regular, configurable interval, then that would be a very useful way of showing one or more values that change over time, like a timestamp, a directory file size, or anything else.

feature request -- setw synchronize-panes on

Wow, as a long-time user of tmux, I really like pymux! To me the output buffering is even a feature, since I do all of my work in tmux via a web-based terminal (running/managing the https://cloud.sagemath.com service via itself). However, I constantly use setw synchronize-panes on (say) to manage several servers at once. This seems to not be implemented in pymux yet, so I can't seriously use pymux. In any case, I hope you'll consider implementing it, since it's really nice to be able to automatically type the same thing into several windows at once.

Bug: ValueError: list.remove(x): x not in list

Traceback (most recent call last):
  File "/home/jonathan/git/pymux/pymux/main.py", line 538, in run_server
    PipeInput(), DummyCallbacks())
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/eventloop/posix.py", line 164, in run
    t()
  File "/home/jonathan/git/pymux/pymux/process.py", line 156, in done
    self.eventloop.remove_reader(self.master)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/eventloop/posix.py", line 289, in remove_reader
    self.selector.unregister(fd)
  File "/home/jonathan/git/python-prompt-toolkit/prompt_toolkit/eventloop/select.py", line 80, in unregister
    self._fds.remove(fd)
ValueError: list.remove(x): x not in list

screen translator

I would like to create a screen translation with a use case like this

  • in pymux, cat a text file
  • press control-B + : then type command "transtate English to Thai"
  • pymux capture anything on screen and send to another translator online, such as bing or google translage, or maybe just an offline dictionary
  • pymux replace text on screen with the result from translator

Which file and function I should start hacking?

No module named ‘prompt_toolkit.key_binding.defaults’

Current pymux (announces itself as 0.13) cannot find prompt_toolkit.key_binding.defaults:

$ /usr/bin/pymux
Traceback (most recent call last):
  File "/usr/bin/pymux", line 11, in <module>
    load_entry_point('pymux==0.13', 'console_scripts', 'pymux')()
  File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 560, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2642, in load_entry_point
    return ep.load()
  File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2296, in load
    return self.resolve()
  File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2302, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/lib/python3.6/site-packages/pymux/entry_points/run_pymux.py", line 28, in <module>
    from pymux.main import Pymux
  File "/usr/lib/python3.6/site-packages/pymux/main.py", line 21, in <module>
    from .key_bindings import KeyBindingsManager
  File "/usr/lib/python3.6/site-packages/pymux/key_bindings.py", line 7, in <module>
    from prompt_toolkit.key_binding.defaults import load_key_bindings, load_mouse_bindings
ModuleNotFoundError: No module named 'prompt_toolkit.key_binding.defaults'

and

$ cd /usr/lib/python3.6/site-packages/prompt_toolkit/key_binding/
$ find . -type f -name '*.py'
./__init__.py
./digraphs.py
./input_processor.py
./manager.py
./registry.py
./vi_state.py
./bindings/__init__.py
./bindings/basic.py
./bindings/completion.py
./bindings/emacs.py
./bindings/named_commands.py
./bindings/scroll.py
./bindings/utils.py
./bindings/vi.py

This is:

  • pymux 0.13 on
  • Python 3.6.0

on ArchLinux.

Installed dependencies:

  • prompt_toolkit 1.0.9
    • wcwidth 0.1.7
  • pyte 0.5.2
  • six 1.10.0
  • docopt 0.6.2

closing a pane does not go to previous pane

Actions performed:

  • split pane (orientation doesn't matter)
  • split pane again
  • close pane (I used exit)

Expected result (as in tmux):

  • Focus goes back to previously active pane (in this case pane 1)

Actual result:

  • Focus goes back to very first pane (pane 0)

Not so much of a bug but a convenience for saner flow.

Binding Ctrl-h to some action prevent backspace to be working fine

In my config file I have Ctrl-h bound to the previous-window command. Unfortunately having this setting makes also the backspace key triggering this command. This behavior prevent me to actually using backspace for what it is meant for.

You mention that ctrl-h and backspace are somehow the same key sequence. However in tmux I do have the similar setting and it works. So it might be possible to have pymux override this default behavior and adopting something similar to what tmux does.

Here is an excerpt from my config file

bind-key -n C-l next-window
bind-key -n C-h previous-window

Windows support

I guess this is another feature request for prompt-toolkit, but it would be fantastic if the VT100 backend could be forced on windows. Having a terminal multiplexor in windows could be great.

Here is an example of pymux running in the windows terminal (conhost). It works great and really fast. It is not really Windows since it runs in the 'Windows subsystem for linux'. So it is the linux versions of pymux, xonsh and PTK that are running. But the windows console handles the VT100 output quite nicely.

image

Windows ?

Hi Jonathan,

Happy New year 2016.

Does this work on Windows too ?

Home / End key doesn't work in vim

Start pymux, hit Home and End keys several times before anything else, then the keys are broken. It doesn't happen all the time though.

Pymux doesn't honor $SHELL

How does pymux decide which shell to start? It doesn't seem to honor the SHELL environment variable:

$ echo $SHELL
/usr/bin/xonsh
$ 1+2
3
$ exec pymux
$ echo $SHELL
/usr/bin/xonsh
$ 1+2
bash: 1+2: command not found
$ 

Writing crash reports to `/tmp/pymux.crash` might be insecure

Hi,

Awesome project! I'm looking at some code in main.py:

            # When something bad happens, always dump the traceback.
            # (Otherwise, when running as a daemon, and stdout/stderr are not
            # available, it's hard to see what went wrong.)
            with open('/tmp/pymux.crash', 'wb') as f:
                f.write(traceback.format_exc().encode('utf-8'))
            raise

I believe this is somewhat dangerous. An attacker can create a symlink like /tmp/pymux.crash -> /etc/passwd. If pymux running as the root user then crashes, pymux will write into the symlink, clobbering the file it points at (in this case, /etc/passwd).

Modern Linux kernels have protection against this as long as fs.protected_symlinks is enabled, but it seems to be disabled by default in the kernel (apparently enabled by default in Debian, though). I don't know about OS X protections.

Some possible ways to avoid that:

  • Write inside a user directory instead (e.g. pip writes to ~/.pip/pip.log).
  • Use a randomized name for the crash file. I think you could use tempfile.mkstemp(prefix='pymux.crash-') which will create files named like /tmp/pymux.crash-vgjKCv

I'd be happy to write a patch.

AttributeError: 'BetterScreen' object has no attribute 'report_device_status'

Crash when running vim

Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/pymux/main.py", line 500, in run_server
PipeInput(), DummyCallbacks())
File "/usr/local/lib/python2.7/dist-packages/prompt_toolkit/eventloop/posix.py", line 150, in run
t()
File "/usr/local/lib/python2.7/dist-packages/pymux/process.py", line 280, in _read
process()
File "/usr/local/lib/python2.7/dist-packages/pymux/process.py", line 274, in process
self.stream.feed(d)
File "/usr/local/lib/python2.7/dist-packages/pymux/stream.py", line 51, in feed
send(c)
File "/usr/local/lib/python2.7/dist-packages/pymux/stream.py", line 157, in _parser_generator
dispatch(csi[char], _params)
File "/usr/local/lib/python2.7/dist-packages/pymux/stream.py", line 91, in dispatch
getattr(listener, event)(_args, **flags)
AttributeError: 'BetterScreen' object has no attribute 'report_device_status'

Session Restore Feature

Hey,
i was wondering if there are any plans to create something along the lines of
tmux resurrect for pymux.
Haven't seen it on the roadmap.
This is one of the key features of tmux for me.

I use a combination of Dropbox/ Symlinks and tmux -resurrect to synch 3 macs that i use.

Scroll bug.

Run find / for a while. Then execute clear. The cursor will stay at the top making only the very last line visible.

Same issue happens when reducing the height of a window at a point where the cursor is at the top.

Does this tool work?

It never seems to work for me.
I install it with: pip install pymux
then: pymux
and the cursor just jumps to the top.

Pymux, vim and netrw can't load selected files with return.

Hey Jonathan,

Mac OS X (10.11.2), vim/macvim -v (7.4) and pymux has some problems with return/enter.
Return/enter fails to load selected files when exploring files in vim using :Ex and returns new lines instead.

  • start pytmux, start vim, type :Ex, select file, hit return, cursor line moves down instead of loading selected file.

zsh: command not found: pymux

I am using iTerm and after installing, I get zsh: command not found: pymux. Thank you in advance for any help you can give me for fixing this. Look forward to using this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.