streamdown 0.19.0__py3-none-any.whl → 0.21.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 +90 -23
- {streamdown-0.19.0.dist-info → streamdown-0.21.0.dist-info}/METADATA +24 -15
- {streamdown-0.19.0.dist-info → streamdown-0.21.0.dist-info}/RECORD +6 -6
- {streamdown-0.19.0.dist-info → streamdown-0.21.0.dist-info}/WHEEL +0 -0
- {streamdown-0.19.0.dist-info → streamdown-0.21.0.dist-info}/entry_points.txt +0 -0
- {streamdown-0.19.0.dist-info → streamdown-0.21.0.dist-info}/licenses/LICENSE.MIT +0 -0
streamdown/sd.py
CHANGED
|
@@ -65,7 +65,7 @@ Symbol = { H = 1.00, S = 1.00, V = 1.50 }
|
|
|
65
65
|
Head = { H = 1.00, S = 1.00, V = 1.75 }
|
|
66
66
|
Grey = { H = 1.00, S = 0.25, V = 1.37 }
|
|
67
67
|
Bright = { H = 1.00, S = 2.00, V = 2.00 }
|
|
68
|
-
Syntax = "
|
|
68
|
+
Syntax = "dracula"
|
|
69
69
|
"""
|
|
70
70
|
|
|
71
71
|
def ensure_config_file():
|
|
@@ -92,6 +92,7 @@ BOLD = ["\033[1m", "\033[22m"]
|
|
|
92
92
|
UNDERLINE = ["\033[4m", "\033[24m"]
|
|
93
93
|
ITALIC = ["\033[3m", "\033[23m"]
|
|
94
94
|
STRIKEOUT = ["\033[9m", "\033[29m"]
|
|
95
|
+
LINK = ["\033]8;;", "\033]8;;\033\\"]
|
|
95
96
|
SUPER = [ 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079 ]
|
|
96
97
|
|
|
97
98
|
ESCAPE = r"\033\[[0-9;]*[mK]"
|
|
@@ -100,7 +101,7 @@ KEYCODE_RE = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
|
100
101
|
|
|
101
102
|
visible = lambda x: re.sub(ANSIESCAPE, "", x)
|
|
102
103
|
# cjk characters are double width
|
|
103
|
-
visible_length = lambda x: len(visible(x)) +
|
|
104
|
+
visible_length = lambda x: len(visible(x)) + dbl_count(x)
|
|
104
105
|
extract_ansi_codes = lambda text: re.findall(ESCAPE, text)
|
|
105
106
|
remove_ansi = lambda line, codeList: reduce(lambda line, code: line.replace(code, ''), codeList, line)
|
|
106
107
|
|
|
@@ -114,10 +115,12 @@ def debug_write(text):
|
|
|
114
115
|
|
|
115
116
|
def savebrace():
|
|
116
117
|
if state.Savebrace and state.code_buffer_raw:
|
|
118
|
+
tmp_dir = os.path.join(tempfile.gettempdir(), "sd")
|
|
119
|
+
os.makedirs(tmp_dir, exist_ok=True)
|
|
117
120
|
path = os.path.join(tempfile.gettempdir(), "sd", 'savebrace')
|
|
118
121
|
with open(path, "a") as f:
|
|
119
|
-
f.write(state.code_buffer_raw)
|
|
120
|
-
|
|
122
|
+
f.write(state.code_buffer_raw + "\x00")
|
|
123
|
+
f.flush()
|
|
121
124
|
|
|
122
125
|
class Goto(Exception):
|
|
123
126
|
pass
|
|
@@ -208,7 +211,7 @@ class ParseState:
|
|
|
208
211
|
return offset + (state.current_width(listwidth = True) if Style.PrettyBroken else self.WidthFull)
|
|
209
212
|
|
|
210
213
|
def current_width(self, listwidth = False):
|
|
211
|
-
return self.Width - (len(visible(self.space_left(listwidth)))
|
|
214
|
+
return self.Width - (len(visible(self.space_left(listwidth))))
|
|
212
215
|
|
|
213
216
|
def space_left(self, listwidth = False):
|
|
214
217
|
pre = ' ' * (len(state.list_item_stack)) * Style.ListIndent if listwidth else ''
|
|
@@ -216,6 +219,20 @@ class ParseState:
|
|
|
216
219
|
|
|
217
220
|
state = ParseState()
|
|
218
221
|
|
|
222
|
+
def override_background(style_name, background_color):
|
|
223
|
+
base_style = get_style_by_name(style_name)
|
|
224
|
+
base_style.background_color = background_color
|
|
225
|
+
for i in base_style:
|
|
226
|
+
i[1]['bgcolor'] = background_color
|
|
227
|
+
for i,v in base_style.styles.items():
|
|
228
|
+
if v and 'bg' in v:
|
|
229
|
+
base_style.styles[i] = re.sub(r'bg:[^ ]*', '', base_style.styles[i] )
|
|
230
|
+
for k,v in base_style._styles.items():
|
|
231
|
+
if v[4] != '':
|
|
232
|
+
v[4] = ''
|
|
233
|
+
|
|
234
|
+
return base_style
|
|
235
|
+
|
|
219
236
|
def format_table(rowList):
|
|
220
237
|
num_cols = len(rowList)
|
|
221
238
|
row_height = 0
|
|
@@ -224,15 +241,20 @@ def format_table(rowList):
|
|
|
224
241
|
# Calculate max width per column (integer division)
|
|
225
242
|
# Subtract num_cols + 1 for the vertical borders '│'
|
|
226
243
|
available_width = state.current_width() - (num_cols + 1)
|
|
227
|
-
|
|
244
|
+
|
|
245
|
+
width_base = available_width // num_cols
|
|
246
|
+
width_mod = available_width % num_cols
|
|
247
|
+
|
|
248
|
+
col_width_list = [width_base + (1 if i < width_mod else 0) for i in range(num_cols)]
|
|
228
249
|
bg_color = Style.Mid if state.in_table == Style.Head else Style.Dark
|
|
229
250
|
state.bg = f"{BG}{bg_color}"
|
|
230
251
|
|
|
231
252
|
# First Pass: Wrap text and calculate row heights
|
|
232
253
|
# Note this is where every cell is formatted so if
|
|
233
254
|
# you are styling, do it before here!
|
|
234
|
-
for
|
|
235
|
-
|
|
255
|
+
for ix in range(len(rowList)):
|
|
256
|
+
row = rowList[ix]
|
|
257
|
+
wrapped_cell = text_wrap(row, width=col_width_list[ix], force_truncate=True)
|
|
236
258
|
|
|
237
259
|
# Ensure at least one line, even for empty cells
|
|
238
260
|
if not wrapped_cell:
|
|
@@ -248,13 +270,14 @@ def format_table(rowList):
|
|
|
248
270
|
line_segments = []
|
|
249
271
|
|
|
250
272
|
# Now we want to snatch this row index from all our cells
|
|
251
|
-
for
|
|
273
|
+
for iy in range(len(wrapped_cellList)):
|
|
274
|
+
cell = wrapped_cellList[iy]
|
|
252
275
|
segment = ''
|
|
253
276
|
if ix < len(cell):
|
|
254
277
|
segment = cell[ix]
|
|
255
278
|
|
|
256
279
|
# Margin logic is correctly indented here
|
|
257
|
-
margin_needed =
|
|
280
|
+
margin_needed = col_width_list[iy] - visible_length(segment)
|
|
258
281
|
margin_segment = segment + (" " * max(0, margin_needed))
|
|
259
282
|
line_segments.append(f"{BG}{bg_color}{extra} {margin_segment}")
|
|
260
283
|
|
|
@@ -269,7 +292,7 @@ def emit_h(level, text):
|
|
|
269
292
|
text = line_format(text)
|
|
270
293
|
spaces_to_center = (state.current_width() - visible_length(text)) / 2
|
|
271
294
|
if level == 1: #
|
|
272
|
-
return f"{state.space_left()}\n{state.space_left()}{BOLD[
|
|
295
|
+
return f"{state.space_left()}\n{state.space_left()}{BOLD[1]}{' ' * math.floor(spaces_to_center)}{text}{BOLD[1]}"
|
|
273
296
|
elif level == 2: ##
|
|
274
297
|
return f"{state.space_left()}\n{state.space_left()}{BOLD[0]}{FG}{Style.Bright}{' ' * math.floor(spaces_to_center)}{text}{' ' * math.ceil(spaces_to_center)}{BOLD[1]}{FGRESET}"
|
|
275
298
|
elif level == 3: ###
|
|
@@ -338,7 +361,16 @@ def ansi_collapse(codelist, inp):
|
|
|
338
361
|
|
|
339
362
|
def split_text(text):
|
|
340
363
|
return [x for x in re.split(
|
|
341
|
-
r'(?<=[
|
|
364
|
+
r'(?<=['
|
|
365
|
+
r'\u3000-\u303F'
|
|
366
|
+
r'\u4E00-\u9FFF'
|
|
367
|
+
r'\u3400-\u4DBF'
|
|
368
|
+
r'\uF900-\uFAFF'
|
|
369
|
+
r'])|(?=['
|
|
370
|
+
#r'\u4E00-\u9FFF'
|
|
371
|
+
r'\u3400-\u4DBF'
|
|
372
|
+
r'\uF900-\uFAFF'
|
|
373
|
+
r'])|\s+',
|
|
342
374
|
text
|
|
343
375
|
) if x]
|
|
344
376
|
|
|
@@ -347,7 +379,11 @@ def text_wrap(text, width = -1, indent = 0, first_line_prefix="", subsequent_lin
|
|
|
347
379
|
width = state.Width
|
|
348
380
|
|
|
349
381
|
# The empty word clears the buffer at the end.
|
|
350
|
-
|
|
382
|
+
formatted = line_format(text)
|
|
383
|
+
#print(bytes(formatted, 'utf-8'), formatted)
|
|
384
|
+
words = split_text(formatted) + [""]
|
|
385
|
+
#print([bytes(i, 'utf-8') for i in words])
|
|
386
|
+
|
|
351
387
|
lines = []
|
|
352
388
|
current_line = ""
|
|
353
389
|
current_style = []
|
|
@@ -378,6 +414,10 @@ def text_wrap(text, width = -1, indent = 0, first_line_prefix="", subsequent_lin
|
|
|
378
414
|
margin = max(0, width - visible_length(line_content))
|
|
379
415
|
|
|
380
416
|
if line_content.strip() != "":
|
|
417
|
+
# We make absolutely positively sure beyond any doubt
|
|
418
|
+
# that we have closed our hyperlink OSC
|
|
419
|
+
if LINK[0] in line_content:
|
|
420
|
+
line_content += LINK[1]
|
|
381
421
|
lines.append(line_content + state.bg + ' ' * margin)
|
|
382
422
|
|
|
383
423
|
current_line = (" " * indent) + "".join(current_style) + word
|
|
@@ -395,14 +435,23 @@ def text_wrap(text, width = -1, indent = 0, first_line_prefix="", subsequent_lin
|
|
|
395
435
|
|
|
396
436
|
return lines
|
|
397
437
|
|
|
438
|
+
def dbl_count(s):
|
|
439
|
+
dbl_re = re.compile(
|
|
440
|
+
r'[\u2e80-\u2eff\u3000-\u303f\u3400-\u4dbf'
|
|
441
|
+
r'\U00004e00-\U00009fff\U0001f300-\U0001f6ff'
|
|
442
|
+
r'\U0001f900-\U0001f9ff\U0001fa70-\U0001faff]',
|
|
443
|
+
re.UNICODE
|
|
444
|
+
)
|
|
445
|
+
return len(dbl_re.findall(visible(s)))
|
|
446
|
+
|
|
398
447
|
def cjk_count(s):
|
|
399
448
|
cjk_re = re.compile(
|
|
400
449
|
r'[\u4E00-\u9FFF' # CJK Unified Ideographs
|
|
401
|
-
r'
|
|
402
|
-
r'
|
|
403
|
-
r'
|
|
404
|
-
r'
|
|
405
|
-
r'
|
|
450
|
+
r'\u3400-\u4DBF' # CJK Unified Ideographs Extension A
|
|
451
|
+
r'\uF900-\uFAFF' # CJK Compatibility Ideographs
|
|
452
|
+
r'\uFF00-\uFFEF' # CJK Compatibility Punctuation
|
|
453
|
+
r'\u3000-\u303F' # CJK Symbols and Punctuation
|
|
454
|
+
r'\U0002F800-\U0002FA1F]' # CJK Compatibility Ideographs Supplement
|
|
406
455
|
)
|
|
407
456
|
|
|
408
457
|
return len(cjk_re.findall(visible(s)))
|
|
@@ -427,7 +476,7 @@ def line_format(line):
|
|
|
427
476
|
def process_links(match):
|
|
428
477
|
description = match.group(1)
|
|
429
478
|
url = match.group(2)
|
|
430
|
-
return f'
|
|
479
|
+
return f'{LINK[0]}{url}\033\\{Style.Link}{description}{UNDERLINE[1]}{LINK[1]}{FGRESET}'
|
|
431
480
|
|
|
432
481
|
line = re.sub(r"\!\[([^\]]*)\]\(([^\)]+)\)", process_images, line)
|
|
433
482
|
line = re.sub(r"\[([^\]]+)\]\(([^\)]+)\)", process_links, line)
|
|
@@ -444,19 +493,24 @@ def line_format(line):
|
|
|
444
493
|
# This trick makes sure that things like `` ` `` render right.
|
|
445
494
|
if "`" in token and (not state.inline_code or state.inline_code == token):
|
|
446
495
|
if state.inline_code:
|
|
496
|
+
if ' ' in state.inline_code:
|
|
497
|
+
savebrace()
|
|
447
498
|
state.inline_code = False
|
|
448
499
|
else:
|
|
449
500
|
state.inline_code = token
|
|
501
|
+
state.code_buffer_raw = ''
|
|
450
502
|
|
|
451
503
|
if state.inline_code:
|
|
452
504
|
result += f'{BG}{Style.Mid}'
|
|
453
505
|
else:
|
|
454
506
|
result += state.bg
|
|
507
|
+
state.code_buffer_raw = ''
|
|
455
508
|
|
|
456
509
|
# This is important here because we ignore formatting
|
|
457
510
|
# inside of our code block.
|
|
458
511
|
elif state.inline_code:
|
|
459
512
|
result += token
|
|
513
|
+
state.code_buffer_raw += token
|
|
460
514
|
|
|
461
515
|
elif token == '~~' and (state.in_strikeout or not_text(prev_token)):
|
|
462
516
|
state.in_strikeout = not state.in_strikeout
|
|
@@ -559,6 +613,7 @@ def parse(stream):
|
|
|
559
613
|
continue
|
|
560
614
|
|
|
561
615
|
state.buffer = b''
|
|
616
|
+
"""
|
|
562
617
|
# Run through the plugins first
|
|
563
618
|
res = latex.Plugin(line, state, Style)
|
|
564
619
|
if res is True:
|
|
@@ -569,6 +624,7 @@ def parse(stream):
|
|
|
569
624
|
for row in res:
|
|
570
625
|
yield row
|
|
571
626
|
continue
|
|
627
|
+
"""
|
|
572
628
|
|
|
573
629
|
# running this here avoids stray |
|
|
574
630
|
block_match = re.match(r"^\s*((>\s*)+|<.?think>)", line)
|
|
@@ -639,7 +695,6 @@ def parse(stream):
|
|
|
639
695
|
state.code_language = 'Bash'
|
|
640
696
|
|
|
641
697
|
if state.in_code:
|
|
642
|
-
savebrace()
|
|
643
698
|
state.code_buffer = state.code_buffer_raw = ""
|
|
644
699
|
state.code_gen = 0
|
|
645
700
|
state.code_first_line = True
|
|
@@ -671,6 +726,7 @@ def parse(stream):
|
|
|
671
726
|
open(os.path.join(state.scrape, f"file_{state.scrape_ix}.{ext}"), 'w').write(state.code_buffer_raw)
|
|
672
727
|
state.scrape_ix += 1
|
|
673
728
|
|
|
729
|
+
savebrace()
|
|
674
730
|
state.code_language = None
|
|
675
731
|
state.code_indent = 0
|
|
676
732
|
code_type = state.in_code
|
|
@@ -698,10 +754,10 @@ def parse(stream):
|
|
|
698
754
|
state.code_first_line = False
|
|
699
755
|
try:
|
|
700
756
|
lexer = get_lexer_by_name(state.code_language)
|
|
701
|
-
custom_style =
|
|
757
|
+
custom_style = override_background(Style.Syntax, ansi2hex(Style.Dark))
|
|
702
758
|
except pygments.util.ClassNotFound:
|
|
703
759
|
lexer = get_lexer_by_name("Bash")
|
|
704
|
-
custom_style =
|
|
760
|
+
custom_style = override_background("default", ansi2hex(Style.Dark))
|
|
705
761
|
|
|
706
762
|
formatter = TerminalTrueColorFormatter(style=custom_style)
|
|
707
763
|
if line.startswith(' ' * state.code_indent):
|
|
@@ -720,6 +776,7 @@ def parse(stream):
|
|
|
720
776
|
else:
|
|
721
777
|
continue
|
|
722
778
|
|
|
779
|
+
highlighted_code = highlight(line, lexer, formatter)
|
|
723
780
|
indent, line_wrap = code_wrap(line)
|
|
724
781
|
|
|
725
782
|
state.where_from = "in code"
|
|
@@ -731,15 +788,18 @@ def parse(stream):
|
|
|
731
788
|
# then naively search back until our visible_lengths() match. This is not fast and there's certainly smarter
|
|
732
789
|
# ways of doing it but this thing is way trickery than you think
|
|
733
790
|
highlighted_code = highlight(state.code_buffer + tline, lexer, formatter)
|
|
791
|
+
#print("(",highlighted_code,")")
|
|
734
792
|
|
|
735
793
|
# Sometimes the highlighter will do things like a full reset or a background reset.
|
|
736
794
|
# This is not what we want
|
|
737
|
-
highlighted_code = re.sub(r"\033\[
|
|
795
|
+
highlighted_code = re.sub(r"\033\[[34]9(;00|)m", '', highlighted_code)
|
|
738
796
|
|
|
739
797
|
# Since we are streaming we ignore the resets and newlines at the end
|
|
740
798
|
if highlighted_code.endswith(FGRESET + "\n"):
|
|
741
799
|
highlighted_code = highlighted_code[: -(1 + len(FGRESET))]
|
|
742
800
|
|
|
801
|
+
#print(bytes(highlighted_code, 'utf-8'))
|
|
802
|
+
|
|
743
803
|
# turns out highlight will eat leading newlines on empty lines
|
|
744
804
|
vislen = visible_length(state.code_buffer.lstrip())
|
|
745
805
|
|
|
@@ -927,6 +987,11 @@ def emit(inp):
|
|
|
927
987
|
if len(buffer):
|
|
928
988
|
print(buffer.pop(0), file=sys.stdout, end="", flush=True)
|
|
929
989
|
|
|
990
|
+
def ansi2hex(ansi_code):
|
|
991
|
+
parts = ansi_code.strip('m').split(";")
|
|
992
|
+
r, g, b = map(int, parts)
|
|
993
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
994
|
+
|
|
930
995
|
def apply_multipliers(name, H, S, V):
|
|
931
996
|
m = _style.get(name)
|
|
932
997
|
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"]))
|
|
@@ -1042,6 +1107,8 @@ def main():
|
|
|
1042
1107
|
os.close(state.exec_master)
|
|
1043
1108
|
if state.exec_sub:
|
|
1044
1109
|
state.exec_sub.wait()
|
|
1110
|
+
|
|
1111
|
+
print(RESET, end="")
|
|
1045
1112
|
sys.exit(state.exit)
|
|
1046
1113
|
|
|
1047
1114
|
if __name__ == "__main__":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamdown
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.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
|
|
@@ -36,44 +36,51 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
</p>
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
Streamdown works with [simonw's llm](https://github.com/simonw/llm)
|
|
40
|
-
|
|
39
|
+
Streamdown works with any streaming markdown such as [simonw's llm](https://github.com/simonw/llm) or even something basic like curl.
|
|
40
|
+
|
|
41
|
+
It supports standard piping and files as arguments like any normal pager but can also run as a wrapper so you retain full keyboard interactivity. Arrow keys, control, alt, all still work.
|
|
41
42
|
```bash
|
|
42
43
|
$ pip install streamdown
|
|
43
44
|
```
|
|
44
45
|

|
|
45
46
|
|
|
46
47
|
### Provides clean copyable code for long code lines
|
|
47
|
-
Other renderers inject line breaks when copying code that wraps around.
|
|
48
|
+
Other renderers inject line breaks when copying code that wraps around. Streamdown's better and now you are too!
|
|
48
49
|

|
|
49
|
-
**Tip**: You can make things prettier if you don't mind if this guarantee is broken. See the `PrettyBroken` flag below!
|
|
50
|
+
**Tip**: You can make things prettier if you don't mind if this guarantee is broken. See the `PrettyBroken` flag below! (There's still 2 other convenient ways of getting code blocks out.)
|
|
50
51
|
|
|
51
52
|
### Supports images
|
|
52
53
|
Here's kitty and alacritty.
|
|
53
54
|

|
|
54
55
|
|
|
55
56
|
### Supports hyperlinks (OSC 8) and clipboard (OSC 52)
|
|
57
|
+
The optional `Clipboard` feature puts the final codeblock into your clipboard. See below for details.
|
|
58
|
+
|
|
56
59
|
[links.webm](https://github.com/user-attachments/assets/a5f71791-7c58-4183-ad3b-309f470c08a3)
|
|
57
60
|
|
|
58
|
-
###
|
|
59
|
-
|
|
61
|
+
### As well as everything else...
|
|
62
|
+
Here's the `Savebrace` feature with `screen-query` and `sd-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.
|
|
63
|
+
|
|
64
|
+
This allows you to interactively debug in a way that the agent doesn't just wander off doing silly things.
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-

|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
Compare how streamdown wraps
|
|
66
|
-
|
|
69
|
+
### ...It even supports CJK
|
|
70
|
+
Compare how streamdown wraps and spaces this tabular Chinese description of programming languages to other leading markdown renderers.
|
|
71
|
+
|
|
72
|
+
Only one generates the text without truncation. 很美!
|
|
73
|
+

|
|
67
74
|
|
|
68
75
|
### Colors are highly (and quickly) configurable for people who care a lot, or just a little.
|
|
69
76
|

|
|
70
77
|
|
|
71
|
-
### Has a [Plugin](https://github.com/kristopolous/Streamdown/tree/main/streamdown/plugins) system to extend the parser and
|
|
78
|
+
### Has a [Plugin](https://github.com/kristopolous/Streamdown/tree/main/streamdown/plugins) system to extend the parser and renderers.
|
|
72
79
|
For instance, here is the [latex plugin](https://github.com/kristopolous/Streamdown/blob/main/streamdown/plugins/latex.py) doing math inside a table:
|
|
73
80
|

|
|
74
81
|
|
|
75
82
|
|
|
76
|
-
##
|
|
83
|
+
## Configuration
|
|
77
84
|
|
|
78
85
|
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.
|
|
79
86
|
|
|
@@ -81,7 +88,9 @@ Here are the sections:
|
|
|
81
88
|
|
|
82
89
|
**`[style]`**
|
|
83
90
|
|
|
84
|
-
Defines the base Hue (H), Saturation (S), and Value (V) from which all other palette colors are derived.
|
|
91
|
+
Defines the base Hue (H), Saturation (S), and Value (V) from which all other palette colors are derived. This can also be specified at runtime via command line arguments. See below!
|
|
92
|
+
|
|
93
|
+
The default values are [at the beginning of the source](https://github.com/kristopolous/Streamdown/blob/main/streamdown/sd.py#L33).
|
|
85
94
|
|
|
86
95
|
* `HSV`: [ 0.0 - 1.0, 0.0 - 1.0, 0.0 - 1.0 ]
|
|
87
96
|
* `Dark`: Multipliers for background elements, code blocks.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
streamdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
streamdown/sd.py,sha256=
|
|
2
|
+
streamdown/sd.py,sha256=GmxttpBmMR3yJU2UGM9ESOYqFQMU28HYhVwAKcsMERM,41579
|
|
3
3
|
streamdown/ss,sha256=sel_phpaecrw6WGIHRLROsD7BFShf0rSDHheflwdUn8,277
|
|
4
4
|
streamdown/ss1,sha256=CUVf86_2zeAle2oQCeTfWYqtHBrAFR_UgvptuYMQzFU,3151
|
|
5
5
|
streamdown/plugins/README.md,sha256=KWqYELs9WkKJmuDzYv3cvPlZMkArsNCBUe4XDoTLjLA,1143
|
|
6
6
|
streamdown/plugins/latex.py,sha256=xZMGMdx_Sw4X1piZejXFHfEG9qazU4fGeceiMI0h13Y,648
|
|
7
|
-
streamdown-0.
|
|
8
|
-
streamdown-0.
|
|
9
|
-
streamdown-0.
|
|
10
|
-
streamdown-0.
|
|
11
|
-
streamdown-0.
|
|
7
|
+
streamdown-0.21.0.dist-info/METADATA,sha256=AcCbhBJvkAvdcB-4_1qDhXkrAp02-4vsKWPaoLW2jXg,8843
|
|
8
|
+
streamdown-0.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
streamdown-0.21.0.dist-info/entry_points.txt,sha256=HroKFsFMGf_h9PRTE96NjvjJQWupMW5TGP5RGUr1O_Q,74
|
|
10
|
+
streamdown-0.21.0.dist-info/licenses/LICENSE.MIT,sha256=SnY46EPirUsF20dZDR8HpyVgS2_4Tjxuc6f-4OdqO7U,1070
|
|
11
|
+
streamdown-0.21.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|