streamdown 0.16.0__tar.gz → 0.18.0__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.
- {streamdown-0.16.0 → streamdown-0.18.0}/PKG-INFO +19 -16
- {streamdown-0.16.0 → streamdown-0.18.0}/README.md +18 -15
- {streamdown-0.16.0 → streamdown-0.18.0}/pyproject.toml +1 -1
- streamdown-0.18.0/requirements.txt +5 -0
- streamdown-0.18.0/ss-new.py +45 -0
- streamdown-0.18.0/ss.py +69 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/streamdown/sd.py +145 -74
- streamdown-0.18.0/streamdown/ss +1 -0
- streamdown-0.18.0/streamdown/ss1 +42 -0
- streamdown-0.18.0/tests/backtick-with-post-spaces.md +0 -0
- streamdown-0.18.0/tests/block.md +21 -0
- streamdown-0.18.0/tests/bold_reset_with_link.md +1 -0
- streamdown-0.18.0/tests/broken-example.md +14 -0
- streamdown-0.18.0/tests/chinese.md +4 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/chunk-buffer.sh +1 -1
- streamdown-0.18.0/tests/cjk-wrap.md +150 -0
- streamdown-0.18.0/tests/escape.md +7 -0
- streamdown-0.18.0/tests/jimmy_webb.md +1458 -0
- streamdown-0.18.0/tests/managerie.md +328 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/markdown.md +2 -2
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/outline.md +2 -0
- streamdown-0.18.0/tests/strip-chunks.sh +2 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/table_test.md +0 -3
- streamdown-0.16.0/streamdown/scrape/file_0.py +0 -22
- streamdown-0.16.0/streamdown/scrape/file_1.js +0 -27
- streamdown-0.16.0/streamdown/scrape/file_2.cpp +0 -23
- streamdown-0.16.0/streamdown/tt.mds +0 -11
- streamdown-0.16.0/tests/block.md +0 -10
- streamdown-0.16.0/tests/line.md +0 -13
- streamdown-0.16.0/tests/longer-example.md +0 -18
- streamdown-0.16.0/tests/new.md +0 -3
- streamdown-0.16.0/tests/sd.log +0 -0
- streamdown-0.16.0/tests/table.md +0 -4
- streamdown-0.16.0/tests/white-space-code.md +0 -19
- {streamdown-0.16.0 → streamdown-0.18.0}/.gitignore +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/.vimrc +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/24-bit-color.sh +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/LICENSE.MIT +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/assets/logo.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/assets/logo.svg +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/configurable.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/copyable.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/dunder.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/error.txt +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/newdir/file_0.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/newdir/file_1.rb +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/newdir/file_2.jl +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/passthrough.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/python-go.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/somelog.txt +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/streamdown/__init__.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/streamdown/plugins/README.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/streamdown/plugins/latex.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/table.png +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/temp.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/test.py +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/test_input.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/README.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/code.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/example.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/fizzbuzz.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/inline.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/line-buffer.sh +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/line-wrap.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/links.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/mandlebrot.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/nested-example.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/pvgo_512.jpg +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/pythonvgo.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/table-break.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/test.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/test_input.md +0 -0
- {streamdown-0.16.0 → streamdown-0.18.0}/tests/wm.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamdown
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: A streaming markdown renderer for modern terminals with syntax highlighting
|
|
5
5
|
Project-URL: Homepage, https://github.com/kristopolous/Streamdown
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/kristopolous/Streamdown/issues
|
|
@@ -31,18 +31,22 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
<img src=https://github.com/user-attachments/assets/0468eac0-2a00-4e98-82ca-09e6ac679357/>
|
|
32
32
|
<br/>
|
|
33
33
|
<a href=https://pypi.org/project/streamdown><img src=https://badge.fury.io/py/streamdown.svg/></a>
|
|
34
|
+
<br/><strong>Terminal streaming markdown that rocks</strong>
|
|
35
|
+
|
|
34
36
|
</p>
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
Streamdown works with [simonw's llm](https://github.com/simonw/llm) along with any other streaming markdown
|
|
38
|
+
|
|
39
|
+
Streamdown works with [simonw's llm](https://github.com/simonw/llm) along with any other streaming markdown, even something basic like curl.
|
|
40
|
+
It supports standard piping like any normal pager and a clean `execvp` option for robustly wrapping around interactive programs with readline or their own ANSI stuff to manage.
|
|
38
41
|
```bash
|
|
39
42
|
$ pip install streamdown
|
|
40
43
|
```
|
|
41
44
|

|
|
42
45
|
|
|
43
46
|
### Provides clean copyable code for long code lines
|
|
44
|
-
|
|
47
|
+
Some *inferior* renderers inject line breaks when copying code that wraps around. We're better and now you are too!
|
|
45
48
|

|
|
49
|
+
**Tip**: You can make things prettier if you don't mind if this guarantee is broken. See the `PrettyBroken` flag below!
|
|
46
50
|
|
|
47
51
|
### Supports images
|
|
48
52
|
Here's kitty and alacritty. Try to do that in glow...
|
|
@@ -57,6 +61,9 @@ Here's kitty and alacritty. Try to do that in glow...
|
|
|
57
61
|
As well as everything else...
|
|
58
62
|

|
|
59
63
|
|
|
64
|
+
Very ... Carefully ... Supported ...
|
|
65
|
+

|
|
66
|
+
|
|
60
67
|
### Colors are highly (and quickly) configurable for people who care a lot, or just a little.
|
|
61
68
|

|
|
62
69
|
|
|
@@ -67,7 +74,7 @@ For instance, here is the [latex plugin](https://github.com/kristopolous/Streamd
|
|
|
67
74
|
|
|
68
75
|
## TOML Configuration
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
It's located at `~/.config/streamdown/config.toml` (following the XDG Base Directory Specification). If this file does not exist upon first run, it will be created with default values.
|
|
71
78
|
|
|
72
79
|
Here are the sections:
|
|
73
80
|
|
|
@@ -85,12 +92,15 @@ Defines the base Hue (H), Saturation (S), and Value (V) from which all other pal
|
|
|
85
92
|
* `Margin` (integer, default: `2`): The left and right indent for the output.
|
|
86
93
|
* `Width` (integer, default: `0`): Along with the `Margin`, `Width` specifies the base width of the content, which when set to 0, means use the terminal width. See [#6](https://github.com/kristopolous/Streamdown/issues/6) for more details
|
|
87
94
|
* `PrettyPad` (boolean, default: `false`): Uses a unicode vertical pad trick to add a half height background to code blocks. This makes copy/paste have artifacts. See [#2](https://github.com/kristopolous/Streamdown/issues/2). I like it on. But that's just me
|
|
95
|
+
* `PrettyBroken` (boolean, default: `false`): This will break the copy/paste assurance above. The output is much prettier, but it's also broken. So it's pretty broken. Works nicely with PrettyPad.
|
|
88
96
|
* `ListIndent` (integer, default: `2`): This is the recursive indent for the list styles.
|
|
89
97
|
* `Syntax` (string, default `monokai`): This is the syntax [highlighting theme which come via pygments](https://pygments.org/styles/).
|
|
90
98
|
|
|
91
99
|
Example:
|
|
92
100
|
```toml
|
|
93
101
|
[style]
|
|
102
|
+
PrettyPad = true
|
|
103
|
+
PrettyBroken = true
|
|
94
104
|
HSV = [0.7, 0.5, 0.5]
|
|
95
105
|
Dark = { H = 1.0, S = 1.2, V = 0.25 } # Make dark elements less saturated and darker
|
|
96
106
|
Symbol = { H = 1.0, S = 1.8, V = 1.8 } # Make symbols more vibrant
|
|
@@ -103,16 +113,13 @@ Controls optional features:
|
|
|
103
113
|
* `CodeSpaces` (boolean, default: `true`): Enables detection of code blocks indented with 4 spaces. Set to `false` to disable this detection method (triple-backtick blocks still work).
|
|
104
114
|
* `Clipboard` (boolean, default: `true`): Enables copying the last code block encountered to the system clipboard using OSC 52 escape sequences upon exit. Set to `false` to disable.
|
|
105
115
|
* `Logging` (boolean, default: `false`): Enables logging to tmpdir (/tmp/sd) of the raw markdown for debugging and bug reporting. The logging uses an emoji as a record separator so the actual streaming delays can be simulated and replayed. If you use the `filename` based invocation, that is to say, `sd <filename>`, this type of logging is always off.
|
|
106
|
-
* `
|
|
116
|
+
* `Savebrace` (boolean, default: `true`): Saves the code blocks of a conversation to the append file `/tmp/sd/savebrace` so you can fzf or whatever you want through it. See how it's used in my [llmehelp](https://github.com/kristopolous/llmehelp) scripts, specifically `screen-query` and `sd-picker`.
|
|
107
117
|
|
|
108
118
|
Example:
|
|
109
119
|
```toml
|
|
110
120
|
[features]
|
|
111
121
|
CodeSpaces = false
|
|
112
122
|
Clipboard = false
|
|
113
|
-
Margin = 4
|
|
114
|
-
Width = 120
|
|
115
|
-
Timeout = 1.0
|
|
116
123
|
```
|
|
117
124
|
|
|
118
125
|
## Command Line
|
|
@@ -146,7 +153,7 @@ Do this
|
|
|
146
153
|
$ ./streamdown/sd.py tests/*md
|
|
147
154
|
|
|
148
155
|
## Install from source
|
|
149
|
-
After the git clone least one of these should work, hopefully. it's using the modern uv pip tool.
|
|
156
|
+
After the git clone least one of these should work, hopefully. it's using the modern uv pip tool but is also backwards compatible to the `pip3 install -r requirements.txt` flow.
|
|
150
157
|
|
|
151
158
|
$ pipx install -e .
|
|
152
159
|
$ pip install -e .
|
|
@@ -154,9 +161,5 @@ After the git clone least one of these should work, hopefully. it's using the mo
|
|
|
154
161
|
|
|
155
162
|
### Future work
|
|
156
163
|
|
|
157
|
-
####
|
|
158
|
-
I'm
|
|
159
|
-
|
|
160
|
-
#### scrape
|
|
161
|
-
This is already partially implemented. The idea is every code block can get extracted and put in a directory so you can have a conversation to generate every piece of a project, similar to Aider, Claude or Goose, but in the most hands-off yet still convenient way possible.
|
|
162
|
-
|
|
164
|
+
#### Glow styles
|
|
165
|
+
I'm going to try to be compatible with other popular markdown styles to help for a smoother transition. Glow compatible json sheets is on my radar. There's also mdless and frogmouth. Might be others
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
<img src=https://github.com/user-attachments/assets/0468eac0-2a00-4e98-82ca-09e6ac679357/>
|
|
3
3
|
<br/>
|
|
4
4
|
<a href=https://pypi.org/project/streamdown><img src=https://badge.fury.io/py/streamdown.svg/></a>
|
|
5
|
+
<br/><strong>Terminal streaming markdown that rocks</strong>
|
|
6
|
+
|
|
5
7
|
</p>
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
Streamdown works with [simonw's llm](https://github.com/simonw/llm) along with any other streaming markdown
|
|
9
|
+
|
|
10
|
+
Streamdown works with [simonw's llm](https://github.com/simonw/llm) along with any other streaming markdown, even something basic like curl.
|
|
11
|
+
It supports standard piping like any normal pager and a clean `execvp` option for robustly wrapping around interactive programs with readline or their own ANSI stuff to manage.
|
|
9
12
|
```bash
|
|
10
13
|
$ pip install streamdown
|
|
11
14
|
```
|
|
12
15
|

|
|
13
16
|
|
|
14
17
|
### Provides clean copyable code for long code lines
|
|
15
|
-
|
|
18
|
+
Some *inferior* renderers inject line breaks when copying code that wraps around. We're better and now you are too!
|
|
16
19
|

|
|
20
|
+
**Tip**: You can make things prettier if you don't mind if this guarantee is broken. See the `PrettyBroken` flag below!
|
|
17
21
|
|
|
18
22
|
### Supports images
|
|
19
23
|
Here's kitty and alacritty. Try to do that in glow...
|
|
@@ -28,6 +32,9 @@ Here's kitty and alacritty. Try to do that in glow...
|
|
|
28
32
|
As well as everything else...
|
|
29
33
|

|
|
30
34
|
|
|
35
|
+
Very ... Carefully ... Supported ...
|
|
36
|
+

|
|
37
|
+
|
|
31
38
|
### Colors are highly (and quickly) configurable for people who care a lot, or just a little.
|
|
32
39
|

|
|
33
40
|
|
|
@@ -38,7 +45,7 @@ For instance, here is the [latex plugin](https://github.com/kristopolous/Streamd
|
|
|
38
45
|
|
|
39
46
|
## TOML Configuration
|
|
40
47
|
|
|
41
|
-
|
|
48
|
+
It's located at `~/.config/streamdown/config.toml` (following the XDG Base Directory Specification). If this file does not exist upon first run, it will be created with default values.
|
|
42
49
|
|
|
43
50
|
Here are the sections:
|
|
44
51
|
|
|
@@ -56,12 +63,15 @@ Defines the base Hue (H), Saturation (S), and Value (V) from which all other pal
|
|
|
56
63
|
* `Margin` (integer, default: `2`): The left and right indent for the output.
|
|
57
64
|
* `Width` (integer, default: `0`): Along with the `Margin`, `Width` specifies the base width of the content, which when set to 0, means use the terminal width. See [#6](https://github.com/kristopolous/Streamdown/issues/6) for more details
|
|
58
65
|
* `PrettyPad` (boolean, default: `false`): Uses a unicode vertical pad trick to add a half height background to code blocks. This makes copy/paste have artifacts. See [#2](https://github.com/kristopolous/Streamdown/issues/2). I like it on. But that's just me
|
|
66
|
+
* `PrettyBroken` (boolean, default: `false`): This will break the copy/paste assurance above. The output is much prettier, but it's also broken. So it's pretty broken. Works nicely with PrettyPad.
|
|
59
67
|
* `ListIndent` (integer, default: `2`): This is the recursive indent for the list styles.
|
|
60
68
|
* `Syntax` (string, default `monokai`): This is the syntax [highlighting theme which come via pygments](https://pygments.org/styles/).
|
|
61
69
|
|
|
62
70
|
Example:
|
|
63
71
|
```toml
|
|
64
72
|
[style]
|
|
73
|
+
PrettyPad = true
|
|
74
|
+
PrettyBroken = true
|
|
65
75
|
HSV = [0.7, 0.5, 0.5]
|
|
66
76
|
Dark = { H = 1.0, S = 1.2, V = 0.25 } # Make dark elements less saturated and darker
|
|
67
77
|
Symbol = { H = 1.0, S = 1.8, V = 1.8 } # Make symbols more vibrant
|
|
@@ -74,16 +84,13 @@ Controls optional features:
|
|
|
74
84
|
* `CodeSpaces` (boolean, default: `true`): Enables detection of code blocks indented with 4 spaces. Set to `false` to disable this detection method (triple-backtick blocks still work).
|
|
75
85
|
* `Clipboard` (boolean, default: `true`): Enables copying the last code block encountered to the system clipboard using OSC 52 escape sequences upon exit. Set to `false` to disable.
|
|
76
86
|
* `Logging` (boolean, default: `false`): Enables logging to tmpdir (/tmp/sd) of the raw markdown for debugging and bug reporting. The logging uses an emoji as a record separator so the actual streaming delays can be simulated and replayed. If you use the `filename` based invocation, that is to say, `sd <filename>`, this type of logging is always off.
|
|
77
|
-
* `
|
|
87
|
+
* `Savebrace` (boolean, default: `true`): Saves the code blocks of a conversation to the append file `/tmp/sd/savebrace` so you can fzf or whatever you want through it. See how it's used in my [llmehelp](https://github.com/kristopolous/llmehelp) scripts, specifically `screen-query` and `sd-picker`.
|
|
78
88
|
|
|
79
89
|
Example:
|
|
80
90
|
```toml
|
|
81
91
|
[features]
|
|
82
92
|
CodeSpaces = false
|
|
83
93
|
Clipboard = false
|
|
84
|
-
Margin = 4
|
|
85
|
-
Width = 120
|
|
86
|
-
Timeout = 1.0
|
|
87
94
|
```
|
|
88
95
|
|
|
89
96
|
## Command Line
|
|
@@ -117,7 +124,7 @@ Do this
|
|
|
117
124
|
$ ./streamdown/sd.py tests/*md
|
|
118
125
|
|
|
119
126
|
## Install from source
|
|
120
|
-
After the git clone least one of these should work, hopefully. it's using the modern uv pip tool.
|
|
127
|
+
After the git clone least one of these should work, hopefully. it's using the modern uv pip tool but is also backwards compatible to the `pip3 install -r requirements.txt` flow.
|
|
121
128
|
|
|
122
129
|
$ pipx install -e .
|
|
123
130
|
$ pip install -e .
|
|
@@ -125,9 +132,5 @@ After the git clone least one of these should work, hopefully. it's using the mo
|
|
|
125
132
|
|
|
126
133
|
### Future work
|
|
127
134
|
|
|
128
|
-
####
|
|
129
|
-
I'm
|
|
130
|
-
|
|
131
|
-
#### scrape
|
|
132
|
-
This is already partially implemented. The idea is every code block can get extracted and put in a directory so you can have a conversation to generate every piece of a project, similar to Aider, Claude or Goose, but in the most hands-off yet still convenient way possible.
|
|
133
|
-
|
|
135
|
+
#### Glow styles
|
|
136
|
+
I'm going to try to be compatible with other popular markdown styles to help for a smoother transition. Glow compatible json sheets is on my radar. There's also mdless and frogmouth. Might be others
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pty
|
|
3
|
+
import sys
|
|
4
|
+
import tty
|
|
5
|
+
import termios
|
|
6
|
+
import select
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
orig_attrs = termios.tcgetattr(sys.stdin)
|
|
11
|
+
try:
|
|
12
|
+
tty.setraw(sys.stdin)
|
|
13
|
+
pid, fd = pty.fork()
|
|
14
|
+
|
|
15
|
+
if pid == 0:
|
|
16
|
+
# Child process: run any command
|
|
17
|
+
os.execvp("llm", ["llm", "chat"])
|
|
18
|
+
else:
|
|
19
|
+
buffer = b""
|
|
20
|
+
|
|
21
|
+
while True:
|
|
22
|
+
r, _, _ = select.select([fd, sys.stdin], [], [])
|
|
23
|
+
|
|
24
|
+
if sys.stdin in r:
|
|
25
|
+
data = os.read(sys.stdin.fileno(), 1024)
|
|
26
|
+
if not data:
|
|
27
|
+
break
|
|
28
|
+
os.write(fd, data)
|
|
29
|
+
|
|
30
|
+
if fd in r:
|
|
31
|
+
data = os.read(fd, 1024)
|
|
32
|
+
if not data:
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
buffer += data
|
|
36
|
+
# Replace "fizz" only in printable content
|
|
37
|
+
output = re.sub(rb'fizz', b'fizzbuzz', buffer)
|
|
38
|
+
os.write(sys.stdout.fileno(), output)
|
|
39
|
+
buffer = b""
|
|
40
|
+
finally:
|
|
41
|
+
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_attrs)
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
main()
|
|
45
|
+
|
streamdown-0.18.0/ss.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pty
|
|
3
|
+
import select
|
|
4
|
+
import sys
|
|
5
|
+
import tty
|
|
6
|
+
import termios
|
|
7
|
+
import fcntl
|
|
8
|
+
import struct
|
|
9
|
+
|
|
10
|
+
def set_pty_size(fd, target_fd):
|
|
11
|
+
"""Set window size of PTY to match the real terminal."""
|
|
12
|
+
s = fcntl.ioctl(target_fd, termios.TIOCGWINSZ, b"\x00" * 8)
|
|
13
|
+
fcntl.ioctl(fd, termios.TIOCSWINSZ, s)
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
# Save original terminal settings
|
|
17
|
+
orig_attrs = termios.tcgetattr(sys.stdin.fileno())
|
|
18
|
+
try:
|
|
19
|
+
tty.setraw(sys.stdin.fileno()) # raw mode to send Ctrl-C, etc.
|
|
20
|
+
pid, fd = pty.fork()
|
|
21
|
+
|
|
22
|
+
if pid == 0:
|
|
23
|
+
os.execvp("bash", ["bash"])
|
|
24
|
+
else:
|
|
25
|
+
set_pty_size(fd, sys.stdin.fileno())
|
|
26
|
+
|
|
27
|
+
while True:
|
|
28
|
+
r, _, _ = select.select([fd, sys.stdin], [], [])
|
|
29
|
+
|
|
30
|
+
if sys.stdin in r:
|
|
31
|
+
user_input = os.read(sys.stdin.fileno(), 1024)
|
|
32
|
+
if not user_input:
|
|
33
|
+
break
|
|
34
|
+
os.write(fd, user_input)
|
|
35
|
+
|
|
36
|
+
if fd in r:
|
|
37
|
+
output = os.read(fd, 1024)
|
|
38
|
+
if not output:
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
# Carefully handle ANSI sequences
|
|
42
|
+
# Split on ANSI escape sequences to avoid breaking them
|
|
43
|
+
chunks = []
|
|
44
|
+
i = 0
|
|
45
|
+
while i < len(output):
|
|
46
|
+
if output[i:i+1] == b'\x1b':
|
|
47
|
+
end = i + 1
|
|
48
|
+
while end < len(output) and not (64 <= output[end] <= 126):
|
|
49
|
+
end += 1
|
|
50
|
+
end += 1 # include final letter
|
|
51
|
+
chunks.append(output[i:end])
|
|
52
|
+
i = end
|
|
53
|
+
else:
|
|
54
|
+
j = i
|
|
55
|
+
while j < len(output) and output[j:j+1] != b'\x1b':
|
|
56
|
+
j += 1
|
|
57
|
+
# Perform replacement on plain text only
|
|
58
|
+
text = output[i:j].replace(b"fizz", b"fizzbuzz")
|
|
59
|
+
chunks.append(text)
|
|
60
|
+
i = j
|
|
61
|
+
|
|
62
|
+
os.write(sys.stdout.fileno(), b''.join(chunks))
|
|
63
|
+
|
|
64
|
+
finally:
|
|
65
|
+
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, orig_attrs)
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|
|
69
|
+
|