streamdown 0.33.0__py3-none-any.whl → 0.35.0__py3-none-any.whl
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/sd.py +40 -15
- streamdown/test.txt +1 -0
- {streamdown-0.33.0.dist-info → streamdown-0.35.0.dist-info}/METADATA +2 -2
- {streamdown-0.33.0.dist-info → streamdown-0.35.0.dist-info}/RECORD +7 -6
- {streamdown-0.33.0.dist-info → streamdown-0.35.0.dist-info}/WHEEL +1 -1
- {streamdown-0.33.0.dist-info → streamdown-0.35.0.dist-info}/entry_points.txt +0 -0
- {streamdown-0.33.0.dist-info → streamdown-0.35.0.dist-info}/licenses/LICENSE.MIT +0 -0
streamdown/sd.py
CHANGED
|
@@ -57,6 +57,8 @@ Clipboard = true
|
|
|
57
57
|
Logging = false
|
|
58
58
|
Timeout = 0.1
|
|
59
59
|
Savebrace = true
|
|
60
|
+
Images = true
|
|
61
|
+
Links = true
|
|
60
62
|
|
|
61
63
|
[style]
|
|
62
64
|
Margin = 2
|
|
@@ -165,6 +167,7 @@ class ParseState:
|
|
|
165
167
|
self.is_pty = False
|
|
166
168
|
self.is_exec = False
|
|
167
169
|
self.maybe_prompt = False
|
|
170
|
+
self.prompt_regex = None
|
|
168
171
|
self.emit_flag = None
|
|
169
172
|
self.scrape = None
|
|
170
173
|
self.scrape_ix = 0
|
|
@@ -204,6 +207,7 @@ class ParseState:
|
|
|
204
207
|
self.in_underline = False
|
|
205
208
|
self.in_strikeout = False
|
|
206
209
|
self.block_depth = 0
|
|
210
|
+
self.block_type = None
|
|
207
211
|
|
|
208
212
|
self.exec_sub = None
|
|
209
213
|
self.exec_master = None
|
|
@@ -496,8 +500,12 @@ def line_format(line):
|
|
|
496
500
|
url = match.group(2)
|
|
497
501
|
return f'{LINK[0]}{url}\033\\{Style.Link}{description}{UNDERLINE[1]}{LINK[1]}{FGRESET}'
|
|
498
502
|
|
|
499
|
-
|
|
500
|
-
|
|
503
|
+
if state.Images:
|
|
504
|
+
line = re.sub(r"\!\[([^\]]*)\]\(([^\)]+)\)", process_images, line)
|
|
505
|
+
|
|
506
|
+
if state.Links:
|
|
507
|
+
line = re.sub(r"\[([^\]]+)\]\(([^\)]+)\)", process_links, line)
|
|
508
|
+
|
|
501
509
|
line = re.sub(r"\[\^(\d+)\]:?", footnotes, line)
|
|
502
510
|
|
|
503
511
|
tokenList = re.finditer(r"((~~|\*\*_|_\*\*|\*{1,3}|_{1,3}|`+)|[^~_*`]+)", line)
|
|
@@ -605,6 +613,7 @@ def parse(stream):
|
|
|
605
613
|
elif stream.fileno() in ready_in:
|
|
606
614
|
byte = os.read(stream.fileno(), 1)
|
|
607
615
|
TimeoutIx = 0
|
|
616
|
+
|
|
608
617
|
elif TimeoutIx == 0:
|
|
609
618
|
# This is our record separator for debugging - hands peaking
|
|
610
619
|
debug_write("🫣".encode('utf-8'))
|
|
@@ -614,7 +623,13 @@ def parse(stream):
|
|
|
614
623
|
byte = stream.read(1)
|
|
615
624
|
|
|
616
625
|
if byte is not None:
|
|
617
|
-
|
|
626
|
+
# This is the eol
|
|
627
|
+
if byte == b'':
|
|
628
|
+
if len(state.buffer) == 0:
|
|
629
|
+
break
|
|
630
|
+
else:
|
|
631
|
+
byte = b'\n'
|
|
632
|
+
|
|
618
633
|
state.buffer += byte
|
|
619
634
|
debug_write(byte)
|
|
620
635
|
|
|
@@ -622,8 +637,8 @@ def parse(stream):
|
|
|
622
637
|
|
|
623
638
|
line = state.buffer.decode('utf-8').replace('\t',' ')
|
|
624
639
|
state.has_newline = line.endswith('\n')
|
|
625
|
-
|
|
626
|
-
state.maybe_prompt = not state.has_newline and state.current()['none'] and re.match(
|
|
640
|
+
|
|
641
|
+
state.maybe_prompt = not state.has_newline and state.current()['none'] and re.match(state.prompt_regex, visible(line))
|
|
627
642
|
|
|
628
643
|
# let's wait for a newline
|
|
629
644
|
if state.maybe_prompt:
|
|
@@ -650,23 +665,31 @@ def parse(stream):
|
|
|
650
665
|
"""
|
|
651
666
|
|
|
652
667
|
# running this here avoids stray |
|
|
653
|
-
|
|
668
|
+
# So kimi doesn't newline after the <think> token and it uses some unicode triangle?. They'll
|
|
669
|
+
# newline at the end of it, but not the beginning.
|
|
670
|
+
block_match = re.match(r"^\s*((>\s*)+|[◁<].?think[>▷])(.*)", line)
|
|
654
671
|
if not state.in_code and block_match:
|
|
655
|
-
|
|
672
|
+
# wtf is this you might ask! Not all thinking models use < and > ...
|
|
673
|
+
# because why make life easy?
|
|
674
|
+
if block_match.group(1)[1:7] == '/think':
|
|
675
|
+
line = ''
|
|
656
676
|
state.block_depth = 0
|
|
657
677
|
yield RESET
|
|
658
|
-
elif block_match.group(1) == '
|
|
678
|
+
elif block_match.group(1)[1:6] == 'think':
|
|
679
|
+
line = block_match.group(3)
|
|
659
680
|
state.block_depth = 1
|
|
681
|
+
state.block_type = 'think'
|
|
660
682
|
else:
|
|
661
|
-
state.block_depth = block_match.group(
|
|
683
|
+
state.block_depth = block_match.group(1).count('>')
|
|
684
|
+
state.block_type = '>'
|
|
662
685
|
# we also need to consume those tokens
|
|
663
|
-
line = line[len(block_match.group(
|
|
686
|
+
line = line[len(block_match.group(1)):]
|
|
664
687
|
else:
|
|
665
|
-
if state.block_depth > 0:
|
|
688
|
+
if state.block_type == '>' and state.block_depth > 0:
|
|
666
689
|
yield FGRESET
|
|
667
690
|
state.block_depth = 0
|
|
668
691
|
|
|
669
|
-
#
|
|
692
|
+
# Collapse Multiple Empty Lines if not in code blocks
|
|
670
693
|
if not state.in_code:
|
|
671
694
|
is_empty = line.strip() == ""
|
|
672
695
|
|
|
@@ -831,7 +854,7 @@ def parse(stream):
|
|
|
831
854
|
while parts[-1] in [FGRESET, FORMATRESET]:
|
|
832
855
|
parts.pop()
|
|
833
856
|
|
|
834
|
-
tline_len = visible_length(tline)
|
|
857
|
+
tline_len = visible_length(tline.rstrip('\r\n'))
|
|
835
858
|
|
|
836
859
|
# now we find the new stuff:
|
|
837
860
|
ttl = 0
|
|
@@ -842,7 +865,7 @@ def parse(stream):
|
|
|
842
865
|
|
|
843
866
|
ttl += len(idx) if idx[0] != '\x1b' else 0
|
|
844
867
|
|
|
845
|
-
if ttl >
|
|
868
|
+
if ttl > tline_len:
|
|
846
869
|
break
|
|
847
870
|
|
|
848
871
|
|
|
@@ -1101,6 +1124,7 @@ def main():
|
|
|
1101
1124
|
parser.add_argument("-c", "--config", default=None, help="Use a custom config override")
|
|
1102
1125
|
parser.add_argument("-w", "--width", default="0", help="Set the width WIDTH")
|
|
1103
1126
|
parser.add_argument("-e", "--exec", help="Wrap a program EXEC for more 'proper' i/o handling")
|
|
1127
|
+
parser.add_argument("-p", "--prompt", default="^.*>\\s+$", help="A PCRE regex prompt to detect (default: %(default)s)")
|
|
1104
1128
|
parser.add_argument("-s", "--scrape", help="Scrape code snippets to a directory SCRAPE")
|
|
1105
1129
|
parser.add_argument("-v", "--version", action="store_true", help="Show version information")
|
|
1106
1130
|
args = parser.parse_args()
|
|
@@ -1134,7 +1158,7 @@ def main():
|
|
|
1134
1158
|
setattr(Style, color, apply_multipliers(style, color, H, S, V))
|
|
1135
1159
|
for attr in ['PrettyPad', 'PrettyBroken', 'Margin', 'ListIndent', 'Syntax']:
|
|
1136
1160
|
setattr(Style, attr, style.get(attr))
|
|
1137
|
-
for attr in ['CodeSpaces', 'Clipboard', 'Logging', 'Timeout', 'Savebrace']:
|
|
1161
|
+
for attr in ['Links', 'Images', 'CodeSpaces', 'Clipboard', 'Logging', 'Timeout', 'Savebrace']:
|
|
1138
1162
|
setattr(state, attr, features.get(attr))
|
|
1139
1163
|
|
|
1140
1164
|
|
|
@@ -1144,6 +1168,7 @@ def main():
|
|
|
1144
1168
|
|
|
1145
1169
|
Style.MarginSpaces = " " * Style.Margin
|
|
1146
1170
|
state.WidthArg = int(args.width) or style.get("Width") or 0
|
|
1171
|
+
state.prompt_regex = re.compile(args.prompt)
|
|
1147
1172
|
Style.Blockquote = f"{FG}{Style.Grey}│ "
|
|
1148
1173
|
width_calc()
|
|
1149
1174
|
|
streamdown/test.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamdown
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: A streaming markdown renderer for modern terminals with syntax highlighting
|
|
5
5
|
Project-URL: Homepage, https://github.com/day50-dev/Streamdown
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/day50-dev/Streamdown/issues
|
|
@@ -70,7 +70,7 @@ The optional `Clipboard` feature puts the final codeblock into your clipboard. S
|
|
|
70
70
|
[links.webm](https://github.com/user-attachments/assets/a5f71791-7c58-4183-ad3b-309f470c08a3)
|
|
71
71
|
|
|
72
72
|
### As well as everything else...
|
|
73
|
-
Here's the `Savebrace` feature with `
|
|
73
|
+
Here's the `Savebrace` feature with [`sidechat` and `sc-picker`](https://github.com/day50-dev/sidechat). You can have an ongoing conversation in tmux with your terminal session. Then use popups and fzf to insert command or coding blocks all with a keystroke.
|
|
74
74
|
|
|
75
75
|
This allows you to interactively debug in a way that the agent doesn't just wander off doing silly things.
|
|
76
76
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
streamdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
streamdown/qwen3.md,sha256=1e7ELkK-quwUeOmBDwXodFH-DlnfAcQWj32rjK6Zex4,542
|
|
3
|
-
streamdown/sd.py,sha256=
|
|
3
|
+
streamdown/sd.py,sha256=38nDZjHXQKorpUesXlTa0LkeR-gP6DPEw9h6exTthJo,45901
|
|
4
4
|
streamdown/ss,sha256=sel_phpaecrw6WGIHRLROsD7BFShf0rSDHheflwdUn8,277
|
|
5
5
|
streamdown/ss1,sha256=CUVf86_2zeAle2oQCeTfWYqtHBrAFR_UgvptuYMQzFU,3151
|
|
6
|
+
streamdown/test.txt,sha256=j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ,2
|
|
6
7
|
streamdown/plugins/README.md,sha256=KWqYELs9WkKJmuDzYv3cvPlZMkArsNCBUe4XDoTLjLA,1143
|
|
7
8
|
streamdown/plugins/latex.py,sha256=xZMGMdx_Sw4X1piZejXFHfEG9qazU4fGeceiMI0h13Y,648
|
|
8
|
-
streamdown-0.
|
|
9
|
-
streamdown-0.
|
|
10
|
-
streamdown-0.
|
|
11
|
-
streamdown-0.
|
|
12
|
-
streamdown-0.
|
|
9
|
+
streamdown-0.35.0.dist-info/METADATA,sha256=LnPBbC-QwFbxttWL3Kf46lwCYEpt8RPCZsLm5IHo47Y,10195
|
|
10
|
+
streamdown-0.35.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
11
|
+
streamdown-0.35.0.dist-info/entry_points.txt,sha256=HroKFsFMGf_h9PRTE96NjvjJQWupMW5TGP5RGUr1O_Q,74
|
|
12
|
+
streamdown-0.35.0.dist-info/licenses/LICENSE.MIT,sha256=SnY46EPirUsF20dZDR8HpyVgS2_4Tjxuc6f-4OdqO7U,1070
|
|
13
|
+
streamdown-0.35.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|