ipyslides 7.0.2__tar.gz → 7.0.4__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 (41) hide show
  1. {ipyslides-7.0.2 → ipyslides-7.0.4}/PKG-INFO +1 -1
  2. ipyslides-7.0.4/ipyslides/__version__.py +2 -0
  3. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/base.py +32 -39
  4. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/settings.py +5 -5
  5. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/static/interaction.js +1 -0
  6. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/widgets.py +1 -3
  7. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/core.py +2 -0
  8. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides.egg-info/PKG-INFO +1 -1
  9. ipyslides-7.0.2/ipyslides/__version__.py +0 -2
  10. {ipyslides-7.0.2 → ipyslides-7.0.4}/LICENSE +0 -0
  11. {ipyslides-7.0.2 → ipyslides-7.0.4}/README.md +0 -0
  12. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/__init__.py +0 -0
  13. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/__init__.py +0 -0
  14. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/_layout.py +0 -0
  15. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/_syntax.py +0 -0
  16. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/_widgets.py +0 -0
  17. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/export_html.py +0 -0
  18. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/export_template.py +0 -0
  19. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/icons.py +0 -0
  20. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/intro.py +0 -0
  21. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/navigation.py +0 -0
  22. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/notes.py +0 -0
  23. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/slide.py +0 -0
  24. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/static/interaction.css +0 -0
  25. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/static/laser.css +0 -0
  26. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/static/laser.js +0 -0
  27. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/static/notes.js +0 -0
  28. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_base/styles.py +0 -0
  29. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/_demo.py +0 -0
  30. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/dashlab.py +0 -0
  31. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/formatters.py +0 -0
  32. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/source.py +0 -0
  33. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/utils.py +0 -0
  34. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/writer.py +0 -0
  35. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides/xmd.py +0 -0
  36. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides.egg-info/SOURCES.txt +0 -0
  37. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides.egg-info/dependency_links.txt +0 -0
  38. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides.egg-info/requires.txt +0 -0
  39. {ipyslides-7.0.2 → ipyslides-7.0.4}/ipyslides.egg-info/top_level.txt +0 -0
  40. {ipyslides-7.0.2 → ipyslides-7.0.4}/setup.cfg +0 -0
  41. {ipyslides-7.0.2 → ipyslides-7.0.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyslides
3
- Version: 7.0.2
3
+ Version: 7.0.4
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.0.4"
@@ -17,6 +17,7 @@ from .slide import SlideGroup, _build_slide
17
17
  from ..formatters import XTML, htmlize, slidebound
18
18
  from ..xmd import error, get_slides_instance, resolve_included_files, _matched_vars, _parse_as_snapshots, _stream_chunks
19
19
  from ..utils import _css_docstring
20
+ from ..dashlab import FileWatcher
20
21
 
21
22
 
22
23
  class BaseSlides:
@@ -116,6 +117,13 @@ class BaseSlides:
116
117
  if synced:
117
118
  content = self._process_citations(content)
118
119
 
120
+ # included files should be able to trigger updates even if main file not edited yet, so we track them as assets
121
+ if synced and hasattr(self, '_watcher'):
122
+ self._watcher.assets = re.findall(r'include\`(.*?)\`',content, flags = re.DOTALL)
123
+
124
+ # Now flatten incuded files, after detecting them as assets above
125
+ content = resolve_included_files(content)
126
+
119
127
  chunks = list(_stream_chunks(content, '---'))
120
128
  handles = self.create(range(start, start + len(chunks))) # create slides faster or return older
121
129
  mdvars = [{k:v for k,v in md_kws.items() if k in _matched_vars(chunk)} for chunk in chunks] # vars used in each chunk
@@ -137,10 +145,11 @@ class BaseSlides:
137
145
  return SlideGroup(handles)
138
146
 
139
147
  def _process_citations(self, content):
140
- match1, *others = re.findall(r'^```citations.*?^```|^:::\s*citations.*?(?=^:::|\Z|^\S)', content, flags= re.DOTALL | re.MULTILINE)
141
- if others:
142
- raise ValueError(f"Only a single block of citations is parsed, found {len(others) + 1} blocks\n{(match1, *others)}")
148
+ matches = re.findall(r'^```citations.*?^```|^:::\s*citations.*?(?=^:::|\Z|^\S)', content, flags= re.DOTALL | re.MULTILINE)
149
+ if len(matches) > 1:
150
+ raise ValueError(f"Only a single block of citations is parsed, found {len(matches)} blocks\n{matches}")
143
151
 
152
+ match1 = matches[0] if matches else ''
144
153
  content = content.replace(match1, '') # clean up
145
154
  if getattr(self,'_bib_md','') != match1:
146
155
  self._bib_md = match1 # set for next test
@@ -181,47 +190,31 @@ class BaseSlides:
181
190
  raise ValueError("interval should be integer greater than 100 millieconds.")
182
191
 
183
192
  start = self._fix_slide_number(start_slide_number)
193
+ self._watcher = FileWatcher(path, interval=interval)
184
194
 
185
- # NOTE: Background threads and other methods do not work. Do NOT change this way
186
- # First call itself before declaring other things, so errors can be captured safely
187
- self._from_markdown(start, resolve_included_files(path.read_text(encoding="utf-8")), synced=True)
188
- self.widgets._timer.clear() # remove previous updates
189
- self._mtime = os.stat(path).st_mtime
190
- included_files = set()
195
+ # First call after watcher set, so it can observe included files immediately,
196
+ # and errors must be caught before going forward
197
+ self._from_markdown(start, path.read_text(encoding="utf-8"), synced=True)
191
198
 
192
- def update():
193
- if path.is_file():
194
- # Track whichever file is last edited from included or itself
195
- mtime = max(os.stat(f).st_mtime for f in [path, *included_files] if f.is_file())
196
- out_sync = any(['Out-Sync' in s._css_class for s in self.cited_slides]) or False
197
- if out_sync or (mtime > self._mtime): # set by interaction widget
198
- self._mtime = mtime
199
- try:
200
- content = path.read_text(encoding="utf-8")
201
- # included files will be one step behind in sync, beacuse they are only detectable now
202
- included_files.update(
203
- map(Path, re.findall(r'include\`(.*?)\`',content, flags = re.DOTALL))
204
- )
205
- self._from_markdown(start,
206
- resolve_included_files(content), # add included file text to be detected for changes
207
- synced=True # only one citation block allowed for consistency
208
- )
209
- self.notify('x') # need to remove any notification from previous error
210
- self._unregister_postrun_cell() # No cells buttons from inside file code run
211
- except:
212
- e, text = traceback.format_exc(limit=0).split(':',1) # only get last error for notification
213
- self.notify(f"{error('SyncError','something went wrong')}<br/>{error(e,text)}",20)
214
- else:
215
- self.notify(error("SyncError", f"file {path!r} no longer exists!").value, 20)
216
-
217
- self.widgets._timer.run(interval, update, loop = True)
218
- print(f"Syncing content from file {path!r}\nYou can use `Slides.unsync()` to stop syncing.")
219
-
199
+ @self._watcher.on_update
200
+ def update_target_slides(path):
201
+ try:
202
+ self._from_markdown(start, path.read_text(encoding="utf-8"), synced=True)
203
+ self.notify('x') # need to remove any notification from previous error
204
+ self._unregister_postrun_cell() # No cells buttons from inside file code run
205
+ except:
206
+ e, text = traceback.format_exc(limit=0).split(':',1) # only get last error for notification
207
+ self.notify(f"{error('SyncError','something went wrong')}<br/>{error(e,text)}",20)
208
+
209
+ display(self._watcher) # must be displayed to work
210
+ self._unregister_postrun_cell() # avoid unnessary scroll button after postrun cell here
220
211
 
221
212
  def unsync(self):
222
213
  "Stop syncing markdown file synced with `Slides.sync_with_file` function."
223
- if self.widgets._timer._callback[0]:
224
- self.widgets._timer.clear()
214
+ if getattr(self, '_watcher', None):
215
+ self._watcher.stop() # stop the file watcher
216
+ self._watcher.close() # clear the displayed watcher at frontend
217
+ del self._watcher # remove reference to the watcher
225
218
  else:
226
219
  print("There was no markdown file linked to sync!")
227
220
 
@@ -2,7 +2,7 @@
2
2
  Author Notes: Classes in this module should only be instantiated in Slides class or it's parent class
3
3
  and then provided to other classes via composition, not inheritance.
4
4
  """
5
- import json
5
+ import json, re
6
6
  import traitlets
7
7
  from contextlib import suppress
8
8
 
@@ -167,11 +167,9 @@ class Footer(ConfigTraits):
167
167
  style = 'white-space:nowrap;margin-block:0;position:absolute;left:0;bottom:0;width:100%'
168
168
  inner = self.text
169
169
  if self.section and (running := self._running_section_text(slide)):
170
- inner += ("<span style='white-space:pre;opacity:0.4;'> \u2502 </span>" if inner else "") + f"<span class='section'>{running}</span>"
170
+ inner += (" | " if inner else "") + f"<span class='section'>{running}</span>"
171
171
  if self.date:
172
- inner += (
173
- "<span style='white-space:pre;opacity:0.4;'> \u2502 </span>" if inner else ""
174
- ) + f'{today(fg = "var(--fg2-color)") if self.date == "today" else self.date}'
172
+ inner += (" | " if inner else "") + f'{today(fg = "var(--fg2-color)") if self.date == "today" else self.date}'
175
173
 
176
174
  inner = f'<div class="footer-text"><div>{inner}</div>'
177
175
  if self.controls:
@@ -186,6 +184,8 @@ class Footer(ConfigTraits):
186
184
  unit = 100/(self.main._slides._lms_idx or 1) # unit progress value per slide
187
185
  pbar = f'<div class="sprogress-view" style="width:{pv}%;height:100%;background:var(--accent-color,blue);" data-cw="{pv}" data-uw="{unit}"></div>' # attributes for JS
188
186
  inner += f'<div class="slide-progress print-only" style="background:var(--bg2-color,#aaa4);width:100%;height:2px;">{pbar}</div>'
187
+
188
+ inner = re.sub(r'(?<![\`\\])\s*\|\s*', '<span style="white-space:pre;opacity:0.4;"> \u2502 </span>', inner) # replace | with fancy divider before parsing and preserve space around
189
189
  return htmlize(f'<div markdown="1" class="slide-footer" style="{style}">{inner}</div>')
190
190
 
191
191
  @fix_sig
@@ -334,6 +334,7 @@ function printSlides(box, model) {
334
334
  });
335
335
 
336
336
  box.style.setProperty('--printPadding', `${fpad}px`); // set padding for print mode
337
+ zoom.reset(box); // reset any zoom before print to avoid weird scaling
337
338
 
338
339
  for (let n = 0; n < slides.length; n++) {
339
340
  let slide = slides[n];
@@ -15,7 +15,7 @@ from . import styles
15
15
  from ._widgets import InteractionWidget, NotesWidget, LaserPointer
16
16
  from .intro import get_logo, how_to_print, instructions, key_combs
17
17
  from ..utils import html, htmlize
18
- from ..dashlab import ListWidget, JupyTimer, TabsWidget
18
+ from ..dashlab import ListWidget, TabsWidget
19
19
  from .. import formatters as fmtrs
20
20
 
21
21
 
@@ -358,7 +358,6 @@ class Widgets:
358
358
  self.sliders = _Sliders()
359
359
  self.checks = _Checks()
360
360
  self.htmls = _Htmls()
361
- self._timer = JupyTimer()
362
361
  self.drawer = DrawWidget(self)
363
362
  self._ctxmenu= CtxMenu(self, description='Shift + Right Click for Browser Menu')
364
363
  self.iw = InteractionWidget(self)
@@ -384,7 +383,6 @@ class Widgets:
384
383
  self.htmls.theme,
385
384
  self.htmls.usercss,
386
385
  self.iw,
387
- self._timer.widget(minimal=True),
388
386
  self.panelbox,
389
387
  self.htmls.pointer,
390
388
  self.htmls.hilite,
@@ -973,6 +973,8 @@ class Slides(BaseSlides,metaclass=Singleton):
973
973
  ::: note
974
974
  `Slides.PART` is deprecated and kept as an alias for backward compatibility.
975
975
  """
976
+ # DO NOT FALL FOR GLOBAL PAGE STUFF, THAT WOULD BE A NIGHTMARE TO HANDLE
977
+ # AND CANNOT HAVE ITS OWN STATE METADATA, AS WE REMOVED IT EARLIER.
976
978
  _type = "PART"
977
979
 
978
980
  def __init__(self, isolate=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyslides
3
- Version: 7.0.2
3
+ Version: 7.0.4
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.0.2"
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