├── .github └── FUNDING.yml ├── .gitignore ├── .readthedocs.yml ├── LICENSE.md ├── README.md ├── binder ├── environment.yml └── postBuild ├── classic ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── jupyter-config │ └── nbconfig │ │ └── notebook.d │ │ └── rise.json ├── package.json ├── rise │ ├── __init__.py │ └── static │ │ ├── README.md │ │ ├── main.js │ │ ├── rise.png │ │ └── rise.yaml ├── setup.cfg ├── setup.py └── src │ └── less │ └── main.less ├── doc ├── Makefile ├── README.md ├── basic_usage.gif ├── changelog.rst ├── conf.py ├── customize.md ├── dev │ ├── develop.md │ ├── index.rst │ └── release.md ├── exportpdf.md ├── index.rst ├── installation.rst ├── requirements.txt ├── resources.md ├── support.md └── usage.md ├── examples ├── README.css ├── README.ipynb ├── commission.png ├── configurator.png ├── font-sizes-untampered.ipynb ├── font-sizes.css ├── font-sizes.ipynb ├── header-footer-scroll-nocenter.css ├── header-footer-scroll-nocenter.md ├── header-footer-scroll.css ├── header-footer-scroll.md ├── header-footer.css ├── header-footer.md ├── issue-370.ipynb ├── issue-546-newlines-in-bullets.ipynb ├── jupytext-markdown.md ├── jupytext-python.py ├── metadata.png ├── mybackimage.png ├── overlay.css ├── overlay.ipynb ├── rise.css ├── showflow.css ├── showflow.ipynb ├── slide-toolbar.png ├── the-meaning-of-skip.ipynb ├── toolbar-options.png └── xarray.md ├── rise-reveal ├── README.md ├── package.json ├── patch-chalkboard.sh ├── patch-notes-plugin.sh └── patch-reveal-themes.sh └── tests └── themes ├── aa-readme.md ├── master.ipynb └── redo-all.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: damianavila 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | classic/rise/static/main.css 2 | classic/rise/static/reveal.js/ 3 | classic/rise/static/reveal.js-chalkboard/ 4 | classic/build/ 5 | classic/dist/ 6 | classic/rise.egg-info/ 7 | classic/rise/__pycache__/ 8 | rise-reveal/export/ 9 | doc/_build/ 10 | .ipynb_checkpoints/ 11 | node_modules* 12 | vendors-node_modules_* 13 | packages_rise-reveal* 14 | @jupyterlab 15 | export/ 16 | rise/nbextension/reveal.js 17 | rise/nbextension/reveal.js-chalkboard 18 | __pycache__ 19 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.9" 13 | # You can also specify other tool versions: 14 | # nodejs: "16" 15 | # rust: "1.55" 16 | # golang: "1.17" 17 | 18 | # Build documentation in the doc/ directory with Sphinx 19 | sphinx: 20 | configuration: doc/conf.py 21 | 22 | # If using Sphinx, optionally build your docs in additional formats such as PDF 23 | # formats: 24 | # - pdf 25 | 26 | # Optionally declare the Python requirements required to build your docs 27 | python: 28 | install: 29 | - requirements: doc/requirements.txt 30 | - method: pip 31 | path: classic 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | classic/LICENSE.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | classic/README.md -------------------------------------------------------------------------------- /binder/environment.yml: -------------------------------------------------------------------------------- 1 | channels: 2 | - conda-forge 3 | dependencies: 4 | - python 5 | - numpy 6 | - matplotlib 7 | - pandas 8 | - bokeh 9 | - rise 10 | - pip: 11 | - jupyter-contrib-nbextensions 12 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | jupyter contrib nbextension install --user 2 | jupyter nbextension enable splitcell/splitcell 3 | -------------------------------------------------------------------------------- /classic/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2016, Damián Avila and contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /classic/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.md 2 | include README.md 3 | include package.json 4 | include rise/static/main.js 5 | include rise/static/main.css 6 | include rise/static/rise.png 7 | include rise/static/rise.yaml 8 | include rise/static/README.md 9 | recursive-include jupyter-config *.json 10 | recursive-include rise/static/reveal.js * 11 | recursive-include rise/static/reveal.js-chalkboard * 12 | -------------------------------------------------------------------------------- /classic/README.md: -------------------------------------------------------------------------------- 1 | # RISE 2 | 3 | RISE allows you to instantly turn your Jupyter Notebooks into a 4 | slideshow. No out-of-band conversion is needed, switch from jupyter 5 | notebook to a live *reveal.js*-based slideshow in a single keystroke, 6 | and back. 7 | 8 | ![Basic usage](https://media.giphy.com/media/3oxHQtTxAaZwMOHr9u/giphy.gif) 9 | 10 | ## Disclaimer 11 | 12 | Please be aware that the extension built from this repo **is not compatible with JupyterLab 13 | and must be used with the classic notebook** (i.e. notebook <= 6). 14 | 15 | see for more details on its successor. 16 | 17 | ## Resources 18 | 19 | RISE stands for ***Reveal.js - Jupyter/IPython Slideshow Extension***: 20 | 21 | * Demo notebook (no installation required) 22 | * [![](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/damianavila/RISE/master?filepath=examples%2FREADME.ipynb) 23 | 24 | * Source code is on github 25 | * [![Issues](http://img.shields.io/github/issues/damianavila/RISE.svg)](https://github.com/damianavila/RISE/issues) 26 | 27 | * Documentation is hosted on readthedocs 28 | * [![doc status](https://readthedocs.org/projects/rise/badge)](http://rise.readthedocs.io/) 29 | 30 | * Chat room on gitter 31 | * [![Gitter chat](https://badges.gitter.im/damianavila/RISE.png)](https://gitter.im/damianavila/RISE) 32 | 33 | * Videos on youtube 34 | * basic usage (4'30'') 35 | 36 | 37 | 38 | 39 | ## Installation 40 | 41 | You essentially have 2 options: 42 | 43 | ### Option 1 - Using conda: 44 | 45 | ``` 46 | conda install -c conda-forge rise 47 | ``` 48 | 49 | ### Option 2 - Using pip: 50 | 51 | ``` 52 | pip install RISE 53 | ``` 54 | 55 | ## Development 56 | 57 | To install RISE in development mode, see the 58 | [Developer section](https://rise.readthedocs.io/en/latest/dev/index.html) of the RISE 59 | documentation. 60 | 61 | ## Feedback 62 | 63 | If you have any feedback, or find any bugs, please let us know just opening an issue. 64 | 65 | ## Support us 66 | 67 | Please visit this page for more information: https://rise.readthedocs.io/en/latest/support.html 68 | -------------------------------------------------------------------------------- /classic/jupyter-config/nbconfig/notebook.d/rise.json: -------------------------------------------------------------------------------- 1 | { 2 | "load_extensions": { 3 | "rise/main": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /classic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rise", 3 | "description": "Edit and Present Revealjs-based LIVE slides in the Jupyter Notebook", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "reveal.js", 7 | "RISE", 8 | "Jupyter", 9 | "slideshow", 10 | "slides" 11 | ], 12 | "author": { 13 | "name": "Damián Avila", 14 | "url": "https://github.com/damianavila" 15 | }, 16 | "contributors": [ 17 | { 18 | "name": "Thierry Parmentelat", 19 | "url": "https://github.com/parmentelat" 20 | } 21 | ], 22 | "homepage": "https://github.com/damianavila/RISE/tree/classic/README.md", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/damianavila/RISE" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/damianavila/RISE/issues" 29 | }, 30 | "main": "rise/static/main.js", 31 | "scripts": { 32 | "build": "for target in less install-rise-reveal; do npm run $target; done", 33 | "less": "PATH=./node_modules/.bin:$PATH lessc --autoprefix src/less/main.less rise/static/main.css", 34 | "watch-less": "./node_modules/.bin/watch 'npm run less' src/less", 35 | "install-rise-reveal": "cp -r node_modules/rise-reveal/export/ rise/static/", 36 | "clean": "rm -rf rise/static/main.css node_modules rise/static/reveal.js*" 37 | }, 38 | "devDependencies": { 39 | "less": "~2.7.2", 40 | "less-plugin-autoprefix": "~1.4.2", 41 | "less-plugin-clean-css": "~1.5.0", 42 | "watch": "~0.16.0", 43 | "rise-reveal": "file:../rise-reveal/" 44 | }, 45 | "version": "5.7.2.dev2" 46 | } 47 | -------------------------------------------------------------------------------- /classic/rise/__init__.py: -------------------------------------------------------------------------------- 1 | import pkg_resources 2 | 3 | __version__ = pkg_resources.require("rise")[0].version 4 | version_info = pkg_resources.parse_version(__version__) 5 | 6 | def _jupyter_nbextension_paths(): 7 | return [dict(section="notebook", 8 | src="static", 9 | dest="rise", 10 | require="rise/main")] 11 | -------------------------------------------------------------------------------- /classic/rise/static/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /classic/rise/static/main.js: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8; js-indent-level: 2 -*- 2 | * ---------------------------------------------------------------------------- 3 | * Copyright (c) 2013-2017 Damián Avila and contributors. 4 | * 5 | * Distributed under the terms of the Modified BSD License. 6 | * 7 | * A Jupyter notebook extension to support *Live* Reveal.js-based slideshows. 8 | * ----------------------------------------------------------------------------- 9 | */ 10 | 11 | define([ 12 | 'require', 13 | 'jquery', 14 | 'base/js/namespace', 15 | 'base/js/utils', 16 | 'services/config', 17 | ], function(require, $, Jupyter, utils, configmod) { 18 | 19 | "use strict"; 20 | 21 | /* 22 | * load configuration 23 | * 24 | * 1) start from the hardwired settings (in this file) 25 | * 2) add settings configured in python; these typically can be 26 | * 2a) either in a legacy file named `livereveal.json` 27 | * 2b) or, with the official name, in `rise.json` 28 | * 3) add the settings from nbext_configurator (i.e. .jupyter/nbconfig/notebook.json) 29 | * they should all belong in the 'rise' category 30 | * the configurator came after the shift from 'livereveal' to 'rise' 31 | * so no need to consider 'livereveal' here 32 | * 4) and finally add the settings from the notebook metadata 33 | * 4a) for legacy reasons: use the 'livereveal' key 34 | * 4b) for more consistency, then override with the 'rise' key 35 | * 36 | * configLoaded alters the complete_config object in place. 37 | * it will hold a consolidated set of all relevant settings with their priorities resolved 38 | * 39 | * it returns a promise that can be then'ed once the config is loaded 40 | * 41 | * setup waits for the config to be loaded before it actually enables keyboard shortcuts 42 | * and other menu items; so this means that the bulk of the code can assume that the config 43 | * is already loaded and does not need to worry about using promises, or 44 | * waiting for any asynchronous code to complete 45 | */ 46 | 47 | let complete_config = {}; 48 | 49 | // returns a promise; you can do 'then()' on this promise 50 | // to do stuff *after* the configuration is completely loaded 51 | function configLoaded() { 52 | 53 | // see rise.yaml for more details 54 | let hardwired_config = { 55 | 56 | // behaviour 57 | autolaunch: false, 58 | start_slideshow_at: 'selected', 59 | auto_select: 'code', 60 | auto_select_fragment: true, 61 | show_buttons_on_startup: true, 62 | 63 | // aspect 64 | header: undefined, 65 | footer: undefined, 66 | backimage: undefined, 67 | overlay: undefined, 68 | 69 | // timeouts 70 | // wait for that amont before calling ensure_focused on the 71 | // selected cell 72 | restore_timeout: 500, 73 | // wait for that amount before actually selected auto-selected fragment 74 | // when going too short, like 250, size of selected cell get odd 75 | auto_select_timeout: 450, 76 | // wait for that amount before calling sync() again 77 | // this is a workaround that fixes #504 78 | sync_timeout: 250, 79 | 80 | // UI 81 | toolbar_icon: 'fa-bar-chart', 82 | shortcuts: { 83 | 'slideshow' : 'alt-r', 84 | 'relayout' : 'alt-q', 85 | 'toggle-slide': 'shift-i', 86 | 'toggle-subslide': 'shift-b', 87 | 'toggle-fragment': 'shift-g', 88 | // this can be helpful 89 | 'rise-nbconfigurator': 'shift-c', 90 | // unassigned by default 91 | 'toggle-notes': '', 92 | 'toggle-skip': '', 93 | }, 94 | 95 | // reveal native settings passed as-is 96 | // see also the 'inherited' variable below in Revealer 97 | theme: 'simple', 98 | transition: 'linear', 99 | // xxx there might be a need to tweak this one when set 100 | // by the configurator, as e.g. 'false' or 'true' will result 101 | // in a string and not a boolean 102 | slideNumber: true, 103 | width: "100%", 104 | height: "100%", 105 | controls: true, 106 | progress: true, 107 | history: true, 108 | scroll: false, 109 | center: true, 110 | margin: 0.1, 111 | minScale: 1.0, // we need this for codemirror to work right 112 | // turn off reveal's help overlay that is by default bound to question mark / ? 113 | help: false, 114 | 115 | // plugins 116 | enable_chalkboard: false, 117 | enable_leap_motion: false, 118 | }; 119 | 120 | // honour the 2 names: 'livereveal' and 'rise' 121 | // use the ones in livereveal/legacy first 122 | // so they get overridden if redefined in rise 123 | let config_section_legacy = new configmod.ConfigSection( 124 | 'livereveal', 125 | {base_url: utils.get_body_data("baseUrl")}); 126 | // trigger an asynchronous load 127 | config_section_legacy.load(); 128 | let config_section = new configmod.ConfigSection( 129 | 'rise', 130 | {base_url: utils.get_body_data("baseUrl")}); 131 | config_section.load(); 132 | 133 | // this is also a ConfigSection object as per notebook/static/services/config.js 134 | let nbext_configurator = Jupyter.notebook.config; 135 | nbext_configurator.load(); 136 | 137 | // with Promise.all we can wait for all 3 configs to have loaded 138 | return Promise.all([ 139 | config_section_legacy.loaded, 140 | config_section.loaded, 141 | nbext_configurator.loaded, 142 | ]).then( 143 | // and now we can compute the layered config 144 | function() { 145 | // 1) initialize with hardwired defaults 146 | $.extend(true, complete_config, hardwired_config); 147 | // 2a) and 2b) 148 | $.extend(true, complete_config, config_section_legacy.data); 149 | $.extend(true, complete_config, config_section.data); 150 | // 3) 151 | $.extend(true, complete_config, nbext_configurator.data.rise); 152 | // 4a) from the notebook metadata 153 | let metadata_legacy = Jupyter.notebook.metadata.livereveal; 154 | $.extend(true, complete_config, metadata_legacy); 155 | // 4b) ditto 156 | let metadata = Jupyter.notebook.metadata.rise; 157 | $.extend(true, complete_config, metadata); 158 | // console.log("complete_config is OK"); 159 | }); 160 | } 161 | 162 | /* 163 | * this function is a heuristic that says if this notebook seems to 164 | * be meant to be a slideshow. 165 | * this primarily is for autolaunch, so that somebody who would 166 | * enable autolaunch in her ~/.jupyter/ area would not 167 | * see RISE trigger on every single notebook 168 | * 169 | * xxx note that this might take too long on large notebooks 170 | * a possible improvement would be to look for the first, say, 10 cells only 171 | * as a matter of fact, in most cases the first cell would be a slide cell really 172 | */ 173 | function is_slideshow(notebook) { 174 | for (let cell of notebook.get_cells()) 175 | if (is_slide(cell) || is_subslide(cell)) 176 | return true; 177 | return false; 178 | } 179 | 180 | /* 181 | * Version of get_cell_elements that will see cell divs at any depth in the HTML tree, 182 | * allowing container divs, etc to be used without breaking notebook machinery. 183 | * You'll need to make sure the cells are getting detected in the right order. 184 | * NOTE: We use the Object prototype to workaround a firefox issue, check the following 185 | * link to know more about the discussion leading to this use: 186 | * https://github.com/damianavila/RISE/issues/117#issuecomment-127331816 187 | */ 188 | Object.getPrototypeOf(Jupyter.notebook).get_cell_elements = function () { 189 | return this.container.find("div.cell"); 190 | }; 191 | 192 | /* uniform way to access slide type, whether the slideshow metadata is set or not 193 | * also sometimes slide_type is set to '-' by the toolbar 194 | */ 195 | function get_slide_type(cell) { 196 | let slide_type = (cell.metadata.slideshow || {}).slide_type; 197 | return ( (slide_type === undefined) || (slide_type == '-')) ? '' : slide_type; 198 | } 199 | 200 | function is_slide(cell) {return get_slide_type(cell) == 'slide';} 201 | function is_subslide(cell) {return get_slide_type(cell) == 'subslide';} 202 | function is_fragment(cell) {return get_slide_type(cell) == 'fragment';} 203 | function is_skip(cell) {return get_slide_type(cell) == 'skip';} 204 | function is_notes(cell) {return get_slide_type(cell) == 'notes';} 205 | function is_regular(cell) {return get_slide_type(cell) == '';} 206 | 207 | /* Use the slideshow metadata to rearrange cell DOM elements into the 208 | * structure expected by reveal.js 209 | * 210 | * in the process, each cell receives a 'smart_exec' tag that says 211 | * how to behave when the cell gets executed with Shift-Enter 212 | * this tag can be either 213 | * 'smart_exec_slide' : just do exec, which is what RISE did on all cells at first 214 | this is for the last cell on a (sub)slide 215 | i.e. if next cell is slide or subslide 216 | * 'smart_exec_fragment' : do exec + show next fragment 217 | if next cell is a fragment 218 | * 'smart_exec_next' : do the usual exec + select next like in classic notebook 219 | */ 220 | function markupSlides(container) { 221 | // Machinery to create slide/subslide
s and give them IDs 222 | let slide_counter = -1, subslide_counter = -1; 223 | let slide_section, subslide_section; 224 | function new_slide() { 225 | slide_counter++; 226 | subslide_counter = -1; 227 | return $('
').appendTo(container); 228 | } 229 | function new_subslide() { 230 | subslide_counter++; 231 | return $('
').attr('id', 'slide-'+slide_counter+'-'+subslide_counter) 232 | .appendTo(slide_section); 233 | } 234 | 235 | // Containers for the first slide. 236 | slide_section = new_slide(); 237 | subslide_section = new_subslide(); 238 | let current_fragment = subslide_section; 239 | 240 | let selected_cell_idx = Jupyter.notebook.get_selected_index(); 241 | let selected_cell_slide = [0, 0]; 242 | 243 | /* Special handling for the first slide: it will work even if the user 244 | * doesn't start with a 'Slide' cell. But if the user does explicitly 245 | * start with slide/subslide, we don't want a blank first slide. So we 246 | * don't create a new slide/subslide until there is visible content on 247 | * the first slide. 248 | */ 249 | let content_on_slide1 = false; 250 | 251 | let cells = Jupyter.notebook.get_cells(); 252 | 253 | for (let i=0; i < cells.length; i++) { 254 | let cell = cells[i]; 255 | let slide_type = get_slide_type(cell); 256 | 257 | if (content_on_slide1) { 258 | if (slide_type === 'slide') { 259 | // Start new slide 260 | slide_section = new_slide(); 261 | // In each subslide, we insert cells directly into the 262 | //
until we reach a fragment, when we create a div. 263 | current_fragment = subslide_section = new_subslide(); 264 | } else if (slide_type === 'subslide') { 265 | // Start new subslide 266 | current_fragment = subslide_section = new_subslide(); 267 | } else if (slide_type === 'fragment') { 268 | // record the
element corresponding 269 | // to each fragment cell in the 'fragment_div' attribute 270 | cell.fragment_div = current_fragment = $('
').addClass('fragment') 271 | .appendTo(subslide_section); 272 | } 273 | } else if (slide_type !== 'notes' && slide_type !== 'skip') { 274 | // Subsequent cells should be able to start new slides 275 | content_on_slide1 = true; 276 | } 277 | 278 | // Record that this slide contains the selected cell 279 | // this is where we need i as set in the loop over cells 280 | if (i === selected_cell_idx) { 281 | selected_cell_slide = [slide_counter, subslide_counter]; 282 | } 283 | 284 | // Move the cell element into the slide
285 | // N.B. jQuery append takes the element out of the DOM where it was 286 | if (slide_type === 'notes') { 287 | // Notes are wrapped in an