streamtex 0.2.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.
Files changed (78) hide show
  1. streamtex-0.2.0/PKG-INFO +869 -0
  2. streamtex-0.2.0/README.md +311 -0
  3. streamtex-0.2.0/documentation/streamtex_cheatsheet_en.md +850 -0
  4. streamtex-0.2.0/pyproject.toml +64 -0
  5. streamtex-0.2.0/setup.cfg +4 -0
  6. streamtex-0.2.0/streamtex/__init__.py +88 -0
  7. streamtex-0.2.0/streamtex/__main__.py +70 -0
  8. streamtex-0.2.0/streamtex/auth.py +284 -0
  9. streamtex-0.2.0/streamtex/bib.py +1253 -0
  10. streamtex-0.2.0/streamtex/bib_preview.py +382 -0
  11. streamtex-0.2.0/streamtex/block_helpers.py +249 -0
  12. streamtex-0.2.0/streamtex/blocks.py +350 -0
  13. streamtex-0.2.0/streamtex/book.py +1049 -0
  14. streamtex-0.2.0/streamtex/code.py +73 -0
  15. streamtex-0.2.0/streamtex/collection.py +294 -0
  16. streamtex-0.2.0/streamtex/constants.py +4 -0
  17. streamtex-0.2.0/streamtex/container.py +102 -0
  18. streamtex-0.2.0/streamtex/enums.py +47 -0
  19. streamtex-0.2.0/streamtex/export.py +230 -0
  20. streamtex-0.2.0/streamtex/export_widgets.py +280 -0
  21. streamtex-0.2.0/streamtex/grid.py +189 -0
  22. streamtex-0.2.0/streamtex/gsheet.py +340 -0
  23. streamtex-0.2.0/streamtex/image.py +130 -0
  24. streamtex-0.2.0/streamtex/image_utils.py +49 -0
  25. streamtex-0.2.0/streamtex/inspector.py +918 -0
  26. streamtex-0.2.0/streamtex/link_preview.py +181 -0
  27. streamtex-0.2.0/streamtex/list.py +190 -0
  28. streamtex-0.2.0/streamtex/marker.py +499 -0
  29. streamtex-0.2.0/streamtex/mermaid.py +218 -0
  30. streamtex-0.2.0/streamtex/overlay.py +79 -0
  31. streamtex-0.2.0/streamtex/plantuml.py +248 -0
  32. streamtex-0.2.0/streamtex/search.py +124 -0
  33. streamtex-0.2.0/streamtex/space.py +34 -0
  34. streamtex-0.2.0/streamtex/static/default.css +138 -0
  35. streamtex-0.2.0/streamtex/styles/__init__.py +28 -0
  36. streamtex-0.2.0/streamtex/styles/base.py +66 -0
  37. streamtex-0.2.0/streamtex/styles/container.py +389 -0
  38. streamtex-0.2.0/streamtex/styles/core.py +316 -0
  39. streamtex-0.2.0/streamtex/styles/text.py +290 -0
  40. streamtex-0.2.0/streamtex/styles/visibility.py +13 -0
  41. streamtex-0.2.0/streamtex/tikz.py +321 -0
  42. streamtex-0.2.0/streamtex/toc.py +142 -0
  43. streamtex-0.2.0/streamtex/utils.py +33 -0
  44. streamtex-0.2.0/streamtex/write.py +130 -0
  45. streamtex-0.2.0/streamtex/zoom.py +95 -0
  46. streamtex-0.2.0/streamtex.egg-info/PKG-INFO +869 -0
  47. streamtex-0.2.0/streamtex.egg-info/SOURCES.txt +76 -0
  48. streamtex-0.2.0/streamtex.egg-info/dependency_links.txt +1 -0
  49. streamtex-0.2.0/streamtex.egg-info/requires.txt +12 -0
  50. streamtex-0.2.0/streamtex.egg-info/top_level.txt +1 -0
  51. streamtex-0.2.0/tests/test_auth.py +281 -0
  52. streamtex-0.2.0/tests/test_bib.py +895 -0
  53. streamtex-0.2.0/tests/test_book_integration.py +263 -0
  54. streamtex-0.2.0/tests/test_code.py +179 -0
  55. streamtex-0.2.0/tests/test_collection.py +325 -0
  56. streamtex-0.2.0/tests/test_container.py +694 -0
  57. streamtex-0.2.0/tests/test_enums.py +25 -0
  58. streamtex-0.2.0/tests/test_export.py +298 -0
  59. streamtex-0.2.0/tests/test_export_guard.py +70 -0
  60. streamtex-0.2.0/tests/test_export_widgets.py +391 -0
  61. streamtex-0.2.0/tests/test_grid.py +543 -0
  62. streamtex-0.2.0/tests/test_gsheet.py +256 -0
  63. streamtex-0.2.0/tests/test_image.py +276 -0
  64. streamtex-0.2.0/tests/test_inspector.py +870 -0
  65. streamtex-0.2.0/tests/test_lazy_blocks.py +324 -0
  66. streamtex-0.2.0/tests/test_list.py +538 -0
  67. streamtex-0.2.0/tests/test_load_atomic.py +55 -0
  68. streamtex-0.2.0/tests/test_marker.py +230 -0
  69. streamtex-0.2.0/tests/test_mermaid.py +215 -0
  70. streamtex-0.2.0/tests/test_overlay.py +259 -0
  71. streamtex-0.2.0/tests/test_plantuml.py +212 -0
  72. streamtex-0.2.0/tests/test_space.py +312 -0
  73. streamtex-0.2.0/tests/test_styles.py +245 -0
  74. streamtex-0.2.0/tests/test_tikz.py +229 -0
  75. streamtex-0.2.0/tests/test_toc.py +111 -0
  76. streamtex-0.2.0/tests/test_utils.py +128 -0
  77. streamtex-0.2.0/tests/test_write.py +65 -0
  78. streamtex-0.2.0/tests/test_zoom.py +172 -0
@@ -0,0 +1,869 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamtex
3
+ Version: 0.2.0
4
+ Summary: A Streamlit wrapper for styled content rendering
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: streamlit>=1.54.0
9
+ Requires-Dist: beautifulsoup4>=4.10.0
10
+ Requires-Dist: requests>=2.28.0
11
+ Requires-Dist: watchdog
12
+ Requires-Dist: graphviz>=0.21
13
+ Requires-Dist: matplotlib>=3.10.8
14
+ Requires-Dist: streamlit-mermaid>=0.3.0
15
+ Requires-Dist: mermaid-py>=0.5.0
16
+ Requires-Dist: streamlit-ace>=0.1.1
17
+ Provides-Extra: inspector
18
+ Requires-Dist: streamlit-ace>=0.1.1; extra == "inspector"
19
+
20
+ # StreamTeX Complete Cheatsheet
21
+
22
+ ## Essential Imports
23
+
24
+ ```python
25
+ # Block files (mandatory)
26
+ import streamlit as st
27
+ from streamtex import *
28
+ import streamtex as stx
29
+ from streamtex.styles import Style as ns, StyleGrid as sg
30
+ from streamtex.enums import Tags as t, ListTypes as lt
31
+ from custom.styles import Styles as s
32
+
33
+ # Book entry point (book.py)
34
+ import streamlit as st
35
+ import setup
36
+ import streamtex as stx
37
+ from streamtex import st_book, TOCConfig, MarkerConfig
38
+ from pathlib import Path
39
+ from custom.styles import Styles as s
40
+ from custom.themes import dark
41
+ import streamtex.styles as sts
42
+ import blocks
43
+ ```
44
+
45
+ ## Style Organization
46
+
47
+ ### Custom Style Class
48
+
49
+ ```python
50
+ class BlockStyles:
51
+ """Custom styles defined locally and used only for this block"""
52
+ # Composed styles
53
+ content = s.Large + s.center_txt
54
+ lime_bold = s.text.colors.lime + s.bold
55
+ bold_green = s.project.colors.green_01 + s.bold
56
+
57
+ # Styles with alignment
58
+ green_title = bold_green + s.huge + s.center_txt
59
+
60
+ # Styles with borders
61
+ border = s.container.borders.color(s.text.colors.black) + \
62
+ s.container.borders.solid_border + \
63
+ s.container.borders.size("2px")
64
+
65
+ # Styles with padding
66
+ side_padding = ns("padding: 10pt 36pt;")
67
+ bs = BlockStyles
68
+ ```
69
+
70
+ ### Style Composition
71
+
72
+ ```python
73
+ # Create from CSS string
74
+ my_style = Style("color: red; font-weight: bold;", "my_style")
75
+
76
+ # Copy existing style with new ID
77
+ my_title = Style.create(s.bold + s.Large + s.center_txt, "my_title")
78
+
79
+ # Compose with + operator
80
+ heading = s.huge + s.bold + s.project.colors.primary
81
+
82
+ # Remove properties with - operator
83
+ no_bold = heading - s.bold
84
+ ```
85
+
86
+ ## Basic Elements
87
+
88
+ ### Blocks and Text
89
+
90
+ ```python
91
+ # Simple block with style
92
+ with st_block(s.center_txt):
93
+ st_write(bs.green_title, "My Title")
94
+ st_space(size=3)
95
+
96
+ # Block with list
97
+ with st_block(s.center_txt):
98
+ with st_list(
99
+ list_type=lt.ordered,
100
+ li_style=bs.content) as l:
101
+ with l.item(): st_write("First item")
102
+ with l.item(): st_write("Second item")
103
+ ```
104
+
105
+ ### st_write — Full Signature
106
+
107
+ ```python
108
+ st_write(
109
+ *args, # Style objects, text, or (Style, text) tuples
110
+ tag=t.span, # HTML tag: t.div, t.span, t.h1, t.p, t.section...
111
+ link="", # Optional hyperlink URL
112
+ no_link_decor=False, # Remove underline from links
113
+ toc_lvl=None, # TOC level: "1", "+1", "-1"
114
+ label="", # Custom TOC entry label
115
+ marker=None, # Per-heading marker control (True/False/None=auto)
116
+ )
117
+
118
+ # Inline mixed styles — ONE st_write with tuples (multiple calls stack vertically!)
119
+ st_write(s.Large, (s.text.colors.red, "Red "), (s.text.colors.blue, "Blue"))
120
+
121
+ # Register in TOC
122
+ st_write(bs.title, "Section Title", tag=t.div, toc_lvl="1")
123
+ st_write(bs.subtitle, "Subsection", toc_lvl="+1")
124
+
125
+ # Exclude a heading from marker navigation
126
+ st_write(s.huge, "Appendix", toc_lvl="1", marker=False)
127
+ # Force include in markers
128
+ st_write(s.huge, "Important", toc_lvl="2", marker=True)
129
+ ```
130
+
131
+ ### Images and Media
132
+
133
+ ```python
134
+ # Simple image
135
+ st_image(uri="image.png")
136
+
137
+ # Image with dimensions
138
+ st_image(uri="image.png", width="1150px", height="735.34px")
139
+
140
+ # Image with link
141
+ st_image(uri="image.png", link="https://example.com")
142
+
143
+ # Image with auto-height style
144
+ st_image(s.container.sizes.height_auto, uri="image.png")
145
+ ```
146
+
147
+ ### Grids and Tables (Responsive-First)
148
+
149
+ Multi-column grids MUST use responsive patterns so columns stack on narrow screens.
150
+
151
+ ```python
152
+ # GOOD — responsive 2-column (stacks below 350px per column)
153
+ with st_grid(
154
+ cols=s.project.containers.responsive_2col,
155
+ grid_style=s.project.containers.gap_24,
156
+ ) as g:
157
+ with g.cell(): st_write("Panel A")
158
+ with g.cell(): st_write("Panel B")
159
+
160
+ # GOOD — responsive 3-column
161
+ with st_grid(cols=s.project.containers.responsive_3col) as g:
162
+ with g.cell(): st_write("Card 1")
163
+ with g.cell(): st_write("Card 2")
164
+ with g.cell(): st_write("Card 3")
165
+
166
+ # GOOD — responsive card grid
167
+ with st_grid(cols=s.project.containers.responsive_cards) as g:
168
+ with g.cell(): st_image(uri="image1.png")
169
+ with g.cell(): st_image(uri="image2.png")
170
+ with g.cell(): st_image(uri="image3.png")
171
+
172
+ # Responsive presets (defined in custom/styles.py):
173
+ # responsive_2col = "repeat(auto-fit, minmax(350px, 1fr))"
174
+ # responsive_3col = "repeat(auto-fit, minmax(280px, 1fr))"
175
+ # responsive_cards = "repeat(auto-fit, minmax(200px, 1fr))"
176
+
177
+ # BAD — fixed columns, never wraps on narrow screens
178
+ # st_grid(cols=2)
179
+ # st_grid(cols="2fr 3fr")
180
+
181
+ # OK — fixed columns ONLY for data tables with known column count
182
+ with st_grid(cols=2, cell_styles=bs.border) as g:
183
+ with g.cell(): st_write("Header 1")
184
+ with g.cell(): st_write("Header 2")
185
+
186
+ # Grid (table) with per-cell styles
187
+ with st_grid(
188
+ cols=2,
189
+ cell_styles=sg.create("A1,A3", s.project.colors.orange_02) +
190
+ sg.create("A2", s.project.colors.red_01) +
191
+ sg.create("A1:B3", s.bold + s.LARGE)
192
+ ) as g:
193
+ with g.cell(): st_write("Title")
194
+ with g.cell(): st_write("Link")
195
+ with g.cell(): st_write("Item 1")
196
+ with g.cell(): st_write("link1")
197
+ with g.cell(): st_write("Item 2")
198
+ with g.cell(): st_write("link2")
199
+ ```
200
+
201
+ ### Overlays (Absolute Positioning)
202
+
203
+ ```python
204
+ with st_overlay() as ov:
205
+ with ov.layer(top=10, left=20):
206
+ st_write(s.large, "Positioned at top:10px left:20px")
207
+ with ov.layer(top=50, right=20):
208
+ st_image(uri="badge.png", width="80px")
209
+ ```
210
+
211
+ ## Links and Navigation
212
+
213
+ ### Links
214
+
215
+ ```python
216
+ # Simple link
217
+ st_write("Click here", link="https://example.com")
218
+
219
+ # Styled link
220
+ link_style = s.text.colors.blue + s.text.decors.underline_text
221
+ st_write(link_style, "Styled link", link="https://example.com", no_link_decor=True)
222
+ ```
223
+
224
+ ### Table of Contents
225
+
226
+ ```python
227
+ # Register headings
228
+ st_write(style, "Section", toc_lvl="1")
229
+ st_write(style, "Subsection", toc_lvl="+1")
230
+
231
+ # TOCConfig — full options
232
+ toc = TOCConfig(
233
+ numerate_titles=False, # Auto-numbering of headings
234
+ toc_position=0, # 0=start, -1=end, None=no TOC
235
+ title_style=s.project.titles.section_title + s.center_txt,
236
+ content_style=s.large + s.text.colors.reset,
237
+ sidebar_max_level=None, # None = auto (paginated: 1, continuous: 2)
238
+ search=True, # Full-text search in sidebar
239
+ )
240
+ ```
241
+
242
+ ### Marker Navigation
243
+
244
+ ```python
245
+ from streamtex import st_marker, MarkerConfig, st_book
246
+
247
+ # Place markers manually
248
+ st_marker("Section Start", visible=True) # Visible marker (dashed border + label)
249
+ st_marker("Hidden Waypoint") # Invisible marker (default)
250
+
251
+ # Auto-markers from TOC headings (in book.py)
252
+ marker_config = MarkerConfig(
253
+ auto_marker_on_toc=1, # Level-1 TOC headings become markers
254
+ next_keys=["PageDown"], # Navigate forward
255
+ prev_keys=["PageUp"], # Navigate backward
256
+ )
257
+ st_book([...], marker_config=marker_config)
258
+ ```
259
+
260
+ ## Predefined Styles
261
+
262
+ ### Text Colors
263
+
264
+ ```python
265
+ s.text.colors.red # 140+ CSS named colors available
266
+ s.text.colors.lime
267
+ s.text.colors.alice_blue
268
+ s.text.colors.reset # color: initial
269
+ ```
270
+
271
+ ### Text Sizes
272
+
273
+ ```python
274
+ # Title sizes
275
+ s.GIANT # 196pt s.Giant # 128pt s.giant # 112pt
276
+ s.Huge # 96pt s.huge # 80pt
277
+ # Header sizes
278
+ s.LARGE # 64pt s.Large # 48pt s.large # 32pt
279
+ # Body sizes
280
+ s.big # 24pt s.medium # 16pt s.little # 12pt
281
+ s.small # 8pt s.tiny # 4pt
282
+ # Dynamic sizes
283
+ s.text.sizes.size(20, "custom_20pt") # Factory method
284
+ ```
285
+
286
+ ### Alignment and Layout
287
+
288
+ ```python
289
+ s.center_txt # text-align: center
290
+ s.text.alignments.right_align # text-align: right
291
+ s.text.alignments.justify_align # text-align: justify
292
+ s.container.flex.center_align_items # align-items: center
293
+ s.container.layouts.vertical_center_layout # Flex centered both axes
294
+ s.container.layouts.center # width: fit-content + auto margin
295
+ ```
296
+
297
+ ### Decorations
298
+
299
+ ```python
300
+ s.bold # font-weight: bold
301
+ s.italic # font-style: italic
302
+ s.text.decors.underline_text # text-decoration: underline
303
+ s.text.decors.strike_text # text-decoration: line-through
304
+ ```
305
+
306
+ ### Container Styles
307
+
308
+ ```python
309
+ # Padding
310
+ s.container.paddings.little_padding # 9pt
311
+ s.container.paddings.small_padding # 6pt
312
+ s.container.paddings.medium_padding # 12pt
313
+ s.container.paddings.size("10px", "20px", style_id="custom_pad") # Factory
314
+
315
+ # Borders
316
+ s.container.borders.solid_border
317
+ s.container.borders.dashed_border
318
+ s.container.borders.size("2px") # Factory
319
+ s.container.borders.color(s.text.colors.blue) # Factory
320
+
321
+ # Background colors
322
+ s.container.bg_colors.red_bg # 140+ named background colors
323
+ s.container.bg_colors.reset_bg # background-color: initial
324
+
325
+ # Flex
326
+ s.container.flex.flex # display: flex
327
+ s.container.flex.center_flex # flex + center both axes
328
+ s.container.flex.space_between_justify # justify-content: space-between
329
+
330
+ # Sizes
331
+ s.container.sizes.width_full # width: 100%
332
+ s.container.sizes.height_auto # height: auto
333
+
334
+ # Lists
335
+ s.container.lists.g_docs # Google Docs symbols
336
+ s.container.lists.ordered_lowercase # lower-alpha list
337
+ ```
338
+
339
+ ## Book Orchestration
340
+
341
+ ### book.py Pattern
342
+
343
+ ```python
344
+ import streamlit as st
345
+ import setup
346
+ import streamtex as stx
347
+ from streamtex import st_book, TOCConfig, MarkerConfig
348
+ from pathlib import Path
349
+ from custom.styles import Styles as s
350
+ from custom.themes import dark
351
+ import streamtex.styles as sts
352
+ import blocks
353
+
354
+ # Configure static sources
355
+ stx.set_static_sources([str(Path(__file__).parent / "static")])
356
+
357
+ # Page configuration
358
+ st.set_page_config(
359
+ page_title="My Project",
360
+ layout="wide",
361
+ initial_sidebar_state="collapsed",
362
+ )
363
+
364
+ # Inject dark theme
365
+ sts.theme = dark
366
+
367
+ # TOC + Markers
368
+ toc = TOCConfig(numerate_titles=False, toc_position=0, search=True)
369
+ marker_config = MarkerConfig(auto_marker_on_toc=1, next_keys=["PageDown"], prev_keys=["PageUp"])
370
+
371
+ # Orchestrate blocks
372
+ st_book(
373
+ [
374
+ blocks.bck_01_welcome,
375
+ blocks.bck_02_content,
376
+ ],
377
+ toc_config=toc,
378
+ marker_config=marker_config,
379
+ paginate=True,
380
+ inspector=stx.InspectorConfig(enabled=True),
381
+ )
382
+ ```
383
+
384
+ ### st_book — Full Signature
385
+
386
+ ```python
387
+ st_book(
388
+ module_list, # List of block modules
389
+ toc_config=None, # TOCConfig object
390
+ marker_config=None, # MarkerConfig object
391
+ separator=None, # Module rendered between blocks (optional)
392
+ export=True, # Enable HTML export
393
+ export_title="StreamTeX Export",
394
+ paginate=False, # One block per page
395
+ bib_sources=None, # List of .bib/.json/.ris paths
396
+ bib_config=None, # BibConfig for bibliography
397
+ inspector=None, # InspectorConfig for block inspector
398
+ page_width=90, # Page width as % of browser width (default 90)
399
+ )
400
+ ```
401
+
402
+ ### InspectorConfig
403
+
404
+ ```python
405
+ import streamtex as stx
406
+
407
+ # Enable the block inspector panel
408
+ st_book([...], inspector=stx.InspectorConfig(enabled=True))
409
+
410
+ # Full options
411
+ stx.InspectorConfig(
412
+ enabled=True,
413
+ password=None, # Optional password protection
414
+ panel_width="35vw", # Right panel width
415
+ backup=True, # Create .bak files before saving
416
+ )
417
+ ```
418
+
419
+ ## Block Infrastructure
420
+
421
+ ### Block Registry — blocks/__init__.py
422
+
423
+ ```python
424
+ """Blocks package — lazy-loaded via streamtex.ProjectBlockRegistry."""
425
+ from pathlib import Path
426
+ from streamtex import ProjectBlockRegistry, BlockNotFoundError, BlockImportError
427
+
428
+ registry = ProjectBlockRegistry(Path(__file__).parent)
429
+ __all__ = ["registry", "BlockNotFoundError", "BlockImportError"]
430
+
431
+ def __getattr__(name: str):
432
+ try:
433
+ return registry.get(name)
434
+ except (BlockNotFoundError, BlockImportError) as e:
435
+ raise AttributeError(str(e)) from e
436
+
437
+ def __dir__():
438
+ return sorted(registry.list_blocks() + __all__)
439
+ ```
440
+
441
+ ### LazyBlockRegistry — Shared Blocks
442
+
443
+ ```python
444
+ # In book.py — load blocks from external directories
445
+ shared_path = str(Path(__file__).parent.parent / "shared-blocks" / "blocks")
446
+ shared_blocks = stx.LazyBlockRegistry([shared_path])
447
+
448
+ st_book([
449
+ shared_blocks.bck_header, # From shared library
450
+ blocks.bck_content, # From local blocks/
451
+ shared_blocks.bck_footer, # From shared library
452
+ ])
453
+
454
+ # Multi-source with priority (first source wins)
455
+ shared = stx.LazyBlockRegistry([
456
+ "blocks/overrides", # Checked first (project overrides)
457
+ "../../shared-blocks/blocks", # Checked second (originals)
458
+ ])
459
+ ```
460
+
461
+ ### Composite Blocks (Atomic Sub-blocks)
462
+
463
+ ```python
464
+ # Composite block: loads atomic sub-blocks from _atomic/ subfolder
465
+ import streamtex as stx
466
+ from streamtex import st_include
467
+
468
+ bck_text_basics = stx.load_atomic_block("bck_text_basics", __file__)
469
+ bck_text_styles = stx.load_atomic_block("bck_text_styles", __file__)
470
+
471
+ class BlockStyles:
472
+ pass
473
+
474
+ def build():
475
+ st_include(bck_text_basics)
476
+ st_include(bck_text_styles)
477
+ ```
478
+
479
+ - `load_atomic_block(name, __file__)` loads `_atomic/{name}.py` relative to caller
480
+ - Raises `BlockNotFoundError` / `BlockImportError` on failure
481
+
482
+ ### Static Asset Resolution
483
+
484
+ ```python
485
+ import streamtex as stx
486
+ from pathlib import Path
487
+
488
+ # Single source
489
+ stx.set_static_sources([str(Path(__file__).parent / "static")])
490
+
491
+ # Multi-source with priority (first directory containing the file wins)
492
+ stx.set_static_sources([
493
+ str(Path(__file__).parent / "static"), # Local (highest priority)
494
+ str(Path(__file__).parent.parent / "shared-blocks" / "static"), # Shared fallback
495
+ ])
496
+
497
+ # Manual resolution
498
+ path = stx.resolve_static("logo.png") # Searches each source in order
499
+ # st_image() calls resolve_static() internally
500
+ ```
501
+
502
+ ## Block Helpers
503
+
504
+ ### Config Injection Pattern (Recommended)
505
+
506
+ ```python
507
+ # blocks/helpers.py — inject project styles globally
508
+ from streamtex import (
509
+ BlockHelperConfig, set_block_helper_config,
510
+ show_code as _show_code,
511
+ show_explanation as _show_explanation,
512
+ show_details as _show_details,
513
+ )
514
+ from custom.styles import Styles as s
515
+
516
+ class ProjectBlockHelperConfig(BlockHelperConfig):
517
+ def get_code_style(self):
518
+ return s.project.containers.code_box
519
+ def get_explanation_style(self):
520
+ return s.project.containers.explanation_box
521
+ def get_details_style(self):
522
+ return s.project.containers.details_box
523
+
524
+ set_block_helper_config(ProjectBlockHelperConfig())
525
+
526
+ # Convenience wrappers
527
+ def show_code(code_string: str, language: str = "python", line_numbers: bool = True):
528
+ return _show_code(code_string, language, line_numbers)
529
+
530
+ def show_explanation(text: str):
531
+ return _show_explanation(text)
532
+
533
+ def show_details(text: str):
534
+ return _show_details(text)
535
+ ```
536
+
537
+ ### Standalone Functions
538
+
539
+ ```python
540
+ from streamtex import show_code, show_explanation, show_details
541
+
542
+ show_code("print('hello')") # Uses injected config style
543
+ show_code("print('hello')", style=s.custom.style) # Override with explicit style
544
+ show_explanation("This explains the concept...")
545
+ show_details("Additional details here...")
546
+ ```
547
+
548
+ ### OOP Inheritance (Advanced)
549
+
550
+ ```python
551
+ from streamtex import BlockHelper
552
+
553
+ class ProjectBlockHelper(BlockHelper):
554
+ def show_comparison(self, before: str, after: str):
555
+ self.show_code(before)
556
+ self.show_code(after)
557
+
558
+ helper = ProjectBlockHelper()
559
+ helper.show_comparison(old_code, new_code)
560
+ ```
561
+
562
+ ## Raw HTML (`st_html`)
563
+
564
+ Use `stx.st_html()` when you need to render raw HTML content (bar charts, decorative rules, embedded iframes). It routes through the dual-rendering pipeline (live + export buffer) and auto-injects `font-family: Source Sans Pro` in iframes.
565
+
566
+ ```python
567
+ # Inline HTML (height=0, default) — renders via st.html()
568
+ stx.st_html('<hr style="border:none;height:3px;">')
569
+
570
+ # Iframe HTML (height>0) — renders via components.html() with auto font injection
571
+ stx.st_html('<div>chart content</div>', height=400)
572
+
573
+ # Iframe with scrolling
574
+ stx.st_html('<div>long content</div>', height=600, scrolling=True)
575
+
576
+ # Light background (force white bg in dark mode)
577
+ stx.st_html('<svg>...</svg>', height=300, light_bg=True)
578
+ ```
579
+
580
+ **Parameters:**
581
+ - `html` — HTML string to render
582
+ - `height` — When > 0, render in an iframe with explicit pixel height (default: 0 = inline)
583
+ - `light_bg` — Force light color-scheme in the iframe (default: False)
584
+ - `scrolling` — Enable scrolling in the iframe (default: False)
585
+
586
+ ## Export-Aware Widgets
587
+
588
+ Use `stx.st_*` wrappers instead of raw `st.*` calls for data visualization — they appear in both the live app AND the HTML export.
589
+
590
+ ### Charts
591
+
592
+ ```python
593
+ stx.st_line_chart(data, x="col_x", y="col_y")
594
+ stx.st_bar_chart(data, x="Category", y="Value")
595
+ stx.st_area_chart(data)
596
+ stx.st_scatter_chart(data, x="x", y="y")
597
+ ```
598
+
599
+ ### Tables & Data
600
+
601
+ ```python
602
+ stx.st_dataframe(df, use_container_width=True) # Interactive table
603
+ stx.st_table(data) # Static table
604
+ stx.st_json({"key": "value"}) # JSON viewer
605
+ stx.st_metric("Revenue", "$1M", delta="+5%") # KPI metric
606
+ ```
607
+
608
+ ### Diagrams
609
+
610
+ ```python
611
+ # Graphviz (DOT language)
612
+ stx.st_graphviz('digraph { A -> B -> C }')
613
+
614
+ # Mermaid (interactive zoom/pan support)
615
+ stx.st_mermaid("""
616
+ graph TD
617
+ A[Start] --> B{Decision}
618
+ B -->|Yes| C[OK]
619
+ B -->|No| D[End]
620
+ """)
621
+
622
+ # Mermaid with options
623
+ stx.st_mermaid(code, style=my_style, light_bg=True, height=500)
624
+
625
+ # Mermaid fit modes (initial zoom on first render)
626
+ stx.st_mermaid(code, fit="contain") # default: fit entire diagram in viewport
627
+ stx.st_mermaid(code, fit="width") # fill viewport width
628
+ stx.st_mermaid(code, fit="none") # natural size (scale 1)
629
+
630
+ # TikZ (requires LaTeX + Ghostscript)
631
+ stx.st_tikz(r"""
632
+ \begin{tikzpicture}
633
+ \draw (0,0) -- (1,1) -- (2,0) -- cycle;
634
+ \end{tikzpicture}
635
+ """, preamble=r"\usepackage{tikz}")
636
+
637
+ # PlantUML (server-rendered, configurable)
638
+ stx.st_plantuml("""
639
+ @startuml
640
+ Alice -> Bob: Authentication Request
641
+ Bob --> Alice: Authentication Response
642
+ @enduml
643
+ """)
644
+
645
+ # PlantUML with options
646
+ stx.st_plantuml(code, style=my_style, light_bg=True, height=500,
647
+ server="https://www.plantuml.com/plantuml")
648
+ ```
649
+
650
+ ### Audio & Video
651
+
652
+ ```python
653
+ stx.st_audio("path/to/audio.wav", format="audio/wav")
654
+ stx.st_video("path/to/video.mp4")
655
+ stx.st_video("https://www.youtube.com/watch?v=...")
656
+ ```
657
+
658
+ ### Generic Fallback
659
+
660
+ ```python
661
+ # For any widget not covered above
662
+ with stx.st_export('<p>Fallback HTML for export</p>'):
663
+ st.plotly_chart(fig)
664
+ ```
665
+
666
+ > **Note:** Interactive widgets (`st.button`, `st.slider`, `st.selectbox`) have no static representation and are absent from the export.
667
+
668
+ ## Zoom Control
669
+
670
+ ```python
671
+ import streamtex as stx
672
+
673
+ # Zoom is managed automatically by st_book().
674
+ # Two independent sidebar controls: Width % and Zoom % (pure CSS, no JavaScript).
675
+
676
+ # If calling manually:
677
+ stx.add_zoom_options() # Defaults: width=100%, zoom=100%
678
+ stx.add_zoom_options(default_page_width=80) # Start at 80% width
679
+ stx.add_zoom_options(default_page_width=80, default_zoom=125) # 80% width, 125% zoom
680
+
681
+ # Low-level injection (rarely needed):
682
+ stx.inject_zoom_logic(100, 100) # Width 100%, Zoom 100%
683
+ stx.inject_zoom_logic(80, 150) # Width 80%, Zoom 150%
684
+ stx.inject_zoom_logic(120, 50) # Width 120%, Zoom 50%
685
+ ```
686
+
687
+ ## Bibliography
688
+
689
+ ```python
690
+ from streamtex.bib import BibConfig, BibFormat, CitationStyle
691
+
692
+ # Configure bibliography
693
+ bib_config = BibConfig(
694
+ format=BibFormat.APA,
695
+ citation_style=CitationStyle.AUTHOR_YEAR,
696
+ hover_enabled=True, # Hover preview of citations
697
+ hover_show_abstract=True,
698
+ )
699
+
700
+ # Load sources (supports .bib, .json, .ris, .csl-json)
701
+ bib_sources = ["references.bib"]
702
+
703
+ # Pass to st_book
704
+ st_book([...], bib_sources=bib_sources, bib_config=bib_config)
705
+
706
+ # In-text citations (inside blocks)
707
+ from streamtex.bib import st_cite, st_bibliography
708
+ st_cite("author2024key") # Inline citation widget
709
+ st_bibliography() # Render full bibliography
710
+ ```
711
+
712
+ ## Collection System (Multi-Project Hub)
713
+
714
+ ### collection.toml
715
+
716
+ ```toml
717
+ [collection]
718
+ title = "My Course Library"
719
+ description = "A collection of StreamTeX courses"
720
+ cards_per_row = 2
721
+
722
+ [projects.intro]
723
+ title = "Introduction Course"
724
+ description = "Learn the basics"
725
+ cover = "static/images/covers/intro.png"
726
+ project_url = "http://localhost:8502"
727
+ order = 1
728
+
729
+ [projects.advanced]
730
+ title = "Advanced Course"
731
+ description = "Master advanced concepts"
732
+ cover = "static/images/covers/advanced.png"
733
+ project_url = "http://localhost:8503"
734
+ order = 2
735
+ ```
736
+
737
+ ### Automatic Collection UI
738
+
739
+ ```python
740
+ from streamtex import st_collection, CollectionConfig
741
+
742
+ config = CollectionConfig.from_toml("collection.toml")
743
+ st_collection(config=config, home_styles=s)
744
+ ```
745
+
746
+ ### Custom Collection with st_book
747
+
748
+ ```python
749
+ # For full control over the collection UI
750
+ st_book([
751
+ blocks.bck_home, # Custom home page with cards
752
+ blocks.bck_management, # Documentation
753
+ ], toc_config=toc, paginate=False)
754
+ ```
755
+
756
+ ## Google Sheets Import
757
+
758
+ ```python
759
+ from streamtex import GSheetConfig, set_gsheet_config, load_gsheet, load_gsheet_df
760
+
761
+ # Configure
762
+ config = GSheetConfig(...)
763
+ set_gsheet_config(config)
764
+
765
+ # Load as module or DataFrame
766
+ block_module = load_gsheet("path/to/source")
767
+ df = load_gsheet_df("path/to/source")
768
+ ```
769
+
770
+ ## Utilities
771
+
772
+ ### Spacing
773
+
774
+ ```python
775
+ st_space(size=3) # 3em vertical space
776
+ st_space("v", size=2) # 2em vertical space
777
+ st_space("h", size=1) # 1em horizontal space
778
+ st_space("h", size="40px") # 40px horizontal space
779
+ st_br() # Line break
780
+ st_br(count=3) # 3 line breaks
781
+ ```
782
+
783
+ ### Containers
784
+
785
+ ```python
786
+ # Styled block container (vertical)
787
+ with st_block(s.center_txt):
788
+ st_write("Centered content")
789
+
790
+ # Inline styled container
791
+ with st_span(s.bold + s.text.colors.red):
792
+ st_write("Inline bold red")
793
+ ```
794
+
795
+ ## Project Structure
796
+
797
+ ```
798
+ project_name/
799
+ book.py # Entry point
800
+ setup.py # PATH setup
801
+ blocks/
802
+ __init__.py # ProjectBlockRegistry
803
+ bck_*.py # Each block: BlockStyles + build()
804
+ helpers.py # Block helper configuration
805
+ _atomic/ # Atomic sub-blocks (optional)
806
+ custom/
807
+ styles.py # Project styles (extends StxStyles)
808
+ themes.py # Dark theme overrides (dict)
809
+ static/images/ # Image assets
810
+ .streamlit/config.toml # enableStaticServing = true
811
+ ```
812
+
813
+ ## Custom Styles Pattern (custom/styles.py)
814
+
815
+ ```python
816
+ from streamtex.styles import Style, Text, Container, StxStyles
817
+
818
+ class ColorsCustom:
819
+ primary = Style("color: #4A90D9;", "primary")
820
+ accent = Style("color: #2EC4B6;", "accent")
821
+
822
+ class BackgroundsCustom:
823
+ callout_bg = Style("background-color: rgba(74, 144, 217, 0.12);", "callout_bg")
824
+
825
+ class TextStylesCustom:
826
+ course_title = Style.create(
827
+ ColorsCustom.primary + Text.weights.bold_weight + Text.sizes.Giant_size,
828
+ "course_title"
829
+ )
830
+
831
+ class ContainerStylesCustom:
832
+ callout = Style.create(
833
+ BackgroundsCustom.callout_bg
834
+ + Container.borders.solid_border
835
+ + Style("border-color: #4A90D9; border-width: 0 0 0 4px;", "callout_border")
836
+ + Container.paddings.medium_padding,
837
+ "callout"
838
+ )
839
+
840
+ class Custom:
841
+ colors = ColorsCustom
842
+ backgrounds = BackgroundsCustom
843
+ titles = TextStylesCustom
844
+ containers = ContainerStylesCustom
845
+
846
+ class Styles(StxStyles):
847
+ project = Custom
848
+ ```
849
+
850
+ ## Theme Overrides (custom/themes.py)
851
+
852
+ ```python
853
+ # Keys are style_ids, values are replacement CSS strings
854
+ dark = {
855
+ "primary": "color: #7AB8F5;",
856
+ "course_title": "color: #7AB8F5; font-weight: bold; font-size: 128pt;",
857
+ "callout_bg": "background-color: rgba(74, 144, 217, 0.20);",
858
+ }
859
+ ```
860
+
861
+ ## Tips and Best Practices
862
+
863
+ 1. Group common styles in a `BlockStyles` class — one per block
864
+ 2. ONE `st_write()` with tuples for inline mixed-style text (multiple calls stack vertically)
865
+ 3. Never hardcode black/white — let Streamlit handle Light/Dark mode
866
+ 4. No raw HTML/CSS — use Style composition
867
+ 5. Use `stx.*` for content, `st.*` only for interactivity (buttons, sliders, inputs)
868
+ 6. One generic style, reused everywhere — no duplicates
869
+ 7. Use `tag=t.div` for block-level elements, default `t.span` for inline