Giter VIP home page Giter VIP logo

exerslide's Introduction

exerslide

Build Status

A tool to generate simple HTML slideshows/tutorials (from text files). The primary intent is to generate interactive presentations for teaching web technologies. Built on React and webpack.

Examples

Presentations / sites built with exerslide:

Getting started

exerslide comes in two parts: A global command line interface to initialize and build presentations, and a module that contains further dependencies to build the presentation.

  1. First install the exerslide-cli package globally:

    npm install -g exerslide-cli
  2. Initialize a new project / presentation, by creating a new folder and running exerslide init:

    mkdir myPresentation
    cd myPresentation
    exerslide init myPresentation
  3. Create/edit slides in slides/.

  4. Run

    exerslide serve

to start a local webserver to view the presentation.

Concepts

Note: Most of the of the build process is handled by webpack.
exerslide init (or exerslide copy-defaults) copies exerslide.config.js and webpack.config.js into the project. Most of the default behavior here is defined in these files and can be customized.

Slides

Slides are defined as text files which contain content and metadata. The metadata is defined in an optional YAML front matter block, followed by the content:

---
title: Title of the slide
---
This is the content

(basically just like Jekyll)

Organizing Slides

By default, exerslide determines the order of the slides by sorting the file names alphabetically. For example, if you name your slides as

slides/
  00-Intro.md
  01-MainTopic.md
  02-End.md

exerslide will pick up the slides in that order.

Note: Aside from the order, you can name the files however you want.

Slides can be grouped as chapters by putting all slides of a chapter into a folder. For example:

slides/
  00-Intro.md
  01-chapter1/
    00-Problem.md
    01-Solution.md
  02-Summary.md

Like with filenames, exerslide doesn't care about the actual name of the folder.

Metadata

exerslide provides the ability to configure certain aspects of a slide via the YAML front matter. It currently supports the following, optional, configuration options:

  • title: The value of title will be rendered as an <h1> element above the content.

  • toc: The name to show in the table of contents. If not present, the title option will be used. If that one is not present either, it will show "Slide X" where X is the index of the slide.

  • chapter: The name of the chapter this slide belongs to. It serves two purposes:

    1. It can be used as alternative to group slides by chapter (i.e. you don't have to use folders if you don't want to).
    2. This name will be shown for the chapter in the progress bar / table of contents of the presentation. If you organize your slides in folders, you only have to define this key in the first slide of the folder. All other slides "inherit" the chapter name from the first slide.
  • layout: The name of the layout to use (overwrites layout inference).

  • content_type: The markup language used for the content (overwrites content type inference).

  • class_names: A list of CSS class names to be added to the root of page/presentation. This allows for sharing specific styles across slides.

  • style: CSS style declaration, additional CSS to use on this slide.

  • id: A name that is unique among the slides. This provides a "safer" way to link between slides.

  • layout_data: Additional data to be sent to the layout used for this slide.

  • hide_toc: The table of contents is hidden for this slide if set to true.

  • scale: Exerslide automatically adjusts the font size for different screen widths to ensure that the slides are always readable. scale lets you customize the behavior to some degree. Note: This option can only be set on the first slide.

    • false: If set to false, no adjustments will be made
    • Object: Otherwise the value can be object with up to three properties:
      • content_width: Specifies the content width in ems. This can be thought of as how many words / characters per line should be shown.
      • column_width: A number between 0 and 1. How much of the screen (width) should be occupied by the content.
      • max_font_size: Don't make the font any larger than this value (in pixel).

Layouts

Layouts define how the content (and layout data) of a slide are structured. For example the Column layout renders the content in multiple columns.

Layouts are implemented as React components, which allows you to create arbitrarily complex layouts. For example the JavaScriptExercise layout renders a text editor containing the content of the slide and contains logic to validate the user's solution.

The primary idea behind exerslide is to move all the behavior in reusable layouts, to keep the actual content creation simple.

Layout inference

Which layout to use for a slide is determined by the following process:

  1. The slide's layout metadata field.
  2. If not present, the layout is inferred from the file extension. This mapping can be configured.
  3. If it can't be inferred, no layout is used.

Layout file resolution

Both, the layout field and the file extension mapping expect the layout name as value. exerslide will look for a file with the same name (ignoring the file extension) in the layouts/ folder of the project itself or any loaded plugin.

Example:

If the slide contains

---
layout: Columns
---

and the exerslide-plugin-columns-layout is loaded (which it is by default), then exerslide will look for

  • ./layouts/Column.js
  • exerslide-plugin-columns-layout/layouts/Columns.js.

Note: exerslide will show an error if there are multiple layouts with the same name. If you want to specify a layout of a specific plugin, you can use the <pluginName>:<layoutName> syntax, e.g.

---
layout: columns-layout:Columns
---

Layout data

Layouts have access to the layout_data metadata option of a slide. This allows a slide to pass arbitrary data to the layout. What data to pass depends on the layout. For example, the Columns layout can be configured to use a different column divider:

---
layout: Column
layout_data:
  divider: '<myDivider>'
---
This is the first column
<myDivider>
This is the second column

Master layout

As layouts define the structure of a slide, the master layout defines the structure of the whole page. The default master layout doesn't do much: It renders a table-of-contents/progress component and the current slide.

The default master layout is copied into the project folder so you can customize it more easily.

Content types

Layouts primarily describe how a slide is structured. While there can be layouts that expect the content in a specific format/language (the JavaScriptExercise layout expects the content to be JavaScript), there are usually parts that allow you to write content in any format you like.

You can decide which markup language to use for such content, e.g. Markdown, HTML or something else. All you need is a function that can convert the content to something renderable in React (string, React component, etc).

exerslide provides support for HTML and Markdown out of the box (via plugins).

Content type inference

Which converter to use is determined by:

  1. The content_type option in the front matter. This allows you to set the type explicitly. The value is usually a media type name. (this is not common)
  2. The file extension. If content_type is not specified, the media type of the file is determined from the file extension. E.g. we get text/x-markdown for an *.md file.

Content type resolution

Like with layouts, exerslide will automatically look for matching content type converters in the project itself and any loaded plugin. It will look for matching files in the contentTypes folder.

E.g. if the slide contains

---
contentType: text/x-markdown
---

and the exerslide-plugin-markdown-converter plugin is loaded, it will look for

  • ./contentTypes/text_x-markdown.js
  • exerslide-plugin-markdown-converter/contentTypes/text_x-markdown.js.

Note: Just like for layouts, you can prefix the type name with a plugin name (<pluginName>:<typeName>) to avoid conflicts.

Styles

CSS dependencies are bundled into two CSS files:

  • One file contains the CSS dependencies of React components (such as for codemirror) and layouts. If a React component has CSS dependencies then usually because it doesn't function properly without it.

  • The other file bundles the CSS listed in exerslide.config.js, stylesheets.

    // exerslide.config.js
    module.exports = {
      stylesheets: [
        'bootstrap/dist/css/bootstrap.css',
        '...',
        './css/style.css'
      ]
    };

    The default setup includes Foundation, font-awesome and a highlight.js theme. You can adjust these entries to your needs.

Configuration

As mentioned earlier, exerslide is configured through exerslide.config.js and webpack.config.js. You can edit any of them to adjust the build process to your liking.

exerslide.config.js

This file contains settings for exerslide itself. It primarily defines paths and settings for the exerslide's slide loader.

  • stylesheets: An array of paths to stylesheets that should be bundled with the presentation.
  • defaultLayouts: An object that maps file extensions to layout names.
  • plugins: A list of module names to load as plugins. exerslide-plugin- can be omitted from the name. Plugins are intended to provide additional layouts, content type converters, or other functionality.

Settings you probably won't have to change:

  • out: Absolute path to the output directory.
  • slidePaths: A list if patterns that will match all slide files. These patterns are used by to watch for new and deleted slides.
  • processSlides: A function to process the list of paths matched by slidePaths. This can be used to filter out or reorder files.

webpack.config.js

Basically a standard webpack configuration file, with sensible default settings. exerslide will augment this configuration with additional options to ensure that the build process runs properly.

The configuration object contains an additional slideLoader option with which custom slide transformers can be specified.

CLI

exerslide init

Initializes a new presentation project.

exerslide build [OUT_DIR]

Builds the presentation. If OUT_DIR is present, it will save the presentation in that folder instead of what is configured in exerslide.config.js.

exerslide watch [OUT_DIR]

Continuously builds the presentation. If OUT_DIR is present, it will save the presentation in that folder instead of what is configured in exerslide.config.js.

exerslide serve

Starts a web server for local development and rebuilds the presentation when any slide or other dependency (JavaScript files, layout, CSS) changed.

Plugins

Plugins are intended to provide additional layouts, content type converters and other functionality. When looking for layouts or content type converters, exerslide will look into each plugin's layouts/ and contentTypes/ folders.

Additionally, if the plugin has a init.js file in its root folder, which exports a function, the function will be called and gets passed the exerslide config and webpack config objects. This allows plugins to add additional slide transforms or update the webpack configuration.

The default layouts and content converters which are coming with exerslide are implemented as plugins. Have a look at the packages folder for examples.

exerslide's People

Contributors

aheci avatar fkling avatar ggamel avatar keizar901 avatar keyz avatar rubengees avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exerslide's Issues

Unknown content type "text_markdown"

For all md files, I received

 Unknown content type "text_markdown"

For each file, I need to add

content_type: text/x-markdown

I think this error came from mime-types library since lookup function only return text/markdown not text/x-markdown.
https://github.com/jshttp/mime-types#mimelookuppath

I could fix this by putting
https://github.com/kwangkim/exerslide/blob/610cdc7f70e5a6d8ed526220c3b2cbe19450fb1b/packages/exerslide/lib/slide_transforms/registerContentType.js#L45

        if (contentType === "text_markdown"){
          contentType = "text\/x-markdown"
        }

P.S.
https://github.com/jshttp/mime-db/blob/66588fec459c0d61cec5c69cfd161b139f75cf7d/db.json#L6561

Another resolution was using 'mkd' instead 'md'.

Implement swipe gestures

I expected two-finger swipes to do previous/next slide (but fallback to browser behavior of back-page when done from the first slide)

template mode

I usually modified configuration to allow latex or other extra things and I duplicated many configuration manually each time.
Is there anyway to use it as a template mode?

Exerslide not working under Windows

I am running into serveral issues trying to run exerslide under Windows. I will try to report the issues I ran into:

  • The use of process_child only works correctly under UNIX systems. An alternative would be to use cross-spawn
  • The various scripts for linting an building the package only work on UNIX systems. This prevents me from contributing fixes as I am not able to run the tests or the linter properly

exerslide build fails in new projects

KDE Neon (based on Kubuntu 16,04)

sudo npm i -g exerslide-cli
$ exerslide init presentation
module.js:328
    throw err;
    ^

Error: Cannot find module '/home/lex93ushakov/lab/exerslide/example/cli'
    at Function.Module._resolveFilename (module.js:326:15)
    at Function.Module._load (module.js:277:25)
    at Module.require (module.js:354:17)
    at require (internal/module.js:12:17)
    at loadLocalCommands (/usr/local/lib/node_modules/exerslide-cli/bin/exerslide.js:80:20)
    at Liftoff.<anonymous> (/usr/local/lib/node_modules/exerslide-cli/bin/exerslide.js:53:5)
    at Liftoff.<anonymous> (/usr/local/lib/node_modules/exerslide-cli/node_modules/liftoff/index.js:202:12)
    at Liftoff.<anonymous> (/usr/local/lib/node_modules/exerslide-cli/node_modules/liftoff/index.js:169:7)
    at nextTickCallbackWith0Args (node.js:419:9)
    at process._tickCallback (node.js:348:13)

Path must be a string

Since the update, I can't get any presentation to build or serve.

I used the following commands:

npm i exerslide-cli -g
mkdir test3
cd test3
exerslide init test3
exerslide build

On node 6 and 5.
I also installed it on another computer and I still get the same error:

Generating presentation in "out" ...
Presentation generation failed!
List of errors:
path.js:8
    throw new TypeError('Path must be a string. Received ' +
    ^

TypeError: Path must be a string. Received undefined
    at assertPath (path.js:8:11)
    at Object.posix.relative (path.js:496:3)
    at formatWebpackError (/Users/[...]/tutorial/test3/node_modules/exerslide/lib/builder.js:369:29)
    at /Users/[...]/tutorial/test3/node_modules/exerslide/lib/builder.js:343:33
    at Array.forEach (native)
    at logWebpackErrors (/Users/[...]/tutorial/test3/node_modules/exerslide/lib/builder.js:342:30)
    at /Users/[...]/tutorial/test3/node_modules/exerslide/lib/builder.js:194:9
    at Compiler.<anonymous> (/Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:194:14)
    at Compiler.emitRecords (/Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:282:37)
    at Compiler.<anonymous> (/Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:187:11)
    at /Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:275:11
    at Compiler.applyPluginsAsync (/Users/[...]/tutorial/test3/node_modules/tapable/lib/Tapable.js:60:69)
    at Compiler.afterEmit (/Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:272:8)
    at Compiler.<anonymous> (/Users/[...]/tutorial/test3/node_modules/webpack/lib/Compiler.js:267:14)
    at /Users/[...]/tutorial/test3/node_modules/async/lib/async.js:52:16
    at done (/Users/[...]/tutorial/test3/node_modules/async/lib/async.js:246:17)

Any idea where that could come from?
Until then, I'll revert to the previous version.

Thanks

video(mp4) support.

I would like to add a video using

<video src="./images/video.mp4" controls="controls">
</video>

with a modified webpack.config.js

/*
 * This hash helps exerslide to determine whether the file needs to be updated
 * or not. Please don't remove it.
 * @exerslide-file-hash deleted
 */

'use strict';

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const exerslide = require('exerslide');
const exerslideConfig = require('./exerslide.config');
const path = require('path');
const webpack = require('webpack');

const PROD = process.env.NODE_ENV === 'production';

const plugins = [
  new webpack.DefinePlugin({
    '__DEV__': !PROD,
    'process.env.NODE_ENV':
    JSON.stringify(process.env.NODE_ENV || 'development'),
  }),
  new ExtractTextPlugin('[name].css'),
  new HTMLWebpackPlugin({
    hash: true,
    template: './index.html',
    chunksSortMode: function(a,b) {
      // styles.css should always come last so that it can overwrite app specfic
      // rules
      if (a.names[0] === 'styles') {
        return 1;
      }
      if (b.names[0] === 'styles') {
        return -1;
      }
      return a.names[0].localeCompare(b.names[0]);
    },
  }),
];

if (PROD) {
  plugins.push(new webpack.optimize.UglifyJsPlugin({
    sourceMap: false,
    compress: {
      warnings: false,
    },
  }));
}

module.exports = {
  entry: {
    app: './js/presentation.js',
    style: exerslideConfig.stylesheets.map(function(sheetPath) {
      return sheetPath[0] === '.' ? path.join(__dirname, sheetPath) : sheetPath;
    }),
  },
  output: {
    path: exerslideConfig.out,
    filename: '[name].js',
    pathinfo: !PROD,
  },
  debug: !PROD,
  resolveLoader: {
    root: path.join(__dirname, 'node_modules'),
  },
  module: {
    loaders: [
      // By default this loader is only applied to the index.html file, not to
      // HTML slides
      {
        test: /index\.html$/,
        loader: 'html',
        exclude: /slides\//,
      },
      {
        test: /\.jsx?$/,
        loader: 'babel',
        exclude: /node_modules\/(?!exerslide\b)/,
        query: {
          presets: [
            require.resolve('babel-preset-es2015'),
            require.resolve('babel-preset-react'),
            require.resolve('babel-preset-stage-0'),
          ],
          plugins: [
            require.resolve('babel-plugin-transform-runtime'),
          ],
        },
      },
      {
        test: /\.json$/,
        loader: 'json-loader',
      },
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract(
          'style',
          'css?importLoaders=1!autoprefixer?{"browsers": "> 1%"}'
        ),
      },
      {
        test: /\.(png|jpe?g|gif)$/,
        loader: 'file-loader?name=[name]-[sha512:hash:base64:7].[ext]',
      },
      // This is required for font-awesome's font files
      {
        test: /\.(svg|ttf|woff2?|eot)(\?.*)?$/,
        loader: 'file-loader?name=[name]-[sha512:hash:base64:7].[ext]',
      },
      // For video
      {
        test: /\.(webm|mp4)$/,
        loader: 'file-loader?name=[name]-[sha512:hash:base64:7].[ext]&mimetype=video/mp4',
      },
    ],
  },
  plugins: plugins,
  htmlLoader: {
    attrs: ['video:src','source:src','img:src', 'link:href', 'script:src'],// For video
  },
  slideLoader: {
    transforms: [
      /**
       * This finds paths to assets in slides so that they can be processed by
       * webpack. This allows you to reference assets in slides (e.g. displaying
       * an image) without having to explicitly copy them.
       *
       * You might want to adjust this depending on the content of your
       * slides. If you don't want to auto-copy them at all, remove this
       * transform.
       */
      exerslide.transforms.requireAssets({
        // pattern: /(?:\.{1,2}\/)+[-_\/a-z\d.]+\.(?:png|jpe?g|gif|svg)\b/ig,
      }),
    ],
  },
};

Unfortunately, it does not work well.
Could you add a video support?

Handle project names with spaces

If exerslide init is run as

exerslide init "My Project Name"

it will fail silently. It looks like everything will have installed fine but that's not the case. That's because the project name is used in the package.json's name field which doesn't allow spaces, so npm refuses to install any dependencies.

The name is also used in other places where spaces (and other characters) are not valid.

Proposed solution

Use the passed name only as the <title> in the index.html in the HTML template. For all other cases, use the current directory name.

exerslide serve error

I followed the installation instructions and successfully ran exerslide init, but when I try to run exerslide serve I get this error seemingly related to webpack:

test ➜ exerslide serve
module.js:491
    throw err;
    ^

Error: Cannot find module 'webpack'
    at Function.Module._resolveFilename (module.js:489:15)
    at Function.Module._load (module.js:439:25)
    at Module.require (module.js:517:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/todrobbins/dev/test/node_modules/exerslide/lib/builder.js:24:17)
    at Module._compile (module.js:573:30)
    at Object.Module._extensions..js (module.js:584:10)
    at Module.load (module.js:507:32)
    at tryModuleLoad (module.js:470:12)
    at Function.Module._load (module.js:462:3)

Just for context, I have version 3.5.6 of webpack installed.

Problems with Windows paths

Node version: 4.6.0
Exerslide version: 1.1.2

We recently started a new presentation with exerslide and stumbled upon this exception when running
exerslide serve on windows:

Change detected, updating presentation...
Presentation generation failed!
List of errors:

In file "11699-6744-1vf30ii\slides.js":
  SyntaxError: C:/Users/geesr/fh/Exerslide/presentation/11699-6744-1vf30ii/slides.js: Octal literal in strict mode (3:99)

    1 | module.exports = [
    2 | require("C:\Users\geesr\fh\Exerslide\presentation\node_modules\exerslide\lib\slide-loader!..\slides\00-Electron\00-Übersicht.md"),
  > 3 | require("C:\Users\geesr\fh\Exerslide\presentation\node_modules\exerslide\lib\slide-loader!..\slides\01-AngularJS\00-Übersicht.md"),
      |                                                                                                    ^
    4 | require("C:\Users\geesr\fh\Exerslide\presentation\node_modules\exerslide\lib\slide-loader!..\slides\02-Elm\00-Übersicht.md"),
    5 | require("C:\Users\geesr\fh\Exerslide\presentation\node_modules\exerslide\lib\slide-loader!..\slides\03-GraphQL\00-Übersicht.md"),
    6 | require("C:\Users\geesr\fh\Exerslide\presentation\node_modules\exerslide\lib\slide-loader!..\slides\04-Polymer\00-Übersicht.md"),

As you can see, this error appears in the generated slides.js file, due to parts of the paths getting interpreted as an octal literal. We followed your recommendation in the README to name the folders like this, so this should be supported...

If you want to see yourself, our project is located here.

This works fine on Linux and MacOS as no \ is used there.
Please have a look. Thanks in advance!

Can't make it working

Os: Windows 10 x64 Pro
Node: v6.3.1

I created a new Github project, created a folder myPresentation then run the code:

image

Ability to write assertions in separate JS file

First off: this is a really interesting project. Great work!

Re: the JavaScriptExercise layout

I'm wondering if it would be possible for the slide to include an external JS file instead of requiring that assertions be written in the front matter. Maybe a specific path/naming convention would make this easier to achieve?

The use-case is that we'd like to be able to write the assertions and also optionally fire off analytics events on success/fail. That way we can track the success of any given lesson.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.