Init
This commit is contained in:
85
extensions/slide_meta/__init__.py
Normal file
85
extensions/slide_meta/__init__.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from sphinx.util import logging
|
||||
from docutils import nodes
|
||||
|
||||
from .parser import PatchedNotebookParser
|
||||
from .directive import CellMetaDirective, CellMetaNode
|
||||
|
||||
__version__ = "0.1"
|
||||
|
||||
|
||||
# Used to render an element node as HTML
|
||||
# see https://github.com/executablebooks/sphinx-thebe/blob/v0.2.1/sphinx_thebe/__init__.py#L219
|
||||
def visit_element_html(self, node):
|
||||
self.body.append(node.html())
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
# Used for nodes that do not need to be rendered
|
||||
def skip(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def add_static_sources(app):
|
||||
static_path = Path(__file__).parent / "static"
|
||||
app.config.html_static_path.append(static_path.as_posix())
|
||||
|
||||
|
||||
def enable_presentation_mode(app, pagename, templatename, context, doctree):
|
||||
if not doctree or not (meta_nodes := doctree.traverse(CellMetaNode)):
|
||||
# page has no cell metadata -> no slideshow
|
||||
return
|
||||
|
||||
if not any(
|
||||
"slideshow" in json.loads(node["metadata"])
|
||||
for node in meta_nodes
|
||||
):
|
||||
# notebook was not configured to be a slideshow
|
||||
return
|
||||
|
||||
context["header_buttons"].append(
|
||||
{
|
||||
"type": "javascript",
|
||||
"javascript": "startPresentation()",
|
||||
"tooltip": "Start presenting",
|
||||
"icon": "fas fa-chart-bar",
|
||||
}
|
||||
)
|
||||
|
||||
app.add_css_file("vendor/reveal.css")
|
||||
app.add_css_file("vendor/simple.css") # theme
|
||||
app.add_css_file("fix-theme.css")
|
||||
app.add_js_file("vendor/reveal.js")
|
||||
app.add_js_file("present.js")
|
||||
|
||||
|
||||
def setup(app):
|
||||
# override the MyST-NB NotebookParser with our patched version
|
||||
app.add_source_parser(PatchedNotebookParser, override=True)
|
||||
|
||||
# register directive and node required to process the added metadata
|
||||
app.add_directive("cell_meta", CellMetaDirective)
|
||||
app.add_node(
|
||||
CellMetaNode,
|
||||
html=(visit_element_html, None),
|
||||
latex=(skip, None),
|
||||
textinfo=(skip, None),
|
||||
text=(skip, None),
|
||||
man=(skip, None),
|
||||
override=True,
|
||||
)
|
||||
|
||||
# include sources for static js / css content
|
||||
app.connect("builder-inited", add_static_sources)
|
||||
|
||||
# add the button and js / css files
|
||||
# set priority so this runs before the download button is added
|
||||
app.connect("html-page-context", enable_presentation_mode, priority=500)
|
||||
|
||||
return {
|
||||
"version": __version__,
|
||||
"parallel_read_safe": True,
|
||||
"parallel_write_safe": True,
|
||||
}
|
||||
BIN
extensions/slide_meta/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
extensions/slide_meta/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
extensions/slide_meta/__pycache__/directive.cpython-311.pyc
Normal file
BIN
extensions/slide_meta/__pycache__/directive.cpython-311.pyc
Normal file
Binary file not shown.
BIN
extensions/slide_meta/__pycache__/parser.cpython-311.pyc
Normal file
BIN
extensions/slide_meta/__pycache__/parser.cpython-311.pyc
Normal file
Binary file not shown.
16
extensions/slide_meta/directive.py
Normal file
16
extensions/slide_meta/directive.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from docutils import nodes
|
||||
|
||||
|
||||
class CellMetaNode(nodes.Element):
|
||||
def html(self):
|
||||
metadata = self["metadata"]
|
||||
return f'<script type="application/json" data-cell-meta="">{metadata}</script>'
|
||||
|
||||
|
||||
class CellMetaDirective(SphinxDirective):
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
joined = "".join(self.content)
|
||||
return [CellMetaNode(metadata=joined)]
|
||||
218
extensions/slide_meta/parser.py
Normal file
218
extensions/slide_meta/parser.py
Normal file
@@ -0,0 +1,218 @@
|
||||
import json
|
||||
|
||||
from myst_nb.parser import *
|
||||
|
||||
SPHINX_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Mostly taken and slightly adapted from https://github.com/executablebooks/MyST-NB/blob/v0.13.2/myst_nb/parser.py
|
||||
|
||||
|
||||
class PatchedNotebookParser(MystParser):
|
||||
"""Docutils parser for Markedly Structured Text (MyST) and Jupyter Notebooks."""
|
||||
|
||||
supported = ("myst-nb",)
|
||||
translate_section_name = None
|
||||
|
||||
config_section = "myst-nb parser"
|
||||
config_section_dependencies = ("parsers",)
|
||||
|
||||
def parse(
|
||||
self, inputstring: str, document: nodes.document, renderer: str = "sphinx"
|
||||
) -> None:
|
||||
self.reporter = document.reporter
|
||||
self.env = document.settings.env # type: BuildEnvironment
|
||||
|
||||
converter = get_nb_converter(
|
||||
self.env.doc2path(self.env.docname, True),
|
||||
self.env,
|
||||
inputstring.splitlines(keepends=True),
|
||||
)
|
||||
|
||||
if converter is None:
|
||||
# Read the notebook as a text-document
|
||||
super().parse(inputstring, document=document)
|
||||
return
|
||||
|
||||
try:
|
||||
ntbk = converter.func(inputstring)
|
||||
except Exception as error:
|
||||
SPHINX_LOGGER.error(
|
||||
"MyST-NB: Conversion to notebook failed: %s",
|
||||
error,
|
||||
# exc_info=True,
|
||||
location=(self.env.docname, 1),
|
||||
)
|
||||
return
|
||||
|
||||
# add outputs to notebook from the cache
|
||||
if self.env.config["jupyter_execute_notebooks"] != "off":
|
||||
ntbk = generate_notebook_outputs(
|
||||
self.env, ntbk, show_traceback=self.env.config["execution_show_tb"]
|
||||
)
|
||||
|
||||
# Parse the notebook content to a list of syntax tokens and an env
|
||||
# containing global data like reference definitions
|
||||
md_parser, env, tokens = patched_nb_to_tokens( # <-- patched here
|
||||
ntbk,
|
||||
(
|
||||
self.env.myst_config # type: ignore[attr-defined]
|
||||
if converter is None
|
||||
else converter.config
|
||||
),
|
||||
self.env.config["nb_render_plugin"],
|
||||
)
|
||||
|
||||
# Write the notebook's output to disk
|
||||
path_doc = nb_output_to_disc(ntbk, document)
|
||||
|
||||
# Update our glue key list with new ones defined in this page
|
||||
glue_domain = NbGlueDomain.from_env(self.env)
|
||||
glue_domain.add_notebook(ntbk, path_doc)
|
||||
|
||||
# Render the Markdown tokens to docutils AST.
|
||||
tokens_to_docutils(md_parser, env, tokens, document)
|
||||
|
||||
|
||||
def patched_nb_to_tokens(
|
||||
ntbk: nbf.NotebookNode, config: MdParserConfig, renderer_plugin: str
|
||||
) -> Tuple[MarkdownIt, Dict[str, Any], List[Token]]:
|
||||
"""Parse the notebook content to a list of syntax tokens and an env,
|
||||
containing global data like reference definitions.
|
||||
"""
|
||||
md = default_parser(config)
|
||||
# setup the markdown parser
|
||||
# Note we disable front matter parsing,
|
||||
# because this is taken from the actual notebook metadata
|
||||
md.disable("front_matter", ignoreInvalid=True)
|
||||
md.renderer = SphinxNBRenderer(md)
|
||||
# make a sandbox where all the parsing global data,
|
||||
# like reference definitions will be stored
|
||||
env: Dict[str, Any] = {}
|
||||
rules = md.core.ruler.get_active_rules()
|
||||
|
||||
# First only run pre-inline chains
|
||||
# so we can collect all reference definitions, etc, before assessing references
|
||||
def parse_block(src, start_line):
|
||||
with md.reset_rules():
|
||||
# enable only rules up to block
|
||||
md.core.ruler.enableOnly(rules[: rules.index("inline")])
|
||||
tokens = md.parse(src, env)
|
||||
for token in tokens:
|
||||
if token.map:
|
||||
token.map = [start_line + token.map[0], start_line + token.map[1]]
|
||||
for dup_ref in env.get("duplicate_refs", []):
|
||||
if "fixed" not in dup_ref:
|
||||
dup_ref["map"] = [
|
||||
start_line + dup_ref["map"][0],
|
||||
start_line + dup_ref["map"][1],
|
||||
]
|
||||
dup_ref["fixed"] = True
|
||||
return tokens
|
||||
|
||||
block_tokens = []
|
||||
source_map = ntbk.metadata.get("source_map", None)
|
||||
|
||||
# get language lexer name
|
||||
langinfo = ntbk.metadata.get("language_info", {})
|
||||
lexer = langinfo.get("pygments_lexer", langinfo.get("name", None))
|
||||
if lexer is None:
|
||||
ntbk.metadata.get("kernelspec", {}).get("language", None)
|
||||
# TODO log warning if lexer is still None
|
||||
|
||||
for cell_index, nb_cell in enumerate(ntbk.cells):
|
||||
# if the the source_map has been stored (for text-based notebooks),
|
||||
# we use that do define the starting line for each cell
|
||||
# otherwise, we set a pseudo base that represents the cell index
|
||||
start_line = source_map[cell_index] if source_map else (cell_index + 1) * 10000
|
||||
start_line += 1 # use base 1 rather than 0
|
||||
|
||||
# Skip empty cells
|
||||
if len(nb_cell["source"].strip()) == 0:
|
||||
continue
|
||||
|
||||
# skip cells tagged for removal
|
||||
# TODO this logic should be deferred to a transform
|
||||
tags = nb_cell.metadata.get("tags", [])
|
||||
if ("remove_cell" in tags) or ("remove-cell" in tags):
|
||||
continue
|
||||
|
||||
### Patched here
|
||||
|
||||
# Add a Token with a cell_meta directive, i.e.:
|
||||
#
|
||||
# ```cell_meta
|
||||
# {"slideshow": {"slide_type": "slide"}}
|
||||
# ```
|
||||
|
||||
block_tokens.append(
|
||||
Token(
|
||||
type="fence",
|
||||
tag="code",
|
||||
nesting=0,
|
||||
attrs={},
|
||||
map=[start_line, start_line],
|
||||
level=0,
|
||||
children=None,
|
||||
content=json.dumps(nb_cell.metadata),
|
||||
markup="```",
|
||||
info="{cell_meta}",
|
||||
meta={},
|
||||
block=True,
|
||||
hidden=False,
|
||||
)
|
||||
)
|
||||
|
||||
### / Patched here
|
||||
|
||||
if nb_cell["cell_type"] == "markdown":
|
||||
# we add the cell index to tokens,
|
||||
# so they can be included in the error logging,
|
||||
block_tokens.extend(parse_block(nb_cell["source"], start_line))
|
||||
|
||||
elif nb_cell["cell_type"] == "code":
|
||||
# here we do nothing but store the cell as a custom token
|
||||
block_tokens.append(
|
||||
Token(
|
||||
"nb_code_cell",
|
||||
"",
|
||||
0,
|
||||
meta={"cell": nb_cell, "lexer": lexer, "renderer": renderer_plugin},
|
||||
map=[start_line, start_line],
|
||||
)
|
||||
)
|
||||
|
||||
# Now all definitions have been gathered,
|
||||
# we run inline and post-inline chains, to expand the text.
|
||||
# Note we assume here that these rules never require the actual source text,
|
||||
# only acting on the existing tokens
|
||||
state = StateCore("", md, env, block_tokens)
|
||||
with md.reset_rules():
|
||||
md.core.ruler.enableOnly(rules[rules.index("inline") :])
|
||||
md.core.process(state)
|
||||
|
||||
# Add the front matter.
|
||||
# Note that myst_parser serialises dict/list like keys, when rendering to
|
||||
# docutils docinfo. These could be read back with `json.loads`.
|
||||
state.tokens = [
|
||||
Token(
|
||||
"front_matter",
|
||||
"",
|
||||
0,
|
||||
map=[0, 0],
|
||||
content=({k: v for k, v in ntbk.metadata.items()}), # type: ignore[arg-type]
|
||||
)
|
||||
] + state.tokens
|
||||
|
||||
# If there are widgets, this will embed the state of all widgets in a script
|
||||
if contains_widgets(ntbk):
|
||||
state.tokens.append(
|
||||
Token(
|
||||
"jupyter_widget_state",
|
||||
"",
|
||||
0,
|
||||
map=[0, 0],
|
||||
meta={"state": get_widgets(ntbk)},
|
||||
)
|
||||
)
|
||||
|
||||
return md, env, state.tokens
|
||||
14
extensions/slide_meta/static/fix-theme.css
Normal file
14
extensions/slide_meta/static/fix-theme.css
Normal file
@@ -0,0 +1,14 @@
|
||||
.reveal .headerlink,
|
||||
.reveal .copybtn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reveal .highlight > pre {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reveal .past[hidden],
|
||||
.reveal .present[hidden],
|
||||
.reveal .future[hidden] {
|
||||
display: block !important;
|
||||
}
|
||||
145
extensions/slide_meta/static/present.js
Normal file
145
extensions/slide_meta/static/present.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const getContent = (el) => {
|
||||
let result = [];
|
||||
|
||||
for (const child of el.children) {
|
||||
if (child.tagName.toLowerCase() === "section") {
|
||||
result.push(...getContent(child));
|
||||
} else {
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const contentToCells = (elements) => {
|
||||
/*
|
||||
type Cell = {
|
||||
slideType: "slide" | "subslide" | "fragment" | "notes" | "skip" | "",
|
||||
children: HTMLElement[],
|
||||
}
|
||||
*/
|
||||
let cells = [];
|
||||
let currentCell;
|
||||
|
||||
for (const child of elements) {
|
||||
if (child.tagName.toLowerCase() === "script" && child.dataset.hasOwnProperty("cellMeta")) {
|
||||
const meta = JSON.parse(child.innerText);
|
||||
let slideType = (meta.slideshow || {}).slide_type;
|
||||
|
||||
currentCell = {
|
||||
slideType: ((slideType === undefined) || (slideType == '-')) ? '' : slideType,
|
||||
children: [],
|
||||
};
|
||||
|
||||
cells.push(currentCell);
|
||||
} else {
|
||||
if (currentCell === undefined) continue;
|
||||
currentCell.children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
};
|
||||
|
||||
const startPresentation = () => {
|
||||
console.log("starting presentation...");
|
||||
|
||||
// TODO this feels hacky
|
||||
|
||||
const realroot = document.querySelector(".container-xl");
|
||||
|
||||
let fakeroot = document.createElement("div");
|
||||
fakeroot.style.width = "100vw";
|
||||
fakeroot.style.height = "100vh";
|
||||
fakeroot.style.position = "absolute";
|
||||
fakeroot.style.left = 0;
|
||||
fakeroot.style.top = 0;
|
||||
|
||||
// create .reveal > .slides > section structure
|
||||
|
||||
let revealDiv = document.createElement("div");
|
||||
revealDiv.classList.add("reveal");
|
||||
fakeroot.appendChild(revealDiv);
|
||||
|
||||
let revealSlides = document.createElement("div");
|
||||
revealSlides.classList.add("slides");
|
||||
revealDiv.appendChild(revealSlides);
|
||||
|
||||
// get content
|
||||
const main = document.getElementById("main-content");
|
||||
const mainDiv = main.children[0];
|
||||
let content = getContent(mainDiv);
|
||||
|
||||
// see https://github.com/damianavila/RISE/blob/5.7.1/classic/rise/static/main.js#L219
|
||||
|
||||
let slide_counter = -1, subslide_counter = -1;
|
||||
let slide_section, subslide_section;
|
||||
|
||||
function new_slide() {
|
||||
slide_counter += 1;
|
||||
subslide_counter = -1;
|
||||
let element = document.createElement("section");
|
||||
revealSlides.appendChild(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
function new_subslide() {
|
||||
subslide_counter += 1;
|
||||
let element = document.createElement("section");
|
||||
element.id = `slide-${slide_counter}-${subslide_counter}`;
|
||||
slide_section.appendChild(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
let content_on_slide1 = false;
|
||||
|
||||
slide_section = new_slide();
|
||||
subslide_section = new_subslide();
|
||||
let current_fragment = subslide_section;
|
||||
|
||||
let cells = contentToCells(content);
|
||||
|
||||
for (let cell of cells) {
|
||||
|
||||
// update slide structure
|
||||
if (content_on_slide1) {
|
||||
if (cell.slideType === 'slide') {
|
||||
slide_section = new_slide();
|
||||
current_fragment = subslide_section = new_subslide();
|
||||
} else if (cell.slideType === 'subslide') {
|
||||
current_fragment = subslide_section = new_subslide();
|
||||
} else if (cell.slideType === 'fragment') {
|
||||
cell.fragment_div = current_fragment = document.createElement("div");
|
||||
cell.fragment_div.classList.add("fragment");
|
||||
subslide_section.appendChild(cell.fragment_div);
|
||||
}
|
||||
} else if (cell.slideType !== "notes" && cell.slideType !== "skip") {
|
||||
content_on_slide1 = true;
|
||||
}
|
||||
|
||||
|
||||
// add cell content to slides
|
||||
if (cell.slideType === "notes") {
|
||||
let aside = document.createElement("aside");
|
||||
aside.classList.add("notes");
|
||||
for (let child of cell.children) {
|
||||
aside.appendChild(child);
|
||||
}
|
||||
subslide_section.appendChild(aside);
|
||||
} else if (cell.slideType !== "skip") {
|
||||
for (let child of cell.children) {
|
||||
current_fragment.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.body.appendChild(fakeroot);
|
||||
realroot.style.display = "none";
|
||||
|
||||
console.log("fakeroot ready, initializing reveal");
|
||||
let deck = new Reveal(revealDiv, {
|
||||
overview: false,
|
||||
});
|
||||
deck.initialize();
|
||||
}
|
||||
30
extensions/slide_meta/static/vendor/reset.css
vendored
Normal file
30
extensions/slide_meta/static/vendor/reset.css
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v4.0 | 20180602
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
main, menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, main, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
8
extensions/slide_meta/static/vendor/reveal.css
vendored
Normal file
8
extensions/slide_meta/static/vendor/reveal.css
vendored
Normal file
File diff suppressed because one or more lines are too long
9
extensions/slide_meta/static/vendor/reveal.js
vendored
Normal file
9
extensions/slide_meta/static/vendor/reveal.js
vendored
Normal file
File diff suppressed because one or more lines are too long
360
extensions/slide_meta/static/vendor/simple.css
vendored
Normal file
360
extensions/slide_meta/static/vendor/simple.css
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* A simple theme for reveal.js presentations, similar
|
||||
* to the default theme. The accent color is darkblue.
|
||||
*
|
||||
* This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed.
|
||||
* reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
|
||||
*/
|
||||
@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700);
|
||||
@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
|
||||
section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* GLOBAL STYLES
|
||||
*********************************************/
|
||||
:root {
|
||||
--r-background-color: #fff;
|
||||
--r-main-font: Lato, sans-serif;
|
||||
--r-main-font-size: 40px;
|
||||
--r-main-color: #000;
|
||||
--r-block-margin: 20px;
|
||||
--r-heading-margin: 0 0 20px 0;
|
||||
--r-heading-font: News Cycle, Impact, sans-serif;
|
||||
--r-heading-color: #000;
|
||||
--r-heading-line-height: 1.2;
|
||||
--r-heading-letter-spacing: normal;
|
||||
--r-heading-text-transform: none;
|
||||
--r-heading-text-shadow: none;
|
||||
--r-heading-font-weight: normal;
|
||||
--r-heading1-text-shadow: none;
|
||||
--r-heading1-size: 3.77em;
|
||||
--r-heading2-size: 2.11em;
|
||||
--r-heading3-size: 1.55em;
|
||||
--r-heading4-size: 1em;
|
||||
--r-code-font: monospace;
|
||||
--r-link-color: #00008B;
|
||||
--r-link-color-dark: #00003f;
|
||||
--r-link-color-hover: #0000f1;
|
||||
--r-selection-background-color: rgba(0, 0, 0, 0.99);
|
||||
--r-selection-color: #fff;
|
||||
}
|
||||
|
||||
.reveal-viewport {
|
||||
background: #fff;
|
||||
background-color: var(--r-background-color);
|
||||
}
|
||||
|
||||
.reveal {
|
||||
font-family: var(--r-main-font);
|
||||
font-size: var(--r-main-font-size);
|
||||
font-weight: normal;
|
||||
color: var(--r-main-color);
|
||||
}
|
||||
|
||||
.reveal ::selection {
|
||||
color: var(--r-selection-color);
|
||||
background: var(--r-selection-background-color);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.reveal ::-moz-selection {
|
||||
color: var(--r-selection-color);
|
||||
background: var(--r-selection-background-color);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.reveal .slides section,
|
||||
.reveal .slides section > section {
|
||||
line-height: 1.3;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* HEADERS
|
||||
*********************************************/
|
||||
.reveal h1,
|
||||
.reveal h2,
|
||||
.reveal h3,
|
||||
.reveal h4,
|
||||
.reveal h5,
|
||||
.reveal h6 {
|
||||
margin: var(--r-heading-margin);
|
||||
color: var(--r-heading-color);
|
||||
font-family: var(--r-heading-font);
|
||||
font-weight: var(--r-heading-font-weight);
|
||||
line-height: var(--r-heading-line-height);
|
||||
letter-spacing: var(--r-heading-letter-spacing);
|
||||
text-transform: var(--r-heading-text-transform);
|
||||
text-shadow: var(--r-heading-text-shadow);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.reveal h1 {
|
||||
font-size: var(--r-heading1-size);
|
||||
}
|
||||
|
||||
.reveal h2 {
|
||||
font-size: var(--r-heading2-size);
|
||||
}
|
||||
|
||||
.reveal h3 {
|
||||
font-size: var(--r-heading3-size);
|
||||
}
|
||||
|
||||
.reveal h4 {
|
||||
font-size: var(--r-heading4-size);
|
||||
}
|
||||
|
||||
.reveal h1 {
|
||||
text-shadow: var(--r-heading1-text-shadow);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* OTHER
|
||||
*********************************************/
|
||||
.reveal p {
|
||||
margin: var(--r-block-margin) 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Remove trailing margins after titles */
|
||||
.reveal h1:last-child,
|
||||
.reveal h2:last-child,
|
||||
.reveal h3:last-child,
|
||||
.reveal h4:last-child,
|
||||
.reveal h5:last-child,
|
||||
.reveal h6:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Ensure certain elements are never larger than the slide itself */
|
||||
.reveal img,
|
||||
.reveal video,
|
||||
.reveal iframe {
|
||||
max-width: 95%;
|
||||
max-height: 95%;
|
||||
}
|
||||
|
||||
.reveal strong,
|
||||
.reveal b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reveal em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.reveal ol,
|
||||
.reveal dl,
|
||||
.reveal ul {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
margin: 0 0 0 1em;
|
||||
}
|
||||
|
||||
.reveal ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.reveal ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.reveal ul ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.reveal ul ul ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
.reveal ul ul,
|
||||
.reveal ul ol,
|
||||
.reveal ol ol,
|
||||
.reveal ol ul {
|
||||
display: block;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.reveal dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reveal dd {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.reveal blockquote {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 70%;
|
||||
margin: var(--r-block-margin) auto;
|
||||
padding: 5px;
|
||||
font-style: italic;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.reveal blockquote p:first-child,
|
||||
.reveal blockquote p:last-child {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.reveal q {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.reveal pre {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 90%;
|
||||
margin: var(--r-block-margin) auto;
|
||||
text-align: left;
|
||||
font-size: 0.55em;
|
||||
font-family: var(--r-code-font);
|
||||
line-height: 1.2em;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.reveal code {
|
||||
font-family: var(--r-code-font);
|
||||
text-transform: none;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.reveal pre code {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
overflow: auto;
|
||||
max-height: 400px;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.reveal .code-wrapper {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.reveal .code-wrapper code {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.reveal table {
|
||||
margin: auto;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.reveal table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reveal table th,
|
||||
.reveal table td {
|
||||
text-align: left;
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.reveal table th[align=center],
|
||||
.reveal table td[align=center] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reveal table th[align=right],
|
||||
.reveal table td[align=right] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.reveal table tbody tr:last-child th,
|
||||
.reveal table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.reveal sup {
|
||||
vertical-align: super;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.reveal sub {
|
||||
vertical-align: sub;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.reveal small {
|
||||
display: inline-block;
|
||||
font-size: 0.6em;
|
||||
line-height: 1.2em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.reveal small * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.reveal img {
|
||||
margin: var(--r-block-margin) 0;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* LINKS
|
||||
*********************************************/
|
||||
.reveal a {
|
||||
color: var(--r-link-color);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.reveal a:hover {
|
||||
color: var(--r-link-color-hover);
|
||||
text-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.reveal .roll span:after {
|
||||
color: #fff;
|
||||
background: var(--r-link-color-dark);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Frame helper
|
||||
*********************************************/
|
||||
.reveal .r-frame {
|
||||
border: 4px solid var(--r-main-color);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.reveal a .r-frame {
|
||||
transition: all 0.15s linear;
|
||||
}
|
||||
|
||||
.reveal a:hover .r-frame {
|
||||
border-color: var(--r-link-color);
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* NAVIGATION CONTROLS
|
||||
*********************************************/
|
||||
.reveal .controls {
|
||||
color: var(--r-link-color);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* PROGRESS BAR
|
||||
*********************************************/
|
||||
.reveal .progress {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: var(--r-link-color);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* PRINT BACKGROUND
|
||||
*********************************************/
|
||||
@media print {
|
||||
.backgrounds {
|
||||
background-color: var(--r-background-color);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user