CpReveal

Description

Use reveal.js with Pharo Smalltalk and CodeParadise

Details

Source
GitHub
Dialect
pharo (25% confidence)
Created
June 29, 2026
Updated
June 29, 2026

README excerpt

# CpReveal

**Beautiful [reveal.js](https://revealjs.com) presentations, written entirely in Pharo Smalltalk.**

CpReveal is an extension for [CodeParadise](https://github.com/ErikOnBike/CodeParadise) that lets
you describe slide decks with a small, elegant Smalltalk DSL and render them live in the browser —
no JavaScript, no build step. It adds a few things plain reveal.js doesn't give you out of the box:
**Mermaid diagrams built with a Smalltalk DSL** and a **live code component** that runs Smalltalk on
the server and shows the result on the slide.

---

## Features

- Clean slide-building DSL — a bullet list is just an array of strings
- All the reveal.js essentials: themes, transitions, keyboard/touch navigation, slide overview
- **Fragments** (step-by-step reveals) and **vertical slides** (2D navigation)
- **Speaker notes** view (press `S`)
- **Code** with syntax highlighting (highlight.js) and **math** with KaTeX
- **Mermaid diagrams** from a Smalltalk builder: flowchart, sequence, class diagram, gantt
- **Live `doIt`**: evaluate Smalltalk on the server and display the result on the slide
- **Images** by URL or embedded from a local file
- Edit live in the browser with CodeParadise's view inspector; presentations are stored in the image
- Full escape hatch: raw HTML and custom web components anywhere

---

## Requirements

- Pharo 10–13 (developed on Pharo 13)
- [CodeParadise](https://github.com/ErikOnBike/CodeParadise)

---

## Installation

Load CodeParadise first:

```smalltalk
Metacello new
    repository: 'github://ErikOnBike/CodeParadise';
    baseline: 'CodeParadise';
    load.
```

Then load this package (adjust the repository to your fork):

```smalltalk
Metacello new
    repository: 'github://StefanKrecher/CpReveal';
    baseline: 'CpReveal';
    load.
```

Start the CodeParadise development server:

```smalltalk
CpDevTools start.
```

---

## Quickstart

Subclass `CpRevealWebApplication` and implement `#createPresentationModel`:

```smalltalk
CpRevealWebApplication subclass: #MyTalkWebApplication
    instanceVariableNames: ''
    classVariableNames: ''
    package: 'MyTalk'.

MyTalkWebApplication >> createPresentationModel
    ^ (CpRevealPresentation titled: 'My First Talk')
        theme: #black;
        transition: #slide;
        title: 'My First Talk' subtitle: 'Made with CpReveal';
        slide: 'Why Smalltalk?' bullets: #('Live' 'Simple' 'Powerful');
        slide: 'Run it live' doIt: '42 factorial';
        yourself
```

Register and open it:

```smalltalk
MyTalkWebApplication register; openInBrowser.
```

The URL is derived automatically from the class name
(`MyTalkWebApplication` → `http://localhost:8080/static/app.html?my-talk`).

---

## The DSL

A presentation is an ordered list of slides. Every `slide:…:` message appends one slide; the
messages all return the presentation, so you build it with a cascade ending in `yourself`.

```smalltalk
(CpRevealPresentation titled: 'Demo')
    theme: #black;                  "black white league beige sky night serif simple solarized moon dracula"
    transition: #slide;             "none fade slide convex concave zoom"

    "Title slide (centered h1 + optional subtitle)"
    title: 'Main Title' subtitle: 'Subtitle';

    "Bullets: just an array of strings"
    slide: 'Heading' bullets: #('one' 'two' 'three');

    "Bullets with fragments: text -> appearsAsFragment"
    slide: 'Step by step' bullets: { 'shown first' -> false. 'as fragment' -> true };

    "Code with highlighting"
    slide: 'Code' code: 'foo ^ 42' language: #smalltalk;

    "Math (KaTeX)"
    slide: 'Math' math: 'E = mc^2';

    "Image by URL, or from a local file"
    slide: 'Photo' image: 'https://example.org/pic.png' alt: 'A picture';
    slide: 'Logo'  imageFile: '/path/to/logo.png';

    "Live Smalltalk execution"
    slide: 'Live' doIt: '(1 to: 10) inject: 0 into: [ :a :b | a + b ]';

    "Vertical 2D stack"
    verticals: [ :v |
        v slide: 'Top' text: 'press down for more'.
        v slide: 'Below' bullets: #('a' 'b') ];

    "Multi-content slide — compose freely"
    slide: 'Mixed' with: [ :s |
        s title: 'Header' subtitle: 'sub'.
        s bullets: #('point').
        s code: 'self halt' language: #smalltalk.
        s html: '<b>raw HTML too</b>' ];

    "Speaker notes for the slide just added (press S in the browser)"
    notesForLastSlide: 'Remember to smile';
    yourself
```

### Content types

| Message (on a slide builder) | Content |
|---|---|
| `title:subtitle:` | Big title + subtitle |
| `text:` | A paragraph |
| `bullets:` | Unordered list (strings, or `text -> isFragment` associations) |
| `code:language:` | Highlighted code block |
| `math:` | KaTeX formula |
| `image:alt:` / `imageFile:` | Image by URL / embedded local file |
| `mermaid:` | Mermaid diagram (builder or raw string) |
| `doIt:` | Live server-side Smalltalk evaluation |
| `html:` / `markdown:` | Raw HTML markup |
| `custom:model:` | Your own CodeParadise view |

`slide:KIND:` creates a one-block slide; `slide:with:` and `verticals:` take a block for richer
composition. Send `beFragment` to any content to reveal the whole block as a fragment.

---

## Mermaid diagrams

Build diagrams with a Smalltalk DSL (or pass a raw mermaid `String`):

```smalltalk
"Flowchart"
CpMermaidFlowchart new
    direction: #LR;                                "#TD #TB #BT #LR #RL"
    node: #a label: 'Start';
    node: #b label: 'Decision' shape: #diamond;    "#box #round #diamond #stadium"
    edge: #a to: #b;
    edge: #b to: #c label: 'yes';
    yourself.

"Sequence"
CpMermaidSequence new
    participant: #u as: 'User';
    participant: #s as: 'Server';
    message: #u to: #s text: 'request';
    reply: #s to: #u text: 'response';
    yourself.

"Class diagram"
CpMermaidClass new
    class: #Animal attribute: 'String name';
    class: #Animal method: 'makeSound';
    relation: #Dog inherits: #Animal;
    yourself.

"Gantt"
CpMermaidGantt new
    title: 'Roadmap';
    section: 'Phase 1';
    task: 'Design' id: #d1 start: '2024-01-01' duration: '7d';
    task: 'Build' after: #d1 duration: '14d';
    yourself.
```

Pass any of these to a slide: `slide: 'Architecture' mermaid: aDiagram`.

---

## Live code execution (`doIt`)

`slide: 'Live' doIt: '42 factorial'` renders an editable code area, a **Run** button and a result
area. Clicking **Run** sends the (possibly edited) code to the Pharo server, evaluates it and shows
the result — or a red error message.

> ⚠️ `doIt` evaluates arbitrary Smalltalk on the server. Use it as a local presentation/authoring
> tool only; do not expose it to untrusted users.

---

## Authoring in the browser

**Edit the deck without leaving the browser.** Click the **Edit deck** button (bottom-right) to open
an editor showing the Smalltalk source of your `#createPresentationModel`. Edit the DSL, hit
**Apply & reload**, and the method is recompiled on the server and the deck re-renders live. Compile
errors are shown inline.

For the web components themselves (templates / CSS), CodeParadise's view inspector is also enabled:

- `Ctrl/Cmd+I` — open the view inspector
- `Ctrl/Cmd+B` — pick a web component
- `Ctrl/Cmd+S` — apply changes in the browser
- `Shift+Ctrl/Cmd+S` — save changes back into the Pharo image

Presentation content lives in your `#createPresentationModel` method and is stored with the image
(`Smalltalk snapshot: true andQuit: false`).

---

## Example

`CpRevealExampleWebApplication` is a complete demo of every feature. Open it at:

```
http://localhost:8080/static/app.html?reveal-example
```

Read its `#createPresentationModel` to learn the DSL by example.

---

## How it works

CpReveal reuses CodeParadise's MVP model. The presentation model
(`CpRevealPresentation` → `CpRevealSection` → `CpRevealContent`) is rendered to the reveal.js
`.reveal > .slides` HTML by `CpRevealHtmlRenderer` on the server and handed to a single client view
(`CpRevealPresentationView`), which loads reveal.js + plugins + Mermaid from a CDN and manages the
deck lifecycle. reveal.js runs in the light DOM so its stylesheet applies. Interactive widgets
(`doIt`) talk back to the server via CodeParadise announcements.

---

## Limitations (v1)

- `custom:model:` (live child views inside a slide) currently renders a placeholder
- `markdown:` is treated as raw HTML (the reveal.js markdown plugin is not wired up)
- External assets (reveal.js, plugins, Mermaid) are loaded from a CDN, so an internet connection is
  needed unless you mirror them locally

---

## License

MIT (or your preference).
← Back to results