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.
Files changed (42) hide show
  1. {ipyslides-7.3.0 → ipyslides-7.3.2}/PKG-INFO +1 -1
  2. ipyslides-7.3.2/ipyslides/__version__.py +2 -0
  3. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_syntax.py +17 -15
  4. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/styles.py +12 -8
  5. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/core.py +2 -1
  6. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/formatters.py +7 -3
  7. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/pkg_nbs/ips-demo.ipynb +24 -17
  8. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/pkg_nbs/ips-docs.ipynb +4 -6
  9. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/utils.py +8 -8
  10. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/writer.py +7 -5
  11. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/xmd.py +44 -41
  12. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/PKG-INFO +1 -1
  13. ipyslides-7.3.0/ipyslides/__version__.py +0 -2
  14. {ipyslides-7.3.0 → ipyslides-7.3.2}/LICENSE +0 -0
  15. {ipyslides-7.3.0 → ipyslides-7.3.2}/README.md +0 -0
  16. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/__init__.py +0 -0
  17. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/__init__.py +0 -0
  18. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_layout.py +0 -0
  19. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/_widgets.py +0 -0
  20. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/base.py +0 -0
  21. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/export_html.py +0 -0
  22. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/export_template.py +0 -0
  23. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/icons.py +0 -0
  24. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/intro.py +0 -0
  25. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/navigation.py +0 -0
  26. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/notes.py +0 -0
  27. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/settings.py +0 -0
  28. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/slide.py +0 -0
  29. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/interaction.css +0 -0
  30. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/interaction.js +0 -0
  31. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/laser.css +0 -0
  32. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/laser.js +0 -0
  33. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/static/notes.js +0 -0
  34. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/_base/widgets.py +0 -0
  35. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/dashlab.py +0 -0
  36. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides/source.py +0 -0
  37. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/SOURCES.txt +0 -0
  38. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/dependency_links.txt +0 -0
  39. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/requires.txt +0 -0
  40. {ipyslides-7.3.0 → ipyslides-7.3.2}/ipyslides.egg-info/top_level.txt +0 -0
  41. {ipyslides-7.3.0 → ipyslides-7.3.2}/setup.cfg +0 -0
  42. {ipyslides-7.3.0 → ipyslides-7.3.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyslides
3
- Version: 7.3.0
3
+ Version: 7.3.2
4
4
  Summary: Live rich content slides in jupyter notebook
5
5
  Home-page: https://github.com/asaboor-gh/ipyslides
6
6
  Author: Abdul Saboor
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = "7.3.2"
@@ -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\! "target_id", "Jump to slide" \/]` to jump between slides. See `Slides.link` for more details.
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':'inline-flex',
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\! "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.
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
- width = (f'width:{width}px' if isinstance(width,int) else f'width:{width}') + ';max-width:100%;' # important to avoid overflow
155
- svg = f'<svg style="{width};height:auto;"' + plot_bytes.getvalue().decode('utf-8').split('<svg')[1]
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):\n",
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
- " for idx in range(10,19):\n",
383
- " _, ax = plt.subplots(figsize=(3.4,2.6))\n",
384
- " x = np.linspace(0,idx,50)\n",
385
- " ax.plot(x,np.sin(x))\n",
386
- " ax.set_title(rf'$f(x)=\\sin(x)$, 0 < x < {idx+1}')\n",
387
- " ax.set_xlim([0,18])\n",
388
- " ax.set_axis_off()\n",
389
- " plots.append(ax)\n",
390
- " # Fixed source at left + row-wise reveals of plots at right.\n",
391
- " write(src, slides.steps(plots), widths=[60,40], paused=True)\n",
392
- " write('Unlike `interact/interactive`, this animation is based on slide frames, all of which are exported to HTML.',css_class='note-tip')"
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
- " slides.on_load(highlight_code)\n",
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`. `dots_loc` controls
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
- '@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
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('', {'@media screen': view_nodes(selector,*idxs)}) # only screen mode to avoid print conflicts
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
- base_outputs = list(cap.outputs)
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 self._split_parts(obj, delimited=True):
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
- _cs = '\n</div>\n<div markdown="1">\n'.join(_stream_chunks(data, sep='--'))
697
- data = '\n'.join(['<div markdown="1">', _cs, '</div>'])
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}%;}}" for i, w in enumerate(widths)) # nth-child selectors
717
- style += '\n' + f".{klass} > :nth-child({len(widths)}) ~ * {{flex: {min(widths):.3f} {min(widths):.3f} {100*min(widths):.3f}%;}}" # rest all columns get min width
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
- return [XTML(f"<div class='{_class}' {_inline_style(css_props)} {attrs}>{self._parse_nested(data, returns=True)}</div>{style}")]
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 self._split_parts(col):
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 split by ++ and show alternate chunks through a steps widget."
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
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyslides
3
- Version: 7.3.0
3
+ Version: 7.3.2
4
4
  Summary: Live rich content slides in jupyter notebook
5
5
  Home-page: https://github.com/asaboor-gh/ipyslides
6
6
  Author: Abdul Saboor
@@ -1,2 +0,0 @@
1
-
2
- __version__ = "7.3.0"
File without changes
File without changes
File without changes
File without changes
File without changes