streamdown 0.34.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 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
- line = re.sub(r"\!\[([^\]]*)\]\(([^\)]+)\)", process_images, line)
500
- line = re.sub(r"\[([^\]]+)\]\(([^\)]+)\)", process_links, line)
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)
@@ -630,8 +638,7 @@ def parse(stream):
630
638
  line = state.buffer.decode('utf-8').replace('\t',' ')
631
639
  state.has_newline = line.endswith('\n')
632
640
 
633
- # I hate this. There should be better ways.
634
- state.maybe_prompt = not state.has_newline and state.current()['none'] and re.match(r'^.*>\s+$', visible(line))
641
+ state.maybe_prompt = not state.has_newline and state.current()['none'] and re.match(state.prompt_regex, visible(line))
635
642
 
636
643
  # let's wait for a newline
637
644
  if state.maybe_prompt:
@@ -658,19 +665,27 @@ def parse(stream):
658
665
  """
659
666
 
660
667
  # running this here avoids stray |
661
- block_match = re.match(r"^\s*((>\s*)+|<.?think>)", line)
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)
662
671
  if not state.in_code and block_match:
663
- if block_match.group(1) == '</think>':
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 = ''
664
676
  state.block_depth = 0
665
677
  yield RESET
666
- elif block_match.group(1) == '<think>':
678
+ elif block_match.group(1)[1:6] == 'think':
679
+ line = block_match.group(3)
667
680
  state.block_depth = 1
681
+ state.block_type = 'think'
668
682
  else:
669
- state.block_depth = block_match.group(0).count('>')
683
+ state.block_depth = block_match.group(1).count('>')
684
+ state.block_type = '>'
670
685
  # we also need to consume those tokens
671
- line = line[len(block_match.group(0)):]
686
+ line = line[len(block_match.group(1)):]
672
687
  else:
673
- if state.block_depth > 0:
688
+ if state.block_type == '>' and state.block_depth > 0:
674
689
  yield FGRESET
675
690
  state.block_depth = 0
676
691
 
@@ -839,7 +854,7 @@ def parse(stream):
839
854
  while parts[-1] in [FGRESET, FORMATRESET]:
840
855
  parts.pop()
841
856
 
842
- tline_len = visible_length(tline)
857
+ tline_len = visible_length(tline.rstrip('\r\n'))
843
858
 
844
859
  # now we find the new stuff:
845
860
  ttl = 0
@@ -850,7 +865,7 @@ def parse(stream):
850
865
 
851
866
  ttl += len(idx) if idx[0] != '\x1b' else 0
852
867
 
853
- if ttl > 1+tline_len:
868
+ if ttl > tline_len:
854
869
  break
855
870
 
856
871
 
@@ -1109,6 +1124,7 @@ def main():
1109
1124
  parser.add_argument("-c", "--config", default=None, help="Use a custom config override")
1110
1125
  parser.add_argument("-w", "--width", default="0", help="Set the width WIDTH")
1111
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)")
1112
1128
  parser.add_argument("-s", "--scrape", help="Scrape code snippets to a directory SCRAPE")
1113
1129
  parser.add_argument("-v", "--version", action="store_true", help="Show version information")
1114
1130
  args = parser.parse_args()
@@ -1142,7 +1158,7 @@ def main():
1142
1158
  setattr(Style, color, apply_multipliers(style, color, H, S, V))
1143
1159
  for attr in ['PrettyPad', 'PrettyBroken', 'Margin', 'ListIndent', 'Syntax']:
1144
1160
  setattr(Style, attr, style.get(attr))
1145
- for attr in ['CodeSpaces', 'Clipboard', 'Logging', 'Timeout', 'Savebrace']:
1161
+ for attr in ['Links', 'Images', 'CodeSpaces', 'Clipboard', 'Logging', 'Timeout', 'Savebrace']:
1146
1162
  setattr(state, attr, features.get(attr))
1147
1163
 
1148
1164
 
@@ -1152,6 +1168,7 @@ def main():
1152
1168
 
1153
1169
  Style.MarginSpaces = " " * Style.Margin
1154
1170
  state.WidthArg = int(args.width) or style.get("Width") or 0
1171
+ state.prompt_regex = re.compile(args.prompt)
1155
1172
  Style.Blockquote = f"{FG}{Style.Grey}│ "
1156
1173
  width_calc()
1157
1174
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: streamdown
3
- Version: 0.34.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 `screen-query` and `sq-picker` from [llmehelp](https://github.com/kristopolous/llmehelp). 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.
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,13 +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=zN3UlFR7v1iZkJSuu_SS5ie9Wurwcd_5ZUTaiwbziQQ,45105
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
6
  streamdown/test.txt,sha256=j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ,2
7
7
  streamdown/plugins/README.md,sha256=KWqYELs9WkKJmuDzYv3cvPlZMkArsNCBUe4XDoTLjLA,1143
8
8
  streamdown/plugins/latex.py,sha256=xZMGMdx_Sw4X1piZejXFHfEG9qazU4fGeceiMI0h13Y,648
9
- streamdown-0.34.0.dist-info/METADATA,sha256=0PwXjpsC4UNnlwe7QVIpQwv5jiszUq0xBFqlNlYMGFg,10216
10
- streamdown-0.34.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- streamdown-0.34.0.dist-info/entry_points.txt,sha256=HroKFsFMGf_h9PRTE96NjvjJQWupMW5TGP5RGUr1O_Q,74
12
- streamdown-0.34.0.dist-info/licenses/LICENSE.MIT,sha256=SnY46EPirUsF20dZDR8HpyVgS2_4Tjxuc6f-4OdqO7U,1070
13
- streamdown-0.34.0.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any