Source code for lookatme.output.html

"""
Output an entire slide deck into an interactive html file
"""


from collections import OrderedDict
import os
from typing import Any, Tuple, List, Optional


import urwid


from lookatme.output.base import BaseOutputFormat
from lookatme.pres import Presentation
import lookatme.render.html as html
import lookatme.utils.fs as fs_utils
from lookatme.tutorial import tutor


[docs]class HtmlSlideDeckOutputFormat(BaseOutputFormat): NAME = "html" DEFAULT_OPTIONS = { "cols": 100, "rows": 30, "title_delim": ":", "render_images": True, }
[docs] def do_format_pres(self, pres: Presentation, output_path: str): """ """ slides_html, slides_titles = self._create_html( pres=pres, width=self.opt("cols"), render_images=self.opt("render_images"), ) self._create_html_output( output_dir=output_path, slides_html=slides_html, slides_titles=slides_titles, title_category_delim=self.opt("title_delim"), )
def _create_html( self, pres: Presentation, width: int = 150, render_images: bool = True, ) -> Tuple[List[Tuple[str, str, str]], List[Tuple[str, Optional[urwid.Canvas]]]]: pres.tui_init_sync() ctx = html.HtmlContext() if pres.tui is None: raise RuntimeError() # header, body, footer contents, not wrapped in divs with classes # yet raw_slides_html: List[Tuple[str, str, str]] = [] titles: List[Tuple[str, Optional[urwid.Canvas]]] = [] for slide_idx, slide in enumerate(pres.slides): titles.append(slide.get_title(pres.tui.ctx)) pres.tui.set_slide_idx(slide_idx) header, body, footer = pres.tui.render_without_scrollbar(width) classname = "slide" if slide_idx != 0: classname += " hidden" html.canvas_to_html(ctx, header, render_images=render_images) header_html = ctx.get_html_consumed() html.canvas_to_html(ctx, body, render_images=render_images) body_html = ctx.get_html_consumed() html.canvas_to_html(ctx, footer, render_images=render_images) footer_html = ctx.get_html_consumed() raw_slides_html.append((header_html, body_html, footer_html)) return raw_slides_html, titles def _create_slide_nav( self, titles: List[Tuple[str, Optional[urwid.Canvas]]], category_delim: Optional[str] = None, ) -> str: nav: OrderedDict[str, Any] = OrderedDict() nav["__children__"] = OrderedDict() for slide_idx, (title_text, title_canvas) in enumerate(titles): if category_delim is not None: categories = [x.strip() for x in title_text.split(category_delim)] else: categories = [title_text] curr_nav = nav for idx, category in enumerate(categories): if title_canvas is None: html_title = "<span>" + title_text + "</span>" else: ctx = html.HtmlContext() html.canvas_to_html(ctx, title_canvas, only_keep=category) html_title = ctx.get_html().strip() children = curr_nav.setdefault("__children__", OrderedDict()) cat_info = children.get(category, None) # type: ignore if not cat_info: cat_info = children[category] = OrderedDict( {"__slide__": None, "__html__": html_title} ) # only keep the first slide (main use case here is progressive # slides that all have the same title) if idx == len(categories) - 1 and not cat_info["__slide__"]: cat_info["__slide__"] = slide_idx # type: ignore else: curr_nav = cat_info ctx = html.HtmlContext() self._render_nav_tree(ctx, nav) return ctx.get_html() def _create_slide_deck(self, slides_html: List[Tuple[str, str, str]]) -> str: ctx = html.HtmlContext() for slide_idx, (header_html, body_html, footer_html) in enumerate(slides_html): with ctx.use_tag("div", classname="slide", **{"data-slide-idx": slide_idx}): with ctx.use_tag("div", classname="slide-header"): ctx.write(header_html) with ctx.use_tag("div", classname="slide-body"): with ctx.use_tag("div", classname="slide-body-inner", tabindex=-1): with ctx.use_tag("div", classname="slide-body-inner-inner"): ctx.write(body_html) with ctx.use_tag("div", classname="slide-footer"): ctx.write(footer_html) return ctx.get_html() def _create_html_output( self, output_dir: str, slides_html: List[Tuple[str, str, str]], slides_titles: List[Tuple[str, Optional[urwid.Canvas]]], title_category_delim: str = ":", ): slide_deck = self._create_slide_deck(slides_html) slide_nav = self._create_slide_nav(slides_titles, title_category_delim) context = {} html.add_styles_to_context(context) script = self.render_template("script.template.js", context) styles = self.render_template("styles.template.css", context) context.update( { "styles": styles, "script": script, "nav": slide_nav, "slide_deck": slide_deck, } ) single_page_html = self.render_template("single_page.template.html", context) static_dir = os.path.join(os.path.dirname(__file__), "html_static") dst_static_dir = os.path.join(output_dir, "static") fs_utils.copy_tree_update(static_dir, dst_static_dir) index_html_path = os.path.join(output_dir, "index.html") with open(index_html_path, "w") as f: f.write(single_page_html) def _render_nav_tree(self, ctx: html.HtmlContext, tree: OrderedDict): with ctx.use_tag("ul"): for _, info in tree["__children__"].items(): classname = "navitem" html_title = info["__html__"] attrs = {} if info["__slide__"] is not None: attrs["data-slide-idx"] = info["__slide__"] classname += " navitem-slide" with ctx.use_tag("li", classname=classname, **attrs): ctx.write(html_title) if "__children__" in info: self._render_nav_tree(ctx, info)