ipyslides 7.3.0__tar.gz → 7.3.2__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.
- {ipyslides-7.3.0 → ipyslides-7.3.2}/PKG-INFO +1 -1
- ipyslides-7.3.2/ipyslides/__version__.py +2 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_syntax.py +17 -15
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/styles.py +12 -8
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/core.py +2 -1
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/formatters.py +7 -3
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/pkg_nbs/ips-demo.ipynb +24 -17
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/pkg_nbs/ips-docs.ipynb +4 -6
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/utils.py +8 -8
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/writer.py +7 -5
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/xmd.py +44 -41
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/PKG-INFO +1 -1
- ipyslides-7.3.0/ipyslides/__version__.py +0 -2
- {ipyslides-7.3.0 → ipyslides-7.3.2}/LICENSE +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/README.md +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/__init__.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/__init__.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_layout.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_widgets.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/base.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/export_html.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/export_template.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/icons.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/intro.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/navigation.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/notes.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/settings.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/slide.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/interaction.css +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/interaction.js +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/laser.css +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/laser.js +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/notes.js +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/widgets.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/dashlab.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/source.py +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/SOURCES.txt +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/dependency_links.txt +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/requires.txt +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/top_level.txt +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/setup.cfg +0 -0
- {ipyslides-7.3.0 → ipyslides-7.3.2}/setup.py +0 -0
|
@@ -27,13 +27,14 @@ Use any or combination of these styles in markdown blocks or `css_class` argumen
|
|
|
27
27
|
`raw-text` | Text will be shown as printed style. {: .raw-text}
|
|
28
28
|
`focus-self` | Double-click on element to zoom into popup view. Double-click again or navigate slides to exit. {: .focus-self}
|
|
29
29
|
`focus-child` | Focus on child objects of node with this class. Same double-click to zoom in/out behavior.
|
|
30
|
+
`column` | Inside `columns`, if an element with no siblings has `column` class, it will stretch to full height of columns no matter how deeply nested it is.
|
|
30
31
|
|
|
31
32
|
Besides these CSS classes, you always have `Slide.css`, `Slides.html('style',...)` functions at your disposal.
|
|
32
33
|
'''
|
|
33
34
|
# as function to let any call use escaped functions, otherwise they are lost on first use
|
|
34
35
|
xmd_syntax = lambda: rf'''
|
|
35
36
|
## Extended Markdown
|
|
36
|
-
|
|
37
|
+
--
|
|
37
38
|
Extended syntax on top of [Python-Markdown](https://python-markdown.github.io/) supports almost full presentation from Markdown.
|
|
38
39
|
|
|
39
40
|
**Presentation Structure**{{.text-big}}
|
|
@@ -78,7 +79,7 @@ Citations
|
|
|
78
79
|
key2: A citations can span multiple lines, but key should start on new line
|
|
79
80
|
/]
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
--
|
|
82
83
|
|
|
83
84
|
**Content Blocks**{{.text-big}}
|
|
84
85
|
|
|
@@ -118,7 +119,7 @@ The general block syntax is `::: type-or-classes [args] attributes`.
|
|
|
118
119
|
- `md-[position or variable]` accepts same parameters as `code` block for syntax highlighting and get deleted on first use.
|
|
119
120
|
- Both `code` and `md-var` blocks support attribute access such as `::: code.collapsed` or `::: md-var.inline` to show selected view.
|
|
120
121
|
You can also use `::: code 1 3` to focus on specific lines based on index 1 in markdown unlike Python.
|
|
121
|
-
|
|
122
|
+
--
|
|
122
123
|
|
|
123
124
|
**Layouts**{{.text-big}}
|
|
124
125
|
|
|
@@ -156,7 +157,7 @@ Use `++` before `::: columns.paused` to separate previous content from first col
|
|
|
156
157
|
::: li .. This follows disc marker from parent `ul` block.
|
|
157
158
|
```
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
--
|
|
160
161
|
|
|
161
162
|
**Code Display**{{.text-big}}
|
|
162
163
|
|
|
@@ -168,10 +169,10 @@ Code Blocks
|
|
|
168
169
|
|
|
169
170
|
```md-src
|
|
170
171
|
::: columns
|
|
171
|
-
::: block-green
|
|
172
|
+
::: column block-green
|
|
172
173
|
[md-src/]
|
|
173
174
|
--
|
|
174
|
-
::: block-red
|
|
175
|
+
::: column block-red
|
|
175
176
|
```python
|
|
176
177
|
print('Hello, I was highlighted from a code block!')
|
|
177
178
|
```
|
|
@@ -190,7 +191,7 @@ Code Blocks
|
|
|
190
191
|
[md-src/]
|
|
191
192
|
```
|
|
192
193
|
|
|
193
|
-
|
|
194
|
+
--
|
|
194
195
|
|
|
195
196
|
**Variables Substitution**{{.text-big}}
|
|
196
197
|
|
|
@@ -218,7 +219,7 @@ Variables from Python code can be embedded directly into Markdown.
|
|
|
218
219
|
- Widgets behave same with or without `:nb` format spec.
|
|
219
220
|
- Formatting is done using `str.format` method, so f-string like literal expressions are not supported.
|
|
220
221
|
|
|
221
|
-
|
|
222
|
+
--
|
|
222
223
|
|
|
223
224
|
{esc(xmd.funcs)}
|
|
224
225
|
|
|
@@ -235,19 +236,20 @@ Inline functions can be nested, thanks to new function call pattern that must en
|
|
|
235
236
|
--
|
|
236
237
|
[md-src/]
|
|
237
238
|
|
|
238
|
-
|
|
239
|
+
--
|
|
239
240
|
**General Syntax**{{.text-big}}
|
|
240
241
|
|
|
241
242
|
- To avoid any further markdown parsing, use ` Slides.esc ` while interpolating variables or expressions in f-strings, such as `f"Hello {{esc(var)}}"` or `f"Hello {{esc(func())}}"`.
|
|
242
243
|
- Use [alert! include\`markdown_file.md[optional list slicing to pick lines from file such as [2:5], [10:]]\` /] to include a file in markdown format.
|
|
243
244
|
- Use [alert! [fa\! icon_name \/] /] to add FontAwesome icons, e.g. [fa\! arrow-right \/] → [fa! arrow-right /], [fa\! check .. "green" \/] → [fa! check .. "green" /] etc.
|
|
244
245
|
- Drop a link target in any place using `[#target_id/]` and use `target_id` in `link` commnad or html anchor to jump to that target. Use `Slides.link` for more details.
|
|
245
|
-
- Use syntax `[link
|
|
246
|
+
- Use syntax `[link\!! "target_id", "Jump to slide" \/]` to jump between slides. See `Slides.link` for more details.
|
|
246
247
|
- Cells in markdown table can be spanned to multiple rows/columns by attributes `| cell text \{{: rowspan="2" colspan="1"}}|` inside a cell, should be a space bewteen text and attributes.
|
|
247
248
|
- Escape a backtick with backslash, i.e. [alert! \\` → \` /], other escape characters are [alert! {xmd.escaped_chars} /]. In Python >=3.12, you need to make escape strings raw, including the use of $ \LaTeX $ and re module.
|
|
248
249
|
- Use html entities for special characters, e.g. `→` →, `←` ←, `↑` ↑, `↓` ↓, `↔` ↔, `⇅` ⇅, `⇐` ⇐, `⇒` ⇒, `⇑` ⇑, `⇓` ⇓, `⇔` ⇔ etc.
|
|
249
250
|
- Use `sub` and `sup` functions for subscript and superscript respectively, e.g. H[sub!2/]O, E = mc[sup!2/].
|
|
250
251
|
- See `Slides.css_styles` for available CSS classes to use in markdown blocks and other places.
|
|
252
|
+
- Slide width/height in container query units can be used in functions/blocks that accept `width`/`height`, e.g. `[image\!..., width='50cqw' \/]` will span half the slide width.
|
|
251
253
|
- See `Slides.css_animations` for available CSS animation classes to use in markdown blocks and other places.
|
|
252
254
|
- Definition list syntax:
|
|
253
255
|
```md-src.inline
|
|
@@ -274,7 +276,7 @@ Inline functions can be nested, thanks to new function call pattern that must en
|
|
|
274
276
|
css_animations = '''
|
|
275
277
|
## 🎬 IPySlides Content Animations
|
|
276
278
|
|
|
277
|
-
|
|
279
|
+
--
|
|
278
280
|
|
|
279
281
|
All animations are **fully composable** - combine multiple animation classes to create complex effects! Use `anim-group` on a parent container to apply staggered animations to all children.
|
|
280
282
|
|
|
@@ -325,7 +327,7 @@ Variable | Default | Description
|
|
|
325
327
|
`--kf-count` | 1 | Iteration count for `.anim-kf`.
|
|
326
328
|
`--kf-direction` | normal | Animation direction for `.anim-kf`.
|
|
327
329
|
|
|
328
|
-
|
|
330
|
+
--
|
|
329
331
|
|
|
330
332
|
### 💡 Usage Examples
|
|
331
333
|
|
|
@@ -353,7 +355,7 @@ Variable | Default | Description
|
|
|
353
355
|
Iris opens from top-left corner while zooming!
|
|
354
356
|
```
|
|
355
357
|
|
|
356
|
-
|
|
358
|
+
--
|
|
357
359
|
|
|
358
360
|
**Custom Variables**
|
|
359
361
|
```md-after
|
|
@@ -384,7 +386,7 @@ Define `@keyframes` in Slides.css(...) or in a notebook cell.
|
|
|
384
386
|
// Delays follow smooth sine curve: 100ms per 10 items
|
|
385
387
|
```
|
|
386
388
|
|
|
387
|
-
|
|
389
|
+
--
|
|
388
390
|
|
|
389
391
|
**Per Item Origins** (Dynamic Effects)
|
|
390
392
|
```md-after
|
|
@@ -413,7 +415,7 @@ Define `@keyframes` in Slides.css(...) or in a notebook cell.
|
|
|
413
415
|
Flips up from bottom while zooming!
|
|
414
416
|
```
|
|
415
417
|
|
|
416
|
-
|
|
418
|
+
--
|
|
417
419
|
|
|
418
420
|
### ✨ Key Features
|
|
419
421
|
|
|
@@ -576,6 +576,7 @@ def style_css(colors, fonts, layout, _root = False):
|
|
|
576
576
|
'position': 'absolute !important',
|
|
577
577
|
'width':'210mm !important', # A4 width letter page can have a little extra margin, important to have fixed width
|
|
578
578
|
'height': f'{_safe_height(layout.aspect)}mm !important',
|
|
579
|
+
'container-type': 'size !important', # allows using cqh,cqw type relative units for better content scaling
|
|
579
580
|
'transform-origin': 'center !important' if layout.centered else 'left top !important',
|
|
580
581
|
'transform': 'translateZ(0) scale(var(--contentScale,1)) !important', # Set by Javascript , translateZ is important to avoid blurry text
|
|
581
582
|
'padding' : '8px !important', # don't make 1em to avoid change size with fonts
|
|
@@ -586,11 +587,8 @@ def style_css(colors, fonts, layout, _root = False):
|
|
|
586
587
|
'.jp-OutputArea': {
|
|
587
588
|
'max-height': f'{maxheight} !important', # avoid things get overflowing
|
|
588
589
|
'overflow': 'auto',
|
|
589
|
-
'.jp-OutputArea-child, .jp-OutputArea-output': {
|
|
590
|
+
'^, .jp-OutputArea-child, .jp-OutputArea-output': {
|
|
590
591
|
"position": "static !important", # absolute content should not be stuck here
|
|
591
|
-
"^:only-child, > *:only-child" : {
|
|
592
|
-
'min-height': 'stretch !important', # useful for backgrounds stretching, like in write a full column from outside
|
|
593
|
-
}
|
|
594
592
|
},
|
|
595
593
|
},
|
|
596
594
|
'> .jp-OutputArea': {
|
|
@@ -825,17 +823,23 @@ def style_css(colors, fonts, layout, _root = False):
|
|
|
825
823
|
'.columns':{
|
|
826
824
|
'width':'100%',
|
|
827
825
|
'max-width':'100%',
|
|
828
|
-
'display':'
|
|
826
|
+
'display':'flex',
|
|
829
827
|
'flex-direction':'row',
|
|
830
828
|
'column-gap':'0.2em',
|
|
831
829
|
'align-items':'stretch',
|
|
832
830
|
'> *': {
|
|
833
831
|
'min-width':'0 !important', # avoid overflow due to stubborn elements
|
|
834
|
-
'^, > *:only-child': {
|
|
835
|
-
'min-height': 'stretch !important',
|
|
836
|
-
}, # make single child span full height, useful for backgrounds and nested content without setting class on column itself, (like in writer)
|
|
837
832
|
},
|
|
838
833
|
'table': {'width':'calc(100% - 0.5em)'}, # make table full width inside columns with some padding
|
|
834
|
+
':only-child:has(.column), .column:only-child': { # strecth column to full height if single parents
|
|
835
|
+
'display': 'flex',
|
|
836
|
+
'flex-direction': 'column',
|
|
837
|
+
'flex': '1 1 100% !important',
|
|
838
|
+
'min-height': '0 !important',
|
|
839
|
+
'height': '100% !important',
|
|
840
|
+
'margin': '0 !important', # must to avoid extra space that causes overflow
|
|
841
|
+
'box-sizing': 'border-box !important',
|
|
842
|
+
}
|
|
839
843
|
},
|
|
840
844
|
},
|
|
841
845
|
'code': {
|
|
@@ -128,7 +128,8 @@ class Slides(BaseSlides,metaclass=Singleton):
|
|
|
128
128
|
- [code! SlideGroup /] can be used to apply batch operations on many slides at once, e.g. [code! Slides[[1,3,5]].vars.update(name='Alice') /].
|
|
129
129
|
- Use indexing with given number to apply persistent effects such as CSS or acess via attributes such as
|
|
130
130
|
`Slides.s0`, `Slides.s1` etc. for existing slides, so `Slides.s10 == Slides[10,]` if slide with number 10 exists.
|
|
131
|
-
- Use `[section
|
|
131
|
+
- Use `[section\!! "Backup slides", True \/]` to mark start of supplemental slides. Progress completes before this section and supplemental frames/slides are numbered as `S.1`, `S.2`, ... while remaining navigable.
|
|
132
|
+
- For functions that accept `width`/`height`, you can use `cqw`/`cqh` CSS units for relative size to current slide, e.g. `image(..., width='50cqw')` will span half the slide width.
|
|
132
133
|
"""
|
|
133
134
|
|
|
134
135
|
@classmethod
|
|
@@ -151,11 +151,15 @@ def plt2html(plt_fig = None,transparent=True,width = None, caption=None, crop=No
|
|
|
151
151
|
plt.close(_fig) #AVoids throwing text outside figure
|
|
152
152
|
if width is None:
|
|
153
153
|
width = f'{_fig.get_size_inches()[0]}in'
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
|
|
155
|
+
props = {
|
|
156
|
+
'width': f'{width}px' if isinstance(width,int) else f'{width}',
|
|
157
|
+
'height': 'auto', 'max-width': '100%' # max-width to avoid overflow
|
|
158
|
+
}
|
|
159
|
+
svg = f'<svg {_inline_style(props)}' + plot_bytes.getvalue().decode('utf-8').split('<svg')[1]
|
|
156
160
|
|
|
157
161
|
from .utils import svg as USVG # Avoid circular import
|
|
158
|
-
return USVG(svg, width=width,crop=crop,caption=caption)
|
|
162
|
+
return USVG(svg, width=width, crop=crop, caption=caption)
|
|
159
163
|
|
|
160
164
|
def plt2image(plt_fig=None, transparent=True, width=None, caption=None, format='png', dpi=300):
|
|
161
165
|
"""Convert matplotlib figure to image with base64 encoding.
|
|
@@ -369,27 +369,34 @@
|
|
|
369
369
|
"source": [
|
|
370
370
|
"%%slide -1\n",
|
|
371
371
|
"@slides.src\n",
|
|
372
|
-
"def _(s)
|
|
373
|
-
" write(\n",
|
|
374
|
-
" \"## Animating Matplotlib!\", \n",
|
|
375
|
-
" slides.link('frames-end', 'Skip All Next Frames', icon='arrowr', uid='frames-start')\n",
|
|
376
|
-
" )\n",
|
|
377
|
-
" \n",
|
|
372
|
+
"def _(s): \n",
|
|
378
373
|
" with slides.code.context(returns = True) as src:\n",
|
|
379
374
|
" import numpy as np\n",
|
|
380
375
|
" import matplotlib.pyplot as plt\n",
|
|
381
376
|
" plots = [] # row list for in place reveal\n",
|
|
382
|
-
"
|
|
383
|
-
"
|
|
384
|
-
"
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"
|
|
377
|
+
" x = np.linspace(0, 2 * np.pi, 400)\n",
|
|
378
|
+
" for idx in range(1,20):\n",
|
|
379
|
+
" fig, ax = plt.subplots(figsize=(3.4, 2))\n",
|
|
380
|
+
" y = np.zeros_like(x)\n",
|
|
381
|
+
" for n in range(1, idx + 1):\n",
|
|
382
|
+
" k = 2 * n - 1\n",
|
|
383
|
+
" y += (1 / k) * np.sin(np.pi * k * x)\n",
|
|
384
|
+
" y *= (4 / np.pi)\n",
|
|
385
|
+
" \n",
|
|
386
|
+
" ax.plot(x, y, color='#3b82f6', lw=1)\n",
|
|
387
|
+
" ax.axhline(0, alpha=0.75, lw=0.6,ls='dashed')\n",
|
|
388
|
+
" ax.set_ylim(-1.9, 1.9)\n",
|
|
389
|
+
" ax.axis('off')\n",
|
|
390
|
+
" fig.patch.set_facecolor('#111827')\n",
|
|
391
|
+
" plots.append(slides.plt2html(fig, caption=f\"Harmonics: {idx}\"))\n",
|
|
392
|
+
" \n",
|
|
393
|
+
" write([slides.link('frames-end', 'Skip All Next Frames', icon='arrowr', uid='frames-start'), src], \n",
|
|
394
|
+
" [\n",
|
|
395
|
+
" \"## Animating Matplotlib!\", \n",
|
|
396
|
+
" slides.steps(plots, 'bottom',interval=800),\n",
|
|
397
|
+
" \"::: block-blue .. [code! steps(plots, 'bottom', interval=800) /]\",\n",
|
|
398
|
+
" \"::: note-info .. Only one frame (default last) is exported to PDF/HTML.\",\n",
|
|
399
|
+
" ], widths=[60,40])"
|
|
393
400
|
]
|
|
394
401
|
},
|
|
395
402
|
{
|
|
@@ -456,16 +456,14 @@
|
|
|
456
456
|
" write('## Adding content on frames incrementally')\n",
|
|
457
457
|
" slides.frozen(widget := (code := s.get_source()).as_widget()).display()\n",
|
|
458
458
|
"\n",
|
|
459
|
+
" @slides.on_load\n",
|
|
459
460
|
" def highlight_code(slide):\n",
|
|
460
461
|
" widget.value = code.focus(range(slide.indexf + 1)).value\n",
|
|
461
462
|
"\n",
|
|
462
|
-
"
|
|
463
|
-
" slides.pause()\n",
|
|
463
|
+
" style='background:var(--bg3-color);margin-block:0.05em !important;'\n",
|
|
464
|
+
" slides.pause() # isolate write calls from previous content\n",
|
|
464
465
|
" for ws, cols in zip([None, (2, 3), None], [(0, 1), (2, 3), (4, 5, 6, 7)]):\n",
|
|
465
|
-
" cols = [\n",
|
|
466
|
-
" slides.html('h1', f\"{c}\", style='background:var(--bg3-color);margin-block:0.05em !important;')\n",
|
|
467
|
-
" for c in cols\n",
|
|
468
|
-
" ]\n",
|
|
466
|
+
" cols = [slides.html('h1', f\"{c}\", style=style) for c in cols]\n",
|
|
469
467
|
" write(*cols, widths=ws, css_class='anim-group anim-wipe-right', paused=True) # incremental reveal \n",
|
|
470
468
|
"\n",
|
|
471
469
|
"with slides.slide(16):\n",
|
|
@@ -1101,7 +1101,8 @@ _css_info = (f"""
|
|
|
1101
1101
|
@_fix_init_sig
|
|
1102
1102
|
class steps(ipw.GridBox):
|
|
1103
1103
|
"""A stepper widget to step through given objects with a slider. `objs` should be a list/tuple of objects
|
|
1104
|
-
to step through and can be any object that can be converted to a widget using `as_widget`.
|
|
1104
|
+
to step through and can be any object that can be converted to a widget using `as_widget`. Multiple objects
|
|
1105
|
+
in a single step can be given as a nested list/tuple of objects. `dots_loc` controls
|
|
1105
1106
|
the location of step dots, which can be 'left', 'top', 'right' or 'bottom'. `interval` controls the time interval
|
|
1106
1107
|
in milliseconds for automatic stepping. `css_class` and `css_props` can be used to style the widget.
|
|
1107
1108
|
`static_index` can be used to set a specific index to be displayed statically in PDF and HTML export, while the stepper will still function normally in the notebook.
|
|
@@ -1139,17 +1140,16 @@ class steps(ipw.GridBox):
|
|
|
1139
1140
|
display(*outputs) # only clean outputs
|
|
1140
1141
|
|
|
1141
1142
|
first, last = self._sidxs[self._expidx] if self._sidxs else [0, None]
|
|
1142
|
-
self._fixstyle = html('style', _build_css(
|
|
1143
|
+
self._fixstyle = html('style', _build_css((), {
|
|
1143
1144
|
f'.{self._uclass}': css_props, # apply user css to output
|
|
1144
1145
|
'.ips-steps-wrapper > .abs-style': {'position': 'absolute', 'width': '0', 'height': '0', 'padding': '0'}, # take style widgets out of flow
|
|
1145
1146
|
':not(.SlideArea) .ips-steps-output': {'max-height': '400px', 'overflow': 'auto'},
|
|
1146
1147
|
})).as_widget().add_class('abs-style')
|
|
1147
1148
|
|
|
1148
|
-
self._printstyle = html('style', _build_css('', {
|
|
1149
|
-
'
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}})).as_widget().add_class('abs-style').add_class('jupyter-only') # will not export this style to avoid conflicts
|
|
1149
|
+
self._printstyle = html('style', _build_css(('@media print',), {
|
|
1150
|
+
**view_nodes(f'.{self._uclass} > div > .jp-OutputArea-child',first, last),
|
|
1151
|
+
'.ips-steps-wrapper .steps-widget': {'opacity': '0.2 !important'}, # dim it
|
|
1152
|
+
})).as_widget().add_class('abs-style').add_class('jupyter-only') # will not export this style to avoid conflicts
|
|
1153
1153
|
|
|
1154
1154
|
self._stepper = StepSlider(vertical=True if dots_loc in ("left","right") else False, nsteps=len(self._sidxs), interval=interval)
|
|
1155
1155
|
|
|
@@ -1194,6 +1194,6 @@ class steps(ipw.GridBox):
|
|
|
1194
1194
|
slides._send_nav_msg(forward, parts=True, selector=f'.{self._uclass}')
|
|
1195
1195
|
|
|
1196
1196
|
selector = f'.{self._uclass} > div > .jp-OutputArea-child'
|
|
1197
|
-
css = _build_css('
|
|
1197
|
+
css = _build_css(('@media screen',), view_nodes(selector,*idxs)) # only screen mode to avoid print conflicts
|
|
1198
1198
|
self._viewstyle.value = f"<style>\n{css}\n</style>"
|
|
1199
1199
|
|
|
@@ -13,7 +13,7 @@ from IPython.utils.capture import CapturedIO
|
|
|
13
13
|
from dashlab.utils import _build_css
|
|
14
14
|
|
|
15
15
|
from .formatters import ipw, XTML, RichOutput, _Output, serializer, htmlize, _inline_style, toc_from_meta, _delim
|
|
16
|
-
from .xmd import BoundXMD, xmd, capture_content, get_slides_instance
|
|
16
|
+
from .xmd import BoundXMD, xmd, capture_content, get_slides_instance, _split_parts
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class hold:
|
|
@@ -134,6 +134,8 @@ class Writer(ipw.HBox):
|
|
|
134
134
|
|
|
135
135
|
cols = []
|
|
136
136
|
for i, (w, obj) in enumerate(zip(widths, objs)):
|
|
137
|
+
if isinstance(obj, str):
|
|
138
|
+
obj = list(_split_parts(obj, delimited=False)) # split at '++' to support markdown column ~ list of strings
|
|
137
139
|
outputs = obj if isinstance(obj, (list, tuple)) else [obj] # flatten to list of outputs
|
|
138
140
|
cols.append({'flex': f'{w:.3f} {w:.3f} {w*100:.3f}%', 'outputs': outputs})
|
|
139
141
|
|
|
@@ -161,7 +163,7 @@ class Writer(ipw.HBox):
|
|
|
161
163
|
display(c)
|
|
162
164
|
elif isinstance(c, CapturedIO):
|
|
163
165
|
c.show() # Display captured outputs, all of them
|
|
164
|
-
elif isinstance(c,str):
|
|
166
|
+
elif isinstance(c, str):
|
|
165
167
|
xmd(c, returns = False)
|
|
166
168
|
elif isinstance(c, BoundXMD):
|
|
167
169
|
c.parse(returns = False) # parse and display extended markdown bounded object
|
|
@@ -176,9 +178,7 @@ class Writer(ipw.HBox):
|
|
|
176
178
|
raise RuntimeError(f'Error in column {i+1}:\n{cap.stderr}')
|
|
177
179
|
|
|
178
180
|
# can have last row delimiter, useful for single column automatically dropping last separator
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
cols[i]['outputs'] = base_outputs
|
|
181
|
+
cols[i]['outputs'] = list(cap.outputs)
|
|
182
182
|
|
|
183
183
|
if paused:
|
|
184
184
|
for r, out in enumerate(cols[i]['outputs']):
|
|
@@ -245,6 +245,7 @@ def write(*objs,widths = None, css_class=None, paused=False, **css_props):
|
|
|
245
245
|
Write any object that can be displayed in a cell with some additional features:
|
|
246
246
|
|
|
247
247
|
- Strings will be parsed as as extended markdown that can have citations/python code blocks/Javascript etc. Variables are resolved from notebook scope.
|
|
248
|
+
A whole column as string will be splitted at '++' to create rows. This is equivalent to list of strings.
|
|
248
249
|
- The output of `xmd.gather` can be passed to parse and display content with user given and scoped variables.
|
|
249
250
|
- Use `paused=True` with rows in columns to reveal content incrementally during frame navigation.
|
|
250
251
|
- Use `slides.steps([...])` for slider-driven step transitions where content is swapped in place.
|
|
@@ -272,6 +273,7 @@ def write(*objs,widths = None, css_class=None, paused=False, **css_props):
|
|
|
272
273
|
- In markdown, `::: columns` maps to static `write(..., paused=False)`, while `::: columns.paused` maps to incremental `write(..., paused=True)`.
|
|
273
274
|
- `::: columns.inline` is inline display mode and ignores incremental framing.
|
|
274
275
|
- Use `++` before `::: columns.paused` to isolate previous content from the first reveal step.
|
|
276
|
+
- A single object/block with `column` class inside `write`/`::: columns` will stretch to full height of columns no matter how deeply nested it is.
|
|
275
277
|
|
|
276
278
|
::: note-tip
|
|
277
279
|
To make a group of rows as single item visually for incremental display purpose, wrap them in a nested list/tuple.
|
|
@@ -429,7 +429,7 @@ class XMarkdown(Markdown):
|
|
|
429
429
|
outputs.extend(self._parse_block(*obj))
|
|
430
430
|
continue # vars are substituted already inside, obj = (header, data)
|
|
431
431
|
|
|
432
|
-
for part in
|
|
432
|
+
for part in _split_parts(obj, delimited=True):
|
|
433
433
|
if not isinstance(part, str):
|
|
434
434
|
outputs.append(part) # Delimiter
|
|
435
435
|
continue
|
|
@@ -466,37 +466,7 @@ class XMarkdown(Markdown):
|
|
|
466
466
|
content = re.sub(r"(\[load\!.*?\/\])", error(r"Nested loading of files is not supported \1").value, content)
|
|
467
467
|
# after loading content, raise deprecation warning for old syntax instead of silently adopting it.
|
|
468
468
|
content = re.sub(r"(?<![\`\.\\])\bload\[?(.*?)\]?\`([^\`]*)\`", r"[alert! Use [load\! \2 \.. \1 \/] syntax instead! /]", content)
|
|
469
|
-
return content
|
|
470
|
-
|
|
471
|
-
def _split_parts(self, content, delimited=False):
|
|
472
|
-
"Split content at '++', optionally yielding Delimiter objects."
|
|
473
|
-
def _part_delim():
|
|
474
|
-
delim = _delim("PAUSE")
|
|
475
|
-
if opt == 'isolate':
|
|
476
|
-
warn("The '[isolate]' option after ++ is deprecated. Use 'columns.paused' directive instead.", name="DeprecationWarning").display()
|
|
477
|
-
return delim
|
|
478
|
-
|
|
479
|
-
start = 0
|
|
480
|
-
first = True
|
|
481
|
-
|
|
482
|
-
content = textwrap.dedent(content) # Dedent content before processing to make sure ++ is at start of line
|
|
483
|
-
for m in _PLUS_RE.finditer(content):
|
|
484
|
-
opt = (m.group('opt') or '').strip().lower().replace('_', '-')
|
|
485
|
-
chunk = content[start:m.start()].rstrip() # preserve leading indentation, clear trailing junk
|
|
486
|
-
|
|
487
|
-
if chunk:
|
|
488
|
-
yield chunk
|
|
489
|
-
if delimited:
|
|
490
|
-
yield _part_delim()
|
|
491
|
-
elif first and delimited:
|
|
492
|
-
yield _part_delim()
|
|
493
|
-
|
|
494
|
-
first = False
|
|
495
|
-
start = m.end()
|
|
496
|
-
|
|
497
|
-
if tail := content[start:].rstrip():
|
|
498
|
-
yield tail
|
|
499
|
-
|
|
469
|
+
return content
|
|
500
470
|
|
|
501
471
|
def _parse_params(self, param_string):
|
|
502
472
|
"""Parse parameter string with simple regex."""
|
|
@@ -693,8 +663,9 @@ class XMarkdown(Markdown):
|
|
|
693
663
|
data = self._ignore_incremental(self._fix_legacy_col_sep(data)) # handle legacy mode and ++ separator
|
|
694
664
|
|
|
695
665
|
if re.search(r'^\-\-\s*$', data, flags=re.MULTILINE): # make columns by optional -- separator
|
|
696
|
-
|
|
697
|
-
|
|
666
|
+
data = XTML('<div>' + '</div>\n<div>'.join(
|
|
667
|
+
[self._parse_nested(c, returns=True) for c in _stream_chunks(data, sep='--')]
|
|
668
|
+
) + '</div>') # wrapping in divs is important otherwise can be so many columns based on parsed content
|
|
698
669
|
|
|
699
670
|
if tag in CAPTURED_TAGS:
|
|
700
671
|
if tag in STRICT_TAGS: # keep as is from further processing
|
|
@@ -713,13 +684,14 @@ class XMarkdown(Markdown):
|
|
|
713
684
|
_class = f"{_class} {klass}" if _class else klass # add klass
|
|
714
685
|
widths = [float(w) for w in widths]
|
|
715
686
|
widths = [w/(sum(widths) or 1) for w in widths] # allow relative column widths
|
|
716
|
-
style = '\n'.join(f".{klass} > :nth-child({i+1}) {{flex: {w:.3f} {w:.3f} {100*w:.3f}
|
|
717
|
-
style += '\n' + f".{klass} > :nth-child({len(widths)}) ~ * {{flex: {min(widths):.3f} {min(widths):.3f} {100*min(widths):.3f}
|
|
687
|
+
style = '\n'.join(f".{klass} > :nth-child({i+1}) {{flex: {w:.3f} {w:.3f} {100*w:.3f}%}}" for i, w in enumerate(widths)) # nth-child selectors
|
|
688
|
+
style += '\n' + f".{klass} > :nth-child({len(widths)}) ~ * {{flex: {min(widths):.3f} {min(widths):.3f} {100*min(widths):.3f}% !important;}}" # rest all columns get min width
|
|
718
689
|
style = f"<style>.{klass} > p:empty {{display:none;}}\n{style}</style>" # empty p tags should not be treated as columns
|
|
719
690
|
|
|
720
691
|
# treat tag as class if not given at end
|
|
721
692
|
_class = " ".join([tag, _class])
|
|
722
|
-
|
|
693
|
+
data = data.value if isinstance(data, XTML) else self._parse_nested(data, returns=True) # -- columns parsed already
|
|
694
|
+
return [XTML(f"<div class='{_class}' {_inline_style(css_props)} {attrs}>{data}</div>{style}")]
|
|
723
695
|
|
|
724
696
|
def _parse_code(self, data, mode, focus_lines, _class, props, attrs):
|
|
725
697
|
params = inspect.signature(_highlight).parameters.keys()
|
|
@@ -808,7 +780,7 @@ class XMarkdown(Markdown):
|
|
|
808
780
|
cap_cols = []
|
|
809
781
|
for col in cols:
|
|
810
782
|
rows = [] # list to make row-wise parts
|
|
811
|
-
for row in
|
|
783
|
+
for row in _split_parts(col):
|
|
812
784
|
with capture_content() as cap:
|
|
813
785
|
if self._returns: self._show_disply_error = True # to show error in display var resolution if top level returns
|
|
814
786
|
try:
|
|
@@ -1314,11 +1286,11 @@ class xmd(metaclass=_XMDMeta):
|
|
|
1314
1286
|
return XMarkdown()._parse(content, returns=returns, tag=tag)
|
|
1315
1287
|
|
|
1316
1288
|
def _parse_as_steps(markdown):
|
|
1317
|
-
"Parse markdown chunks
|
|
1289
|
+
"Parse markdown chunks splitted by -- and show alternate chunks through a steps widget. First chunk is treated as common header and shown always."
|
|
1318
1290
|
if not isinstance(markdown, str):
|
|
1319
1291
|
raise TypeError(f"markdown expects a string, got {markdown!r}")
|
|
1320
1292
|
|
|
1321
|
-
pages = list(_stream_chunks(markdown, sep='
|
|
1293
|
+
pages = list(_stream_chunks(markdown, sep='--'))
|
|
1322
1294
|
with capture_content() as cap:
|
|
1323
1295
|
xmd(pages[0], returns=False) # render common header part
|
|
1324
1296
|
|
|
@@ -1332,7 +1304,7 @@ def _parse_as_steps(markdown):
|
|
|
1332
1304
|
|
|
1333
1305
|
|
|
1334
1306
|
def _stream_chunks(text, sep='---'):
|
|
1335
|
-
"Used for slides and pages splitting, yields chunks."
|
|
1307
|
+
"Used for slides and pages splitting, yields chunks. Use _split_parts for splitting by ++ more flexibly and yielding delimiter objects."
|
|
1336
1308
|
s = sep.strip()
|
|
1337
1309
|
if s.startswith('```'):
|
|
1338
1310
|
raise ValueError(f"Invalid separator '{s}'. To split by backticks, use re.split instead!")
|
|
@@ -1365,3 +1337,34 @@ def _stream_chunks(text, sep='---'):
|
|
|
1365
1337
|
if in_block:
|
|
1366
1338
|
final_chunk += '\n```' # close unclosed code block in forgiven manner instead of raising error
|
|
1367
1339
|
yield cmnt_esc.restore(final_chunk)
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
def _split_parts(content, delimited=False):
|
|
1343
|
+
"Split content at '++', optionally yielding delimiter objects. '++ ' inline is also supported unlinke strict '++' on a line by itself in _stream_chunks."
|
|
1344
|
+
def _part_delim():
|
|
1345
|
+
delim = _delim("PAUSE")
|
|
1346
|
+
if opt == 'isolate':
|
|
1347
|
+
warn("The '[isolate]' option after ++ is deprecated. Use 'columns.paused' directive instead.", name="DeprecationWarning").display()
|
|
1348
|
+
return delim
|
|
1349
|
+
|
|
1350
|
+
start = 0
|
|
1351
|
+
first = True
|
|
1352
|
+
|
|
1353
|
+
content = textwrap.dedent(content) # Dedent content before processing to make sure ++ is at start of line
|
|
1354
|
+
for m in _PLUS_RE.finditer(content):
|
|
1355
|
+
opt = (m.group('opt') or '').strip().lower().replace('_', '-')
|
|
1356
|
+
chunk = content[start:m.start()].rstrip() # preserve leading indentation, clear trailing junk
|
|
1357
|
+
|
|
1358
|
+
if chunk:
|
|
1359
|
+
yield chunk
|
|
1360
|
+
if delimited:
|
|
1361
|
+
yield _part_delim()
|
|
1362
|
+
elif first and delimited:
|
|
1363
|
+
yield _part_delim()
|
|
1364
|
+
|
|
1365
|
+
first = False
|
|
1366
|
+
start = m.end()
|
|
1367
|
+
|
|
1368
|
+
if tail := content[start:].rstrip():
|
|
1369
|
+
yield tail
|
|
1370
|
+
|
|
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
|