streamdown 0.12.0__tar.gz → 0.13.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.12.0 → streamdown-0.13.0}/PKG-INFO +26 -3
- {streamdown-0.12.0 → streamdown-0.13.0}/README.md +25 -2
- {streamdown-0.12.0 → streamdown-0.13.0}/pyproject.toml +1 -1
- {streamdown-0.12.0 → streamdown-0.13.0}/streamdown/sd.py +112 -39
- streamdown-0.13.0/tests/block.md +10 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/.gitignore +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/.vimrc +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/24-bit-color.sh +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/LICENSE.MIT +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/configurable.png +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/copyable.png +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/dunder.png +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/error.txt +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/newdir/file_0.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/newdir/file_1.rb +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/newdir/file_2.jl +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/passthrough.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/somelog.txt +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/streamdown/__init__.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/streamdown/plugins/README.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/streamdown/plugins/latex.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/streamdown/tt.mds +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/table.png +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/temp.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/test.py +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/test_input.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/README.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/chunk-buffer.sh +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/code.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/example.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/fizzbuzz.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/inline.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/line-buffer.sh +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/line-wrap.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/line.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/links.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/longer-example.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/mandlebrot.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/markdown.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/nested-example.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/new.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/outline.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/sd.log +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/table-break.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/table.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/table_test.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/test.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/test_input.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.0}/tests/white-space-code.md +0 -0
- {streamdown-0.12.0 → streamdown-0.13.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.13.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
|
|
@@ -109,6 +109,31 @@ Width = 120
|
|
|
109
109
|
Timeout = 1.0
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
## Invocation
|
|
113
|
+
The most exciting feature here is `--exec` with it you can do full readline support like this:
|
|
114
|
+
|
|
115
|
+
$ sd --exec "llm chat"
|
|
116
|
+
|
|
117
|
+
And now you have all your readline stuff. It's pretty great.
|
|
118
|
+
|
|
119
|
+
```shell
|
|
120
|
+
Streamdown - A markdown renderer for modern terminals
|
|
121
|
+
|
|
122
|
+
positional arguments:
|
|
123
|
+
filenameList Input file to process (also takes stdin)
|
|
124
|
+
|
|
125
|
+
options:
|
|
126
|
+
-h, --help show this help message and exit
|
|
127
|
+
-l LOGLEVEL, --loglevel LOGLEVEL
|
|
128
|
+
Set the logging level
|
|
129
|
+
-c COLOR, --color COLOR
|
|
130
|
+
Set the hsv base: h,s,v
|
|
131
|
+
-w WIDTH, --width WIDTH
|
|
132
|
+
Set the width
|
|
133
|
+
-e EXEC, --exec EXEC Wrap a program for more 'proper' i/o handling
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
|
|
112
137
|
## Demo
|
|
113
138
|
Do this
|
|
114
139
|
|
|
@@ -131,5 +156,3 @@ I'm really considering using `tinycss2` and making an actual stylesheet engine.
|
|
|
131
156
|
#### scrape
|
|
132
157
|
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
158
|
|
|
134
|
-
#### exec
|
|
135
|
-
I'm trying to get a readline capable wrapper so that interaction is as transparent as possible. After many days of research I've given up on trying to hack it through tty/pty hijacking and pipes and decided it has to be a standard wrapper. This should be low effort.
|
|
@@ -81,6 +81,31 @@ Width = 120
|
|
|
81
81
|
Timeout = 1.0
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Invocation
|
|
85
|
+
The most exciting feature here is `--exec` with it you can do full readline support like this:
|
|
86
|
+
|
|
87
|
+
$ sd --exec "llm chat"
|
|
88
|
+
|
|
89
|
+
And now you have all your readline stuff. It's pretty great.
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
Streamdown - A markdown renderer for modern terminals
|
|
93
|
+
|
|
94
|
+
positional arguments:
|
|
95
|
+
filenameList Input file to process (also takes stdin)
|
|
96
|
+
|
|
97
|
+
options:
|
|
98
|
+
-h, --help show this help message and exit
|
|
99
|
+
-l LOGLEVEL, --loglevel LOGLEVEL
|
|
100
|
+
Set the logging level
|
|
101
|
+
-c COLOR, --color COLOR
|
|
102
|
+
Set the hsv base: h,s,v
|
|
103
|
+
-w WIDTH, --width WIDTH
|
|
104
|
+
Set the width
|
|
105
|
+
-e EXEC, --exec EXEC Wrap a program for more 'proper' i/o handling
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
|
|
84
109
|
## Demo
|
|
85
110
|
Do this
|
|
86
111
|
|
|
@@ -103,5 +128,3 @@ I'm really considering using `tinycss2` and making an actual stylesheet engine.
|
|
|
103
128
|
#### scrape
|
|
104
129
|
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.
|
|
105
130
|
|
|
106
|
-
#### exec
|
|
107
|
-
I'm trying to get a readline capable wrapper so that interaction is as transparent as possible. After many days of research I've given up on trying to hack it through tty/pty hijacking and pipes and decided it has to be a standard wrapper. This should be low effort.
|
|
@@ -12,6 +12,7 @@ import appdirs, toml
|
|
|
12
12
|
import logging, tempfile
|
|
13
13
|
import os, sys
|
|
14
14
|
import pty, select
|
|
15
|
+
import termios, tty
|
|
15
16
|
|
|
16
17
|
import math
|
|
17
18
|
import re
|
|
@@ -20,6 +21,7 @@ import subprocess
|
|
|
20
21
|
import traceback
|
|
21
22
|
import colorsys
|
|
22
23
|
import base64
|
|
24
|
+
import importlib
|
|
23
25
|
from io import BytesIO
|
|
24
26
|
import pygments.util
|
|
25
27
|
from argparse import ArgumentParser
|
|
@@ -28,7 +30,10 @@ from pygments.lexers import get_lexer_by_name
|
|
|
28
30
|
from pygments.formatters import Terminal256Formatter
|
|
29
31
|
from pygments.styles import get_style_by_name
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
if __package__ is None:
|
|
34
|
+
from plugins import latex
|
|
35
|
+
else:
|
|
36
|
+
from .plugins import latex
|
|
32
37
|
|
|
33
38
|
default_toml = """
|
|
34
39
|
[features]
|
|
@@ -77,7 +82,8 @@ UNDERLINE = ["\033[4m", "\033[24m"]
|
|
|
77
82
|
ITALIC = ["\033[3m", "\033[23m"]
|
|
78
83
|
|
|
79
84
|
ESCAPE = r"\033\[[0-9;]*[mK]"
|
|
80
|
-
ANSIESCAPE = r
|
|
85
|
+
ANSIESCAPE = r'\033(?:\[[0-9;?]*[a-zA-Z]|][0-9]*;;.*?\\|\\)'
|
|
86
|
+
#r"\033(\[[0-9;]*[mK]|][0-9]*;;.*?\\|\\)"
|
|
81
87
|
KEYCODE_RE = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
82
88
|
|
|
83
89
|
visible = lambda x: re.sub(ANSIESCAPE, "", x)
|
|
@@ -114,15 +120,18 @@ class ParseState:
|
|
|
114
120
|
self.first_line = True
|
|
115
121
|
self.last_line_empty = False
|
|
116
122
|
self.is_pty = False
|
|
123
|
+
self.is_exec = False
|
|
117
124
|
self.maybe_prompt = False
|
|
118
125
|
self.emit_flag = None
|
|
119
126
|
self.scrape = None
|
|
120
127
|
self.scrape_ix = 0
|
|
128
|
+
self.terminal = None
|
|
121
129
|
|
|
122
130
|
self.CodeSpaces = _features.get("CodeSpaces")
|
|
123
131
|
self.Clipboard = _features.get("Clipboard")
|
|
124
132
|
self.Logging = _features.get("Logging")
|
|
125
133
|
self.Timeout = _features.get("Timeout")
|
|
134
|
+
self.WidthArg = None
|
|
126
135
|
|
|
127
136
|
# If the entire block is indented this will
|
|
128
137
|
# tell us what that is
|
|
@@ -150,7 +159,13 @@ class ParseState:
|
|
|
150
159
|
self.in_italic = False
|
|
151
160
|
self.in_table = False # (Code.[Header|Body] | False)
|
|
152
161
|
self.in_underline = False
|
|
153
|
-
self.
|
|
162
|
+
self.block_depth = 0
|
|
163
|
+
|
|
164
|
+
self.exec_sub = None
|
|
165
|
+
self.exec_master = None
|
|
166
|
+
self.exec_slave = None
|
|
167
|
+
self.exec_kb = 0
|
|
168
|
+
self.exec_israw = False
|
|
154
169
|
|
|
155
170
|
self.exit = 0
|
|
156
171
|
self.where_from = None
|
|
@@ -161,7 +176,7 @@ class ParseState:
|
|
|
161
176
|
return state
|
|
162
177
|
|
|
163
178
|
def space_left(self):
|
|
164
|
-
return (
|
|
179
|
+
return (Style.MarginSpaces if len(self.current_line) == 0 else "") + (Style.Blockquote * self.block_depth)
|
|
165
180
|
|
|
166
181
|
state = ParseState()
|
|
167
182
|
|
|
@@ -210,7 +225,7 @@ def format_table(rowList):
|
|
|
210
225
|
# Correct indentation: This should be outside the c_idx loop
|
|
211
226
|
joined_line = f"{BG}{bg_color}{extra}{FG}{Style.Symbol}│{RESET}".join(line_segments)
|
|
212
227
|
# Correct indentation and add missing characters
|
|
213
|
-
yield f"{
|
|
228
|
+
yield f"{Style.MarginSpaces}{joined_line}{RESET}"
|
|
214
229
|
|
|
215
230
|
state.bg = BGRESET
|
|
216
231
|
|
|
@@ -218,21 +233,21 @@ def emit_h(level, text):
|
|
|
218
233
|
text = line_format(text)
|
|
219
234
|
spaces_to_center = ((state.Width - visible_length(text)) / 2)
|
|
220
235
|
if level == 1: #
|
|
221
|
-
return f"\n{
|
|
236
|
+
return f"\n{Style.MarginSpaces}{BOLD[0]}{' ' * math.floor(spaces_to_center)}{text}{' ' * math.ceil(spaces_to_center)}{BOLD[1]}\n"
|
|
222
237
|
elif level == 2: ##
|
|
223
|
-
return f"\n{
|
|
238
|
+
return f"\n{Style.MarginSpaces}{BOLD[0]}{FG}{Style.Bright}{' ' * math.floor(spaces_to_center)}{text}{' ' * math.ceil(spaces_to_center)}{RESET}\n\n"
|
|
224
239
|
elif level == 3: ###
|
|
225
|
-
return f"{
|
|
240
|
+
return f"{Style.MarginSpaces}{FG}{Style.Head}{BOLD[0]}{text}{RESET}"
|
|
226
241
|
elif level == 4: ####
|
|
227
|
-
return f"{
|
|
242
|
+
return f"{Style.MarginSpaces}{FG}{Style.Symbol}{text}{RESET}"
|
|
228
243
|
else: # level 5 or 6
|
|
229
|
-
return f"{
|
|
244
|
+
return f"{Style.MarginSpaces}{text}{RESET}"
|
|
230
245
|
|
|
231
246
|
def code_wrap(text_in):
|
|
232
247
|
# get the indentation of the first line
|
|
233
248
|
indent = len(text_in) - len(text_in.lstrip())
|
|
234
249
|
text = text_in.lstrip()
|
|
235
|
-
mywidth = state.
|
|
250
|
+
mywidth = state.WidthFull - indent
|
|
236
251
|
|
|
237
252
|
# We take special care to preserve empty lines
|
|
238
253
|
if len(text) == 0:
|
|
@@ -378,11 +393,39 @@ def parse(stream):
|
|
|
378
393
|
byte = None
|
|
379
394
|
TimeoutIx = 0
|
|
380
395
|
while True:
|
|
381
|
-
if state.is_pty:
|
|
396
|
+
if state.is_pty or state.is_exec:
|
|
382
397
|
byte = None
|
|
383
|
-
|
|
398
|
+
ready_in, _, _ = select.select(
|
|
399
|
+
[stream.fileno(), state.exec_master], [], [], state.Timeout)
|
|
400
|
+
|
|
401
|
+
if state.is_exec:
|
|
402
|
+
# This is keyboard input
|
|
403
|
+
if stream.fileno() in ready_in:
|
|
404
|
+
byte = os.read(stream.fileno(), 1)
|
|
405
|
+
|
|
406
|
+
state.exec_kb += 1
|
|
407
|
+
os.write(state.exec_master, byte)
|
|
408
|
+
|
|
409
|
+
if byte == b'\n':
|
|
410
|
+
state.buffer = b''
|
|
411
|
+
print("")
|
|
412
|
+
state.exec_kb = 0
|
|
413
|
+
else:
|
|
414
|
+
continue
|
|
384
415
|
|
|
385
|
-
|
|
416
|
+
if state.exec_master in ready_in:
|
|
417
|
+
TimeoutIx = 0
|
|
418
|
+
byte = os.read(state.exec_master, 1)
|
|
419
|
+
|
|
420
|
+
if state.exec_kb:
|
|
421
|
+
os.write(sys.stdout.fileno(), byte)
|
|
422
|
+
|
|
423
|
+
if len(ready_in) == 0:
|
|
424
|
+
TimeoutIx += 1
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
elif stream.fileno() in ready_in:
|
|
386
429
|
byte = os.read(stream.fileno(), 1)
|
|
387
430
|
TimeoutIx = 0
|
|
388
431
|
elif TimeoutIx == 0:
|
|
@@ -402,14 +445,15 @@ def parse(stream):
|
|
|
402
445
|
|
|
403
446
|
line = state.buffer.decode('utf-8')
|
|
404
447
|
state.has_newline = line.endswith('\n')
|
|
405
|
-
|
|
448
|
+
# I hate this. There should be better ways.
|
|
449
|
+
state.maybe_prompt = not state.has_newline and state.current()['none'] and re.match(r'^.*>\s+$', visible(line))
|
|
406
450
|
|
|
407
451
|
# let's wait for a newline
|
|
408
452
|
if state.maybe_prompt:
|
|
409
453
|
state.emit_flag = Code.Flush
|
|
410
454
|
yield line
|
|
455
|
+
state.current_line = ''
|
|
411
456
|
state.buffer = b''
|
|
412
|
-
continue
|
|
413
457
|
|
|
414
458
|
if not state.has_newline:
|
|
415
459
|
continue
|
|
@@ -447,7 +491,7 @@ def parse(stream):
|
|
|
447
491
|
else:
|
|
448
492
|
state.in_list = False
|
|
449
493
|
|
|
450
|
-
if state.first_indent
|
|
494
|
+
if state.first_indent is None:
|
|
451
495
|
state.first_indent = len(line) - len(line.lstrip())
|
|
452
496
|
if len(line) - len(line.lstrip()) >= state.first_indent:
|
|
453
497
|
line = line[state.first_indent:]
|
|
@@ -462,13 +506,21 @@ def parse(stream):
|
|
|
462
506
|
if state.in_table and not state.in_code and not re.match(r"^\s*\|.+\|\s*$", line):
|
|
463
507
|
state.in_table = False
|
|
464
508
|
|
|
465
|
-
block_match = re.match(r"
|
|
509
|
+
block_match = re.match(r"^((> )*|<.?think>)", line)
|
|
466
510
|
if block_match:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
511
|
+
if block_match.group(1) == '</think>':
|
|
512
|
+
state.block_depth = 0
|
|
513
|
+
yield(RESET)
|
|
514
|
+
elif block_match.group(1) == '<think>':
|
|
515
|
+
state.block_depth = 1
|
|
516
|
+
else:
|
|
517
|
+
state.block_depth = int(len(block_match.group(0)) / 2)
|
|
518
|
+
# we also need to consume those tokens
|
|
519
|
+
line = line[state.block_depth * 2:]
|
|
520
|
+
else:
|
|
521
|
+
if state.block_depth > 0:
|
|
522
|
+
yield RESET
|
|
523
|
+
state.block_depth = 0
|
|
472
524
|
|
|
473
525
|
#
|
|
474
526
|
# <code><pre>
|
|
@@ -532,7 +584,7 @@ def parse(stream):
|
|
|
532
584
|
|
|
533
585
|
|
|
534
586
|
if code_type == Code.Backtick:
|
|
535
|
-
|
|
587
|
+
continue
|
|
536
588
|
else:
|
|
537
589
|
# otherwise we don't want to consume
|
|
538
590
|
# nor do we want to be here.
|
|
@@ -600,10 +652,10 @@ def parse(stream):
|
|
|
600
652
|
|
|
601
653
|
code_line = ' ' * indent + this_batch.strip()
|
|
602
654
|
|
|
603
|
-
margin = state.
|
|
655
|
+
margin = state.WidthFull - visible_length(code_line)
|
|
604
656
|
yield f"{Style.Codebg}{code_line}{' ' * max(0, margin)}{BGRESET}"
|
|
605
657
|
continue
|
|
606
|
-
except Goto
|
|
658
|
+
except Goto:
|
|
607
659
|
pass
|
|
608
660
|
|
|
609
661
|
except Exception as ex:
|
|
@@ -694,7 +746,7 @@ def parse(stream):
|
|
|
694
746
|
if hr_match:
|
|
695
747
|
if state.last_line_empty or last_line_empty_cache:
|
|
696
748
|
# print a horizontal rule using a unicode midline
|
|
697
|
-
yield f"{
|
|
749
|
+
yield f"{Style.MarginSpaces}{FG}{Style.Symbol}{'─' * state.Width}{RESET}"
|
|
698
750
|
else:
|
|
699
751
|
# We tell the next level up that the beginning of the buffer should be a flag.
|
|
700
752
|
# Underneath this condition it will no longer yield
|
|
@@ -722,6 +774,7 @@ def emit(inp):
|
|
|
722
774
|
buffer = []
|
|
723
775
|
flush = False
|
|
724
776
|
for chunk in parse(inp):
|
|
777
|
+
width_calc()
|
|
725
778
|
if state.emit_flag:
|
|
726
779
|
if state.emit_flag == Code.Flush:
|
|
727
780
|
flush = True
|
|
@@ -770,8 +823,17 @@ def apply_multipliers(name, H, S, V):
|
|
|
770
823
|
r, g, b = colorsys.hsv_to_rgb(min(1.0, H * m["H"]), min(1.0, S * m["S"]), min(1.0, V * m["V"]))
|
|
771
824
|
return ';'.join([str(int(x * 256)) for x in [r, g, b]]) + "m"
|
|
772
825
|
|
|
826
|
+
def width_calc():
|
|
827
|
+
state.WidthFull = state.WidthArg or int(get_terminal_width())
|
|
828
|
+
state.Width = state.WidthFull - 2 * Style.Margin
|
|
829
|
+
Style.Codepad = [
|
|
830
|
+
f"{RESET}{FG}{Style.Dark}{'▄' * state.WidthFull}{RESET}\n",
|
|
831
|
+
f"{RESET}{FG}{Style.Dark}{'▀' * state.WidthFull}{RESET}"
|
|
832
|
+
]
|
|
833
|
+
|
|
773
834
|
def main():
|
|
774
|
-
global H, S, V
|
|
835
|
+
global H, S, V
|
|
836
|
+
|
|
775
837
|
parser = ArgumentParser(description="Streamdown - A markdown renderer for modern terminals")
|
|
776
838
|
parser.add_argument("filenameList", nargs="*", help="Input file to process (also takes stdin)")
|
|
777
839
|
parser.add_argument("-l", "--loglevel", default="INFO", help="Set the logging level")
|
|
@@ -796,24 +858,27 @@ def main():
|
|
|
796
858
|
os.makedirs(args.scrape, exist_ok=True)
|
|
797
859
|
state.scrape = args.scrape
|
|
798
860
|
|
|
799
|
-
|
|
800
|
-
state.
|
|
801
|
-
|
|
861
|
+
Style.MarginSpaces = " " * Style.Margin
|
|
862
|
+
state.WidthArg = int(args.width) or _style.get("Width") or 0
|
|
863
|
+
width_calc()
|
|
864
|
+
|
|
802
865
|
Style.Codebg = f"{BG}{Style.Dark}"
|
|
803
866
|
Style.Link = f"{FG}{Style.Symbol}{UNDERLINE[0]}"
|
|
804
867
|
Style.Blockquote = f"{FG}{Style.Grey} \u258E "
|
|
805
868
|
|
|
806
|
-
Style.Codepad = [
|
|
807
|
-
f"{RESET}{FG}{Style.Dark}{'▄' * state.FullWidth}{RESET}\n",
|
|
808
|
-
f"{RESET}{FG}{Style.Dark}{'▀' * state.FullWidth}{RESET}"
|
|
809
|
-
]
|
|
810
869
|
|
|
811
870
|
logging.basicConfig(stream=sys.stdout, level=args.loglevel.upper(), format=f'%(message)s')
|
|
871
|
+
state.exec_master, state.exec_slave = pty.openpty()
|
|
812
872
|
try:
|
|
813
873
|
inp = sys.stdin
|
|
814
874
|
if args.exec:
|
|
815
|
-
state.
|
|
816
|
-
|
|
875
|
+
state.terminal = termios.tcgetattr(sys.stdin)
|
|
876
|
+
state.is_exec = True
|
|
877
|
+
state.exec_sub = subprocess.Popen(args.exec.split(' '), stdin=state.exec_slave, stdout=state.exec_slave, stderr=state.exec_slave, close_fds=True)
|
|
878
|
+
os.close(state.exec_slave) # We don't need slave in parent
|
|
879
|
+
# Set stdin to raw mode so we don't need to press enter
|
|
880
|
+
tty.setcbreak(sys.stdin.fileno())
|
|
881
|
+
emit(sys.stdin)
|
|
817
882
|
|
|
818
883
|
elif args.filenameList:
|
|
819
884
|
# Let's say we only care about logging in streams
|
|
@@ -832,11 +897,13 @@ def main():
|
|
|
832
897
|
os.set_blocking(inp.fileno(), False)
|
|
833
898
|
emit(inp)
|
|
834
899
|
|
|
835
|
-
except KeyboardInterrupt:
|
|
900
|
+
except (OSError, KeyboardInterrupt):
|
|
836
901
|
state.exit = 130
|
|
837
902
|
|
|
838
903
|
except Exception as ex:
|
|
839
|
-
|
|
904
|
+
if state.terminal:
|
|
905
|
+
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, state.terminal)
|
|
906
|
+
logging.warning(f"Exception thrown: {type(ex)} {ex}")
|
|
840
907
|
traceback.print_exc()
|
|
841
908
|
|
|
842
909
|
if state.Clipboard and state.code_buffer:
|
|
@@ -847,6 +914,12 @@ def main():
|
|
|
847
914
|
base64_string = base64_bytes.decode('utf-8')
|
|
848
915
|
print(f"\033]52;c;{base64_string}\a", end="", flush=True)
|
|
849
916
|
|
|
917
|
+
|
|
918
|
+
if state.terminal:
|
|
919
|
+
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, state.terminal)
|
|
920
|
+
os.close(state.exec_master)
|
|
921
|
+
if state.exec_sub:
|
|
922
|
+
state.exec_sub.wait()
|
|
850
923
|
sys.exit(state.exit)
|
|
851
924
|
|
|
852
925
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
So here is some text
|
|
2
|
+
|
|
3
|
+
> and technically we are in blockquote
|
|
4
|
+
> territory with these. Blockquote is one
|
|
5
|
+
> > of the few things that can be embedded
|
|
6
|
+
> > in markdown. Stylistically they are
|
|
7
|
+
> > different than lists, but
|
|
8
|
+
> not by much.
|
|
9
|
+
|
|
10
|
+
Now we are out of the blockquote
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|