Hyperbook Documentation - Version 0.102.0 # Getting Started # Hyperbook Hyperbook is a quick and easy way to build interactive workbooks, that support modern standards and run superfast. It works by compiling markdown to static pages. Hyperbook makes writing interactive workbooks super simple while providing a good feature set. You do not need any coding experience. You only need to create and edit Markdown files. ## Getting Started You can start working on a Hyperbook in three different ways. Choose the one which suits your style best. :::::tabs{id="options"} :::tab{title="VS Code" id="vscode"} 1. Install [VS Code](https://code.visualstudio.com/) or [VS Codium](https://vscodium.com/) 1. Install the Hyperbook Extension for [VS Code](https://marketplace.visualstudio.com/items?itemName=openpatch.hyperbook-studio) or [VS Codium](https://open-vsx.org/extension/openpatch/hyperbook-studio) 1. Download the [hyperbook-anywhere](https://github.com/openpatch/hyperbook-anywhere/archive/refs/heads/main.zip) starter 1. Unzip the file 1. Open the folder in VS Code 1. Run the `Hyperbook: Show side preview` command ::: :::tab{title="CLI" id="cli"} You need to have [Node](https://nodejs.org/) version 16 or higher installed on your system. 1. Run `npm create hyperbook` and follow the wizard to create your first Hyperbook. 1. Run `npx hyperbook dev` to start the development server. 1. Visit https://localhost:8080 with your favorite web browser. 1. Modify or add new pages to your Hyperbook by using your favorite editor. ::: ::::tab{title="Web IDE" id="web-ide"} Most platforms for collaborative version control have an integrated Web IDE like [GitLab](https://docs.gitlab.com/ee/user/project/web_ide/) or [GitHub](https://docs.github.com/en/codespaces/the-githubdev-web-based-editor). So you can just fork one of our starters and get going: - GitHub: https://github.com/mikebarkmin/hyperbook-github-pages/fork - GitLab: https://gitlab.com/mikebarkmin/hyperbook-gitlab-pages/-/forks/new - EduGit: https://edugit.org/mikebarkmin/hyperbook-edugit-pages/-/forks/new :::alert{warn} If you use this approach you will not be able to see a representative preview. In most cases only a normal Markdown preview, which lacks some features of Hyperbook. ::: :::: ::::: ## Deploy The main goal of writing a Hyperbook is to have an interactive workbook. For this you have to deploy the exported Hyperbook to a host. Luckily GitHub Pages, GitLab Pages and Vercel are free to use options, which are already setup for you, if you used one of our starters, like [hyperbook-anywhere](https://github.com/openpatch/hyperbook-anywhere). You just have to push your files to [GitHub](https://github.com), [GitLab](https://gitlab.com) or [EduGit](edugit.org/). If you use the CLI version you can also export your Hyperbook to static HTML files, which can be uploaded anywhere. For this you need to run: ``` npx hyperbook build ``` Then you need to copy the files from `.hyperbook/out` to your desired location. :::alert{warn} Do not forget to set a `basePath` in your hyperbook.json. ::: ## Update ::::tabs{id="options"} :::tab{title="VS Code" id="vscode"} Updates should happen automatically. ::: :::tab{title="CLI" id="cli"} Update to the latest release of Hyperbook CLI using the following command. ```bash npm update hyperbook --global ``` ::: :::tab{title="Web IDE" id="web-ide"} No Integration. No updates. ::: :::: ## Support We are [happy to hear from you](mailto:contact@openpatch.org), if you need custom support or features for your application. You can also join our [Matrix Channel](https://matrix.to/#/#hyperbook:matrix.org) or connect with us on [Twitter](https://twitter.com/openpatchorg). --- Hyperbook is maintained by [OpenPatch](https://openpatch.org), an organization for educational assessments and training. If you need help or create a Hyperbook [get in touch](mailto:contact@openpatch.org). # Changelog # Changelog The entire release archive of hyperbook is available on [NPM](https://www.npmjs.com/package/hyperbook) :::alert{info} If you need a new feature, open an [issue](https://github.com/openpatch/hyperbook/issues) and let's discuss. ::: ## v0.102.0 ::::tabs :::tab{title="New :rocket:" id="new"} **navigation**: A section can sit between two pages, and a page can sit after a section. This works at every level, so a subsection can sit between two pages of its section. The `index` of a page and the `index` of a section are one order now, instead of pages always coming first. Without an `index` nothing moves: pages still come before sections. ::: :::tab{title="Improved :+1:" id="improved"} **navigation**: The pages of a section are indented, so they read as belonging to it when a page of the level above follows them. ::: :::tab{title="Fixed :bug:" id="fixed"} **shell**: The search and table of contents drawers no longer flash over the page for a moment while it loads. **shell**: Pages paint sooner. The script that loads the light and dark stylesheets is part of the page now, instead of a file that had to be fetched before anything could be drawn. ::: :::: ## v0.101.0 ::::tabs :::tab{title="New :rocket:" id="new"} **emoji**: Emojis can be rendered as images, so they look the same on every platform instead of being drawn with the emoji font of the reader's operating system. Set `elements.emoji.style` to `twemoji` in your `hyperbook.json`. Only the emojis your book uses end up in your build. ::: :::tab{title="Improved :+1:" id="improved"} **bookmarks**: The bookmark icon is drawn by the stylesheet instead of being an emoji. It looks the same everywhere, takes the color of its heading, and a saved bookmark shows a filled icon. **bookmarks**: An entry in the bookmark list shows the same emojis as the heading it points at. If you call `hyperbook.ui.toggleBookmark` yourself, it no longer takes a label. ::: :::tab{title="Fixed :bug:" id="fixed"} **markdown**: `mailto:` and `tel:` links in your content kept the base path of the book in front of them, so `[Write us](mailto:hi@example.org)` became `/mailto:hi@example.org`. **bookmarks**: A heading that contains a quote or a backslash can be bookmarked again. ::: :::: ## v0.100.5 ::::tabs :::tab{title="Improved :+1:" id="improved"} - onlineide: Update to support java 25 syntax. ::: :::: ## v0.100.4 ::::tabs :::tab{title="Improved :+1:" id="improved"} **cloud**: The sync indicator tells its states apart by shape, not only by color. Every state previously drew the same person icon in a different shade, which made "unsynced" and "synced" indistinguishable for red-green color blind readers. The toolbar button now also carries the current state as its accessible name, and the status line is announced when it changes. **cloud**: States you can act on are surfaced outside the user drawer. A failed save, being offline, or a merge with another session now appear in a notice at the bottom of the page — with a retry button for a failed save, and a count of how much is still waiting while offline. Successful saves stay silent. **cloud**: A sync conflict explains itself before reloading. The page still has to reload so interactive elements pick up the merged state, but it is announced first, with a **Reload now** button, instead of happening without warning. **cloud**: The status line reports how long ago the last save landed. ::: :::tab{title="Fixed :bug:" id="fixed"} **cloud**: Loading from the cloud failed whenever the server held events but no snapshot yet. The failure was silent, so work synced up but never came back down — on a second device, after clearing browser data, or during a conflict merge — until something happened to upload a full snapshot. **cloud**: A sync conflict no longer discards local work. The client fetches the server state, replays its pending changes on top of it both locally and on the server, and only then reloads. **cloud**: Changes made offline are no longer lost on reconnect. Every queued batch after the first carried an event ID recorded before the flush, so the server rejected it and the whole queue was discarded. **cloud**: Closing the tab no longer loses changes made in the last few seconds. Saves are debounced by up to two seconds, and nothing was sent when the page went away — it only warned. Pending changes are now flushed with a request that outlives the page. **cloud**: Two hyperbooks served from the same domain no longer share one sync counter, which put both into a permanent conflict loop. **cloud**: The server keys rows by each table's own primary key. Tables not keyed by `id` — bookmarks, online IDE scripts, SQL IDE databases — were rewritten with a bogus `id` field, and updates and deletes against them silently missed. **cloud**: Replaying the same events twice no longer duplicates rows, and updates to nested fields (such as a saved zoom level) are no longer dropped. **cloud**: Cursor position, scroll offset and window size are no longer uploaded, so one device no longer pulls another's scroll position. **cloud**: A change the server rejects is dropped instead of being retried forever, which used to block every later change behind it. ::: :::: ## v0.100.2 ::::tabs :::tab{title="Improved :+1:" id="improved"} - onlineide: Update to the latest version. It fixes some bugs in the scratch implementation. ::: :::: ## v0.100.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - onlineide: Update to the latest version. It now supports scratch-for-java programs. ::: :::: ## v0.100.0 ::::tabs :::tab{title="New :rocket:" id="new"} **pyide**: The editor now autocompletes the `turtle` module. Every function it provides is offered with its signature and a short description, both for `from turtle import *` and for member access on `turtle`, a `Turtle()` or a `Screen()`. Suggestions only appear once a script imports turtle, and never inside comments or strings. **turtle**: The module gained the screen and turtle functions it was missing: - Program flow: `done`, `mainloop`, `exitonclick`, `bye`, `Screen`, `getscreen` - Drawing control: `tracer`, `update`, `delay`, `undo`, `setundobuffer`, `undobufferentries` - Stamps: `stamp`, `clearstamp`, `clearstamps` - Geometry and angles: `distance`, `mode` (standard and logo), `degrees`, `radians`, `filling` - Cursor: `shapesize`/`turtlesize`, `tilt`, `tiltangle`, `settiltangle`, `register_shape`/`addshape` - Screen: `setup`, `title`, `clearscreen`, `resetscreen`, `window_width`, `window_height` - Interaction: `numinput`, `textinput`, `listen`, `onkey`, `onkeypress`, `onkeyrelease`, `onclick`, `onscreenclick`, `ontimer` `tracer(0)` also skips the animation queue, so the usual "draw everything, then `update()`" pattern is fast instead of appearing to hang. ::: :::tab{title="Improved :+1:" id="improved"} **pyide**: `input()` no longer freezes the page. Python runs on the browser's main thread, so the old `window.prompt()` blocked it — everything a script had drawn stayed invisible until the program finished. Input is now read from a field in the output panel while Python is suspended, so the canvas keeps painting and queued turtle animation keeps draining while a script waits. This needs WebAssembly JSPI (Chrome and Edge 137+); other browsers keep the previous dialog. ```python from turtle import * # The board is now visible while the program asks. for i in range(12): dot(40) forward(50) karte = int(input("Which card? ")) ``` The output panel reads like a terminal too: the prompt, the answer that was typed, then a newline. **turtle**: `turtle.numinput()` and `turtle.textinput()` use the same field, and follow CPython in asking again when the answer is not a number or falls outside `minval`/`maxval`. **navigation**: A section that links to its own page is now easy to tell apart from one that only expands. Sections with an empty `index.md` are set in italics, and the title of a section that has its own page underlines on hover to show that it is a link. Subsections are judged on their own content, not on their parent's. The highlight for the section you are currently on works again — the stylesheet was still looking for `active` on the `` element, which moved to the surrounding `
` when sections became collapsible. **dev**: `hyperbook dev` now knows which files each page pulls in, so saving a file rebuilds exactly the pages that used it. Editing a script referenced by a directive's `src=` attribute used to reload the browser without rebuilding the page, which brought back the old content; a file under `book/` did nothing at all. Snippets and templates now rebuild only the pages that include them rather than the whole book. Renaming a page also refreshes the navigation everywhere instead of only on the page you edited, and the search index no longer goes stale between full builds. **pyide**, **p5**, **openscad**, **webide**, **typst**, **abc-music**: The reset, copy, download and fullscreen buttons in the editor toolbar are now icons instead of written labels, which wrapped onto several lines in languages with long words. The wording becomes the tooltip, and is still announced by screen readers. Directive icons are now drawn as SVG throughout — the download icons in **download** and **archive**, the lock in **protect**, typst's add-file button and the expand arrows on the binary-file sections. Typst's two download buttons are told apart by their icons: a box for the whole project, a document for the PDF. They follow the light and dark themes, and the fullscreen button no longer renders as an empty box on systems whose fonts lack `⛶`. ::: :::tab{title="Fixed :bug:" id="fixed"} - **turtle**: `begin_fill()` and `end_fill()` drew nothing. Every vertex of the fill collapsed onto the turtle's final position, and fills painted over the lines drawn during them instead of underneath. - **turtle**: Reading a pen setting destroyed it — `pensize()`, `pencolor()`, `fillcolor()`, `color()` and `speed()` without arguments reset the pen instead of returning the current value. - **turtle**: `clear()` called during a fill left the turtle unable to fill for the rest of the program, and also reset the pen width. `speed()` carried over into the next run of a program. - **turtle**: `circle()` ignored its `extent` argument, so arcs were impossible. The signature is now `circle(radius, extent=None, steps=None)`, and a negative radius curves to the right. - **turtle**: `pencolor(r, g, b)` and `color(r, g, b)` produced black, and `speed("slowest")` and the other named speeds selected the fastest setting. - **turtle**: The turtle cursor ignored `fillcolor`, `towards()` rejected a coordinate pair, and `shape()` silently ignored an unknown shape name instead of raising. ::: :::: ## v0.99.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - **openscad**: Fix 3MF export to split geometry into separate objects by color, enabling proper multicolor prints in slicers like BambuStudio and PrusaSlicer. ::: :::: ## v0.99.0 ::::tabs :::tab{title="New :rocket:" id="new"} **links*: Links now support extensions: • `[Hallo](./hallo.md)` → resolves to /hallo • `[Hallo](./hallo)` → resolves to /hallo • `[Template](./template.md.hbs)` → resolves to /template • `[Data](./data.md.json)` → resolves to /data • `[Config](./config.md.yml)` → resolves to /config **Kiri:Moto**: Embed the browser-based 3D slicer [Kiri:Moto](https://grid.space/kiri) in any hyperbook page using the new `::kirimoto` directive. ```md ::kirimoto{mode="FDM" settings="1qzciqo/3"} ``` Supported attributes: `height`, `mode`, `model`, `workspace`, `settings`. Global defaults for `height` and `settings` can be configured in `hyperbook.json`: ```json { "elements": { "kirimoto": { "height": "700px", "settings": "13b1vam/1" } } } ``` ::: :::: ## v0.98.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - **openscad**: Fix unclear error messages — empty files no longer show `[object Object]`, and line numbers in parser errors are now correctly adjusted to match the user's code. ::: :::: ## v0.98.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - **openscad**: Fix axes in openscad to match the standard OpenSCAD orientation (X right, Y forward, Z up) instead of the previous incorrect orientation. ::: :::: ## v0.98.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - **openscad**: Add gizmo and grid for better navigation in 3d space. ::: :::: ## v0.97.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - **openscad**: Add binary file support. ::: :::: ## v0.96.3 ::::tabs :::tab{title="Improved :+1:" id="improved"} - **openscad**: Update to newest version 2026-06-08. ::: :::: ## v0.96.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - **pyide**: `show_animation()` now correctly displays animated GIFs — previously, rendering pytamaro output to a canvas element would only show the first frame. Pytamaro output (including `show_graphic()` and `show_animation()`) always renders to the output panel as an image. The `canvas` attribute is no longer needed for pytamaro. ::: :::: ## v0.96.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - **pyide**: Fix MutationObserver check (`node.type` → `node.nodeType`) so dynamically added pyide elements are correctly initialized - **pyide**: Fix error traceback trimming — when `` is absent, the full traceback is now preserved instead of showing only the last line - **pyide**: Remove duplicate `id="canvas"` HTML attribute on canvas elements (invalid when multiple canvas pyides exist on one page) - **pyide**: Friendly Python error messages now use the page language instead of always loading English - **pyide**: Pygame auto-wrap no longer injects redundant `import asyncio` / `import pygame` when already present in user code ::: :::: ## v0.96.0 ::::tabs :::tab{title="New :rocket:" id="new"} - **pyide**: Add `Turtle()` constructor support for creating multiple simultaneous turtles - **pyide**: Add all standard turtle shapes: `arrow`, `turtle`, `classic`, `triangle`, `square`, `circle` (default: `classic`) - **pyide**: Integrate friendly Python error messages via `@raspberrypifoundation/python-friendly-error-messages` ::: :::tab{title="Improved :+1:" id="improved"} - **pyide**: Default screen size is now 640×480 - **pyide**: Canvas wrapper now shows a checkered transparency pattern to distinguish canvas from page background - **pyide**: Fix `js_svg_graphic` FFI for pytamaro 2.0.1 compatibility (`show_graphic`, `save_graphic_svg`) ::: :::tab{title="Fixed :bug:" id="fixed"} - **pyide**: Fix `write()` alignment — text no longer influences turtle position ::: :::: ## v0.95.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Show the Python `input()` prompt message in the browser dialog. When a script calls e.g. `input("Enter number: ")`, the prompt text is now displayed in the dialog instead of the generic "Input required:" message. ::: :::: ## v0.95.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Migrate all code editors (`webide`, `pyide`, `p5`, `typst`, `abc-music`) from Prism.js to **CodeMirror 6**, with syntax highlighting, GitHub Light/Dark themes, and proper bracket/indent handling. ::: :::tab{title="Improved :+1:" id="improved"} - Always show the OpenSCAD parameters panel. - Support `/* [Tab Name] */` comment syntax to group OpenSCAD parameters into collapsible accordions. Parameters in `/* [Global] */` are shown outside any accordion, and accordion state is preserved across rebuilds. - Parameter changes now auto-trigger a re-render of the OpenSCAD preview. - Parameter changes are written back into the editor source code so code and form stay in sync. - Editing OpenSCAD source code directly also triggers a re-render. ::: :::: ## v0.94.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Improve OpenSCAD rendering by running compile/render and parameter extraction inside a Web Worker so the page stays responsive. - Improve OpenSCAD preview color handling using OFF-based parsing and grouped materials for face colors. - Add STL/3MF download format selection for OpenSCAD. - Add automatic 3MF export generation from indexed polyhedron data, including material and paint-color mapping. ::: :::: ## v0.93.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix text rendering in turtle library. ::: :::: ## v0.93.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Add turtle library to pyide, which mimicks the turtle library in python. - Move pytamaro output to the canvas, which allows for better performance and more features like saving the output as an image. ::: :::: ## v0.92.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Add OpenSCAD element for rendering OpenSCAD code with interactive 3D viewer. [Learn more](/elements/openscad) ::: :::: ## v0.91.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix pyide editors not restoring saved code from the database when `code-input_load` fires before the restore handler is attached. ::: :::: ## v0.91.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - improve fullscreen mode for pyide, web ide, typst and p5 - add canvas rendering to pyide - add graphical output to pyide ::: :::: ## v0.90.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Permaid files now use a lightweight HTML redirect instead of duplicating the full page content. This reduces build output size and build times while providing proper SEO via ``. ::: :::: ## v0.89.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix protect directive not revealing content when wrapping tabs or other directives containing input elements. ::: :::: ## v0.89.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix issue where the base URL was not included in the SQLIDE database URL. ::: :::: ## v0.89.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Show version of the hyperbook in the console. This can be configured to "text" showing it under the Powered by Hyperbook text or "tooltip" showing it as a tooltip when hovering the Powered by Hyperbook text. ::: :::tab{title="Fixed :bug:" id="fixed"} - Show reuse button in h5p elements. ::: :::: ## v0.88.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix cloud integration not working correctly. ::: :::: ## v0.88.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update to blockflows new schema ::: :::: ## v0.88.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Incremental dev rebuilds: content changes now only rebuild the affected page instead of the entire site, resulting in much faster browser updates during development. - Force full rebuild button: a circular button in the bottom-right corner of the page triggers a full rebuild. It spins while the server is rebuilding. - Enhanced WebSocket protocol for targeted reloads — the browser only refreshes when the currently viewed page is affected by a change. ::: :::tab{title="Improved :+1:" id="improved"} - The file watcher now ignores dotfiles (`.git`, `.hyperbook`) and `node_modules`, preventing unnecessary rebuild triggers. ::: :::tab{title="Fixed :bug:" id="fixed"} - Fixed lunr language plugin "Overwriting existing registered function" warnings during dev rebuilds. ::: :::: ## v0.87.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add Blockflow integration for building and playing Scratch-based guided tutorials. Use `::blockflow-player` for playing and `::::blockflow-editor` for creating tutorials with steps, toolbox and UI configuration. [Learn more](/elements/blockflow). ::: :::: ## v0.86.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add struktolab integration for creating structograms. [Learn more](/elements/struktolab). This replaces the old struktog integration. ::: :::: ## v0.85.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Add consent banner to embed and youtube elements. ::: :::: ## v0.84.5 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Updated Onlineide and Sqlide to integrated into the hyperbook store. This invalidates the state of all onlineide and sqlide instances, so your students have to start fresh. - Move to an event driven architecture for hyperbook cloud. This is a breaking change and you have to reset your database. ::: :::: ## v0.84.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix tabs and collapsible processing. - Fix protect processing. ::: :::: ## v0.84.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix hyperbook cloud integration not working correctly. ::: :::: ## v0.84.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Add hyperbook cloud support for managing students data in a central place. - See the documentation for more information: [Hyperbook Cloud](/configuration/cloud) ::: :::: ## v0.83.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Use bundled local versions of the Online IDE and SQL IDE instead of the externally hosted versions on onlineide.openpatch.org. This improves performance and reliability, enables faster updates, and prevents unexpected changes caused by external updates. - This increases the build size by approx. 31MB for the Online-IDE and 21MB for the SQL IDE, but provides a much better experience for users. ::: :::: ## v0.82.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update the Online-IDE to use onlineide2.openpatch.org, which uses the new compiler. If you have used nrw.onlineide.openpatch.org you can use the new version like this: ````md :::onlineide{libraries=['nrw']} ```java Main.java var l = new List(); l.append(1); ``` ::: ```` ::: :::: ## v0.81.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Typst editor not loading assets correctly from the server and handling special characters. - Typst preview not updating when the editor content changes. ::: :::: ## v0.81.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Typst can now load custom fonts. You can use the `@font` directive to load fonts from your hyperbook project. [Learn more](/elements/typst#custom-fonts) ::: :::: ## v0.80.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Typst now loads csv, json, xml, yaml and other files. ::: :::: ## v0.79.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix the table of contents drawer was visible on first load and refresh for a short period of time. ::: :::: ## v0.79.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Typst now loads images and other assets from the server when using relative paths. - Added debounce to Typst rendering to improve performance when typing quickly. ::: :::: ## v0.78.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update pagelist element to match excatly by default. ::: :::: ## v0.77.9 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update dependencies: - p5 to version 2.2.0 - mermaid to version 11.12.2 - wavesurfer.js to version 7.12.1 - @learningmap/web-component to version 0.3.7 - abcjs to version 6.6.0 - jsxgraph to version 1.12.2 ::: :::: ## v0.77.8 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix previous and next buttons. ::: :::: ## v0.77.7 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Dev command now detects port conflicts and prompts users to automatically find a free port when the specified port is already in use. ::: :::: ## v0.77.6 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Fix input width of multievent input ::: :::: ## v0.77.5 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Normalize line-height in code blocks. ::: :::: ## v0.77.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix empty pages show up in prev and next navigation ::: :::: ## v0.77.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Hide floating buttons in standalone mode ::: :::: ## v0.77.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix wide styling and line-height ::: :::: ## v0.77.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix emojis not present. Switch to manual updates. ::: :::: ## v0.77.0 ::::tabs :::tab{title="New :rocket:" id="new"} **New `navigation` Field for Pages and Sections** A new `navigation` field has been added to control how pages and sections appear in the navigation sidebar. **For Pages:** - `navigation: default` - Normal display in navigation - `navigation: hidden` - Hides the page from navigation (replaces deprecated `hide` field) **For Sections:** - `navigation: default` - Collapsible section (default behavior) - `navigation: hidden` - Hides the section from navigation - `navigation: virtual` - Items appear at parent level (replaces deprecated `virtual` field) - `navigation: page` - Renders as a regular page link without showing children - perfect for blog sections! - `navigation: expanded` - Section is expanded by default (replaces deprecated `expanded` field) **Example - Blog Section:** ```yaml --- name: Blog index: 5 navigation: page --- ``` This renders the blog section as a simple page link in the navigation, respecting the `index` for ordering, while the blog posts inside won't clutter the navigation. **Breadcrumb Navigation** A new breadcrumb navigation feature has been added to help users understand their location within the hyperbook structure. **Global Configuration** in `hyperbook.json`: ```json { "breadcrumb": true } ``` Or with custom options: ```json { "breadcrumb": { "home": ":house:", "separator": ">" } } ``` **Per-Page Configuration** in frontmatter: ```yaml --- breadcrumb: true --- ``` Or disable on specific pages: ```yaml --- breadcrumb: false --- ``` The breadcrumb supports emoji shortcodes for the home icon and separator (e.g., `:house:`, `:arrow_right:`). ::: :::tab{title="Improved :+1:" id="improved"} **Styling Improvements** - More coherent styling across the hyperbook interface - Dev server now cleans output folder when files are deleted, preventing stale files from remaining ::: :::: ## v0.76.1 ::::tabs :::tab{title="New :rocket:" id="new"} - use 1rem as the base font size for better accessibility ::: :::: ## v0.76.0 ::::tabs :::tab{title="New :rocket:" id="new"} **Title as Alternative to Name** You can now use `title` as an alternative to `name` in page and section frontmatter. This improves compatibility with other documentation tools like MkDocs that use `title` in their frontmatter. ```yaml --- title: My Page Title --- ``` If both `name` and `title` are present, `name` takes precedence. ::: :::tab{title="Fixed :bug:" id="fixed"} **Navigation Collapsible Symbol Fix** Fixed an issue where nested collapsible sections in the navigation incorrectly showed a minus symbol (`-`) instead of a plus symbol (`+`) when they were not expanded. **Typos and Bug Fixes** - Fixed "Buildung" typo in build progress messages (now correctly shows "Building") - Fixed "lanuage" typo in error message when an invalid language is configured - Fixed incorrect MIME type `plain/text` to `text/plain` in development server error responses - Fixed `aspectRation` typo to `aspectRatio` in excalidraw element configuration types - Fixed CSS property `aspectRatio:` to `aspect-ratio:` in embed directive (was using JavaScript property name instead of CSS property name) - Fixed incorrect repository URL in README (`openpath` → `openpatch`) - Updated minimum Node.js version requirement from 12.22.0 to 18 to match actual requirements ::: :::: ## v0.75.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} **Handlebars helpers in pagelist snippets** Fixed an issue where handlebars helpers like `dateformat`, `truncate`, and `truncateWords` were not available when using custom snippets with the pagelist directive. Note: File-related helpers (`file`, `rfile`, `base64`, `rbase64`) are not available in pagelist snippets. ::: :::: ## v0.75.0 ::::tabs :::tab{title="New :rocket:" id="new"} **New Handlebars Helpers** Three new helpers for snippets and templates: - **dateformat**: Format dates with customizable patterns ```hbs {{dateformat "2026-01-09" "DD.MM.YYYY"}} → 09.01.2026 ``` Supports: `YYYY`, `YY`, `MM`, `M`, `DD`, `D`, `HH`, `H`, `mm`, `m`, `ss`, `s` - **truncate**: Truncate strings by character limit ```hbs {{truncate "Hello World" 5 "..."}} → Hello... ``` - **truncateWords**: Truncate strings by word count ```hbs {{truncateWords "one two three four" 2 "..."}} → one two... ``` ::: :::: ## v0.74.0 ::::tabs :::tab{title="New :rocket:" id="new"} **Enhanced Pagelist Query Language** The pagelist element now supports a powerful query language for filtering pages: - **Boolean operators**: Combine conditions with `AND`, `OR`, `NOT` - **Parentheses**: Group conditions for complex queries like `(href(/blog/.*) OR href(/news/.*)) AND keyword(featured)` - **Custom frontmatter fields**: Query any frontmatter field like `difficulty(beginner)` or `tags(tutorial)` - **Operator precedence**: `NOT` > `AND` > `OR` **New Parameters:** - `limit`: Limit the number of results (e.g., `limit="5"`) - `orderBy`: Sort by any field including custom frontmatter (e.g., `orderBy="date:desc"`) **Example:** ```md ::pagelist{source="href(/blog/.*) AND date(.*)" orderBy="date:desc" limit="5"} ``` ::: :::tab{title="Improved :+1:" id="improved"} - Date objects from YAML frontmatter (e.g., `date: 2025-01-09` without quotes) now work correctly for filtering and sorting in pagelist. ::: :::: ## v0.73.5 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Add emoji support to tiles. ::: :::: ## v0.73.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix duplicate entries in pagelist. ::: :::: ## v0.73.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix resolving relative files, when using an index.md in a directory. ::: :::: ## v0.73.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix Typst styling. Only the page is now colored white. ::: :::: ## v0.73.0 ::::tabs :::tab{title="New :rocket:" id="new"} **Typst Directive** Write and preview Typst documents directly in your Hyperbook with a powerful interactive editor. **Features** - Interactive editor with syntax highlighting - Live preview with automatic rendering - Multi-file project support with tab interface - Binary file upload for images, fonts, and other assets - Export to PDF - Download entire project as ZIP - State persistence across page reloads **Smart Error Handling** - Error messages display as dismissible overlays in the preview - Last successful render is preserved when errors occur - Clean, readable error messages extracted from Typst compiler output - Smooth animations with visual feedback ::: :::: ## v0.72.2 ::::tabs :::tab{title="Improved :+1:" id="improved"} **Improved Accessibility and No-JavaScript Support** Your Hyperbook now works even when JavaScript is disabled or unavailable, making it more accessible and reliable. **What's New** - Navigation, collapsibles, and tabs now work without JavaScript - Cleaner interface when JavaScript is disabled - unnecessary buttons are hidden - Improved keyboard navigation throughout the site - Better compatibility with screen readers and assistive technologies **Benefits for Your Readers** - ✅ Content always accessible, even with JavaScript disabled - ✅ Better experience for users with slow connections - ✅ Improved SEO - search engines can see all your content - ✅ Enhanced accessibility for users with disabilities - ✅ Faster initial page load - ✅ More reliable navigation ::: :::: ## v0.72.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} Performance optimizations - Added `font-display: swap` to all font-face declarations for better page load performance - Added `defer` attribute to script tags to improve page load speed - Minified dexie-export-import.js bundle to reduce file size - Added explicit height attribute to logo image for better CLS scores ::: :::: ## v0.72.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} Add shareable URL builder with sections filter - Added share button (🔗 icon) in header that opens a dialog for creating shareable URLs - Implemented sections filter query parameter to show only specific content sections - Added live URL preview with standalone mode toggle and section selection checkboxes - QR code now includes all query parameters in the generated code - TOC toggle hides automatically when sections are filtered - Floating action buttons (TOC, QR code) now use dynamic flexbox positioning - Both share and QR dialogs moved outside content area to remain visible when filtering ::: :::tab{title="Fixed :bug:" id="fixed"} - Multievent is hidden on page load and gets shown when the javascript is loaded, thus not revealing the solutions. ::: :::: ## v0.71.4 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update pyodide to version 0.29.0 ::: :::: ## v0.71.3 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update learningmap ::: :::: ## v0.71.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Improve the new command and make it accessible via `npx create hyperbook` and in the vs code extension. ::: :::: ## v0.70.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update dependencies: - learningmap to 0.3.0 - abcjs to 6.5.2 - jsxgraph to 1.12.0 - mermaid to 11.12.1 - p5 to 2.1.1 - wavesurfer.js to 7.11.1 ::: ## v0.70.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Support relative links for prev and next in the frontmatter. You can now use relative links for the previous and next buttons in the frontmatter like so: ```yaml prev: ../previous-page.md next: ./next-page.md ``` ::: ## v0.69.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - The previous and next button are now always enabled, even if hide is true. You have to manually disable them in the frontmatter like so: ```yaml prev: next: ``` ::: ## v0.68.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fixed images have doubled base paths. ::: ## v0.68.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Learningmaps not loading from relative urls. - **VSCode Extension (v0.44.1)**: Fix learningmap with relative src not loading in VSCode extension preview. The extension now properly resolves relative paths by correctly passing navigation context to the markdown processor. ::: :::: ## v0.68.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - The `::multievent` directive now fully persists its state, including all visual feedback from evaluations. When users reload the page, they can see whether their task was evaluated as correct or incorrect, with all green highlighting for correct answers, orange striped backgrounds for incorrect answers, and error indicators preserved. State is saved automatically on all interactions and restored on page load. ::: :::: ## v0.67.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add new `custom` table to the Hyperbook Dexie store for user-managed state persistence. Users can now store and retrieve arbitrary JSON data in the browser's IndexedDB using `store.custom.put()` and `store.custom.get()`. Custom data is automatically included in export/import functionality. [Learn more](/advanced/custom-scripts#using-the-hyperbook-store-api) ::: :::: ## v0.63.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add new `::textinput` directive for persistent text input. This directive creates interactive text input areas that automatically save to the browser's local storage using Dexie. Supports customizable placeholder and height attributes, multiple independent inputs via custom IDs, and full light/dark mode theming. [Learn more](/elements/textinput) ::: :::: ## v0.62.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix relative urls for sqlite. ::: :::: ## v0.62.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix SQLIde not loading custom database files. ::: :::: ## v0.62.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Use Learningmap web component from the new "@learningmap/web-component" package. This is best used together with the new Learningmap editor. [Learn more](/elements/learningmap) ::: :::: ## v0.61.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Changed last fix to keep backward compatibility. ::: :::: ## v0.61.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fixed GeoGebra not loading the correct file. ::: :::: ## v0.61.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fixed an issue where the width and height of nodes were not being saved correctly in Learning Maps. - Improved button labels for topics in Learning Maps for better clarity and usability. - Resolved a bug that prevented the "needs" and "optional" properties from being set in the Learning Maps editor. ::: :::: ## v0.61.0 ::::tabs :::tab{title="New :rocket:" id="new"} - **Page Layout Options**: Added three layout options for Hyperbook pages: - **Default Layout**: Standard layout with visible sidebar (unchanged behavior for existing pages) - **Wide Layout**: Full-width content with drawer-only navigation, ideal for data tables, code examples, and galleries. [Learn more](/advanced/layouts) - **Standalone Layout**: Content-only display (no header, sidebar, footer) perfect for iframe embedding. [Learn more](/advanced/layouts) - **Automatic Iframe Detection**: Pages automatically switch to standalone mode when embedded in iframes - zero configuration needed! - **Three Activation Methods for Standalone**: Can be activated via frontmatter (`layout: standalone`), URL parameter (`?standalone=true`), or automatic iframe detection - **Smart UI Hiding**: TOC toggle and QR code buttons automatically hide in standalone mode for cleaner embedded experience - **Backward Compatible**: All existing pages work unchanged without any configuration ::: :::: ## v0.60.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Vastly improved learningmap element for displaying interactive learning maps. [Learn more](/elements/learningmap) - **Automatic Favicon and PWA Icon Generation**: When building a Hyperbook project, if no `favicon.ico` exists and a `logo` is defined in `hyperbook.json`, a complete set of favicons and PWA assets are automatically generated (60+ files including Android icons, Apple touch icons, Apple startup images, and web manifest with full metadata). The system intelligently searches for logos in the root folder, book folder, or public folder, and uses metadata from `hyperbook.json` (name, description, theme color, language, author) to create a professional PWA-ready icon set. ::: :::: ## v0.58.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Retain scroll position in dev-mode when the page is reloaded. ::: :::: ## v0.58.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix url in learningmaps not prefixed with basePath. ::: :::: ## v0.58.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add learningmap element to create interactive learning maps. [Learn more](/elements/learningmap) ::: :::: ## v0.57.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fixed IDEs not showing all characters correctly. ::: :::: ## v0.57.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fixed alert element not working correctly in container elements like tabs. ::: :::: ## v0.57.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Horizontal rule not break image aligment. - Collapsibles and alerts work better with image alignment. ::: :::: ## v0.57.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Auto load math display libraries in all h5p elements. - Add c and l variables to allow for dynamic amount of colons in snippets. ::: :::: ## v0.56.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Use alert colors for links, headings and bold text. ::: :::: ## v0.55.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Custom alerts can now be defined. See the documentation for [alerts](@alerts). ::: :::: ## v0.54.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add custom image attributes support using curly brace syntax `{}`. Example: `![](/image.jpg){#hero .rounded width="200"}` adds ID `hero`, class `rounded`, and sets width to 200px. - Add enhanced image alignment options with special characters: - `![](/image.jpg)` - Center (default) - `-![](/image.jpg)` - Left aligned - `--![](/image.jpg)` - Left aligned with extended spacing - `![](/image.jpg)-` - Right aligned - `![](/image.jpg)--` - Right aligned with extended spacing - `--![](/image.jpg)--` - Center with extended spacing - Add comprehensive image styling documentation with live examples. ::: :::tab{title="Improved :+1:" id="improved"} - Improve lightbox to eliminate layout shifts when opening/closing. - Improve lightbox rendering performance and smoother transitions. ::: :::: ## v0.53.7 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix sub and superscript interfering with links and other markdown syntax. - Convert mjs to js for better compatibility. ::: :::: ## v0.53.6 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix toc configuration not working correctly. ::: :::: ## v0.53.5 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix toc and qrcode configuration not working correctly. ::: :::: ## v0.53.4 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update emoji shortscodes to match GitHub's emoji shortcodes. This means that you can now use the same shortcodes as on GitHub, e.g. `:smile:` for 😄. ::: :::: ## v0.53.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix headings with colons did not display correctly. - Fix files with spacing in the name did not work correctly. ::: :::tab{title="Improved :+1:" id="improved"} - Allow syntax highlighting and copy code to be disabled for inline code blocks. For this you have to set the `bypassInline` options in your `hyperbook.json` to `true`. ::: :::: ## v0.53.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Build does not crash anymore, when an empty index.md is present. ::: :::: ## v0.53.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix sections need an index.md. Folders without an index.md are now ignored. ::: :::: ## v0.53.0 ::::tabs :::tab{title="New :rocket:" id="new"} You can now include images, videos, and other files directly within your book directory, making it easy to reference them using relative paths. This greatly improves your ability to: - Organize content intuitively - Collaborate with others - Share Hyperbook pages with media included Example usage: ```md ![Image in the same directory](./image.png) ![Image one directory up](../image.png) ``` This change enables a more seamless and portable authoring experience—no need to manage separate static folders or rely on absolute paths. **🧩 Smarter Link Handling** You can now link to files with .md, .md.json, and .md.yml extensions. This improves compatibility with editor-based file completion, making navigation and linking easier during editing. **⚠️ BREAKING CHANGE** To support these features, the template file extension format has changed: If a book page is defined in YAML or JSON, it must now use: - .md.yml instead of .yml - .md.json instead of .json This ensures the markdown parser can correctly process the content and resolve relative paths to media. ::: :::: ## v0.52.7 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Do not parse e.g. :1 as a element. Only parse the documented elements. ::: :::: ## v0.52.6 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix QR Code not showing correctly, because escaped characters were used. ::: :::: ## v0.52.5 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - CLI argument port was not used. Now it is working again. For example: `hyperbook dev --port 3000` ::: :::: ## v0.52.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Dev server was not respecting the `basePath` configuration option. Now it is working again. ::: :::: ## v0.52.3 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Default to youtube-nocookie for youtube videos. ::: :::: ## v0.52.2 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Improve table responsive design. ::: :::: ## v0.52.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Update dependencies - P5 to version 2.0.0 - Excalidraw to version 0.18.0 - Mermaid to version 11.6.0 ::: ::: :::: ## v0.52.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - A new element multievent that allows you to handle multiple events in a single interactive component. This feature is particularly useful for creating quizzes and other complex interactive scenarios where a single user's answers are evaluated directly with immediate feedback on whether they are correct or not. [Learn more](/elements/multievent) ::: ::: :::: ## v0.51.1 ::::tabs :::tab{title="Improved :+1:" id="improved"} Styling Improvements: - Increased the width of the navigation to utilize more horizontal space. - Reduced base font size to 14px for improved readability and a more compact layout. - Decreased header height to 60px to maximize vertical content space. ::: :::: ## v0.51.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - In addition to GeoGebra you can now use JSXGraph for creating math visualizations. [Learn more](/elements/jsxgraph) ::: :::: ## v0.50.5 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - The development server does not crash anymore, when an error occurs. ::: :::: ## v0.50.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - The GeoGebra reset button now resets the GeoGebra element to its initial state. ::: :::: ## v0.50.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix GeoGebra element not resize correctly in collapsibles, again. ::: :::: ## v0.50.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix GeoGebra element not resize correctly in collapsibles. ::: :::: ## v0.50.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Improve responsive scaling of the GeoGebra element. - Save and load the state of the GeoGebra element. ::: :::: ## v0.50.0 ::::tabs :::tab{title="New :rocket:" id="new"} - The h5p element allows you to embed H5P content directly into your hyperbook. H5P is an open-source platform for creating, sharing, and reusing interactive content. With the h5p element, you can easily integrate H5P content such as quizzes, interactive videos, and presentations into your hyperbook. [Learn more](/elements/h5p) ::: :::: ## v0.49.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Update mermaid to 11.4.1 - Update p5 to 1.11.3 - Update wavesurfer.js to 7.9.0 ::: :::: ## v0.49.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix missing right border in the struktog element ::: :::: ## v0.49.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Add footnote translation for german. ::: :::: ## v0.49.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix collapsibles not having a default id and therefore not working correctly. ::: :::: ## v0.49.0 ::::tabs :::tab{title="Improved :+1:" id="improved"} - Make hyperbook config accessible in snippets. ```md {{ hyperbook.name }} ``` ::: :::: ## v0.48.8 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix p5 element not accepting editor=false. ::: :::: ## v0.48.7 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix sections collapsing before navigating to the new URL. - Fix active sections not using the brand color. ::: :::: ## v0.48.6 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Remove p5.sound. The library caused the p5-element to stay in the loading stage in Safari for iOS and MacOS. - Fix the parsing of snippet parameters. Snippet parameters can now include parentheses and other symbols. ::: :::: ## v0.48.5 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix link to section not including the basePath. ::: :::: ## v0.48.4 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix build breaking, when a heading is in a collapsible. ::: :::: ## v0.48.3 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Remove unwanted `console.log`. ::: :::: ## v0.48.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix search not working. ::: :::: ## v0.48.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix collapsible button styles bleeding into other button styles. ::: :::: ## v0.48.0 ::::tabs :::tab{title="New :rocket:" id="new"} - The webide element allows you to embed a web-based integrated development environment (IDE) directly into your hyperbook. This feature is particularly useful for interactive coding tutorials and exercises, enabling users to write and run code within the book itself. [Learn more](/elements/webide) ::: :::: ## v0.47.2 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Fix tabs and collapsibles not rendering hyperbook elements. ::: :::: ## v0.47.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Development server injected the reload script at the wrong place, if there was an additional body tag. Thus, leading to unwanted behavior e.g. for the p5-element. - Pyide: The event listener for the run button was not removed, which lead to refreshing the page when the run button was clicked multiple times. ::: :::: ## v0.47.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Save every state of the hyperbook and make it available for download. To enable this feature, set `importExport` to `true` in the configuration file. The buttons for importing and exporting will be at the bottom of the page. The state of the hyperbook will be saved as a JSON file. The file can be imported again to restore the state of the hyperbook. - The code of the editor for the elements P5, Pyide, ABC-Music can now be copied, download or resetted. - Add i18n support. Currently, only `en` and `de` are supported. You need to set the `language` in the configuration file. [Learn more](/configuration/book) - If you want to contribute a new language, please create a pull-request and add a new locale in `packages/markdown/locales`. ::: :::: ## v0.46.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Resolved an issue where interactive elements failed to initialize correctly after being revealed within the protect element. - Fixed an issue where collapsibles with the same ID were not synchronized. ::: :::: ## v0.46.1 ::::tabs :::tab{title="Fixed :bug:" id="fixed"} - Resolved an issue where interactive elements failed to initialize correctly after being revealed within the protect element. - Fixed an issue where collapsibles with the same ID were not synchronized. ::: :::: ## v0.46.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add `trailingSlash` option. When this option is enabled only directories and `index.html` files will be produced, when building the hyperbook. [Learn more](/configuration/book) ::: :::: ## v0.45.0 ::::tabs :::tab{title="New :rocket:" id="new"} - You can now add inputs and tests to your pyide element. [Learn more](/elements/pyide) ::: :::: ## v0.44.0 ::::tabs :::tab{title="New :rocket:" id="new"} - A new element pyide that allows you to run python in your browser. [Learn more](/elements/pyide) ::: :::: ## v0.43.0 ::::tabs :::tab{title="New :rocket:" id="new"} - A new element p5 that allows you to create p5.js sketches was added. [Learn more](/elements/p5) ::: :::: ## v0.42.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Add abc-music element for creating and listing to note written in the abcnotation. [Learn more](/elements/abc-music) ::: :::: ## v0.41.0 ::::tabs :::tab{title="New :rocket:" id="new"} - Allow emojis in tab titles. E.g.: `:::tab{title="Hi :smile:"}` ::: :::: # Code # Code ```md Inline `code` has `back-ticks around` it. ``` Inline `code` has `back-ticks around` it. Multiline code block are fenced by lines with three back-ticks ```. ````md ```python showLineNumbers title="MyPython.py" {1} /test/#r /Python/#y /syntax/#l /print/ s = "Python syntax highlighting" print s ``` ``` If no language is indicated, the language will be guessed. If you do not want syntax-highlighting, uses the language `plain`. ``` ```` ```python showLineNumbers title="MyPython.py" {1} /test/#r /Python/#y /syntax/#l /print/ s = "Python syntax highlighting" print s ``` ``` If no language is indicated, plain will be used. ``` ## Configuration You can define default values for code blocks in your `hyperbook.json`. You can find valid values for themes here: https://shiki.style/themes#themes. bypassInline is `false` by default, if you set it to `true`, inline code will not be processed. This means that no copy button will be shown and no syntax highlighting will be applied. ```json { "elements": { "code": { "showLineNumbers": true, "theme": { "dark": "dracula", "light": "evergreen" }, "bypassInline": true } } } ``` # Comment # Comment You can use comment. The content of a comment will not appear in the final Hyperbook. ``` ``` # Emphasis # Emphasis ```md Italics with _underscore_. Strong emphasis with **asterisks**. Combined emphasis with **asterisks and _underscore_** Strikethrough uses two tildes. ~~Scratch this.~~ ``` Italics with _underscore_. Strong emphasis with **asterisks**. Combined emphasis with **asterisks and _underscore_** Strikethrough uses two tildes. ~~Scratch this.~~ # Footnote # Footnote Here is a simple footnote[^1]. With some additional text after it. [^1]: My reference. # Header # Header ```md # H1 ## H2 ### H3 #### H4 ##### H5 ###### H6 ``` # H1 ## H2 ### H3 #### H4 ##### H5 ###### H6 :::alert{warn} Please use the correct hierarchy of headers, because these will be used for generating a table of contents. ::: ## Custom Ids If you want stable links to your headings, even if the content of the heading changes you can use custom ids: ```md # Stable H1 {#stable} ``` # Stable H1 {#stable} # Horizontal Rule # Horizontal Rule ``` Three or more dashes... --- ``` The horizontal rule is not visible, but it separates sections and is therefore useful when images are aligned left or right. Three or more dashes... --- # Image # Image To add an image use this. You should prefix local images with a slash. These will be loaded from your public directory. You can also use external images from other sources. ```md ![](/test.jpg) ``` ![](/test.jpg) You can also add an alternate description for the image, which can be used by screen readers. ```md ![A description](/test.jpg) ``` ![A description](/test.jpg) You can also add a visible caption to your image like so: ```md ![A description](/test.jpg "A caption") ``` ![A description](/test.jpg "A caption") If you want to link your image you can use the normal link syntax: ```md [![A description](/test.jpg "A caption")](/elements/hints) ``` [![A description](/test.jpg "A caption")](/elements/hints) :::alert{info} If your Hyperbook uses the [basePath](/configuration/book) property, the basePath will automatically be prepended. ::: ## Customize position and styling You can customize the position and styling of your images by adding special characters before or after the image markdown. Custom classes and attributes can be added by using curly braces `{}` after the image markdown. ```md ![](/test.jpg){#hero .rounded width="200"} ``` This will add an image with the ID `hero`, the class `rounded` and a width of `200px`. :::alert{info} This is best used with custom css styles. ::: You can also align your image to the left, right or center by adding special characters before or after the image markdown. center: `![](/test.jpg)` left: `-![](/test.jpg)` leftplus: `--![](/test.jpg)` right: `![](/test.jpg)-` rightplus: `![](/test.jpg)--` centerplus: `--![](/test.jpg)--` You can use the horizontal rule to stop text flowing around the image. You can check all options on this [example page](./image-styling). # Line Break # Line Break Here is an example for using line breaks in Markdown: ```md Here's a line for us to start with. This line is separated from the one above by two newlines, so it will be a _separate paragraph_. This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. ``` Here's a line for us to start with. This line is separated from the one above by two newlines, so it will be a _separate paragraph_. This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. # Link # Link There are two ways to create links. ```md [Link in text](https://www.openpatch.org) [Link in text with a title](https://www.openpatch.org "Webseite of OpenPatch") [Reference link][case-insensitive reference text] [Reference link with a number][1] URLs and URLs in angle brackets will automatically get turned into links http://www.example.com or . Some text to show that the reference links can follow later. [case-insensitive reference text]: https://edugit.org [1]: http://codeberg.org ``` [Link in text](https://www.openpatch.org) [Link in text with a title](https://www.openpatch.org "Webseite of OpenPatch") [Reference link][case-insensitive reference text] [Reference link with a number][1] URLs and URLs in angle brackets will automatically get turned into links http://www.example.com or . Some text to show that the reference links can follow later. [case-insensitive reference text]: https://edugit.org [1]: http://codeberg.org ## Internal Links You can link to other pages in your book using relative paths. Hyperbook automatically handles markdown file extensions, so you can use links with or without the `.md` extension: ```md [With extension](./other-page.md) [Without extension](./other-page) ``` Both links will point to the same page. This also works for other markdown file types: ```md [Template file](./template.md.hbs) [JSON file](./data.md.json) [YAML file](./config.md.yml) ``` # List # List ```md 1. First ordered list item 2. Another item - Unordered sub-list 3. Actual numbers don't matter, just that it's a number 1. Ordered sub-list 4. An Another item. You can have properly indeted paragraphs within list items. ``` 1. First ordered list item 2. Another item - Unordered sub-list 3. Actual numbers don't matter, just that it's a number 1. Ordered sub-list 4. An Another item. You can have properly indeted paragraphs within list items. # Quote # Quote ```md > Quote can expand over > many lines. With one line two quotes can be separated. > If a quote is longer than a line, it will be wrapped. You can also use **Markdown** in quotes. ``` > Quote can expand over > many lines. With one line two quotes can be separated. > If a quote is longer than a line, it will be wrapped. You can also use **Markdown** in quotes. # Sub- and Superscript # Sub- and Superscript With subscript and superscript, you can raise or lower text. This is especially useful for mathematical formulas or chemical compounds. ```md H_{2}0 f^{2}(x) ``` H_{2}0 f^{2}(x) :::alert{info} Subscript and superscript can also be disabled by placing a backslashe before the subscript or superscript: ```md H\_{2}0 f\^{2}(x) ``` H\_{2}0 f\^{2}(x) ::: # Table # Table You can declare tables by using a combination of pipe symbols (|), for defining columns and three dashed for defining the header row. Here is a simple example: ```md | Name | Value | | ---- | ----- | | Red | 5 | | Blue | 4 | ``` | Name | Value | | ---- | ----- | | Red | 5 | | Blue | 4 | The pipe symboles do not need to be aligned. ## Alignment The alignment of columns can be declared by using colons. ```md | Left | Center | Right | | :----- | :----: | ----: | | Text 1 | Red | 1600 | | Text 2 | Blue | 12 | | Text 3 | Green | 1 | ``` | Left | Center | Right | | :----- | :----: | ----: | | Text 1 | Red | 1600 | | Text 2 | Blue | 12 | | Text 3 | Green | 1 | :::alert{info} The width of a column can not be declared. It will be calculated depending on the content and the webbrowser. ::: # Book Configuration # Book Configuration In your new Hyperbook project you will find a `hyperbook.json` file. This file is for configuring Hyperbook. Here is a list of options you can and part wise must set (indicated by a \*). | Property | Description | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name\* | Name of your Hyperbook. Used for the page header. | | description | Description of your Hyperbook. Used for SEO. | | search | Allows searching your hyperbook | | logo | URL to a logo. Used for the page title. Can be relative to the public folder or an absolute URL | | author.name | Author name of your Hyperbook. Used in the footer. | | author.url | Used to link the author name in the footer. | | font | URL to a font. Used for headings and body. You can add ":90%" for adjusting the font size. | | fonts.heading | URL to a font. Used for headings. You can add ":90%" for adjusting the font size. | | fonts.body | URL to a font. Used for body. You can add ":90%" for adjusting the font size. | | fonts.code | URL to a font. Used for code. You can add ":90%" for adjusting the font size. | | colors.brand | The color for the header and the accents for example on links. | | colors.brandDark | The color for the header and the accents for example on links, if the user prefers a dark theme. Brand text is not used for the dark theme. | | colors.brandText | The color for the text in the header | | basePath | When deploying to a subdirectory, for example on GitHub pages, you can set a base path. | | license | License under the Hyperbook is published. | | language | The language of the Hyperbook. | | repo | The link to the GitHub repo. Used for showing an edit button. The %path% placeholder will be replaced by the current path or the current path will be appended. | | repo.url | The link to the repo. Used for showing an edit button. The %path% placeholder will be replaced by the current path or the current path will be appended. | | repo.label | The label for the repo link. | | elements | Here you can configure the elements. See the element pages for configuration options. | | links | Here you can add custom links, which will be shown in the top right corner. See the example below on how to use them. | | styles | Here you can add Links to custom CSS files. | | scripts | Here you can add links to custom JavaScript files. | | allowDangerousHtml | Allow HTML. This can lead to incompatibilities in future versions. | | qrcode | Shows an icon, which opens a qr code to the current page. | | toc | Show or hide a table of content for the page. This is on for pages and off for glossary entries by default | | llms | When set to true, generates an llms.txt file that combines all markdown files in order. The file includes the book name and version in a header format. | | trailingSlash | Outputs all files into ther own folders and produces only index.html files. | | importExport | Allows to import and export the state of the Hyperbook as a file. Buttons for importing and exporting will be at the bottom of the page. | | cloud.url | URL of your [Hyperbook Cloud](/configuration/cloud) server. Enables student login and cloud sync. | | cloud.id | The hyperbook slug/ID on the cloud server. Must match the slug configured in the cloud admin interface. | | version | Configure where the version of the Hyperbook is shown. "text" show it under the Powered by Hyperbook text. "tooltip" as a tooltip when hovering the Powered by Hyperbook text and "console" only in the console. | Here is an example configuration: ```json { "name": "Hyperbook Documentation", "description": "Documentation for Hyperbook created with Hyperbook", "search": true, "qrcode": false, "author": { "name": "OpenPatch", "url": "https://openpatch.org" }, "font": "/fonts/my-font.woff2:90%", "logo": "/logo.png", "license": "CC-BY-SA", "language": "en", "basePath": "/hyperbook-github-pages", "repo": { "url": "https://github.com/mikebarkmin/hyperbook-github-pages/edit/main/%path%", "label": "Edit on GitHub" }, "colors": { "brand": "#FF0000" }, "cloud": { "url": "https://cloud.example.com", "id": "my-hyperbook" }, "elements": { "bookmarks": false }, "links": [ { "label": "Contact", "links": [ { "label": "Mail", "icon": "📧", "href": "mailto:contact@openpatch.org" }, { "label": "Twitter", "icon": "🐦", "href": "https://twitter.com/openpatchorg" }, { "label": "Mastodon", "icon": "🐘", "href": "https://fosstodon.org/@openpatch" }, { "label": "Matrix (Chat)", "icon": "👨‍💻", "href": "https://matrix.to/#/#hyperbook:matrix.org" } ] }, { "label": "OpenPatch", "href": "https://openpatch.org" } ] } ``` # Page Configuration # Page Configuration A page is a markdown file. It consists of a frontmatter and a content. The frontmatter gives Hyperbook some information to work with. The frontmatter is at the very top of the markdown file and is fenced by three ---. ```md --- name: The Name of a Page --- # A Headline And some content ``` Here are the properties you can set in the frontmatter: | Property | Description | | :---------- | :---------------------------------------------------------------------------------------------------------------------------------- | | name | Name of the page used in the navigation | | title | Alternative to `name` for compatibility with other tools like MkDocs. If both are set, `name` takes precedence | | description | Description of the page used for SEO | | permaid | An id which will make the page also available at e.g. /@/audio, when setting permaid to audio | | keywords | A list of keywords used for SEO | | index | A number indicating the position in the menu, in relation to the pages and the sections on the same level. Without it, a page comes before the sections. | | prev | The absolute path to the prev page or a absolute path to a permaid e.g. /@/audio. This overrides index and hide. You can also set it to nothing and it will hide the prev button. | | next | The absoulte path to the next page or a absolute path to a permaid e.g. /@/audio. This overrides index and hide. You can also set it to nothing and it will hide the next button. | | hide | Hides the page from the navigation (deprecated, use `navigation: hidden` instead) | | navigation | Controls how the page appears in navigation. Options: `default` (normal display), `hidden` (hides from navigation) | | toc | Show or hide a table of content for the page. This is on for pages and off for glossary entries by default | | layout | Choose the page layout. Options: `default` (standard layout with sidebar), `wide` (full-width content with navigation in drawer mode), or `standalone` (content only, hides all navigation - ideal for iframe embedding). The standalone layout can also be activated via URL parameter: `?standalone=true` | | styles | Here you can add Links to custom CSS files. | | scripts | Here you can add links to custom JavaScript files. | | qrcode | Shows an icon, which opens a qr code to this page. | # Section Configuration # Section Configuration Each folder inside `book` defines a section. Each folder must contain a `index.md`. The index file can be configured like a [page](/configuration/page). You can also make folders `virtual`. If a folder is declared virtual, the folder will not create another level. This is helpful if you want to structure your hyperbook with folders, but they should not affect the navigation. To summarize, here are the properties you can set in the frontmatter: | Property | Description | | :--------- | :--------------------------------------------------------------------------------------------------------------------- | | ... | Every Property from a [page](/configuration/page) | | index | A number indicating the position in the menu, in relation to the pages and the sections on the same level. Without it, a section comes after the pages. | | virtual | Everything will appear on the same level as the parent. Be aware that the index property does not work across folders. (deprecated, use `navigation: virtual` instead) | | expanded | Defines whether the section is expanded on page load. (deprecated, use `navigation: expanded` instead) | | navigation | Controls how the section appears in navigation. Options: `default` (collapsible section), `hidden` (hides from navigation), `virtual` (items appear at parent level), `page` (renders as a regular page link without showing children), `expanded` (section is expanded by default) | If the `index.md` file only contains a front matter and is otherwise empty, it will appear in the navigation as a non-clickable item. # Cloud [Alpha] # Cloud [Alpha] :::alert{warn} This feature is still in alpha. It may be buggy and subject to change. Please test it out and share your feedback with us! ::: Hyperbook Cloud is a self-hosted student management platform that enables user login, progress tracking, and cloud synchronization for your hyperbooks. ## Setup To connect a hyperbook to a cloud server, add the `cloud` property to your `hyperbook.json`: ```json { "name": "My Hyperbook", "cloud": { "url": "https://cloud.example.com", "id": "my-hyperbook" } } ``` | Property | Description | |---|---| | `url` | The URL of your Hyperbook Cloud server. | | `id` | The slug of the hyperbook as configured in the cloud admin interface. Must match exactly. | ## How It Works When cloud is configured, a login button appears in the hyperbook. Students log in with the credentials created by their teacher in the cloud admin interface. Once logged in, the following happens automatically: - **State synchronization** — All interactive element state (code editors, bookmarks, collapsibles, excalidraw drawings, etc.) is saved to the cloud server. - **Cross-device access** — Students can continue where they left off on any device. - **Offline support** — Changes are queued locally when offline and synced when the connection is restored. - **Auto-save indicator** — A status icon in the toolbar shows the current sync state. Each state has its own badge shape, so it does not rely on color alone, and the button's label reads out the state for screen readers. - **Sync notices** — States you can act on — a failed save, being offline, a merge with another session — are surfaced in a notice at the bottom of the page, with a retry button where one applies. Successful saves stay silent. When logged into the cloud, local export, import, and reset buttons are hidden to prevent conflicts with cloud-managed state. ## Event-Sourcing Architecture Instead of sending the entire store on every change, Hyperbook Cloud uses an event-sourcing approach for efficient synchronization. ### How It Works 1. **Granular event capture** — Every change to the local Dexie database (create, update, delete) is captured as an individual event via Dexie hooks. Only the changed fields are recorded for updates (deltas), not the full row. 2. **Batched sync** — Events are collected and sent in batches to the cloud server after a debounce period (2 seconds of inactivity or 10 seconds maximum wait). 3. **Server-side snapshots** — The server periodically compacts events into snapshots. After 100 events (configurable via `SNAPSHOT_THRESHOLD` environment variable), a new snapshot is created and old events are pruned. 4. **State reconstruction** — When a student loads their state, the server reconstructs it by applying any events since the last snapshot. ### Large Data Handling If an event batch exceeds 512 KB (e.g., large Excalidraw drawings or Geogebra states), the client falls back to sending a full snapshot instead of individual events. This prevents bandwidth issues with large binary data. ### Conflict Detection and Merging The client tracks the last known event ID from the server. When sending events, it includes this ID as `afterEventId`. If the server detects that the client is out of date (e.g., another device sent events in the meantime), it responds with a 409 conflict. A conflict never discards local work. The client: 1. Fetches the current server state and imports it, 2. Replays its pending events on top of that state, locally and on the server, 3. Explains the merge in a notice and reloads the page. The reload is necessary because interactive elements read the store once when the page loads, so the page still shows pre-merge state until it happens. It is announced rather than immediate, and the notice offers a **Reload now** button. The server checks `afterEventId` and appends the batch in a single transaction, so two devices saving at the same moment cannot both pass the check. Each hyperbook tracks its own event ID. Two hyperbooks served from the same domain do not share a watermark. ### Offline Queue When the device is offline, event batches are stored in a local queue. Once the connection is restored, the queue is sent in order, each batch chaining onto the event ID the previous one returned — the watermark cannot advance while offline, so batches cannot carry one recorded at queue time. If the server has moved on in the meantime, the queue is merged rather than discarded: the events still waiting are replayed on top of the fetched state, exactly as for an online conflict. The queue holds at most 100 batches. Beyond that it collapses into a single full snapshot, sent on reconnect. ### Leaving the Page Changes made inside the debounce window would otherwise be lost if the tab were closed before it elapsed. On `beforeunload` and `pagehide` the client flushes anything pending with a `keepalive` request, which outlives the page. Batches over 60 KB are left for the "unsaved changes" prompt instead, because browsers reject oversized keepalive requests outright. ### What Is Not Synced Ephemeral interface state — cursor position, scroll offset, window size — is excluded from both events and snapshots. It is per-device by nature, and syncing it made one device pull another's scroll position. ## Cloud Server The cloud server is a separate application located in `platforms/cloud/` of the Hyperbook repository. See the [Cloud README](https://github.com/openpatch/hyperbook/tree/main/platforms/cloud) for installation and deployment instructions. ### Key Concepts - **Hyperbooks** — Each hyperbook is registered in the cloud with a unique slug. This slug is used as the `cloud.id`. - **Groups** — Students are organized into groups within a hyperbook. - **Students** — Each student has a username and password for logging into the hyperbook. - **Teachers** — Teachers can manage groups and students for their assigned hyperbooks. - **Permissions** — Admins can grant teachers fine-grained permissions per hyperbook. ### Impersonation Teachers and admins can impersonate a student to view their progress in read-only mode. This opens the hyperbook with the student's saved state without allowing any modifications. ### Event Log The admin interface provides an event log per group, showing recent events (last 200) and the latest snapshot per student. Teachers and admins can: - **View the event log** — See all recent database changes across students in a group. - **Download snapshots** — Download the latest reconstructed state for a student as a JSON file. Snapshots are labeled with their source: `auto` (created automatically when the event threshold is reached) or `manual` (created from a full snapshot upload, import, or reset). ## Data Flow ### Saving Changes ``` Student interacts with hyperbook ↓ Dexie hook captures change as event ↓ Events batched (debounced 2s / max 10s) ↓ POST /api/store/:hyperbookId/events ↓ Events stored on cloud server ``` If the event batch exceeds 512 KB: ``` Batch too large ↓ Full Dexie export created ↓ POST /api/store/:hyperbookId/snapshot ↓ Snapshot replaces all events + previous snapshot ``` ### Loading State ``` Student logs in ↓ GET /api/store/:hyperbookId ↓ Server reconstructs state (snapshot + events) ↓ State imported into local IndexedDB ↓ Interactive elements restored ``` ## API Endpoints | Method | Endpoint | Description | |---|---|---| | `GET` | `/api/store/:hyperbookId` | Fetch reconstructed state (snapshot + event replay). | | `POST` | `/api/store/:hyperbookId/events` | Append a batch of events. Includes `afterEventId` for conflict detection. | | `POST` | `/api/store/:hyperbookId/snapshot` | Full-state overwrite. Replaces all events and snapshots. | ## Configuration | Environment Variable | Default | Description | |---|---|---| | `SNAPSHOT_THRESHOLD` | `100` | Number of events before the server automatically creates a new snapshot and prunes old events. | # Library Configuration # Library Configuration A Hyperlibrary is a collection of Hyperbooks and Hyperlibraries. You can use it to merge several books into one big one. The books are automatically linked to each other. For this purpose a new link will be added to the links in the upper right corner. In its submenus you will find the linked Hyperbooks. This documentation is also a Hyperlibrary. For the translations there is a separate Hyperbook for each translation. These individual Hyperbooks are combined in a Hyperlibrary. The menu item 'Translations' is the product of this linkage. All you have to do is create a `hyperlibrary.json` file. | Property | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | | name\* | Name of the library. | | library[] | An array of books and libraries. | | library[].src\* | Path to a book or library. | | library[].basePath\* | Overwrites the basePath of a book or library. | | library[].icon | An icon für the menu. | | library[].name | A name für the menu. If no name is provided, it will use the one defined in the `hyperbook.json` or `hyperlibrary.json` | | basePath | Defines the basePath of the library. | Here is an example of a `hyperlibrary.json`: ```json { "name": "Translations", "library": [ { "src": "de", "name": "Deutsch", "basePath": "de", "icon": "🇩🇪" }, { "src": "en", "name": "English", "basePath": "/", "icon": "🇬🇧" } ] } ``` You can also define translations. The translations will be selected based on the `lanauge` option in the corresponding `hyperbook.json` ```json { "name": { "en": "Translations", "de": "Übersetzungen" }, "library": [ { "src": "de", "name": { "en": "German", "de": "Deutsch" }, "basePath": "de", "icon": "🇩🇪" }, { "src": "en", "name": { "en": "English", "de": "Englisch" }, "basePath": "/", "icon": "🇬🇧" } ] } ``` # ABC Music # ABC Music ABC music is a simple text-based music notation system. It is easy to learn and use, and it is widely used in the folk music community. You can use it to write down tunes, and you can also use it to play tunes. The ABC music notation system is supported by the `abcjs` library, which is integrated into hyperbook. Learn more about ABC music at [abcnotation.com](http://abcnotation.com/). ## Attributes | Attribute | Description | Default | |---|---|---| | `editor` | Optional `abcjs` code fence flag that enables the interactive editor; add `editor` after `abcjs` in the fence info string | - | ````md ```abcjs X: 1 T: Cooley's M: 4/4 L: 1/8 K: Emin |:D2|EB{c}BA B2 EB|~B2 AB dBAG|FDAD BDAD|FDAD dAFD| ``` ```abcjs editor X:1 T: Cooley's Long M: 4/4 L: 1/8 R: reel K: Emin D2|:"Em"EB{c}BA B2 EB|~B2 AB dBAG|"D"FDAD BDAD|FDAD dAFD| "Em"EBBA B2 EB|B2 AB defg|"D"afe^c dBAF|1"Em"DEFD E2 D2:|2"Em"DEFD E2 gf|| |:"Em"eB B2 efge|eB B2 gedB|"D"A2 FA DAFA|A2 FA defg| "Em"eB B2 eBgB|eB B2 defg|"D"afe^c dBAF|1"Em"DEFD E2 gf:|2"Em"DEFD E4|] ``` ```` ```abcjs X: 1 T: Cooley's M: 4/4 L: 1/8 K: Emin |:D2|EB{c}BA B2 EB|~B2 AB dBAG|FDAD BDAD|FDAD dAFD| ``` ```abcjs editor X:1 T: Cooley's Long M: 4/4 L: 1/8 R: reel K: Emin D2|:"Em"EB{c}BA B2 EB|~B2 AB dBAG|"D"FDAD BDAD|FDAD dAFD| "Em"EBBA B2 EB|B2 AB defg|"D"afe^c dBAF|1"Em"DEFD E2 D2:|2"Em"DEFD E2 gf|| |:"Em"eB B2 efge|eB B2 gedB|"D"A2 FA DAFA|A2 FA defg| "Em"eB B2 eBgB|eB B2 defg|"D"afe^c dBAF|1"Em"DEFD E2 gf:|2"Em"DEFD E4|] ``` # Alert # Alert Alerts are a great way to get the attention of a reader. ## Attributes | Attribute | Description | Default | |---|---|---| | `error` / `success` / `info` / `warn` | Positional alert type, for example `:::alert{warn}` | neutral alert | | `color` | Custom color for the alert | - | | `label` | Custom label or emoji shown as the alert icon | - | ```md :::alert Default ::: :::alert{error} Error ::: :::alert{success} Success ::: :::alert{info} Info ::: :::alert{warn} Warn ::: ``` :::alert Default ::: :::alert{error} Error ::: :::alert{success} Success ::: :::alert{info} Info ::: :::alert{warn} Warn ::: ## Custom Alert Alerts with custom colors and labels can also be used. ```md :::alert{color="#FF00FF" label="💡"} My custom note ::: ``` :::alert{color="#FF00FF" label="💡"} My custom note ::: :::alert{info} It is good practice to place your custom alerts in a [snippet](/@/snippets). ::: # Archive # Archive All folders in the folder `archives` will be zipped when you build your hyperbook. You can use the `archive` directive to add a download button for these zipped folders. You only have to pass the folder name, everything else is handled for you. ## Attributes | Attribute | Description | Default | |---|---|---| | `name` | Folder name inside the `archives` directory | - | ```md :archive[Project Template]{name="project-1"} ``` :archive[Project Template]{name="project-1"} This is useful for providing project templates, solutions for a coding problem etc. # Audio # Audio The `audio` directive embeds an audio player with optional metadata and a thumbnail image. ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | URL or path to the audio file | - | | `thumbnail` | URL or path to a thumbnail image | - | | `title` | Title shown below the player | - | | `author` | Author shown below the player | - | | `position` | Position of the play button and thumbnail: `left` or `right` | `left` | ## Usage ```markdown ::audio{src="/Free_Test_Data_1MB_MP3.mp3" thumbnail="/group-people.png" title="Hallo" author="Max Mustermann"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" thumbnail="/group-people.png" title="Hallo" author="Max Mustermann" position="right"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" title="Hallo" author="Max Mustermann"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" position="right"} ``` ::audio{src="/Free_Test_Data_1MB_MP3.mp3" thumbnail="/group-people.png" title="Hallo" author="Max Mustermann"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" thumbnail="/group-people.png" title="Hallo" author="Max Mustermann" position="right"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" title="Hallo" author="Max Mustermann"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3"} ::audio{src="/Free_Test_Data_1MB_MP3.mp3" position="right"} # Blockflow # Blockflow [Blockflow](https://blockflow.openpatch.org) is a fork of Scratch for building guided tutorials. Hyperbook provides two directives for embedding Blockflow: a **player** for playing tutorials and an **editor** for creating them. ## Player The Blockflow Player embeds a player for playing Scratch-based guided tutorials. ### Player Attributes | Attribute | Description | Default | |---|---|---| | `src` | URL to the `.sb3` file | - | | `width` | Width of the player | `100%` | | `height` | Height of the player | `600px` | | `aspectRatio` | Aspect ratio of the player container | `4/3` | ```md ::blockflow-player{src="https://hyperbook.openpatch.org/elements/platformer.sb3"} ``` ::blockflow-player{src="https://hyperbook.openpatch.org/elements/platformer.sb3"} ## Editor The Blockflow Editor embeds an editor for creating Scratch-based guided tutorials. You can define tutorial steps directly in markdown, and the configuration is generated automatically at build time. ### Editor Attributes | Attribute | Description | Default | |---|---|---| | `title` | Title of the tutorial | - | | `src` | Path or URL to the `.sb3` Scratch project file | - | | `project` | URL to a `.json` project file; when set, inline configuration is ignored | - | | `width` | Width of the editor | `100%` | | `height` | Height of the editor | `700px` | | `aspectRatio` | Aspect ratio of the editor container | - | | `allowExtensions` | Allow Scratch extensions (`true` or `false`) | `true` | | `showCostumesTab` | Show or hide the Costumes/Backdrops tab (`true` or `false`) | `true` | | `showSoundsTab` | Show or hide the Sounds tab (`true` or `false`) | `true` | | `categories` | Comma-separated list of toolbox categories | - | | `blocks-` | Comma-separated list of blocks available in a category | - | ### Basic Usage ```md ::::blockflow-editor{title="My Tutorial" src="./platformer.sb3"} :::step{title="Welcome"} This is the first step of the tutorial. ::: :::step{title="Move the Cat"} Use the move block to move the cat 10 steps. ::: :::: ``` ::::blockflow-editor{title="My Tutorial" src="./platformer.sb3"} :::step{title="Welcome"} This is the first step of the tutorial. ::: :::step{title="Move the Cat"} Use the move block to move the cat 10 steps. ::: :::: ### Steps Each step is defined with a `:::step` directive inside the editor block. Steps support the following attributes: - **title**: The title of the step. - **image**: An optional image URL for the step. - **video**: An optional video URL for the step. The text content of the step is used as the step description. ```md ::::blockflow-editor{title="Tutorial" src="./project.sb3"} :::step{title="Welcome" image="./welcome.png"} Welcome to this tutorial! ::: :::step{title="Watch this" video="./demo.mp4"} Watch the video to see how it works. ::: :::: ``` ### Toolbox Configuration You can restrict which block categories and blocks are available in the editor using the `categories` and `blocks-` attributes. - **categories**: A comma-separated list of block categories to show, e.g. `"motion,events,control,operators"`. - **blocks-\**: A comma-separated list of block IDs available in a specific category, e.g. `blocks-motion="motion_movesteps,motion_turnright"`. ```md ::::blockflow-editor{title="Tutorial" src="./project.sb3" categories="motion,events,control" blocks-motion="motion_movesteps,motion_turnright,motion_turnleft"} :::step{title="Step 1"} Try using the motion blocks! ::: :::: ``` ### UI Configuration - **allowExtensions**: Whether to allow Scratch extensions. Set to `"false"` to disable. Defaults to `true`. - **showCostumesTab**: Whether to show the Costumes/Backdrops tab. Set to `"false"` to hide. Defaults to `true`. (Maps to `costumes.enabled` in the Blockflow config.) - **showSoundsTab**: Whether to show the Sounds tab. Set to `"false"` to hide. Defaults to `true`. (Maps to `sounds.enabled` in the Blockflow config.) ```md ::::blockflow-editor{title="Tutorial" src="./project.sb3" allowExtensions="false"} :::step{title="Step 1"} Let's get started! ::: :::: ``` ### Project File Instead of configuring the editor inline, you can provide a URL to a `.json` project file using the `project` attribute. This is useful when you want to reuse a configuration or manage it externally. You can use the [Blockflow Generator](https://blockflow.openpatch.org/generator.html) to create a project file. ```md ::::blockflow-editor{project="https://example.com/tutorial.json"} :::: ``` When `project` is set, all other configuration attributes (`title`, `src`, `allowExtensions`, `categories`, `blocks-*`) and `:::step` children are ignored. # Bookmarks # Bookmarks Every heading can be saved as a bookmark by clicking the icon on the right-hand-side. Bookmarks can be displayed everywhere, you just have to use this directive. ```md ::bookmarks ``` ::bookmarks ## Configuration You can disable bookmarks by setting it to false in the `hyperbook.json`. ```json { "elements": { "bookmarks": false } } ``` # Collapsible # Collapsible If you want to show additional content, which should not be seen at first glance, you can use a collapsible. Collapsibles are great for hints. They can also be nested. ## Attributes | Attribute | Description | Default | |---|---|---| | `title` | Title shown in the collapsible summary | - | | `id` | Optional shared id for syncing multiple collapsibles | auto-generated | ```md ::::collapsible{title="Hallo"} This is a panel :::collapsible{title="Nested"} This is a stacked collapsible ::: This is normal Test in-between. :::collapsible{title="With an Image"} ![](/test.jpg) :::: ``` ::::collapsible{title="Hallo"} This is a panel :::collapsible{title="Nested"} This is a stacked collapsible ::: This is normal Test in-between. :::collapsible{title="With an Image"} ![](/test.jpg) :::: You can sync collapsibles by using the same id. ```md :::collapsible{title="Collapse 0" id="synced"} This is a synced collapsible ::: Synced :::collapsible{title="Collapse 1" id="synced"} In sync with above. ::: ``` :::collapsible{title="Collapse 0" id="synced"} This is a synced collapsible ::: Synced :::collapsible{title="Collapse 1" id="synced"} In sync with above. ::: :::alert{warn} If you want to nest even further be sure to add more `:` to the outer levels. ::: # Download # Download You can show a download button. If the file is not available the button will be marked with "Offline". You should prefix local downloads with a slash. These will be linked to your public directory. You can also use external downloads from other sources. ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | URL or path to the file to download | - | The text inside `[]` is used as the download button label. ```md :download[Herunterladen]{src="/test.zip"} :download[Herunterladen]{src="/test.jpg"} ``` This download links to a file `test.zip` in the public folder. Unfortunately, the file is not available. :download[Herunterladen]{src="/test.zip"} This download links to a file `test.jpg` in the public folder. :download[Herunterladen]{src="/test.jpg"} :::alert{info} If your hyperbook uses the [basePath](/configuration/book) property, the basePath will automatically be prepended. ::: # Embed # Embed The embed element helps with content embedding. For example to embed a LearningApp you can use the following syntax: ```md ::embed{src="https://learningapps.org/watch?app=15767435"} ``` ::embed{src="https://learningapps.org/watch?app=15767435"} ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | URL that should be embedded | - | | `aspectRatio` | Aspect ratio, for example `16/9`, `4/3`, or `1/1` | - | | `height` | Height of the embed | `400px` | | `width` | Width of the embed | `100%` | | `allowFullscreen` | Allow fullscreen mode for the iframe | `true` | # Emoji # Emoji Just like in most chat apps you can insert an emoji by using its name. ```md :smiley: :apple: :penguin: ``` :smiley: :apple: :penguin: ## Consistent Emojis Emojis are drawn by the operating system of your reader, so the same emoji looks different on Windows, macOS, Android and Linux. Set `elements.emoji.style` to `twemoji` to replace them with [Twemoji](https://github.com/jdecked/twemoji) images, which look the same everywhere. ```json { "elements": { "emoji": { "style": "twemoji" } } } ``` This covers emojis in your content as well as the icons you configure, for example for links. Emojis in code blocks are never replaced, and only the emojis you actually use are copied into your build. Twemoji graphics are licensed CC-BY 4.0 by Twitter, Inc and other contributors. # GitHub Emoji Cheat Sheet This file lists all supported GitHub-style emoji shortcodes and their corresponding Unicode characters. | Emoji | Shortcode | |:------|:----------| | 👎 | `:-1:` | | 👍 | `:+1:` | | 💯 | `:100:` | | 🔢 | `:1234:` | | 🥇 | `:1st_place_medal:` | | 🥈 | `:2nd_place_medal:` | | 🥉 | `:3rd_place_medal:` | | 🎱 | `:8ball:` | | 🅰 | `:a:` | | 🆎 | `:ab:` | | 🧮 | `:abacus:` | | 🔤 | `:abc:` | | 🔡 | `:abcd:` | | 🉑 | `:accept:` | | 🪗 | `:accordion:` | | 🩹 | `:adhesive_bandage:` | | 🧑 | `:adult:` | | 🚡 | `:aerial_tramway:` | | 🇦‍🇫 | `:afghanistan:` | | ✈ | `:airplane:` | | 🇦‍🇽 | `:aland_islands:` | | ⏰ | `:alarm_clock:` | | 🇦‍🇱 | `:albania:` | | ⚗ | `:alembic:` | | 🇩‍🇿 | `:algeria:` | | 👽 | `:alien:` | | 🚑 | `:ambulance:` | | 🇦‍🇸 | `:american_samoa:` | | 🏺 | `:amphora:` | | 🫀 | `:anatomical_heart:` | | ⚓ | `:anchor:` | | 🇦‍🇩 | `:andorra:` | | 👼 | `:angel:` | | 💢 | `:anger:` | | 🇦‍🇴 | `:angola:` | | 😠 | `:angry:` | | 🇦‍🇮 | `:anguilla:` | | 😧 | `:anguished:` | | 🐜 | `:ant:` | | 🇦‍🇶 | `:antarctica:` | | 🇦‍🇬 | `:antigua_barbuda:` | | 🍎 | `:apple:` | | ♒ | `:aquarius:` | | 🇦‍🇷 | `:argentina:` | | ♈ | `:aries:` | | 🇦‍🇲 | `:armenia:` | | ◀ | `:arrow_backward:` | | ⏬ | `:arrow_double_down:` | | ⏫ | `:arrow_double_up:` | | ⬇ | `:arrow_down:` | | 🔽 | `:arrow_down_small:` | | ▶ | `:arrow_forward:` | | ⤵ | `:arrow_heading_down:` | | ⤴ | `:arrow_heading_up:` | | ⬅ | `:arrow_left:` | | ↙ | `:arrow_lower_left:` | | ↘ | `:arrow_lower_right:` | | ➡ | `:arrow_right:` | | ↪ | `:arrow_right_hook:` | | ⬆ | `:arrow_up:` | | ↕ | `:arrow_up_down:` | | 🔼 | `:arrow_up_small:` | | ↖ | `:arrow_upper_left:` | | ↗ | `:arrow_upper_right:` | | 🔃 | `:arrows_clockwise:` | | 🔄 | `:arrows_counterclockwise:` | | 🎨 | `:art:` | | 🚛 | `:articulated_lorry:` | | 🛰 | `:artificial_satellite:` | | 🧑‍🎨 | `:artist:` | | 🇦‍🇼 | `:aruba:` | | 🇦‍🇨 | `:ascension_island:` | | *‍⃣ | `:asterisk:` | | 😲 | `:astonished:` | | 🧑‍🚀 | `:astronaut:` | | 👟 | `:athletic_shoe:` | | 🏧 | `:atm:` | | ⚛ | `:atom_symbol:` | | 🇦‍🇺 | `:australia:` | | 🇦‍🇹 | `:austria:` | | 🛺 | `:auto_rickshaw:` | | 🥑 | `:avocado:` | | 🪓 | `:axe:` | | 🇦‍🇿 | `:azerbaijan:` | | 🅱 | `:b:` | | 👶 | `:baby:` | | 🍼 | `:baby_bottle:` | | 🐤 | `:baby_chick:` | | 🚼 | `:baby_symbol:` | | 🔙 | `:back:` | | 🥓 | `:bacon:` | | 🦡 | `:badger:` | | 🏸 | `:badminton:` | | 🥯 | `:bagel:` | | 🛄 | `:baggage_claim:` | | 🥖 | `:baguette_bread:` | | 🇧‍🇸 | `:bahamas:` | | 🇧‍🇭 | `:bahrain:` | | ⚖ | `:balance_scale:` | | 👨‍🦲 | `:bald_man:` | | 👩‍🦲 | `:bald_woman:` | | 🩰 | `:ballet_shoes:` | | 🎈 | `:balloon:` | | 🗳 | `:ballot_box:` | | ☑ | `:ballot_box_with_check:` | | 🎍 | `:bamboo:` | | 🍌 | `:banana:` | | ‼ | `:bangbang:` | | 🇧‍🇩 | `:bangladesh:` | | 🪕 | `:banjo:` | | 🏦 | `:bank:` | | 📊 | `:bar_chart:` | | 🇧‍🇧 | `:barbados:` | | 💈 | `:barber:` | | ⚾ | `:baseball:` | | 🧺 | `:basket:` | | 🏀 | `:basketball:` | | ⛹‍♂ | `:basketball_man:` | | ⛹‍♀ | `:basketball_woman:` | | 🦇 | `:bat:` | | 🛀 | `:bath:` | | 🛁 | `:bathtub:` | | 🔋 | `:battery:` | | 🏖 | `:beach_umbrella:` | | 🫘 | `:beans:` | | 🐻 | `:bear:` | | 🧔 | `:bearded_person:` | | 🦫 | `:beaver:` | | 🛏 | `:bed:` | | 🐝 | `:bee:` | | 🍺 | `:beer:` | | 🍻 | `:beers:` | | 🪲 | `:beetle:` | | 🔰 | `:beginner:` | | 🇧‍🇾 | `:belarus:` | | 🇧‍🇪 | `:belgium:` | | 🇧‍🇿 | `:belize:` | | 🔔 | `:bell:` | | 🫑 | `:bell_pepper:` | | 🛎 | `:bellhop_bell:` | | 🇧‍🇯 | `:benin:` | | 🍱 | `:bento:` | | 🇧‍🇲 | `:bermuda:` | | 🧃 | `:beverage_box:` | | 🇧‍🇹 | `:bhutan:` | | 🚴 | `:bicyclist:` | | 🚲 | `:bike:` | | 🚴‍♂ | `:biking_man:` | | 🚴‍♀ | `:biking_woman:` | | 👙 | `:bikini:` | | 🧢 | `:billed_cap:` | | ☣ | `:biohazard:` | | 🐦 | `:bird:` | | 🎂 | `:birthday:` | | 🦬 | `:bison:` | | 🫦 | `:biting_lip:` | | 🐦‍⬛ | `:black_bird:` | | 🐈‍⬛ | `:black_cat:` | | ⚫ | `:black_circle:` | | 🏴 | `:black_flag:` | | 🖤 | `:black_heart:` | | 🃏 | `:black_joker:` | | ⬛ | `:black_large_square:` | | ◾ | `:black_medium_small_square:` | | ◼ | `:black_medium_square:` | | ✒ | `:black_nib:` | | ▪ | `:black_small_square:` | | 🔲 | `:black_square_button:` | | 👱‍♂ | `:blond_haired_man:` | | 👱 | `:blond_haired_person:` | | 👱‍♀ | `:blond_haired_woman:` | | 👱‍♀ | `:blonde_woman:` | | 🌼 | `:blossom:` | | 🐡 | `:blowfish:` | | 📘 | `:blue_book:` | | 🚙 | `:blue_car:` | | 💙 | `:blue_heart:` | | 🟦 | `:blue_square:` | | 🫐 | `:blueberries:` | | 😊 | `:blush:` | | 🐗 | `:boar:` | | ⛵ | `:boat:` | | 🇧‍🇴 | `:bolivia:` | | 💣 | `:bomb:` | | 🦴 | `:bone:` | | 📖 | `:book:` | | 🔖 | `:bookmark:` | | 📑 | `:bookmark_tabs:` | | 📚 | `:books:` | | 💥 | `:boom:` | | 🪃 | `:boomerang:` | | 👢 | `:boot:` | | 🇧‍🇦 | `:bosnia_herzegovina:` | | 🇧‍🇼 | `:botswana:` | | ⛹‍♂ | `:bouncing_ball_man:` | | ⛹ | `:bouncing_ball_person:` | | ⛹‍♀ | `:bouncing_ball_woman:` | | 💐 | `:bouquet:` | | 🇧‍🇻 | `:bouvet_island:` | | 🙇 | `:bow:` | | 🏹 | `:bow_and_arrow:` | | 🙇‍♂ | `:bowing_man:` | | 🙇‍♀ | `:bowing_woman:` | | 🥣 | `:bowl_with_spoon:` | | 🎳 | `:bowling:` | | 🥊 | `:boxing_glove:` | | 👦 | `:boy:` | | 🧠 | `:brain:` | | 🇧‍🇷 | `:brazil:` | | 🍞 | `:bread:` | | 🤱 | `:breast_feeding:` | | 🧱 | `:bricks:` | | 👰‍♀ | `:bride_with_veil:` | | 🌉 | `:bridge_at_night:` | | 💼 | `:briefcase:` | | 🇮‍🇴 | `:british_indian_ocean_territory:` | | 🇻‍🇬 | `:british_virgin_islands:` | | 🥦 | `:broccoli:` | | 💔 | `:broken_heart:` | | 🧹 | `:broom:` | | 🟤 | `:brown_circle:` | | 🤎 | `:brown_heart:` | | 🟫 | `:brown_square:` | | 🇧‍🇳 | `:brunei:` | | 🧋 | `:bubble_tea:` | | 🫧 | `:bubbles:` | | 🪣 | `:bucket:` | | 🐛 | `:bug:` | | 🏗 | `:building_construction:` | | 💡 | `:bulb:` | | 🇧‍🇬 | `:bulgaria:` | | 🚅 | `:bullettrain_front:` | | 🚄 | `:bullettrain_side:` | | 🇧‍🇫 | `:burkina_faso:` | | 🌯 | `:burrito:` | | 🇧‍🇮 | `:burundi:` | | 🚌 | `:bus:` | | 🕴 | `:business_suit_levitating:` | | 🚏 | `:busstop:` | | 👤 | `:bust_in_silhouette:` | | 👥 | `:busts_in_silhouette:` | | 🧈 | `:butter:` | | 🦋 | `:butterfly:` | | 🌵 | `:cactus:` | | 🍰 | `:cake:` | | 📆 | `:calendar:` | | 🤙 | `:call_me_hand:` | | 📲 | `:calling:` | | 🇰‍🇭 | `:cambodia:` | | 🐫 | `:camel:` | | 📷 | `:camera:` | | 📸 | `:camera_flash:` | | 🇨‍🇲 | `:cameroon:` | | 🏕 | `:camping:` | | 🇨‍🇦 | `:canada:` | | 🇮‍🇨 | `:canary_islands:` | | ♋ | `:cancer:` | | 🕯 | `:candle:` | | 🍬 | `:candy:` | | 🥫 | `:canned_food:` | | 🛶 | `:canoe:` | | 🇨‍🇻 | `:cape_verde:` | | 🔠 | `:capital_abcd:` | | ♑ | `:capricorn:` | | 🚗 | `:car:` | | 🗃 | `:card_file_box:` | | 📇 | `:card_index:` | | 🗂 | `:card_index_dividers:` | | 🇧‍🇶 | `:caribbean_netherlands:` | | 🎠 | `:carousel_horse:` | | 🪚 | `:carpentry_saw:` | | 🥕 | `:carrot:` | | 🤸 | `:cartwheeling:` | | 🐱 | `:cat:` | | 🐈 | `:cat2:` | | 🇰‍🇾 | `:cayman_islands:` | | 💿 | `:cd:` | | 🇨‍🇫 | `:central_african_republic:` | | 🇪‍🇦 | `:ceuta_melilla:` | | 🇹‍🇩 | `:chad:` | | ⛓ | `:chains:` | | 🪑 | `:chair:` | | 🍾 | `:champagne:` | | 💹 | `:chart:` | | 📉 | `:chart_with_downwards_trend:` | | 📈 | `:chart_with_upwards_trend:` | | 🏁 | `:checkered_flag:` | | 🧀 | `:cheese:` | | 🍒 | `:cherries:` | | 🌸 | `:cherry_blossom:` | | ♟ | `:chess_pawn:` | | 🌰 | `:chestnut:` | | 🐔 | `:chicken:` | | 🧒 | `:child:` | | 🚸 | `:children_crossing:` | | 🇨‍🇱 | `:chile:` | | 🐿 | `:chipmunk:` | | 🍫 | `:chocolate_bar:` | | 🥢 | `:chopsticks:` | | 🇨‍🇽 | `:christmas_island:` | | 🎄 | `:christmas_tree:` | | ⛪ | `:church:` | | 🎦 | `:cinema:` | | 🎪 | `:circus_tent:` | | 🌇 | `:city_sunrise:` | | 🌆 | `:city_sunset:` | | 🏙 | `:cityscape:` | | 🆑 | `:cl:` | | 🗜 | `:clamp:` | | 👏 | `:clap:` | | 🎬 | `:clapper:` | | 🏛 | `:classical_building:` | | 🧗 | `:climbing:` | | 🧗‍♂ | `:climbing_man:` | | 🧗‍♀ | `:climbing_woman:` | | 🥂 | `:clinking_glasses:` | | 📋 | `:clipboard:` | | 🇨‍🇵 | `:clipperton_island:` | | 🕐 | `:clock1:` | | 🕙 | `:clock10:` | | 🕥 | `:clock1030:` | | 🕚 | `:clock11:` | | 🕦 | `:clock1130:` | | 🕛 | `:clock12:` | | 🕧 | `:clock1230:` | | 🕜 | `:clock130:` | | 🕑 | `:clock2:` | | 🕝 | `:clock230:` | | 🕒 | `:clock3:` | | 🕞 | `:clock330:` | | 🕓 | `:clock4:` | | 🕟 | `:clock430:` | | 🕔 | `:clock5:` | | 🕠 | `:clock530:` | | 🕕 | `:clock6:` | | 🕡 | `:clock630:` | | 🕖 | `:clock7:` | | 🕢 | `:clock730:` | | 🕗 | `:clock8:` | | 🕣 | `:clock830:` | | 🕘 | `:clock9:` | | 🕤 | `:clock930:` | | 📕 | `:closed_book:` | | 🔐 | `:closed_lock_with_key:` | | 🌂 | `:closed_umbrella:` | | ☁ | `:cloud:` | | 🌩 | `:cloud_with_lightning:` | | ⛈ | `:cloud_with_lightning_and_rain:` | | 🌧 | `:cloud_with_rain:` | | 🌨 | `:cloud_with_snow:` | | 🤡 | `:clown_face:` | | ♣ | `:clubs:` | | 🇨‍🇳 | `:cn:` | | 🧥 | `:coat:` | | 🪳 | `:cockroach:` | | 🍸 | `:cocktail:` | | 🥥 | `:coconut:` | | 🇨‍🇨 | `:cocos_islands:` | | ☕ | `:coffee:` | | ⚰ | `:coffin:` | | 🪙 | `:coin:` | | 🥶 | `:cold_face:` | | 😰 | `:cold_sweat:` | | 💥 | `:collision:` | | 🇨‍🇴 | `:colombia:` | | ☄ | `:comet:` | | 🇰‍🇲 | `:comoros:` | | 🧭 | `:compass:` | | 💻 | `:computer:` | | 🖱 | `:computer_mouse:` | | 🎊 | `:confetti_ball:` | | 😖 | `:confounded:` | | 😕 | `:confused:` | | 🇨‍🇬 | `:congo_brazzaville:` | | 🇨‍🇩 | `:congo_kinshasa:` | | ㊗ | `:congratulations:` | | 🚧 | `:construction:` | | 👷 | `:construction_worker:` | | 👷‍♂ | `:construction_worker_man:` | | 👷‍♀ | `:construction_worker_woman:` | | 🎛 | `:control_knobs:` | | 🏪 | `:convenience_store:` | | 🧑‍🍳 | `:cook:` | | 🇨‍🇰 | `:cook_islands:` | | 🍪 | `:cookie:` | | 🆒 | `:cool:` | | 👮 | `:cop:` | | © | `:copyright:` | | 🪸 | `:coral:` | | 🌽 | `:corn:` | | 🇨‍🇷 | `:costa_rica:` | | 🇨‍🇮 | `:cote_divoire:` | | 🛋 | `:couch_and_lamp:` | | 👫 | `:couple:` | | 💑 | `:couple_with_heart:` | | 👨‍❤‍👨 | `:couple_with_heart_man_man:` | | 👩‍❤‍👨 | `:couple_with_heart_woman_man:` | | 👩‍❤‍👩 | `:couple_with_heart_woman_woman:` | | 💏 | `:couplekiss:` | | 👨‍❤‍💋‍👨 | `:couplekiss_man_man:` | | 👩‍❤‍💋‍👨 | `:couplekiss_man_woman:` | | 👩‍❤‍💋‍👩 | `:couplekiss_woman_woman:` | | 🐮 | `:cow:` | | 🐄 | `:cow2:` | | 🤠 | `:cowboy_hat_face:` | | 🦀 | `:crab:` | | 🖍 | `:crayon:` | | 💳 | `:credit_card:` | | 🌙 | `:crescent_moon:` | | 🦗 | `:cricket:` | | 🏏 | `:cricket_game:` | | 🇭‍🇷 | `:croatia:` | | 🐊 | `:crocodile:` | | 🥐 | `:croissant:` | | 🤞 | `:crossed_fingers:` | | 🎌 | `:crossed_flags:` | | ⚔ | `:crossed_swords:` | | 👑 | `:crown:` | | 🩼 | `:crutch:` | | 😢 | `:cry:` | | 😿 | `:crying_cat_face:` | | 🔮 | `:crystal_ball:` | | 🇨‍🇺 | `:cuba:` | | 🥒 | `:cucumber:` | | 🥤 | `:cup_with_straw:` | | 🧁 | `:cupcake:` | | 💘 | `:cupid:` | | 🇨‍🇼 | `:curacao:` | | 🥌 | `:curling_stone:` | | 👨‍🦱 | `:curly_haired_man:` | | 👩‍🦱 | `:curly_haired_woman:` | | ➰ | `:curly_loop:` | | 💱 | `:currency_exchange:` | | 🍛 | `:curry:` | | 🤬 | `:cursing_face:` | | 🍮 | `:custard:` | | 🛃 | `:customs:` | | 🥩 | `:cut_of_meat:` | | 🌀 | `:cyclone:` | | 🇨‍🇾 | `:cyprus:` | | 🇨‍🇿 | `:czech_republic:` | | 🗡 | `:dagger:` | | 💃 | `:dancer:` | | 👯 | `:dancers:` | | 👯‍♂ | `:dancing_men:` | | 👯‍♀ | `:dancing_women:` | | 🍡 | `:dango:` | | 🕶 | `:dark_sunglasses:` | | 🎯 | `:dart:` | | 💨 | `:dash:` | | 📅 | `:date:` | | 🇩‍🇪 | `:de:` | | 🧏‍♂ | `:deaf_man:` | | 🧏 | `:deaf_person:` | | 🧏‍♀ | `:deaf_woman:` | | 🌳 | `:deciduous_tree:` | | 🦌 | `:deer:` | | 🇩‍🇰 | `:denmark:` | | 🏬 | `:department_store:` | | 🏚 | `:derelict_house:` | | 🏜 | `:desert:` | | 🏝 | `:desert_island:` | | 🖥 | `:desktop_computer:` | | 🕵 | `:detective:` | | 💠 | `:diamond_shape_with_a_dot_inside:` | | ♦ | `:diamonds:` | | 🇩‍🇬 | `:diego_garcia:` | | 😞 | `:disappointed:` | | 😥 | `:disappointed_relieved:` | | 🥸 | `:disguised_face:` | | 🤿 | `:diving_mask:` | | 🪔 | `:diya_lamp:` | | 💫 | `:dizzy:` | | 😵 | `:dizzy_face:` | | 🇩‍🇯 | `:djibouti:` | | 🧬 | `:dna:` | | 🚯 | `:do_not_litter:` | | 🦤 | `:dodo:` | | 🐶 | `:dog:` | | 🐕 | `:dog2:` | | 💵 | `:dollar:` | | 🎎 | `:dolls:` | | 🐬 | `:dolphin:` | | 🇩‍🇲 | `:dominica:` | | 🇩‍🇴 | `:dominican_republic:` | | 🫏 | `:donkey:` | | 🚪 | `:door:` | | 🫥 | `:dotted_line_face:` | | 🍩 | `:doughnut:` | | 🕊 | `:dove:` | | 🐉 | `:dragon:` | | 🐲 | `:dragon_face:` | | 👗 | `:dress:` | | 🐪 | `:dromedary_camel:` | | 🤤 | `:drooling_face:` | | 🩸 | `:drop_of_blood:` | | 💧 | `:droplet:` | | 🥁 | `:drum:` | | 🦆 | `:duck:` | | 🥟 | `:dumpling:` | | 📀 | `:dvd:` | | 📧 | `:e-mail:` | | 🦅 | `:eagle:` | | 👂 | `:ear:` | | 🌾 | `:ear_of_rice:` | | 🦻 | `:ear_with_hearing_aid:` | | 🌍 | `:earth_africa:` | | 🌎 | `:earth_americas:` | | 🌏 | `:earth_asia:` | | 🇪‍🇨 | `:ecuador:` | | 🥚 | `:egg:` | | 🍆 | `:eggplant:` | | 🇪‍🇬 | `:egypt:` | | 8‍⃣ | `:eight:` | | ✴ | `:eight_pointed_black_star:` | | ✳ | `:eight_spoked_asterisk:` | | ⏏ | `:eject_button:` | | 🇸‍🇻 | `:el_salvador:` | | 🔌 | `:electric_plug:` | | 🐘 | `:elephant:` | | 🛗 | `:elevator:` | | 🧝 | `:elf:` | | 🧝‍♂ | `:elf_man:` | | 🧝‍♀ | `:elf_woman:` | | 📧 | `:email:` | | 🪹 | `:empty_nest:` | | 🔚 | `:end:` | | 🏴‍󠁧‍󠁢‍󠁥‍󠁮‍󠁧‍󠁿 | `:england:` | | ✉ | `:envelope:` | | 📩 | `:envelope_with_arrow:` | | 🇬‍🇶 | `:equatorial_guinea:` | | 🇪‍🇷 | `:eritrea:` | | 🇪‍🇸 | `:es:` | | 🇪‍🇪 | `:estonia:` | | 🇪‍🇹 | `:ethiopia:` | | 🇪‍🇺 | `:eu:` | | 💶 | `:euro:` | | 🏰 | `:european_castle:` | | 🏤 | `:european_post_office:` | | 🇪‍🇺 | `:european_union:` | | 🌲 | `:evergreen_tree:` | | ❗ | `:exclamation:` | | 🤯 | `:exploding_head:` | | 😑 | `:expressionless:` | | 👁 | `:eye:` | | 👁‍🗨 | `:eye_speech_bubble:` | | 👓 | `:eyeglasses:` | | 👀 | `:eyes:` | | 😮‍💨 | `:face_exhaling:` | | 🥹 | `:face_holding_back_tears:` | | 😶‍🌫 | `:face_in_clouds:` | | 🫤 | `:face_with_diagonal_mouth:` | | 🤕 | `:face_with_head_bandage:` | | 🫢 | `:face_with_open_eyes_and_hand_over_mouth:` | | 🫣 | `:face_with_peeking_eye:` | | 😵‍💫 | `:face_with_spiral_eyes:` | | 🤒 | `:face_with_thermometer:` | | 🤦 | `:facepalm:` | | 👊 | `:facepunch:` | | 🏭 | `:factory:` | | 🧑‍🏭 | `:factory_worker:` | | 🧚 | `:fairy:` | | 🧚‍♂ | `:fairy_man:` | | 🧚‍♀ | `:fairy_woman:` | | 🧆 | `:falafel:` | | 🇫‍🇰 | `:falkland_islands:` | | 🍂 | `:fallen_leaf:` | | 👪 | `:family:` | | 👨‍👦 | `:family_man_boy:` | | 👨‍👦‍👦 | `:family_man_boy_boy:` | | 👨‍👧 | `:family_man_girl:` | | 👨‍👧‍👦 | `:family_man_girl_boy:` | | 👨‍👧‍👧 | `:family_man_girl_girl:` | | 👨‍👨‍👦 | `:family_man_man_boy:` | | 👨‍👨‍👦‍👦 | `:family_man_man_boy_boy:` | | 👨‍👨‍👧 | `:family_man_man_girl:` | | 👨‍👨‍👧‍👦 | `:family_man_man_girl_boy:` | | 👨‍👨‍👧‍👧 | `:family_man_man_girl_girl:` | | 👨‍👩‍👦 | `:family_man_woman_boy:` | | 👨‍👩‍👦‍👦 | `:family_man_woman_boy_boy:` | | 👨‍👩‍👧 | `:family_man_woman_girl:` | | 👨‍👩‍👧‍👦 | `:family_man_woman_girl_boy:` | | 👨‍👩‍👧‍👧 | `:family_man_woman_girl_girl:` | | 👩‍👦 | `:family_woman_boy:` | | 👩‍👦‍👦 | `:family_woman_boy_boy:` | | 👩‍👧 | `:family_woman_girl:` | | 👩‍👧‍👦 | `:family_woman_girl_boy:` | | 👩‍👧‍👧 | `:family_woman_girl_girl:` | | 👩‍👩‍👦 | `:family_woman_woman_boy:` | | 👩‍👩‍👦‍👦 | `:family_woman_woman_boy_boy:` | | 👩‍👩‍👧 | `:family_woman_woman_girl:` | | 👩‍👩‍👧‍👦 | `:family_woman_woman_girl_boy:` | | 👩‍👩‍👧‍👧 | `:family_woman_woman_girl_girl:` | | 🧑‍🌾 | `:farmer:` | | 🇫‍🇴 | `:faroe_islands:` | | ⏩ | `:fast_forward:` | | 📠 | `:fax:` | | 😨 | `:fearful:` | | 🪶 | `:feather:` | | 🐾 | `:feet:` | | 🕵‍♀ | `:female_detective:` | | ♀ | `:female_sign:` | | 🎡 | `:ferris_wheel:` | | ⛴ | `:ferry:` | | 🏑 | `:field_hockey:` | | 🇫‍🇯 | `:fiji:` | | 🗄 | `:file_cabinet:` | | 📁 | `:file_folder:` | | 📽 | `:film_projector:` | | 🎞 | `:film_strip:` | | 🇫‍🇮 | `:finland:` | | 🔥 | `:fire:` | | 🚒 | `:fire_engine:` | | 🧯 | `:fire_extinguisher:` | | 🧨 | `:firecracker:` | | 🧑‍🚒 | `:firefighter:` | | 🎆 | `:fireworks:` | | 🌓 | `:first_quarter_moon:` | | 🌛 | `:first_quarter_moon_with_face:` | | 🐟 | `:fish:` | | 🍥 | `:fish_cake:` | | 🎣 | `:fishing_pole_and_fish:` | | ✊ | `:fist:` | | 🤛 | `:fist_left:` | | 👊 | `:fist_oncoming:` | | ✊ | `:fist_raised:` | | 🤜 | `:fist_right:` | | 5‍⃣ | `:five:` | | 🎏 | `:flags:` | | 🦩 | `:flamingo:` | | 🔦 | `:flashlight:` | | 🥿 | `:flat_shoe:` | | 🫓 | `:flatbread:` | | ⚜ | `:fleur_de_lis:` | | 🛬 | `:flight_arrival:` | | 🛫 | `:flight_departure:` | | 🐬 | `:flipper:` | | 💾 | `:floppy_disk:` | | 🎴 | `:flower_playing_cards:` | | 😳 | `:flushed:` | | 🪈 | `:flute:` | | 🪰 | `:fly:` | | 🥏 | `:flying_disc:` | | 🛸 | `:flying_saucer:` | | 🌫 | `:fog:` | | 🌁 | `:foggy:` | | 🪭 | `:folding_hand_fan:` | | 🫕 | `:fondue:` | | 🦶 | `:foot:` | | 🏈 | `:football:` | | 👣 | `:footprints:` | | 🍴 | `:fork_and_knife:` | | 🥠 | `:fortune_cookie:` | | ⛲ | `:fountain:` | | 🖋 | `:fountain_pen:` | | 4‍⃣ | `:four:` | | 🍀 | `:four_leaf_clover:` | | 🦊 | `:fox_face:` | | 🇫‍🇷 | `:fr:` | | 🖼 | `:framed_picture:` | | 🆓 | `:free:` | | 🇬‍🇫 | `:french_guiana:` | | 🇵‍🇫 | `:french_polynesia:` | | 🇹‍🇫 | `:french_southern_territories:` | | 🍳 | `:fried_egg:` | | 🍤 | `:fried_shrimp:` | | 🍟 | `:fries:` | | 🐸 | `:frog:` | | 😦 | `:frowning:` | | ☹ | `:frowning_face:` | | 🙍‍♂ | `:frowning_man:` | | 🙍 | `:frowning_person:` | | 🙍‍♀ | `:frowning_woman:` | | 🖕 | `:fu:` | | ⛽ | `:fuelpump:` | | 🌕 | `:full_moon:` | | 🌝 | `:full_moon_with_face:` | | ⚱ | `:funeral_urn:` | | 🇬‍🇦 | `:gabon:` | | 🇬‍🇲 | `:gambia:` | | 🎲 | `:game_die:` | | 🧄 | `:garlic:` | | 🇬‍🇧 | `:gb:` | | ⚙ | `:gear:` | | 💎 | `:gem:` | | ♊ | `:gemini:` | | 🧞 | `:genie:` | | 🧞‍♂ | `:genie_man:` | | 🧞‍♀ | `:genie_woman:` | | 🇬‍🇪 | `:georgia:` | | 🇬‍🇭 | `:ghana:` | | 👻 | `:ghost:` | | 🇬‍🇮 | `:gibraltar:` | | 🎁 | `:gift:` | | 💝 | `:gift_heart:` | | 🫚 | `:ginger_root:` | | 🦒 | `:giraffe:` | | 👧 | `:girl:` | | 🌐 | `:globe_with_meridians:` | | 🧤 | `:gloves:` | | 🥅 | `:goal_net:` | | 🐐 | `:goat:` | | 🥽 | `:goggles:` | | ⛳ | `:golf:` | | 🏌 | `:golfing:` | | 🏌‍♂ | `:golfing_man:` | | 🏌‍♀ | `:golfing_woman:` | | 🪿 | `:goose:` | | 🦍 | `:gorilla:` | | 🍇 | `:grapes:` | | 🇬‍🇷 | `:greece:` | | 🍏 | `:green_apple:` | | 📗 | `:green_book:` | | 🟢 | `:green_circle:` | | 💚 | `:green_heart:` | | 🥗 | `:green_salad:` | | 🟩 | `:green_square:` | | 🇬‍🇱 | `:greenland:` | | 🇬‍🇩 | `:grenada:` | | ❕ | `:grey_exclamation:` | | 🩶 | `:grey_heart:` | | ❔ | `:grey_question:` | | 😬 | `:grimacing:` | | 😁 | `:grin:` | | 😀 | `:grinning:` | | 🇬‍🇵 | `:guadeloupe:` | | 🇬‍🇺 | `:guam:` | | 💂 | `:guard:` | | 💂‍♂ | `:guardsman:` | | 💂‍♀ | `:guardswoman:` | | 🇬‍🇹 | `:guatemala:` | | 🇬‍🇬 | `:guernsey:` | | 🦮 | `:guide_dog:` | | 🇬‍🇳 | `:guinea:` | | 🇬‍🇼 | `:guinea_bissau:` | | 🎸 | `:guitar:` | | 🔫 | `:gun:` | | 🇬‍🇾 | `:guyana:` | | 🪮 | `:hair_pick:` | | 💇 | `:haircut:` | | 💇‍♂ | `:haircut_man:` | | 💇‍♀ | `:haircut_woman:` | | 🇭‍🇹 | `:haiti:` | | 🍔 | `:hamburger:` | | 🔨 | `:hammer:` | | ⚒ | `:hammer_and_pick:` | | 🛠 | `:hammer_and_wrench:` | | 🪬 | `:hamsa:` | | 🐹 | `:hamster:` | | ✋ | `:hand:` | | 🤭 | `:hand_over_mouth:` | | 🫰 | `:hand_with_index_finger_and_thumb_crossed:` | | 👜 | `:handbag:` | | 🤾 | `:handball_person:` | | 🤝 | `:handshake:` | | 💩 | `:hankey:` | | #‍⃣ | `:hash:` | | 🐥 | `:hatched_chick:` | | 🐣 | `:hatching_chick:` | | 🎧 | `:headphones:` | | 🪦 | `:headstone:` | | 🧑‍⚕ | `:health_worker:` | | 🙉 | `:hear_no_evil:` | | 🇭‍🇲 | `:heard_mcdonald_islands:` | | ❤ | `:heart:` | | 💟 | `:heart_decoration:` | | 😍 | `:heart_eyes:` | | 😻 | `:heart_eyes_cat:` | | 🫶 | `:heart_hands:` | | ❤‍🔥 | `:heart_on_fire:` | | 💓 | `:heartbeat:` | | 💗 | `:heartpulse:` | | ♥ | `:hearts:` | | ✔ | `:heavy_check_mark:` | | ➗ | `:heavy_division_sign:` | | 💲 | `:heavy_dollar_sign:` | | 🟰 | `:heavy_equals_sign:` | | ❗ | `:heavy_exclamation_mark:` | | ❣ | `:heavy_heart_exclamation:` | | ➖ | `:heavy_minus_sign:` | | ✖ | `:heavy_multiplication_x:` | | ➕ | `:heavy_plus_sign:` | | 🦔 | `:hedgehog:` | | 🚁 | `:helicopter:` | | 🌿 | `:herb:` | | 🌺 | `:hibiscus:` | | 🔆 | `:high_brightness:` | | 👠 | `:high_heel:` | | 🥾 | `:hiking_boot:` | | 🛕 | `:hindu_temple:` | | 🦛 | `:hippopotamus:` | | 🔪 | `:hocho:` | | 🕳 | `:hole:` | | 🇭‍🇳 | `:honduras:` | | 🍯 | `:honey_pot:` | | 🐝 | `:honeybee:` | | 🇭‍🇰 | `:hong_kong:` | | 🪝 | `:hook:` | | 🐴 | `:horse:` | | 🏇 | `:horse_racing:` | | 🏥 | `:hospital:` | | 🥵 | `:hot_face:` | | 🌶 | `:hot_pepper:` | | 🌭 | `:hotdog:` | | 🏨 | `:hotel:` | | ♨ | `:hotsprings:` | | ⌛ | `:hourglass:` | | ⏳ | `:hourglass_flowing_sand:` | | 🏠 | `:house:` | | 🏡 | `:house_with_garden:` | | 🏘 | `:houses:` | | 🤗 | `:hugs:` | | 🇭‍🇺 | `:hungary:` | | 😯 | `:hushed:` | | 🛖 | `:hut:` | | 🪻 | `:hyacinth:` | | 🍨 | `:ice_cream:` | | 🧊 | `:ice_cube:` | | 🏒 | `:ice_hockey:` | | ⛸ | `:ice_skate:` | | 🍦 | `:icecream:` | | 🇮‍🇸 | `:iceland:` | | 🆔 | `:id:` | | 🪪 | `:identification_card:` | | 🉐 | `:ideograph_advantage:` | | 👿 | `:imp:` | | 📥 | `:inbox_tray:` | | 📨 | `:incoming_envelope:` | | 🫵 | `:index_pointing_at_the_viewer:` | | 🇮‍🇳 | `:india:` | | 🇮‍🇩 | `:indonesia:` | | ♾ | `:infinity:` | | 💁 | `:information_desk_person:` | | ℹ | `:information_source:` | | 😇 | `:innocent:` | | ⁉ | `:interrobang:` | | 📱 | `:iphone:` | | 🇮‍🇷 | `:iran:` | | 🇮‍🇶 | `:iraq:` | | 🇮‍🇪 | `:ireland:` | | 🇮‍🇲 | `:isle_of_man:` | | 🇮‍🇱 | `:israel:` | | 🇮‍🇹 | `:it:` | | 🏮 | `:izakaya_lantern:` | | 🎃 | `:jack_o_lantern:` | | 🇯‍🇲 | `:jamaica:` | | 🗾 | `:japan:` | | 🏯 | `:japanese_castle:` | | 👺 | `:japanese_goblin:` | | 👹 | `:japanese_ogre:` | | 🫙 | `:jar:` | | 👖 | `:jeans:` | | 🪼 | `:jellyfish:` | | 🇯‍🇪 | `:jersey:` | | 🧩 | `:jigsaw:` | | 🇯‍🇴 | `:jordan:` | | 😂 | `:joy:` | | 😹 | `:joy_cat:` | | 🕹 | `:joystick:` | | 🇯‍🇵 | `:jp:` | | 🧑‍⚖ | `:judge:` | | 🤹 | `:juggling_person:` | | 🕋 | `:kaaba:` | | 🦘 | `:kangaroo:` | | 🇰‍🇿 | `:kazakhstan:` | | 🇰‍🇪 | `:kenya:` | | 🔑 | `:key:` | | ⌨ | `:keyboard:` | | 🔟 | `:keycap_ten:` | | 🪯 | `:khanda:` | | 🛴 | `:kick_scooter:` | | 👘 | `:kimono:` | | 🇰‍🇮 | `:kiribati:` | | 💋 | `:kiss:` | | 😗 | `:kissing:` | | 😽 | `:kissing_cat:` | | 😚 | `:kissing_closed_eyes:` | | 😘 | `:kissing_heart:` | | 😙 | `:kissing_smiling_eyes:` | | 🪁 | `:kite:` | | 🥝 | `:kiwi_fruit:` | | 🧎‍♂ | `:kneeling_man:` | | 🧎 | `:kneeling_person:` | | 🧎‍♀ | `:kneeling_woman:` | | 🔪 | `:knife:` | | 🪢 | `:knot:` | | 🐨 | `:koala:` | | 🈁 | `:koko:` | | 🇽‍🇰 | `:kosovo:` | | 🇰‍🇷 | `:kr:` | | 🇰‍🇼 | `:kuwait:` | | 🇰‍🇬 | `:kyrgyzstan:` | | 🥼 | `:lab_coat:` | | 🏷 | `:label:` | | 🥍 | `:lacrosse:` | | 🪜 | `:ladder:` | | 🐞 | `:lady_beetle:` | | 🏮 | `:lantern:` | | 🇱‍🇦 | `:laos:` | | 🔵 | `:large_blue_circle:` | | 🔷 | `:large_blue_diamond:` | | 🔶 | `:large_orange_diamond:` | | 🌗 | `:last_quarter_moon:` | | 🌜 | `:last_quarter_moon_with_face:` | | ✝ | `:latin_cross:` | | 🇱‍🇻 | `:latvia:` | | 😆 | `:laughing:` | | 🥬 | `:leafy_green:` | | 🍃 | `:leaves:` | | 🇱‍🇧 | `:lebanon:` | | 📒 | `:ledger:` | | 🛅 | `:left_luggage:` | | ↔ | `:left_right_arrow:` | | 🗨 | `:left_speech_bubble:` | | ↩ | `:leftwards_arrow_with_hook:` | | 🫲 | `:leftwards_hand:` | | 🫷 | `:leftwards_pushing_hand:` | | 🦵 | `:leg:` | | 🍋 | `:lemon:` | | ♌ | `:leo:` | | 🐆 | `:leopard:` | | 🇱‍🇸 | `:lesotho:` | | 🎚 | `:level_slider:` | | 🇱‍🇷 | `:liberia:` | | ♎ | `:libra:` | | 🇱‍🇾 | `:libya:` | | 🇱‍🇮 | `:liechtenstein:` | | 🩵 | `:light_blue_heart:` | | 🚈 | `:light_rail:` | | 🔗 | `:link:` | | 🦁 | `:lion:` | | 👄 | `:lips:` | | 💄 | `:lipstick:` | | 🇱‍🇹 | `:lithuania:` | | 🦎 | `:lizard:` | | 🦙 | `:llama:` | | 🦞 | `:lobster:` | | 🔒 | `:lock:` | | 🔏 | `:lock_with_ink_pen:` | | 🍭 | `:lollipop:` | | 🪘 | `:long_drum:` | | ➿ | `:loop:` | | 🧴 | `:lotion_bottle:` | | 🪷 | `:lotus:` | | 🧘 | `:lotus_position:` | | 🧘‍♂ | `:lotus_position_man:` | | 🧘‍♀ | `:lotus_position_woman:` | | 🔊 | `:loud_sound:` | | 📢 | `:loudspeaker:` | | 🏩 | `:love_hotel:` | | 💌 | `:love_letter:` | | 🤟 | `:love_you_gesture:` | | 🪫 | `:low_battery:` | | 🔅 | `:low_brightness:` | | 🧳 | `:luggage:` | | 🫁 | `:lungs:` | | 🇱‍🇺 | `:luxembourg:` | | 🤥 | `:lying_face:` | | Ⓜ | `:m:` | | 🇲‍🇴 | `:macau:` | | 🇲‍🇰 | `:macedonia:` | | 🇲‍🇬 | `:madagascar:` | | 🔍 | `:mag:` | | 🔎 | `:mag_right:` | | 🧙 | `:mage:` | | 🧙‍♂ | `:mage_man:` | | 🧙‍♀ | `:mage_woman:` | | 🪄 | `:magic_wand:` | | 🧲 | `:magnet:` | | 🀄 | `:mahjong:` | | 📫 | `:mailbox:` | | 📪 | `:mailbox_closed:` | | 📬 | `:mailbox_with_mail:` | | 📭 | `:mailbox_with_no_mail:` | | 🇲‍🇼 | `:malawi:` | | 🇲‍🇾 | `:malaysia:` | | 🇲‍🇻 | `:maldives:` | | 🕵‍♂ | `:male_detective:` | | ♂ | `:male_sign:` | | 🇲‍🇱 | `:mali:` | | 🇲‍🇹 | `:malta:` | | 🦣 | `:mammoth:` | | 👨 | `:man:` | | 👨‍🎨 | `:man_artist:` | | 👨‍🚀 | `:man_astronaut:` | | 🧔‍♂ | `:man_beard:` | | 🤸‍♂ | `:man_cartwheeling:` | | 👨‍🍳 | `:man_cook:` | | 🕺 | `:man_dancing:` | | 🤦‍♂ | `:man_facepalming:` | | 👨‍🏭 | `:man_factory_worker:` | | 👨‍🌾 | `:man_farmer:` | | 👨‍🍼 | `:man_feeding_baby:` | | 👨‍🚒 | `:man_firefighter:` | | 👨‍⚕ | `:man_health_worker:` | | 👨‍🦽 | `:man_in_manual_wheelchair:` | | 👨‍🦼 | `:man_in_motorized_wheelchair:` | | 🤵‍♂ | `:man_in_tuxedo:` | | 👨‍⚖ | `:man_judge:` | | 🤹‍♂ | `:man_juggling:` | | 👨‍🔧 | `:man_mechanic:` | | 👨‍💼 | `:man_office_worker:` | | 👨‍✈ | `:man_pilot:` | | 🤾‍♂ | `:man_playing_handball:` | | 🤽‍♂ | `:man_playing_water_polo:` | | 👨‍🔬 | `:man_scientist:` | | 🤷‍♂ | `:man_shrugging:` | | 👨‍🎤 | `:man_singer:` | | 👨‍🎓 | `:man_student:` | | 👨‍🏫 | `:man_teacher:` | | 👨‍💻 | `:man_technologist:` | | 👲 | `:man_with_gua_pi_mao:` | | 👨‍🦯 | `:man_with_probing_cane:` | | 👳‍♂ | `:man_with_turban:` | | 👰‍♂ | `:man_with_veil:` | | 🍊 | `:mandarin:` | | 🥭 | `:mango:` | | 👞 | `:mans_shoe:` | | 🕰 | `:mantelpiece_clock:` | | 🦽 | `:manual_wheelchair:` | | 🍁 | `:maple_leaf:` | | 🪇 | `:maracas:` | | 🇲‍🇭 | `:marshall_islands:` | | 🥋 | `:martial_arts_uniform:` | | 🇲‍🇶 | `:martinique:` | | 😷 | `:mask:` | | 💆 | `:massage:` | | 💆‍♂ | `:massage_man:` | | 💆‍♀ | `:massage_woman:` | | 🧉 | `:mate:` | | 🇲‍🇷 | `:mauritania:` | | 🇲‍🇺 | `:mauritius:` | | 🇾‍🇹 | `:mayotte:` | | 🍖 | `:meat_on_bone:` | | 🧑‍🔧 | `:mechanic:` | | 🦾 | `:mechanical_arm:` | | 🦿 | `:mechanical_leg:` | | 🎖 | `:medal_military:` | | 🏅 | `:medal_sports:` | | ⚕ | `:medical_symbol:` | | 📣 | `:mega:` | | 🍈 | `:melon:` | | 🫠 | `:melting_face:` | | 📝 | `:memo:` | | 🤼‍♂ | `:men_wrestling:` | | ❤‍🩹 | `:mending_heart:` | | 🕎 | `:menorah:` | | 🚹 | `:mens:` | | 🧜‍♀ | `:mermaid:` | | 🧜‍♂ | `:merman:` | | 🧜 | `:merperson:` | | 🤘 | `:metal:` | | 🚇 | `:metro:` | | 🇲‍🇽 | `:mexico:` | | 🦠 | `:microbe:` | | 🇫‍🇲 | `:micronesia:` | | 🎤 | `:microphone:` | | 🔬 | `:microscope:` | | 🖕 | `:middle_finger:` | | 🪖 | `:military_helmet:` | | 🥛 | `:milk_glass:` | | 🌌 | `:milky_way:` | | 🚐 | `:minibus:` | | 💽 | `:minidisc:` | | 🪞 | `:mirror:` | | 🪩 | `:mirror_ball:` | | 📴 | `:mobile_phone_off:` | | 🇲‍🇩 | `:moldova:` | | 🇲‍🇨 | `:monaco:` | | 🤑 | `:money_mouth_face:` | | 💸 | `:money_with_wings:` | | 💰 | `:moneybag:` | | 🇲‍🇳 | `:mongolia:` | | 🐒 | `:monkey:` | | 🐵 | `:monkey_face:` | | 🧐 | `:monocle_face:` | | 🚝 | `:monorail:` | | 🇲‍🇪 | `:montenegro:` | | 🇲‍🇸 | `:montserrat:` | | 🌔 | `:moon:` | | 🥮 | `:moon_cake:` | | 🫎 | `:moose:` | | 🇲‍🇦 | `:morocco:` | | 🎓 | `:mortar_board:` | | 🕌 | `:mosque:` | | 🦟 | `:mosquito:` | | 🛥 | `:motor_boat:` | | 🛵 | `:motor_scooter:` | | 🏍 | `:motorcycle:` | | 🦼 | `:motorized_wheelchair:` | | 🛣 | `:motorway:` | | 🗻 | `:mount_fuji:` | | ⛰ | `:mountain:` | | 🚵 | `:mountain_bicyclist:` | | 🚵‍♂ | `:mountain_biking_man:` | | 🚵‍♀ | `:mountain_biking_woman:` | | 🚠 | `:mountain_cableway:` | | 🚞 | `:mountain_railway:` | | 🏔 | `:mountain_snow:` | | 🐭 | `:mouse:` | | 🪤 | `:mouse_trap:` | | 🐁 | `:mouse2:` | | 🎥 | `:movie_camera:` | | 🗿 | `:moyai:` | | 🇲‍🇿 | `:mozambique:` | | 🤶 | `:mrs_claus:` | | 💪 | `:muscle:` | | 🍄 | `:mushroom:` | | 🎹 | `:musical_keyboard:` | | 🎵 | `:musical_note:` | | 🎼 | `:musical_score:` | | 🔇 | `:mute:` | | 🧑‍🎄 | `:mx_claus:` | | 🇲‍🇲 | `:myanmar:` | | 💅 | `:nail_care:` | | 📛 | `:name_badge:` | | 🇳‍🇦 | `:namibia:` | | 🏞 | `:national_park:` | | 🇳‍🇷 | `:nauru:` | | 🤢 | `:nauseated_face:` | | 🧿 | `:nazar_amulet:` | | 👔 | `:necktie:` | | ❎ | `:negative_squared_cross_mark:` | | 🇳‍🇵 | `:nepal:` | | 🤓 | `:nerd_face:` | | 🪺 | `:nest_with_eggs:` | | 🪆 | `:nesting_dolls:` | | 🇳‍🇱 | `:netherlands:` | | 😐 | `:neutral_face:` | | 🆕 | `:new:` | | 🇳‍🇨 | `:new_caledonia:` | | 🌑 | `:new_moon:` | | 🌚 | `:new_moon_with_face:` | | 🇳‍🇿 | `:new_zealand:` | | 📰 | `:newspaper:` | | 🗞 | `:newspaper_roll:` | | ⏭ | `:next_track_button:` | | 🆖 | `:ng:` | | 🙅‍♂ | `:ng_man:` | | 🙅‍♀ | `:ng_woman:` | | 🇳‍🇮 | `:nicaragua:` | | 🇳‍🇪 | `:niger:` | | 🇳‍🇬 | `:nigeria:` | | 🌃 | `:night_with_stars:` | | 9‍⃣ | `:nine:` | | 🥷 | `:ninja:` | | 🇳‍🇺 | `:niue:` | | 🔕 | `:no_bell:` | | 🚳 | `:no_bicycles:` | | ⛔ | `:no_entry:` | | 🚫 | `:no_entry_sign:` | | 🙅 | `:no_good:` | | 🙅‍♂ | `:no_good_man:` | | 🙅‍♀ | `:no_good_woman:` | | 📵 | `:no_mobile_phones:` | | 😶 | `:no_mouth:` | | 🚷 | `:no_pedestrians:` | | 🚭 | `:no_smoking:` | | 🚱 | `:non-potable_water:` | | 🇳‍🇫 | `:norfolk_island:` | | 🇰‍🇵 | `:north_korea:` | | 🇲‍🇵 | `:northern_mariana_islands:` | | 🇳‍🇴 | `:norway:` | | 👃 | `:nose:` | | 📓 | `:notebook:` | | 📔 | `:notebook_with_decorative_cover:` | | 🎶 | `:notes:` | | 🔩 | `:nut_and_bolt:` | | ⭕ | `:o:` | | 🅾 | `:o2:` | | 🌊 | `:ocean:` | | 🐙 | `:octopus:` | | 🍢 | `:oden:` | | 🏢 | `:office:` | | 🧑‍💼 | `:office_worker:` | | 🛢 | `:oil_drum:` | | 🆗 | `:ok:` | | 👌 | `:ok_hand:` | | 🙆‍♂ | `:ok_man:` | | 🙆 | `:ok_person:` | | 🙆‍♀ | `:ok_woman:` | | 🗝 | `:old_key:` | | 🧓 | `:older_adult:` | | 👴 | `:older_man:` | | 👵 | `:older_woman:` | | 🫒 | `:olive:` | | 🕉 | `:om:` | | 🇴‍🇲 | `:oman:` | | 🔛 | `:on:` | | 🚘 | `:oncoming_automobile:` | | 🚍 | `:oncoming_bus:` | | 🚔 | `:oncoming_police_car:` | | 🚖 | `:oncoming_taxi:` | | 1‍⃣ | `:one:` | | 🩱 | `:one_piece_swimsuit:` | | 🧅 | `:onion:` | | 📖 | `:open_book:` | | 📂 | `:open_file_folder:` | | 👐 | `:open_hands:` | | 😮 | `:open_mouth:` | | ☂ | `:open_umbrella:` | | ⛎ | `:ophiuchus:` | | 🍊 | `:orange:` | | 📙 | `:orange_book:` | | 🟠 | `:orange_circle:` | | 🧡 | `:orange_heart:` | | 🟧 | `:orange_square:` | | 🦧 | `:orangutan:` | | ☦ | `:orthodox_cross:` | | 🦦 | `:otter:` | | 📤 | `:outbox_tray:` | | 🦉 | `:owl:` | | 🐂 | `:ox:` | | 🦪 | `:oyster:` | | 📦 | `:package:` | | 📄 | `:page_facing_up:` | | 📃 | `:page_with_curl:` | | 📟 | `:pager:` | | 🖌 | `:paintbrush:` | | 🇵‍🇰 | `:pakistan:` | | 🇵‍🇼 | `:palau:` | | 🇵‍🇸 | `:palestinian_territories:` | | 🫳 | `:palm_down_hand:` | | 🌴 | `:palm_tree:` | | 🫴 | `:palm_up_hand:` | | 🤲 | `:palms_up_together:` | | 🇵‍🇦 | `:panama:` | | 🥞 | `:pancakes:` | | 🐼 | `:panda_face:` | | 📎 | `:paperclip:` | | 🖇 | `:paperclips:` | | 🇵‍🇬 | `:papua_new_guinea:` | | 🪂 | `:parachute:` | | 🇵‍🇾 | `:paraguay:` | | ⛱ | `:parasol_on_ground:` | | 🅿 | `:parking:` | | 🦜 | `:parrot:` | | 〽 | `:part_alternation_mark:` | | ⛅ | `:partly_sunny:` | | 🥳 | `:partying_face:` | | 🛳 | `:passenger_ship:` | | 🛂 | `:passport_control:` | | ⏸ | `:pause_button:` | | 🐾 | `:paw_prints:` | | 🫛 | `:pea_pod:` | | ☮ | `:peace_symbol:` | | 🍑 | `:peach:` | | 🦚 | `:peacock:` | | 🥜 | `:peanuts:` | | 🍐 | `:pear:` | | 🖊 | `:pen:` | | 📝 | `:pencil:` | | ✏ | `:pencil2:` | | 🐧 | `:penguin:` | | 😔 | `:pensive:` | | 🧑‍🤝‍🧑 | `:people_holding_hands:` | | 🫂 | `:people_hugging:` | | 🎭 | `:performing_arts:` | | 😣 | `:persevere:` | | 🧑‍🦲 | `:person_bald:` | | 🧑‍🦱 | `:person_curly_hair:` | | 🧑‍🍼 | `:person_feeding_baby:` | | 🤺 | `:person_fencing:` | | 🧑‍🦽 | `:person_in_manual_wheelchair:` | | 🧑‍🦼 | `:person_in_motorized_wheelchair:` | | 🤵 | `:person_in_tuxedo:` | | 🧑‍🦰 | `:person_red_hair:` | | 🧑‍🦳 | `:person_white_hair:` | | 🫅 | `:person_with_crown:` | | 🧑‍🦯 | `:person_with_probing_cane:` | | 👳 | `:person_with_turban:` | | 👰 | `:person_with_veil:` | | 🇵‍🇪 | `:peru:` | | 🧫 | `:petri_dish:` | | 🇵‍🇭 | `:philippines:` | | ☎ | `:phone:` | | ⛏ | `:pick:` | | 🛻 | `:pickup_truck:` | | 🥧 | `:pie:` | | 🐷 | `:pig:` | | 🐽 | `:pig_nose:` | | 🐖 | `:pig2:` | | 💊 | `:pill:` | | 🧑‍✈ | `:pilot:` | | 🪅 | `:pinata:` | | 🤌 | `:pinched_fingers:` | | 🤏 | `:pinching_hand:` | | 🍍 | `:pineapple:` | | 🏓 | `:ping_pong:` | | 🩷 | `:pink_heart:` | | 🏴‍☠ | `:pirate_flag:` | | ♓ | `:pisces:` | | 🇵‍🇳 | `:pitcairn_islands:` | | 🍕 | `:pizza:` | | 🪧 | `:placard:` | | 🛐 | `:place_of_worship:` | | 🍽 | `:plate_with_cutlery:` | | ⏯ | `:play_or_pause_button:` | | 🛝 | `:playground_slide:` | | 🥺 | `:pleading_face:` | | 🪠 | `:plunger:` | | 👇 | `:point_down:` | | 👈 | `:point_left:` | | 👉 | `:point_right:` | | ☝ | `:point_up:` | | 👆 | `:point_up_2:` | | 🇵‍🇱 | `:poland:` | | 🐻‍❄ | `:polar_bear:` | | 🚓 | `:police_car:` | | 👮 | `:police_officer:` | | 👮‍♂ | `:policeman:` | | 👮‍♀ | `:policewoman:` | | 🐩 | `:poodle:` | | 💩 | `:poop:` | | 🍿 | `:popcorn:` | | 🇵‍🇹 | `:portugal:` | | 🏣 | `:post_office:` | | 📯 | `:postal_horn:` | | 📮 | `:postbox:` | | 🚰 | `:potable_water:` | | 🥔 | `:potato:` | | 🪴 | `:potted_plant:` | | 👝 | `:pouch:` | | 🍗 | `:poultry_leg:` | | 💷 | `:pound:` | | 🫗 | `:pouring_liquid:` | | 😡 | `:pout:` | | 😾 | `:pouting_cat:` | | 🙎 | `:pouting_face:` | | 🙎‍♂ | `:pouting_man:` | | 🙎‍♀ | `:pouting_woman:` | | 🙏 | `:pray:` | | 📿 | `:prayer_beads:` | | 🫃 | `:pregnant_man:` | | 🫄 | `:pregnant_person:` | | 🤰 | `:pregnant_woman:` | | 🥨 | `:pretzel:` | | ⏮ | `:previous_track_button:` | | 🤴 | `:prince:` | | 👸 | `:princess:` | | 🖨 | `:printer:` | | 🦯 | `:probing_cane:` | | 🇵‍🇷 | `:puerto_rico:` | | 👊 | `:punch:` | | 🟣 | `:purple_circle:` | | 💜 | `:purple_heart:` | | 🟪 | `:purple_square:` | | 👛 | `:purse:` | | 📌 | `:pushpin:` | | 🚮 | `:put_litter_in_its_place:` | | 🇶‍🇦 | `:qatar:` | | ❓ | `:question:` | | 🐰 | `:rabbit:` | | 🐇 | `:rabbit2:` | | 🦝 | `:raccoon:` | | 🐎 | `:racehorse:` | | 🏎 | `:racing_car:` | | 📻 | `:radio:` | | 🔘 | `:radio_button:` | | ☢ | `:radioactive:` | | 😡 | `:rage:` | | 🚃 | `:railway_car:` | | 🛤 | `:railway_track:` | | 🌈 | `:rainbow:` | | 🏳‍🌈 | `:rainbow_flag:` | | 🤚 | `:raised_back_of_hand:` | | 🤨 | `:raised_eyebrow:` | | ✋ | `:raised_hand:` | | 🖐 | `:raised_hand_with_fingers_splayed:` | | 🙌 | `:raised_hands:` | | 🙋 | `:raising_hand:` | | 🙋‍♂ | `:raising_hand_man:` | | 🙋‍♀ | `:raising_hand_woman:` | | 🐏 | `:ram:` | | 🍜 | `:ramen:` | | 🐀 | `:rat:` | | 🪒 | `:razor:` | | 🧾 | `:receipt:` | | ⏺ | `:record_button:` | | ♻ | `:recycle:` | | 🚗 | `:red_car:` | | 🔴 | `:red_circle:` | | 🧧 | `:red_envelope:` | | 👨‍🦰 | `:red_haired_man:` | | 👩‍🦰 | `:red_haired_woman:` | | 🟥 | `:red_square:` | | ® | `:registered:` | | ☺ | `:relaxed:` | | 😌 | `:relieved:` | | 🎗 | `:reminder_ribbon:` | | 🔁 | `:repeat:` | | 🔂 | `:repeat_one:` | | ⛑ | `:rescue_worker_helmet:` | | 🚻 | `:restroom:` | | 🇷‍🇪 | `:reunion:` | | 💞 | `:revolving_hearts:` | | ⏪ | `:rewind:` | | 🦏 | `:rhinoceros:` | | 🎀 | `:ribbon:` | | 🍚 | `:rice:` | | 🍙 | `:rice_ball:` | | 🍘 | `:rice_cracker:` | | 🎑 | `:rice_scene:` | | 🗯 | `:right_anger_bubble:` | | 🫱 | `:rightwards_hand:` | | 🫸 | `:rightwards_pushing_hand:` | | 💍 | `:ring:` | | 🛟 | `:ring_buoy:` | | 🪐 | `:ringed_planet:` | | 🤖 | `:robot:` | | 🪨 | `:rock:` | | 🚀 | `:rocket:` | | 🤣 | `:rofl:` | | 🙄 | `:roll_eyes:` | | 🧻 | `:roll_of_paper:` | | 🎢 | `:roller_coaster:` | | 🛼 | `:roller_skate:` | | 🇷‍🇴 | `:romania:` | | 🐓 | `:rooster:` | | 🌹 | `:rose:` | | 🏵 | `:rosette:` | | 🚨 | `:rotating_light:` | | 📍 | `:round_pushpin:` | | 🚣 | `:rowboat:` | | 🚣‍♂ | `:rowing_man:` | | 🚣‍♀ | `:rowing_woman:` | | 🇷‍🇺 | `:ru:` | | 🏉 | `:rugby_football:` | | 🏃 | `:runner:` | | 🏃 | `:running:` | | 🏃‍♂ | `:running_man:` | | 🎽 | `:running_shirt_with_sash:` | | 🏃‍♀ | `:running_woman:` | | 🇷‍🇼 | `:rwanda:` | | 🈂 | `:sa:` | | 🧷 | `:safety_pin:` | | 🦺 | `:safety_vest:` | | ♐ | `:sagittarius:` | | ⛵ | `:sailboat:` | | 🍶 | `:sake:` | | 🧂 | `:salt:` | | 🫡 | `:saluting_face:` | | 🇼‍🇸 | `:samoa:` | | 🇸‍🇲 | `:san_marino:` | | 👡 | `:sandal:` | | 🥪 | `:sandwich:` | | 🎅 | `:santa:` | | 🇸‍🇹 | `:sao_tome_principe:` | | 🥻 | `:sari:` | | 💁‍♂ | `:sassy_man:` | | 💁‍♀ | `:sassy_woman:` | | 📡 | `:satellite:` | | 😆 | `:satisfied:` | | 🇸‍🇦 | `:saudi_arabia:` | | 🧖‍♂ | `:sauna_man:` | | 🧖 | `:sauna_person:` | | 🧖‍♀ | `:sauna_woman:` | | 🦕 | `:sauropod:` | | 🎷 | `:saxophone:` | | 🧣 | `:scarf:` | | 🏫 | `:school:` | | 🎒 | `:school_satchel:` | | 🧑‍🔬 | `:scientist:` | | ✂ | `:scissors:` | | 🦂 | `:scorpion:` | | ♏ | `:scorpius:` | | 🏴‍󠁧‍󠁢‍󠁳‍󠁣‍󠁴‍󠁿 | `:scotland:` | | 😱 | `:scream:` | | 🙀 | `:scream_cat:` | | 🪛 | `:screwdriver:` | | 📜 | `:scroll:` | | 🦭 | `:seal:` | | 💺 | `:seat:` | | ㊙ | `:secret:` | | 🙈 | `:see_no_evil:` | | 🌱 | `:seedling:` | | 🤳 | `:selfie:` | | 🇸‍🇳 | `:senegal:` | | 🇷‍🇸 | `:serbia:` | | 🐕‍🦺 | `:service_dog:` | | 7‍⃣ | `:seven:` | | 🪡 | `:sewing_needle:` | | 🇸‍🇨 | `:seychelles:` | | 🫨 | `:shaking_face:` | | 🥘 | `:shallow_pan_of_food:` | | ☘ | `:shamrock:` | | 🦈 | `:shark:` | | 🍧 | `:shaved_ice:` | | 🐑 | `:sheep:` | | 🐚 | `:shell:` | | 🛡 | `:shield:` | | ⛩ | `:shinto_shrine:` | | 🚢 | `:ship:` | | 👕 | `:shirt:` | | 💩 | `:shit:` | | 👞 | `:shoe:` | | 🛍 | `:shopping:` | | 🛒 | `:shopping_cart:` | | 🩳 | `:shorts:` | | 🚿 | `:shower:` | | 🦐 | `:shrimp:` | | 🤷 | `:shrug:` | | 🤫 | `:shushing_face:` | | 🇸‍🇱 | `:sierra_leone:` | | 📶 | `:signal_strength:` | | 🇸‍🇬 | `:singapore:` | | 🧑‍🎤 | `:singer:` | | 🇸‍🇽 | `:sint_maarten:` | | 6‍⃣ | `:six:` | | 🔯 | `:six_pointed_star:` | | 🛹 | `:skateboard:` | | 🎿 | `:ski:` | | ⛷ | `:skier:` | | 💀 | `:skull:` | | ☠ | `:skull_and_crossbones:` | | 🦨 | `:skunk:` | | 🛷 | `:sled:` | | 😴 | `:sleeping:` | | 🛌 | `:sleeping_bed:` | | 😪 | `:sleepy:` | | 🙁 | `:slightly_frowning_face:` | | 🙂 | `:slightly_smiling_face:` | | 🎰 | `:slot_machine:` | | 🦥 | `:sloth:` | | 🇸‍🇰 | `:slovakia:` | | 🇸‍🇮 | `:slovenia:` | | 🛩 | `:small_airplane:` | | 🔹 | `:small_blue_diamond:` | | 🔸 | `:small_orange_diamond:` | | 🔺 | `:small_red_triangle:` | | 🔻 | `:small_red_triangle_down:` | | 😄 | `:smile:` | | 😸 | `:smile_cat:` | | 😃 | `:smiley:` | | 😺 | `:smiley_cat:` | | 🥲 | `:smiling_face_with_tear:` | | 🥰 | `:smiling_face_with_three_hearts:` | | 😈 | `:smiling_imp:` | | 😏 | `:smirk:` | | 😼 | `:smirk_cat:` | | 🚬 | `:smoking:` | | 🐌 | `:snail:` | | 🐍 | `:snake:` | | 🤧 | `:sneezing_face:` | | 🏂 | `:snowboarder:` | | ❄ | `:snowflake:` | | ⛄ | `:snowman:` | | ☃ | `:snowman_with_snow:` | | 🧼 | `:soap:` | | 😭 | `:sob:` | | ⚽ | `:soccer:` | | 🧦 | `:socks:` | | 🥎 | `:softball:` | | 🇸‍🇧 | `:solomon_islands:` | | 🇸‍🇴 | `:somalia:` | | 🔜 | `:soon:` | | 🆘 | `:sos:` | | 🔉 | `:sound:` | | 🇿‍🇦 | `:south_africa:` | | 🇬‍🇸 | `:south_georgia_south_sandwich_islands:` | | 🇸‍🇸 | `:south_sudan:` | | 👾 | `:space_invader:` | | ♠ | `:spades:` | | 🍝 | `:spaghetti:` | | ❇ | `:sparkle:` | | 🎇 | `:sparkler:` | | ✨ | `:sparkles:` | | 💖 | `:sparkling_heart:` | | 🙊 | `:speak_no_evil:` | | 🔈 | `:speaker:` | | 🗣 | `:speaking_head:` | | 💬 | `:speech_balloon:` | | 🚤 | `:speedboat:` | | 🕷 | `:spider:` | | 🕸 | `:spider_web:` | | 🗓 | `:spiral_calendar:` | | 🗒 | `:spiral_notepad:` | | 🧽 | `:sponge:` | | 🥄 | `:spoon:` | | 🦑 | `:squid:` | | 🇱‍🇰 | `:sri_lanka:` | | 🇧‍🇱 | `:st_barthelemy:` | | 🇸‍🇭 | `:st_helena:` | | 🇰‍🇳 | `:st_kitts_nevis:` | | 🇱‍🇨 | `:st_lucia:` | | 🇲‍🇫 | `:st_martin:` | | 🇵‍🇲 | `:st_pierre_miquelon:` | | 🇻‍🇨 | `:st_vincent_grenadines:` | | 🏟 | `:stadium:` | | 🧍‍♂ | `:standing_man:` | | 🧍 | `:standing_person:` | | 🧍‍♀ | `:standing_woman:` | | ⭐ | `:star:` | | ☪ | `:star_and_crescent:` | | ✡ | `:star_of_david:` | | 🤩 | `:star_struck:` | | 🌟 | `:star2:` | | 🌠 | `:stars:` | | 🚉 | `:station:` | | 🗽 | `:statue_of_liberty:` | | 🚂 | `:steam_locomotive:` | | 🩺 | `:stethoscope:` | | 🍲 | `:stew:` | | ⏹ | `:stop_button:` | | 🛑 | `:stop_sign:` | | ⏱ | `:stopwatch:` | | 📏 | `:straight_ruler:` | | 🍓 | `:strawberry:` | | 😛 | `:stuck_out_tongue:` | | 😝 | `:stuck_out_tongue_closed_eyes:` | | 😜 | `:stuck_out_tongue_winking_eye:` | | 🧑‍🎓 | `:student:` | | 🎙 | `:studio_microphone:` | | 🥙 | `:stuffed_flatbread:` | | 🇸‍🇩 | `:sudan:` | | 🌥 | `:sun_behind_large_cloud:` | | 🌦 | `:sun_behind_rain_cloud:` | | 🌤 | `:sun_behind_small_cloud:` | | 🌞 | `:sun_with_face:` | | 🌻 | `:sunflower:` | | 😎 | `:sunglasses:` | | ☀ | `:sunny:` | | 🌅 | `:sunrise:` | | 🌄 | `:sunrise_over_mountains:` | | 🦸 | `:superhero:` | | 🦸‍♂ | `:superhero_man:` | | 🦸‍♀ | `:superhero_woman:` | | 🦹 | `:supervillain:` | | 🦹‍♂ | `:supervillain_man:` | | 🦹‍♀ | `:supervillain_woman:` | | 🏄 | `:surfer:` | | 🏄‍♂ | `:surfing_man:` | | 🏄‍♀ | `:surfing_woman:` | | 🇸‍🇷 | `:suriname:` | | 🍣 | `:sushi:` | | 🚟 | `:suspension_railway:` | | 🇸‍🇯 | `:svalbard_jan_mayen:` | | 🦢 | `:swan:` | | 🇸‍🇿 | `:swaziland:` | | 😓 | `:sweat:` | | 💦 | `:sweat_drops:` | | 😅 | `:sweat_smile:` | | 🇸‍🇪 | `:sweden:` | | 🍠 | `:sweet_potato:` | | 🩲 | `:swim_brief:` | | 🏊 | `:swimmer:` | | 🏊‍♂ | `:swimming_man:` | | 🏊‍♀ | `:swimming_woman:` | | 🇨‍🇭 | `:switzerland:` | | 🔣 | `:symbols:` | | 🕍 | `:synagogue:` | | 🇸‍🇾 | `:syria:` | | 💉 | `:syringe:` | | 🦖 | `:t-rex:` | | 🌮 | `:taco:` | | 🎉 | `:tada:` | | 🇹‍🇼 | `:taiwan:` | | 🇹‍🇯 | `:tajikistan:` | | 🥡 | `:takeout_box:` | | 🫔 | `:tamale:` | | 🎋 | `:tanabata_tree:` | | 🍊 | `:tangerine:` | | 🇹‍🇿 | `:tanzania:` | | ♉ | `:taurus:` | | 🚕 | `:taxi:` | | 🍵 | `:tea:` | | 🧑‍🏫 | `:teacher:` | | 🫖 | `:teapot:` | | 🧑‍💻 | `:technologist:` | | 🧸 | `:teddy_bear:` | | ☎ | `:telephone:` | | 📞 | `:telephone_receiver:` | | 🔭 | `:telescope:` | | 🎾 | `:tennis:` | | ⛺ | `:tent:` | | 🧪 | `:test_tube:` | | 🇹‍🇭 | `:thailand:` | | 🌡 | `:thermometer:` | | 🤔 | `:thinking:` | | 🩴 | `:thong_sandal:` | | 💭 | `:thought_balloon:` | | 🧵 | `:thread:` | | 3‍⃣ | `:three:` | | 👎 | `:thumbsdown:` | | 👍 | `:thumbsup:` | | 🎫 | `:ticket:` | | 🎟 | `:tickets:` | | 🐯 | `:tiger:` | | 🐅 | `:tiger2:` | | ⏲ | `:timer_clock:` | | 🇹‍🇱 | `:timor_leste:` | | 💁‍♂ | `:tipping_hand_man:` | | 💁 | `:tipping_hand_person:` | | 💁‍♀ | `:tipping_hand_woman:` | | 😫 | `:tired_face:` | | ™ | `:tm:` | | 🇹‍🇬 | `:togo:` | | 🚽 | `:toilet:` | | 🇹‍🇰 | `:tokelau:` | | 🗼 | `:tokyo_tower:` | | 🍅 | `:tomato:` | | 🇹‍🇴 | `:tonga:` | | 👅 | `:tongue:` | | 🧰 | `:toolbox:` | | 🦷 | `:tooth:` | | 🪥 | `:toothbrush:` | | 🔝 | `:top:` | | 🎩 | `:tophat:` | | 🌪 | `:tornado:` | | 🇹‍🇷 | `:tr:` | | 🖲 | `:trackball:` | | 🚜 | `:tractor:` | | 🚥 | `:traffic_light:` | | 🚋 | `:train:` | | 🚆 | `:train2:` | | 🚊 | `:tram:` | | 🏳‍⚧ | `:transgender_flag:` | | ⚧ | `:transgender_symbol:` | | 🚩 | `:triangular_flag_on_post:` | | 📐 | `:triangular_ruler:` | | 🔱 | `:trident:` | | 🇹‍🇹 | `:trinidad_tobago:` | | 🇹‍🇦 | `:tristan_da_cunha:` | | 😤 | `:triumph:` | | 🧌 | `:troll:` | | 🚎 | `:trolleybus:` | | 🏆 | `:trophy:` | | 🍹 | `:tropical_drink:` | | 🐠 | `:tropical_fish:` | | 🚚 | `:truck:` | | 🎺 | `:trumpet:` | | 👕 | `:tshirt:` | | 🌷 | `:tulip:` | | 🥃 | `:tumbler_glass:` | | 🇹‍🇳 | `:tunisia:` | | 🦃 | `:turkey:` | | 🇹‍🇲 | `:turkmenistan:` | | 🇹‍🇨 | `:turks_caicos_islands:` | | 🐢 | `:turtle:` | | 🇹‍🇻 | `:tuvalu:` | | 📺 | `:tv:` | | 🔀 | `:twisted_rightwards_arrows:` | | 2‍⃣ | `:two:` | | 💕 | `:two_hearts:` | | 👬 | `:two_men_holding_hands:` | | 👭 | `:two_women_holding_hands:` | | 🈹 | `:u5272:` | | 🈴 | `:u5408:` | | 🈺 | `:u55b6:` | | 🈯 | `:u6307:` | | 🈷 | `:u6708:` | | 🈶 | `:u6709:` | | 🈵 | `:u6e80:` | | 🈚 | `:u7121:` | | 🈸 | `:u7533:` | | 🈲 | `:u7981:` | | 🈳 | `:u7a7a:` | | 🇺‍🇬 | `:uganda:` | | 🇬‍🇧 | `:uk:` | | 🇺‍🇦 | `:ukraine:` | | ☔ | `:umbrella:` | | 😒 | `:unamused:` | | 🔞 | `:underage:` | | 🦄 | `:unicorn:` | | 🇦‍🇪 | `:united_arab_emirates:` | | 🇺‍🇳 | `:united_nations:` | | 🔓 | `:unlock:` | | 🆙 | `:up:` | | 🙃 | `:upside_down_face:` | | 🇺‍🇾 | `:uruguay:` | | 🇺‍🇸 | `:us:` | | 🇺‍🇲 | `:us_outlying_islands:` | | 🇻‍🇮 | `:us_virgin_islands:` | | 🇺‍🇿 | `:uzbekistan:` | | ✌ | `:v:` | | 🧛 | `:vampire:` | | 🧛‍♂ | `:vampire_man:` | | 🧛‍♀ | `:vampire_woman:` | | 🇻‍🇺 | `:vanuatu:` | | 🇻‍🇦 | `:vatican_city:` | | 🇻‍🇪 | `:venezuela:` | | 🚦 | `:vertical_traffic_light:` | | 📼 | `:vhs:` | | 📳 | `:vibration_mode:` | | 📹 | `:video_camera:` | | 🎮 | `:video_game:` | | 🇻‍🇳 | `:vietnam:` | | 🎻 | `:violin:` | | ♍ | `:virgo:` | | 🌋 | `:volcano:` | | 🏐 | `:volleyball:` | | 🤮 | `:vomiting_face:` | | 🆚 | `:vs:` | | 🖖 | `:vulcan_salute:` | | 🧇 | `:waffle:` | | 🏴‍󠁧‍󠁢‍󠁷‍󠁬‍󠁳‍󠁿 | `:wales:` | | 🚶 | `:walking:` | | 🚶‍♂ | `:walking_man:` | | 🚶‍♀ | `:walking_woman:` | | 🇼‍🇫 | `:wallis_futuna:` | | 🌘 | `:waning_crescent_moon:` | | 🌖 | `:waning_gibbous_moon:` | | ⚠ | `:warning:` | | 🗑 | `:wastebasket:` | | ⌚ | `:watch:` | | 🐃 | `:water_buffalo:` | | 🤽 | `:water_polo:` | | 🍉 | `:watermelon:` | | 👋 | `:wave:` | | 〰 | `:wavy_dash:` | | 🌒 | `:waxing_crescent_moon:` | | 🌔 | `:waxing_gibbous_moon:` | | 🚾 | `:wc:` | | 😩 | `:weary:` | | 💒 | `:wedding:` | | 🏋 | `:weight_lifting:` | | 🏋‍♂ | `:weight_lifting_man:` | | 🏋‍♀ | `:weight_lifting_woman:` | | 🇪‍🇭 | `:western_sahara:` | | 🐳 | `:whale:` | | 🐋 | `:whale2:` | | 🛞 | `:wheel:` | | ☸ | `:wheel_of_dharma:` | | ♿ | `:wheelchair:` | | ✅ | `:white_check_mark:` | | ⚪ | `:white_circle:` | | 🏳 | `:white_flag:` | | 💮 | `:white_flower:` | | 👨‍🦳 | `:white_haired_man:` | | 👩‍🦳 | `:white_haired_woman:` | | 🤍 | `:white_heart:` | | ⬜ | `:white_large_square:` | | ◽ | `:white_medium_small_square:` | | ◻ | `:white_medium_square:` | | ▫ | `:white_small_square:` | | 🔳 | `:white_square_button:` | | 🥀 | `:wilted_flower:` | | 🎐 | `:wind_chime:` | | 🌬 | `:wind_face:` | | 🪟 | `:window:` | | 🍷 | `:wine_glass:` | | 🪽 | `:wing:` | | 😉 | `:wink:` | | 🛜 | `:wireless:` | | 🐺 | `:wolf:` | | 👩 | `:woman:` | | 👩‍🎨 | `:woman_artist:` | | 👩‍🚀 | `:woman_astronaut:` | | 🧔‍♀ | `:woman_beard:` | | 🤸‍♀ | `:woman_cartwheeling:` | | 👩‍🍳 | `:woman_cook:` | | 💃 | `:woman_dancing:` | | 🤦‍♀ | `:woman_facepalming:` | | 👩‍🏭 | `:woman_factory_worker:` | | 👩‍🌾 | `:woman_farmer:` | | 👩‍🍼 | `:woman_feeding_baby:` | | 👩‍🚒 | `:woman_firefighter:` | | 👩‍⚕ | `:woman_health_worker:` | | 👩‍🦽 | `:woman_in_manual_wheelchair:` | | 👩‍🦼 | `:woman_in_motorized_wheelchair:` | | 🤵‍♀ | `:woman_in_tuxedo:` | | 👩‍⚖ | `:woman_judge:` | | 🤹‍♀ | `:woman_juggling:` | | 👩‍🔧 | `:woman_mechanic:` | | 👩‍💼 | `:woman_office_worker:` | | 👩‍✈ | `:woman_pilot:` | | 🤾‍♀ | `:woman_playing_handball:` | | 🤽‍♀ | `:woman_playing_water_polo:` | | 👩‍🔬 | `:woman_scientist:` | | 🤷‍♀ | `:woman_shrugging:` | | 👩‍🎤 | `:woman_singer:` | | 👩‍🎓 | `:woman_student:` | | 👩‍🏫 | `:woman_teacher:` | | 👩‍💻 | `:woman_technologist:` | | 🧕 | `:woman_with_headscarf:` | | 👩‍🦯 | `:woman_with_probing_cane:` | | 👳‍♀ | `:woman_with_turban:` | | 👰‍♀ | `:woman_with_veil:` | | 👚 | `:womans_clothes:` | | 👒 | `:womans_hat:` | | 🤼‍♀ | `:women_wrestling:` | | 🚺 | `:womens:` | | 🪵 | `:wood:` | | 🥴 | `:woozy_face:` | | 🗺 | `:world_map:` | | 🪱 | `:worm:` | | 😟 | `:worried:` | | 🔧 | `:wrench:` | | 🤼 | `:wrestling:` | | ✍ | `:writing_hand:` | | ❌ | `:x:` | | 🩻 | `:x_ray:` | | 🧶 | `:yarn:` | | 🥱 | `:yawning_face:` | | 🟡 | `:yellow_circle:` | | 💛 | `:yellow_heart:` | | 🟨 | `:yellow_square:` | | 🇾‍🇪 | `:yemen:` | | 💴 | `:yen:` | | ☯ | `:yin_yang:` | | 🪀 | `:yo_yo:` | | 😋 | `:yum:` | | 🇿‍🇲 | `:zambia:` | | 🤪 | `:zany_face:` | | ⚡ | `:zap:` | | 🦓 | `:zebra:` | | 0‍⃣ | `:zero:` | | 🇿‍🇼 | `:zimbabwe:` | | 🤐 | `:zipper_mouth_face:` | | 🧟 | `:zombie:` | | 🧟‍♂ | `:zombie_man:` | | 🧟‍♀ | `:zombie_woman:` | | 💤 | `:zzz:` | # Excalidraw # Excalidraw Excalidraw is an excellent tool that offers a whiteboard on which you can easily sketch diagrams with a hand-drawn feel. You can use [https://excalidraw.com/](https://excalidraw.com/), download your drawing as a JSON-file, put it in the public directory and create an Excalidraw element (see below). You could also use the integrated editor in hyperbook. When running your hyperbook in development mode, you see two buttons below an Excalidraw whiteboard and all editor options. You can edit your whiteboard here and just hit save. The Excalidraw will be saved at the place which you have defined. ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | Path to an Excalidraw file; in development mode it can be created automatically | - | | `aspectRatio` | Aspect ratio, for example `16/9`, `4/3`, or `1/1` | `16/9` | | `autoZoom` | Automatically zoom the drawing to fit smaller screens | `true` | | `edit` | Enable editing mode | `false` | :::alert{info} autoZoom does not seems to work, when using a file from excalidraw.com, since the file does not come with its original dimensions. ::: ```md ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3"} ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3" autoZoom=false} ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3" autoZoom=true edit=true} ``` ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3"} ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3" autoZoom=false} :::alert{info} Be sure to set the correct language in your hyperbook config src, otherwise Excalidraw will use the English. For example for German use "de". ::: ::excalidraw{src="/excalidraw/hyperbook.excalidraw" aspectRatio="4/3" autoZoom=true edit=true} ## Adding Libraries If you want to add libraries, you can download one from https://libraries.excalidraw.com and import it by clicking the book icon in the top right corner and then the folder icon to select the library. ## Configuration You can configure the default value of the arguments in the `hyperbook.json`. For example: ```json { "elements": { "excalidraw": { "aspectRatio": "4/3", "autoZoom": true, "edit": false } } } ``` # GeoGebra # GeoGebra :::alert{warn} **Requires a network connection.** The GeoGebra application is loaded at runtime from `geogebra.org` and is not bundled with the hyperbook build output. GeoGebra is proprietary and cannot be self-hosted or redistributed. This element will not work in offline or network-restricted environments. ::: GeoGebra applets can be embedded in various ways. ## Attributes | Attribute | Description | Default | |---|---|---| | `perspective` | Visible GeoGebra views, for example `G` for geometry | - | | `height` | Height of the applet in pixels | `600` | | `width` | Width of the applet in pixels | `800` | | `coordsystem` | Visible coordinate system as `xmin,xmax,ymin,ymax` | - | | `src` | URL or path to an existing GeoGebra applet or `.ggb` file | - | | `appName` | GeoGebra app to load, for example `graphing`, `geometry`, or `suite` | - | | `showFullscreenButton` | Show the fullscreen button | `true` | | `showResetIcon` | Show the reset icon | `true` | On one hand, GeoGebra instructions can be passed directly. ```md :::geogebra{perspective="G" height=400 width=700 coordsystem="-10,10,-2,8"} a = Slider[-5,5] SetCoords(a, 450, 370) f_a(x) = x^2 * a + a * x ::: ``` :::geogebra{perspective="G" height=400 width=700 coordsystem="-10,10,-2,8"} a = Slider[-5,5] SetCoords(a, 450, 370) f_a(x) = x^2 * a + a * x ::: On the other hand, existing applets can also be loaded. ```md ::geogebra{src="https://www.geogebra.org/m/sA5Mb4vd"} ``` ::geogebra{src="https://www.geogebra.org/m/sA5Mb4vd"} ## Arguments The GeoGebra element is a simple wrapper for the GeoGebra API. Therefore, the arguments can be taken from the [GeoGebra Documentation](https://geogebra.github.io/docs/reference/en/GeoGebra_App_Parameters/). Additionally, you can set other arguments aswell, which come for the original [web-component](https://somethingorotherwhatever.com/geogebra-component/reference.html). | **Arguments** | **Description** | |---------------------------|-------------------------------------------------------------------------------------------------------------| | perspective | Define which views are visible, and in which order. A string of letters corresponding to views, as described in the documentation for SetPerspective. | | axes | A comma-separated list of true or false values defining whether each axis is visible, in the order x,y,z. | | xaxis | Is the x axis visible? | | yaxis | Is the y axis visible? | | zaxis | Is the z axis visible? | | pointcapture | The point capturing mode: none, snap, fixed, or automatic. | | rounding | How to round numbers. A number followed by either d for "decimal places", or s for "significant figures". | | hidecursorwhendragging | Hide the cursor when dragging objects? | | repaintingactive | Repaint the applet when anything changes? | | errordialogsactive | Should error dialogs be shown? | | coordsystem | A comma-separated list of coordinates for the viewport of the graphics window, in the order xmin,xmax,ymin,ymax. | | axislabels | A comma-separated list of labels for the axes, in the order x,y,z. | | xaxislabel | The label The following example uses some of these arguments: ```md ::geogebra{showToolBar showAlgebraInput algebraInputPosition="bottom" enableUndoRedo showMenuBar enableFileFeatures showZoomButtons} ``` ::geogebra{showToolBar showAlgebraInput algebraInputPosition="bottom" enableUndoRedo showMenuBar enableFileFeatures showZoomButtons} ## Apps ### Suite ```hyperbook :::geogebra{appName="suite" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="suite" showToolbar showAlgebraInput} ::: ### Classic ```hyperbook :::geogebra{appName="classic" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="classic" showToolbar showAlgebraInput} ::: ### Graphing ```hyperbook :::geogebra{appName="graphing" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="graphing" showToolbar showAlgebraInput} ::: ### Geometry ```hyperbook :::geogebra{appName="geometry" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="geometry" showToolbar showAlgebraInput} ::: ### 3D ```hyperbook :::geogebra{appName="3d" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="3d" showToolbar showAlgebraInput} ::: ### Scientific ```hyperbook :::geogebra{appName="scientific" showToolbar showAlgebraInput} ::: ``` :::geogebra{appName="scientific" showToolbar showAlgebraInput} ::: ### Notes ```hyperbook :::geogebra{appName="notes" showToolbar} ::: ``` :::geogebra{appName="notes" showToolbar} ::: # Glossary # Glossary You can keep a glossary of important terms. Just fill the folder `glossary` with markdown files. The name of the file will be the key of the term. You can then reference the terms like so: ## Attributes | Attribute | Description | Default | |---|---|---| | `#` | Glossary entry id, usually matching the filename in `glossary/` | - | | `.` | Optional anchor to link to a specific heading inside the glossary entry | - | ```md :t[Object-oriented Programming]{#oop} ``` :t[Object-oriented Programming]{#oop} You can also link directly to a header. ```md :t[Object-oriented Programming]{#oop.bluej-by-example} ``` :t[Object-oriented Programming]{#oop.bluej-by-example} # H5P # H5P H5P is a free and open-source content collaboration framework based on JavaScript. H5P is short for HTML5 Package, and aims to make it easy for everyone to create, share and reuse interactive HTML5 content. H5P is a popular tool for creating interactive content, such as quizzes, presentations, and games. You can embed H5P content in your book using the `h5p` element. You can load H5P content from a file. ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | Path to the H5P file | - | ```md ::h5p{src="/test.h5p"} ``` ::h5p{src="/test.h5p"} The best way to create H5P content is to use the [ZUM Apps](https://apps.zum.de) website. You can create and edit H5P content on the website, and then download the content as an H5P file. For this you can use the Reuse button. You can then put the h5p file into your public folder and use it in a h5p-element. ## Example with Reuse-Button ::h5p{src="/reuse.h5p"} # JSXGraph # JSXGraph JSXGraph is a JavaScript library for interactive geometry, function plotting, charting, and other mathematical visualizations in a web browser. It is free and open-source, and can be used to create dynamic and interactive mathematical content. You can embed JSXGraph in your book using the `jsxgraph` element. You can use the `height` and `width` attributes to set the size of the JSXGraph board. The `boundingbox` attribute is used to set the bounding box of the board. The `axis` and `grid` attributes are used to show or hide the axis and grid. ## Attributes | Attribute | Description | Default | |---|---|---| | `height` | Height of the JSXGraph board in pixels | `600` | | `width` | Width of the JSXGraph board in pixels | `800` | | `boundingbox` | Visible board area as `[left, top, right, bottom]` | `[-5, 5, 5, -5]` | | `axis` | Show or hide the axes | `true` | | `grid` | Show or hide the grid | `true` | In your code you must use the `board` variable to access the JSXGraph board. You can create points, lines, curves, and other elements on the board using the `board.create` method. The first argument is the type of element you want to create, and the second argument is an array of parameters for the element. You can find the documentation for JSXGraph [here](https://jsxgraph.org/docs/) and examples [here](https://jsxgraph.uni-bayreuth.de/share/). ````md :::jsxgraph{height="500" width="600" boundingbox="[-10,10,14,-10]" axis=false grid=false} ```js var a = board.create('slider', [[1,8],[5,8],[0,1,4]]); var b = board.create('slider', [[1,9],[5,9],[0,0.25,4]]); var c = board.create('curve', [function(phi){return a.Value()+b.Value()*phi; }, [0, 0], 0, 8*Math.PI], {curveType:'polar', strokewidth:4}); ``` ::: ```` :::jsxgraph{height="500" width="600" boundingbox="[-10,10,14,-10]" axis=false grid=false} ```js var a = board.create('slider', [[1,8],[5,8],[0,1,4]]); var b = board.create('slider', [[1,9],[5,9],[0,0.25,4]]); var c = board.create('curve', [function(phi){return a.Value()+b.Value()*phi; }, [0, 0], 0, 8*Math.PI], {curveType:'polar', strokewidth:4}); ``` ::: You can also create very complex JSXGraph visualizations. :::jsxgraph{boundingbox="[-1.5,28.5,28.5,-1.5]"} ```js // Define sliders to dynamically change parameters of the equations and create text elements to describe them s = board.create( "slider", [ [20.0, 26.0], [25.0, 26.0], [0.0, 0.3, 1.0], ], { name: "ε1", strokeColor: "black", fillColor: "black" } ); st = board.create("text", [20, 25, "Birth rate predators"], { fixed: true }); u = board.create( "slider", [ [20.0, 24.0], [25.0, 24.0], [0.0, 0.7, 1.0], ], { name: "ε2", strokeColor: "black", fillColor: "black" } ); ut = board.create("text", [20, 23, "Death rate predators"], { fixed: true }); o = board.create( "slider", [ [10.0, 26.0], [15.0, 26.0], [0.0, 0.1, 1.0], ], { name: "γ1", strokeColor: "black", fillColor: "black" } ); ot = board.create("text", [10, 25, "Death rate preys/per predator"], { fixed: true, }); p = board.create( "slider", [ [10.0, 24.0], [15.0, 24.0], [0.0, 0.3, 1.0], ], { name: "γ2", strokeColor: "black", fillColor: "black" } ); pt = board.create("text", [10, 23, "Reproduction rate pred./per prey"], { fixed: true, }); // Dynamic initial value as gliders on the y-axis startpred = board.create("glider", [0, 10, board.defaultAxes.y], { name: "Preys", strokeColor: "red", fillColor: "red", }); startprey = board.create("glider", [0, 5, board.defaultAxes.y], { name: "Predators", strokeColor: "blue", fillColor: "blue", }); // Variables for the JXG.Curves var g3 = null; var g4 = null; // Initialise ODE and solve it with JXG.Math.Numerics.rungeKutta() function ode() { // evaluation interval var I = [0, 25]; // Number of steps. 1000 should be enough var N = 1000; // Right hand side of the ODE dx/dt = f(t, x) var f = function (t, x) { var bpred = s.Value(); //0.3; var bprey = u.Value(); //0.7; var dpred = o.Value(); //0.1; var dprey = p.Value(); //0.3; var y = []; y[0] = x[0] * (bpred - dpred * x[1]); y[1] = -x[1] * (bprey - dprey * x[0]); return y; }; // Initial value var x0 = [startpred.Y(), startprey.Y()]; // Solve ode var data = JXG.Math.Numerics.rungeKutta("euler", x0, I, N, f); // to plot the data against time we need the times where the equations were solved var t = []; var q = I[0]; var h = (I[1] - I[0]) / N; for (var i = 0; i < data.length; i++) { data[i].push(q); q += h; } return data; } // get data points var data = ode(); // copy data to arrays so we can plot it using JXG.Curve var t = []; var dataprey = []; var datapred = []; for (var i = 0; i < data.length; i++) { t[i] = data[i][2]; datapred[i] = data[i][0]; dataprey[i] = data[i][1]; } // Plot Predator g3 = board.create("curve", [t, datapred], { strokeColor: "red", strokeWidth: "2px", }); g3.updateDataArray = function () { var data = ode(); this.dataX = []; this.dataY = []; for (var i = 0; i < data.length; i++) { this.dataX[i] = t[i]; this.dataY[i] = data[i][0]; } }; // Plot Prey g4 = board.create("curve", [t, dataprey], { strokeColor: "blue", strokeWidth: "2px", }); g4.updateDataArray = function () { var data = ode(); this.dataX = []; this.dataY = []; for (var i = 0; i < data.length; i++) { this.dataX[i] = t[i]; this.dataY[i] = data[i][1]; } }; ``` ::: # Kiri:Moto # Kiri:Moto :::alert{warn} **Requires a network connection.** Kiri:Moto is loaded at runtime from `grid.space` and is not bundled with the hyperbook build output. This element will not work in offline or network-restricted environments. ::: [Kiri:Moto](https://grid.space/kiri) is a browser-based slicer for 3D printers, CNC mills, and laser cutters. It can be embedded directly in a hyperbook page. ```md ::kirimoto ``` ::kirimoto ## Attributes | Attribute | Description | Default | |---|---|---| | `height` | Height of the embedded slicer | `calc(100vh - 60px)` | | `mode` | Starting mode: `FDM`, `CAM`, `LASER`, or `SLA` | — | | `model` | URL of an STL or model file to load automatically | — | | `workspace` | URL of a `.kmz` workspace file to import (must be served over HTTPS with `Access-Control-Allow-Origin: *`) | — | | `settings` | Shared settings key to restore (e.g. `1qzciqo/3` — obtained via the `U` hotkey inside Kiri:Moto) | — | ## Modes Use the `mode` attribute to open Kiri:Moto in a specific mode. ```md ::kirimoto{mode="FDM"} ``` ::kirimoto{mode="FDM"} ## Loading a Model Use the `model` attribute to automatically load an STL file. ```md ::kirimoto{model="/models/cube.stl"} ``` ## Loading a Workspace Use the `workspace` attribute to import a `.kmz` workspace file exported from Kiri:Moto. The file must be served over HTTPS and have CORS header `Access-Control-Allow-Origin` set to `grid.space` or `*`. ```md ::kirimoto{workspace="./kiri-workspace.kmz"} ``` ::kirimoto{workspace="./kiri-workspace.kmz"} ## Shared Settings Use the `settings` attribute to pre-load a saved configuration. Inside Kiri:Moto press `U` to get a settings key, then pass it here: ```md ::kirimoto{settings="1qzciqo/3"} ``` ::kirimoto{settings="1qzciqo/3"} ## Global Configuration You can set defaults for all Kiri:Moto elements in `hyperbook.json`: ```json { "elements": { "kirimoto": { "height": "700px", "settings": "13b1vam/1" } } } ``` ## Content Security Policy If your hyperbook is served with a strict Content Security Policy, you must allow framing from `https://grid.space`: ``` frame-src https://grid.space; ``` # Learningmap # Learningmap The `learningmap` element lets you embed interactive learning roadmaps directly in your Markdown files. You do **not** need to write any HTML. ## Basic Usage To add a learning map, use the following Markdown block: ````markdown ::learningmap{id="learningmap-example" height="600px" src="test.learningmap"} ```` ::learningmap{id="learningmap-example" height="600px" src="test.learningmap"} ## Attributes | Attribute | Description | Default | |---|---|---| | `id` | Unique identifier for the learning map instance | auto-generated | | `height` | Height of the learning map container, for example `600px` or `100%` | `calc(100vh - 80px)` | | `src` | Path to the `.learningmap` file | - | ## Editor You should use the learningmap editor to create and manage your learning maps. The editor provides a user-friendly interface to design your learning paths and export them as JSON files. [Open Learningmap Editor](https://learningmap.app/create) You could also install the VSCode extension for Learningmap for editing learning maps on the fly. [Learningmap VSCode Extension](https://marketplace.visualstudio.com/items?itemName=openpatch.learningmap-studio) # Math # Math Sometimes you wished for $ \LaTeX $ but then you remember you are writing in Markdown. No problem, we have added KaTeX support. ```md $$ f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi $$ ``` $$ f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi $$ You can also use KaTeX inline. ```md This is a $ \Delta $. :smiley: ``` This is a $ \Delta $. :smiley: # Mermaid # Mermaid [Mermaid](https://mermaid-js.github.io/mermaid) lets you create diagrams and visualizations using text and code. ````mermaid-js ```mermaid classDiagram Animal <|-- Duck Animal <|-- Fish Animal <|-- Zebra Animal : +int age Animal : +String gender Animal: +isMammal() Animal: +mate() class Duck{ +String beakColor +swim() +quack() } class Fish{ -int sizeInFeet -canEat() } class Zebra{ +bool is_wild +run() } ``` ```` This syntax is in line with the approach GitHub, GitLab and other have implemented. But we also provide a second syntax for using mermaid diagrams, which is more in line with the other elements of hyperbook. Both support the same features, therefore it comes down to your preferences and if you want cross-compatability with GitHub, GitLab and others. ```mermaid-js :::mermaid classDiagram Animal <|-- Duck Animal <|-- Fish Animal <|-- Zebra Animal : +int age Animal : +String gender Animal: +isMammal() Animal: +mate() class Duck{ +String beakColor +swim() +quack() } class Fish{ -int sizeInFeet -canEat() } class Zebra{ +bool is_wild +run() } ::: ``` ```mermaid classDiagram Animal <|-- Duck Animal <|-- Fish Animal <|-- Zebra Animal : +int age Animal : +String gender Animal: +isMammal() Animal: +mate() class Duck{ +String beakColor +swim() +quack() } class Fish{ -int sizeInFeet -canEat() } class Zebra{ +bool is_wild +run() } ``` # Multievent # Multievent :::alert{info} The Multievent library originates from [Aufgabenfuchs](https://www.aufgabenfuchs.de/sonstiges/multievent.shtml) and has been adapted for Hyperbook. ::: With Multievent, 26 different interactive event formats can be implemented on a webpage. This JavaScript searches the document for areas enclosed in double curly braces {{…}}. The enclosed content is transformed into an interactive element. Characters between the opening curly braces determine the result. Below is a list of formats and the corresponding character sequences. For example, a small {r{…}} creates an evaluable radio button in front of the enclosed term (see below). ## Feedback For tasks that are evaluated, the following encodings can be added: ```md :::multievent {H{Hint displayed after evaluation if the task is correctly evaluated.}} {h{Hint displayed after evaluation if the task is incorrectly evaluated.}} ::: ``` :::multievent {H{Hint displayed after evaluation if the task is correctly evaluated.}} {h{Hint displayed after evaluation if the task is incorrectly evaluated.}} ::: ## Task 1 Click on the fastest land animal in the world. Syntax: {r1{, {r2{, {r3{ (small r with number) Result: Radio button on the left (The exclamation mark ! indicates the correct term. Related buttons share the same number.) ```md :::multievent {r1{Leopard}} {r1{!Cheetah}} {r1{Gazelle}} {r1{Ostrich}} {r1{Greyhound}} ::: ``` :::multievent {r1{Leopard}} {r1{!Cheetah}} {r1{Gazelle}} {r1{Ostrich}} {r1{Greyhound}} ::: ## Task 2 Click on the largest marine animal below. Syntax: {R1{, {R2{, {R3{ (capital R with number) Result: Radio button on the right (The exclamation mark ! indicates the correct term. Related buttons share the same number.) ```md :::multievent {R1{!Blue Whale}} {R1{Sperm Whale}} {R1{Giant Squid}} {R1{Basking Shark}} {R1{Whale Shark}} ::: ``` :::multievent {R1{!Blue Whale}} {R1{Sperm Whale}} {R1{Giant Squid}} {R1{Basking Shark}} {R1{Whale Shark}} ::: ## Task 3 Click on all mammals. As long as not all boxes are correctly checked, magnifying glasses will appear next to the boxes after evaluation. Syntax: {c{ (small c) To group tasks: {c1{; {c2{ ... Result: Checkbox on the left (The exclamation mark ! indicates a correct term.) ```md :::multievent {c{!Gazelle}} {c{!Mouse}} {c{Giant Squid}} {c{Ostrich}} {c{!Greyhound}} ::: ``` :::multievent {c{!Gazelle}} {c{!Mouse}} {c{Giant Squid}} {c{Ostrich}} {c{!Greyhound}} ::: ## Task 4 Click on all freshwater fish. As long as not all boxes are correctly checked, magnifying glasses will appear next to the boxes after evaluation. Syntax: {C{ (capital C) To group tasks: {C1{; {C2{ ... Result: Checkbox on the right (The exclamation mark ! indicates a correct term.) ```md :::multievent {C{Cod}} {C{!Trout}} {C{!Carp}} {C{Herring}} {C{!Pikeperch}} ::: ``` :::multievent {C{Cod}} {C{!Trout}} {C{!Carp}} {C{Herring}} {C{!Pikeperch}} ::: ## Task 5 Arrange the saying below correctly. Syntax: {a{ (small a) Result: Dropdown without empty selection (The exclamation mark ! indicates a correct term. The vertical bar | separates dropdown options.) ```md :::multievent A {a{Duck|Tit|Seagull|!Swallow|Pigeon}} does not make a summer. ::: ``` :::multievent A {a{Duck|Tit|Seagull|!Swallow|Pigeon}} does not make a summer. ::: ## Task 6 Click on the bird with the largest wingspan. Syntax: {A{ (capital A) Result: Dropdown with empty selection (The exclamation mark ! indicates a correct term. The vertical bar | separates dropdown options.) ```md :::multievent {A{Andean Condor|Dalmatian Pelican|Marabou Stork|Trumpeter Swan|!Wandering Albatross}} ::: ``` :::multievent {A{Andean Condor|Dalmatian Pelican|Marabou Stork|Trumpeter Swan|!Wandering Albatross}} ::: ## Task 7 Click on the correct animal. Syntax: {S1{ (capital S with number S1, S2, S3 ...) Result: Dropdown (All areas with the same number are collected in the dropdown.) ```md :::multievent The {S1{Cuckoo}} and the {S1{Donkey}}, they had a quarrel ... ::: ``` :::multievent The {S1{Cuckoo}} and the {S1{Donkey}}, they had a quarrel ... ::: ## Task 8 Click on all mammals. Syntax: {k{ (small k) To group tasks: {k1{; {k2{ ... Result: Clickable button (The exclamation mark ! indicates a correct term. After correct evaluation, excess terms disappear.) ```md :::multievent {k{Cuckoo}} {k{!Donkey}} {k{!Cow}} {k{Fish}} ::: ``` :::multievent {k{Cuckoo}} {k{!Donkey}} {k{!Cow}} {k{Fish}} ::: ## Task 9 Click on all mammals. Syntax: {K{ (capital K) To group tasks: {K1{; {K2{ ... Result: Clickable button (The exclamation mark ! indicates a correct term. After correct evaluation, excess terms remain visible.) ```md :::multievent {K{Cuckoo}}, {K{!Donkey}}, {K{!Cow}}, {K{Fish}} ::: ``` :::multievent {K{Cuckoo}}, {K{!Donkey}}, {K{!Cow}}, {K{Fish}} ::: ## Task 10 Enter the correct animal. Syntax: {l{ (small l) Result: Text field with scrambled word as a hint (Case-insensitive.) ```md :::multievent All my {l{Ducklings}} are swimming on the lake, ... ::: ``` :::multievent All my {l{Ducklings}} are swimming on the lake, ... ::: ## Task 11 Enter the correct animal. Syntax: {L{ (capital L) Result: Text field with scrambled word as a hint (Case-sensitive.) ```md :::multievent On the wall, on the wall lies a little {L{Bug}} ... ::: ``` :::multievent On the wall, on the wall lies a little {L{Bug}} ... ::: ## Task 12 Enter the correct animal. Syntax: {t{ (small t) Result: Empty text field (Case-insensitive.) ```md :::multievent The cuckoo and the {t{Donkey}}, they had a quarrel ... ::: ``` :::multievent The cuckoo and the {t{Donkey}}, they had a quarrel ... ::: ## Task 13 Enter the correct animal. Syntax: {T{ (capital T) Result: Empty text field (Case-sensitive.) ```md :::multievent Fox, you stole the {T{Goose}}, give it back ... ::: ``` :::multievent Fox, you stole the {T{Goose}}, give it back ... ::: ## Task 14 How many legs do a farmer, 2 cows, and 3 ducks have together? Syntax: {z{ (small z) Result: Empty text field for number input (In the example, 16; 16.0 and 16.00 are considered correct.) ```md :::multievent Answer: They have {z{16}} legs. ::: ``` :::multievent Answer: They have {z{16}} legs. ::: ## Task 15 Enter an odd number < 10. Syntax: {z{ (small z) Result: Empty text field for number input from a group of numbers (The vertical bar | separates alternatives. In the example, the numbers 1, 3, 5, 7, and 9 are considered correct.) ```md :::multievent Answer: {z{1|3|5|7|9}} ::: ``` :::multievent Answer: {z{1|3|5|7|9}} ::: ## Task 16 How many legs do a farmer, x cows, and y ducks have together? Syntax: {X{ with {Z{ (capital X with capital Z) Result: Differently filled text sections {X{ with adjusted number input values {Z{ (The vertical bar | separates values, which are called sequentially when the square "Enter" arrow ↵ next to the evaluation is clicked.) ```md :::multievent How many legs do a farmer, {X{4|2|3|5}} cows, and {X{2|4|5|3}} ducks have together? Answer: They have {Z{22|18|24|28}} legs. ::: ``` :::multievent How many legs do a farmer, {X{4|2|3|5}} cows, and {X{2|4|5|3}} ducks have together? Answer: They have {Z{22|18|24|28}} legs. ::: ## Task 17 Enter the German term. Syntax: {X{ with {y{ (capital X with small y) Result: Differently filled text sections {X{ with adjusted text input values {y{ (The vertical bar | separates values, which are called sequentially when the square "Enter" arrow ↵ next to the evaluation is clicked. Case-insensitive.) ```md :::multievent English: {X{cat|cow|dog|horse|pig}} → German: {y{Cat|Cow|Dog|Horse|Pig}} ::: ``` :::multievent English: {X{cat|cow|dog|horse|pig}} → German: {y{Cat|Cow|Dog|Horse|Pig}} ::: ## Task 18 Enter the German term. Syntax: {X{ with {Y{ (capital X with capital Y) Result: Differently filled text sections {X{ with adjusted text input values {Y{ (The vertical bar | separates values, which are called sequentially when the square "Enter" arrow ↵ next to the evaluation is clicked. Case-sensitive.) ```md :::multievent English: {X{bee|dolphin|eagle|shark|spider}} → German: {Y{Bee|Dolphin|Eagle|Shark|Spider}} ::: ``` :::multievent English: {X{bee|dolphin|eagle|shark|spider}} → German: {Y{Bee|Dolphin|Eagle|Shark|Spider}} ::: ## Task 19 Take notes. Syntax: {n{ (small n) Result: Text area for notes. (For the duration of the browser session, helper texts can be entered in the browser.) ```md :::multievent {n{200|22|Notes}} {n{Field Width|Field Height|Text}} ::: ``` :::multievent {n{200|22|Notes}} {n{Field Width|Field Height|Text}} ::: ## Task 20 Click on the eye for more information. Syntax: {b{ (small b) Result: Toggle for hidden help offers. (Clicking on the eye shows or hides the help.) ```md :::multievent {b{ Help 1, Help 2, ...}} ::: ``` :::multievent {b{ Help 1, Help 2, ...}} ::: ## Task 21 Click on the eye for more information. Syntax: {B{ (capital B) Result: Indented toggle for hidden help offers. (Clicking on the eye shows or hides the help.) ```md :::multievent {B{ Help 1, Help 2, ...}} ::: ``` :::multievent {B{ Help 1, Help 2, ...}} ::: ## Task 22 Click in the input window and press the correct letters for the searched bird. Syntax: {v{ (small v) Result: Word search (The vertical bar | separates the terms.) ```md :::multievent {v{Woodpecker|Eagle|Pheasant}} ::: ``` :::multievent {v{Woodpecker|Eagle|Pheasant}} ::: ## Task 23 Click on the letters of the animals in the word search. Syntax: {w{ (small w) Result: Word search (The vertical bar | separates the cells. The double tilde ~~ separates the rows. Uppercase letters - correct; lowercase letters - incorrect.) ```md :::multievent {w{ a|M|p|b~~ H|A|H|N~~ d|U|g|d~~ E|S|E|L}} ::: ``` :::multievent {w{ a|M|p|b~~ H|A|H|N~~ d|U|g|d~~ E|S|E|L}} ::: ## Task 24 Solve the crossword puzzle. Syntax: {W{ (capital W) Result: Crossword puzzle (The vertical bar | separates the cells. The double tilde ~~ separates the rows.) ```md :::multievent {W{ | | |1| ~~ 2|D|U|C|K~~ | | |A| ~~ | | |U| ~~ | | |B| ~~ | | |E| }} ::: ``` :::multievent {W{ | | |1| ~~ 2|D|U|C|K~~ | | |A| ~~ | | |U| ~~ | | |B| ~~ | | |E| }} ::: ## Combine The different events can be freely combined. ```md :::multievent - a) {r1{!correct}} {r1{wrong}} - b) {R1{!correct}} {R1{wrong}} - c) {c{!correct}} {c{wrong}} - d) {C{!correct}} {C{wrong}} - e) {a{!correct|wrong}} - f) {A{!correct|wrong}} ::: ``` :::multievent - a) {r1{!correct}} {r1{wrong}} - b) {R1{!correct}} {R1{wrong}} - c) {c{!correct}} {c{wrong}} - d) {C{!correct}} {C{wrong}} - e) {a{!correct|wrong}} - f) {A{!correct|wrong}} ::: ## Mathematical Formulas Mathematical formulas can also be used in the events. These are enclosed in curly braces. Select all irrational numbers. ```md :::multievent {c{!$\sqrt{2}$}} {c{!$\pi$}} {c{$\frac{1}{2}$}} ::: ``` :::multievent {c{!$\sqrt{2}$}} {c{!$\pi$}} {c{$\frac{1}{2}$}} ::: Select the correct exponent. :::multievent 2^{{a{2|3|!4|8}}}=16 ::: # Online IDE # Online IDE Java-like programming language (compiler, interpreter, debugger) with IDE that runs entirely in the browser. ## Attributes | Attribute | Description | Default | |---|---|---| | `fileList` | Show the file list on the left | `true` | | `pCode` | Show the PCode tab in the bottom panel | `false` | | `bottomPanel` | Show the bottom panel | `true` | | `errorList` | Show the error list in the bottom panel | `true` | | `height` | Height of the editor | `calc(100dvh - 80px)` | | `speed` | Execution speed | `1000` | | `libraries` | Comma-separated list of libraries to preload, for example `nrw` | - | (See: https://github.com/martin-pabst/Online-IDE-new-compiler). :::onlineide ````markdown A Hint ## Tip: Tips are written in a simple Markdown syntax which which allows **boldface** and the like, but also syntax syntax highlighting in body text (`class square extends rectangle { }`) and in whole paragraphs: ``` double v = Math.random()\*8 + 2; // Amount of speed between 2 and 10 double w = Math.random()*2*Math.PI; // angle between 0 and 2\*PI vx = v \* Math.cos(w); vy = v \* Math.sin(w); ``` ```` ```java Feuerwerk.java new Feuerwerk(); class Feuerwerk extends Actor { public void act() { if(Math.random() < 0.03) { int funkenzahl = Math.floor(Math.random() * 50 + 30); int farbe = Color.randomColor(128); double x = Math.random() * 400 + 200; double y = Math.random() * 600; double lebensdauer = 60 + Math.random() * 60; for (int i = 0; i < funkenzahl; i++) { new Funke(x, y, farbe, lebensdauer); } Sound.playSound(Sound.cannon_boom); } } } class Funke extends Circle { double vx; double vy; double lebensdauer; // lebensdauer in 1/30 s Funke(double x, double y, int farbe, double lebensdauer) { super(x, y, 4); double winkel = Math.random() * 2 * Math.PI; double v = Math.random() * 15 + 5; vx = v * Math.cos(winkel); vy = v * Math.sin(winkel); setFillColor(farbe); this.lebensdauer = lebensdauer; } public void act() { lebensdauer--; move(vx, vy); vy = vy + 0.2; if(lebensdauer < 30) { setAlpha(lebensdauer / 30); } if(isOutsideView() || lebensdauer < 0) { destroy(); } } } ``` ::: `````markdown :::onlineide ````markdown A Hint ## Tip: Tips are written in a simple Markdown syntax which which allows **boldface** and the like, but also syntax syntax highlighting in body text (`class square extends rectangle { }`) and in whole paragraphs: ``` double v = Math.random()\*8 + 2; // Amount of speed between 2 and 10 double w = Math.random()*2*Math.PI; // angle between 0 and 2\*PI vx = v \* Math.cos(w); vy = v \* Math.sin(w); ``` ```` ```java Feuerwerk.java new Feuerwerk(); class Feuerwerk extends Actor { public void act() { if(Math.random() < 0.03) { int funkenzahl = Math.floor(Math.random() * 50 + 30); int farbe = Color.randomColor(128); double x = Math.random() * 400 + 200; double y = Math.random() * 600; double lebensdauer = 60 + Math.random() * 60; for (int i = 0; i < funkenzahl; i++) { new Funke(x, y, farbe, lebensdauer); } Sound.playSound(Sound.cannon_boom); } } } class Funke extends Circle { double vx; double vy; double lebensdauer; // lebensdauer in 1/30 s Funke(double x, double y, int farbe, double lebensdauer) { super(x, y, 4); double winkel = Math.random() * 2 * Math.PI; double v = Math.random() * 15 + 5; vx = v * Math.cos(winkel); vy = v * Math.sin(winkel); setFillColor(farbe); this.lebensdauer = lebensdauer; } public void act() { lebensdauer--; move(vx, vy); vy = vy + 0.2; if(lebensdauer < 30) { setAlpha(lebensdauer / 30); } if(isOutsideView() || lebensdauer < 0) { destroy(); } } } ``` ::: ````` ## With others libraries | Library | Parameter | Description | | --- | --- | --- | | Abitur classes NRW | libraries="nrw" | Class library for use in the central Abitur of North Rhine-Westphalia | | Graphics and Games Library | libraries="gng" | Graphical class library for the Bavarian computer science books of the Cornelsen publishing house | | Abitur classes Lower Saxony | libraries="niedersachsen" | Class library for use in the Abitur Lower Saxony | | Scratch for Java | libraries="scratch" | Scratch-like class library (Stage, Sprite, costumes, sounds) — a port of [org.openpatch.scratch](https://scratch4j.openpatch.org) | :::onlineide{height="500px" libraries="nrw"} ```java Test.java var l = new List(); l.append(1); ``` ::: ```` :::onlineide{height="500px" libraries="nrw"} ```java Test.java var l = new List(); l.append(1); ``` ::: ```` ### Scratch for Java `libraries="scratch"` makes the classes of [Scratch for Java](https://scratch4j.openpatch.org) available: `Stage`, `Sprite`, `AnimatedSprite`, `UISprite`, `Pen`, `Text`, `Camera`, `Timer` and the rest of the library. Programs written for the desktop library run unchanged in the browser, with two differences: - No `import org.openpatch.scratch.*;` — the Online IDE has no packages, all classes are available right away. - Everything that only a desktop program can do (shaders, the pixel buffer, recording, the file system, Tiled maps, fullscreen) compiles, but reports in the output that it does nothing. The 841 costumes, 266 sounds and the backdrops of the library are bundled, so an example needs no assets of its own. Browse the costumes on the [Sprites](https://scratch4j.openpatch.org/sprites) page of the Scratch for Java documentation. In the example below the bunny follows the mouse pointer and plays a sound whenever it reaches the carrot. :::onlineide{height="500px" libraries="scratch"} ```java MyStage.java new MyStage(); class MyStage extends Stage { MyStage() { super(480, 360); addBackdrop("background"); add(new Bunny()); add(new Carrot()); } } class Bunny extends Sprite { public void whenAddedToStage() { addCostume("bunny1_walk1"); addCostume("bunny1_walk2"); addSound("handleCoins"); setSize(60); setRotationStyle(RotationStyle.LEFT_RIGHT); setPosition(-180, -40); say("Move your mouse!", 2000); } public void run() { if (distanceToMousePointer() > 10) { pointTowardsMousePointer(); move(2); if (getTimer().everyMillis(150)) { nextCostume(); } } if (isTouchingSprite(Carrot.class)) { playSound("handleCoins"); getTouchingSprite(Carrot.class).goToRandomPosition(); } } public void whenClicked() { say("Hop!", 1000); } } class Carrot extends Sprite { public void whenAddedToStage() { addCostume("carrot"); setSize(50); setPosition(120, -40); } public void run() { turnRight(1); } } ``` ::: `````markdown :::onlineide{height="500px" libraries="scratch"} ```java MyStage.java new MyStage(); class MyStage extends Stage { MyStage() { super(480, 360); addBackdrop("background"); add(new Bunny()); add(new Carrot()); } } class Bunny extends Sprite { public void whenAddedToStage() { addCostume("bunny1_walk1"); addCostume("bunny1_walk2"); addSound("handleCoins"); setSize(60); setRotationStyle(RotationStyle.LEFT_RIGHT); setPosition(-180, -40); say("Move your mouse!", 2000); } public void run() { if (distanceToMousePointer() > 10) { pointTowardsMousePointer(); move(2); if (getTimer().everyMillis(150)) { nextCostume(); } } if (isTouchingSprite(Carrot.class)) { playSound("handleCoins"); getTouchingSprite(Carrot.class).goToRandomPosition(); } } public void whenClicked() { say("Hop!", 1000); } } class Carrot extends Sprite { public void whenAddedToStage() { addCostume("carrot"); setSize(50); setPosition(120, -40); } public void run() { turnRight(1); } } ``` ::: ````` # OpenSCAD # OpenSCAD :::alert{warn} **Requires a network connection for optional libraries and fonts.** When you select a library (BOSL2, BOSL, MCAD, NopSCADlib, fonts) in the editor, the corresponding ZIP file (~56 KB–4.3 MB) is downloaded from `ochafik.com`. The Roboto font used by the editor UI is loaded from Google Fonts. These resources are not bundled with the hyperbook build output. The core OpenSCAD editor works offline, but libraries and the custom font will not be available. ::: The `openscad` directive provides an interactive OpenSCAD editor with: - a **code view**, - a **parameter view** (JSON object mapped to `-D` variables), - and a **3D preview**. You can render the model, copy the code, and download exports as **STL** or **3MF**. ## Usage Wrap OpenSCAD code in a `:::openscad` block and use a `scad` (or `openscad`) code fence. ````md :::openscad ```scad cube([20,20,20], center=true); ``` ::: ```` :::openscad ```scad cube([20,20,20], center=true); ``` ::: ## Attributes | Attribute | Description | Default | |---|---|---| | `id` | Unique id for persistence | auto-generated | | `src` | Load source from an external file path | inline code block | | `height` | Height of the editor/preview container | `calc(100dvh - 80px)` | | `library` | Comma-separated list of libraries to load into the OpenSCAD environment | none | Possible libraries include: BOSL2, BOSL, MCAD, NopSCADlib, fonts ## Load code from file ````md :::openscad{src="openscad/example.scad"} ::: ```` ## Load binary files into the OpenSCAD filesystem Use `@file` directives inside the `:::openscad` block to mount binary files before rendering. In edit mode, you can also use the **Binary Files** panel to upload files directly (stored locally in browser storage for this block). ````md :::openscad @file dest="/input/model.stl" src="models/model.stl" ```scad import("/input/model.stl"); ``` ::: ```` Relative paths in `import()` are also supported and resolved like Typst assets (relative to the current page). Example: ````md :::openscad ```scad import("./koala.svg", center=true); ``` ::: ```` ## Example with variables ````md :::openscad ```scad segments = 5; size = 5; height = 10; rounded = 1; label = "K"; $fn = segments; module body(size, height, rounded) { if (rounded) { minkowski() { cube([size, size, height], center=true); sphere(r=1); } } else { cube([size, size, height], center=true); } } difference() { body(size, height, rounded); translate([0, 0, height / 2 + 0.1]) linear_extrude(height=1) text(label, size=8, halign="center", valign="center"); } ``` ::: ```` :::openscad ```scad segments = 5; size = 5; height = 10; rounded = 1; label = "K"; $fn = segments; module body(size, height, rounded) { if (rounded) { minkowski() { cube([size, size, height], center=true); sphere(r=1); } } else { cube([size, size, height], center=true); } } difference() { body(size, height, rounded); translate([0, 0, height / 2 + 0.1]) linear_extrude(height=1) text(label, size=8, halign="center", valign="center"); } ``` ::: ## Example with Library ````hyperbook :::openscad{library="BOSL2"} ```scad include include $fn = 100; rod_diameter = 12; module bridge(width = 230, height = 80, rod_diameter = 12) { pilar_width = height / 2; pilar_height = height + 5; middle_width = width - pilar_width * 2; middle_thickness = max(rod_diameter, 19); rod_diameter_plus_threshold = rod_diameter + 0.5; module pilar() { difference() { cube([pilar_width, middle_thickness + 10, pilar_height], center=true); rotate([90, 0, 0]) translate([0, 0, -middle_thickness/2]) cylinder(h=middle_thickness, d=rod_diameter_plus_threshold, center=true); rotate([90, 0, 0]) translate([0, 0, middle_thickness/2]) cylinder(h=middle_thickness, d=rod_diameter_plus_threshold / 4, center=true); translate([0, 0, pilar_height/2]) cylinder(h=rod_diameter_plus_threshold*2, d=rod_diameter_plus_threshold, center=true); translate([0, 0, -pilar_height/2]) cylinder(h=rod_diameter_plus_threshold*2, d=rod_diameter_plus_threshold, center=true); } } module middle() { s = [[0, 0], [middle_width, 0], [middle_width, -height / 2 - 20], [middle_width - 20, -height / 2 - 10], [20, -height / 2 - 10], [0, -height / 2 - 20]]; rotate([90, 0, 0]) translate([0,2.5,0]) hex_panel(s, 2, 10, h=12, frame=5); } middle(); translate([-pilar_width / 2, 0, -height/2]) pilar(); translate([middle_width + pilar_width / 2, 0, -height/2]) pilar(); } bridge(rod_diameter=rod_diameter); ``` ::: ```` :::openscad{library="BOSL2"} ```scad include include $fn = 100; rod_diameter = 12; module bridge(width = 230, height = 80, rod_diameter = 12) { pilar_width = height / 2; pilar_height = height + 5; middle_width = width - pilar_width * 2; middle_thickness = max(rod_diameter, 19); rod_diameter_plus_threshold = rod_diameter + 0.5; module pilar() { difference() { cube([pilar_width, middle_thickness + 10, pilar_height], center=true); rotate([90, 0, 0]) translate([0, 0, -middle_thickness/2]) cylinder(h=middle_thickness, d=rod_diameter_plus_threshold, center=true); rotate([90, 0, 0]) translate([0, 0, middle_thickness/2]) cylinder(h=middle_thickness, d=rod_diameter_plus_threshold / 4, center=true); translate([0, 0, pilar_height/2]) cylinder(h=rod_diameter_plus_threshold*2, d=rod_diameter_plus_threshold, center=true); translate([0, 0, -pilar_height/2]) cylinder(h=rod_diameter_plus_threshold*2, d=rod_diameter_plus_threshold, center=true); } } module middle() { s = [[0, 0], [middle_width, 0], [middle_width, -height / 2 - 20], [middle_width - 20, -height / 2 - 10], [20, -height / 2 - 10], [0, -height / 2 - 20]]; rotate([90, 0, 0]) translate([0,2.5,0]) hex_panel(s, 2, 10, h=12, frame=5); } middle(); translate([-pilar_width / 2, 0, -height/2]) pilar(); translate([middle_width + pilar_width / 2, 0, -height/2]) pilar(); } bridge(rod_diameter=rod_diameter); ``` ::: # P5 # P5 This element allows you to create and run [p5.js](https://p5js.org/) sketches in your book. The p5.js library is a JavaScript library that allows you to create graphics and animations in the browser. You can either load a p5.js sketch from a file or write it directly in the book. ## Attributes | Attribute | Description | Default | |---|---|---| | `src` | Path to a p5.js sketch file | - | | `height` | Height of the preview or editor container | `calc(100dvh - 80px)` | | `editor` | Show the interactive editor | `false` | ````md ::p5{src="p5test.js"} :::p5{height=400 editor=true} ```js function setup() { createCanvas(400, 400); } function draw() { background(220); ellipse(200, 200, 100, 100); } ``` ::: ```` ::p5{src="p5test.js" height=200} :::p5{height=400 editor=true} ```js function setup() { createCanvas(400, 400); } function draw() { background(220); ellipse(200, 200, 100, 100); } ``` ::: # Pagelist # Pagelist The ::pagelist element allows you to create a dynamic page directory that displays a list of pages on a website. Various options can be used to filter the displayed pages, choose the format of the list, and determine the order. ```md ::pagelist{format="" source="" orderBy=""} ``` ## Attributes | Attribute | Description | Default | |---|---|---| | `format` | Render format: `ul`, `ol`, `glossary`, or `#` | `ul` | | `source` | Query expression that filters the pages to include | `href(.*)` | | `orderBy` | Sort expression in the form `field:direction` | `name:asc` | | `limit` | Maximum number of results to return | - | ## Parameters ### format Defines how the list is rendered: - `ol`: Creates an ordered list - `ul`: Creates an unordered list - `glossary`: Creates a grouped list by first letter - `#`: Uses a custom snippet to generate the list. The snippet receives `pages` containing all filtered pages. ### source A query expression to filter pages. Supports: **Fields:** - `href(regex)`: Match the page URL - `name(regex)`: Match the page name - `keyword(value)`: Match any of the page's keywords (exact match by default; use `^regex$` for regex matching) - `description(regex)`: Match the page description - `isEmpty(true|false)`: Check if the page has no content - `(regex)`: Match any custom frontmatter field **Operators:** - `AND`: Both conditions must match - `OR`: Either condition must match - `NOT`: Negate a condition - `()`: Group conditions to control precedence Operator precedence (highest to lowest): `NOT` > `AND` > `OR` ### orderBy Sort the results by any field. Format: `field:direction` **Built-in fields:** - `name`: Sort by page name - `index`: Sort by page index - `href`: Sort by page URL **Custom fields:** - Any frontmatter field can be used (e.g., `difficulty:asc`, `priority:desc`) **Directions:** - `asc`: Ascending order - `desc`: Descending order (default) Pages with missing or null values for the sort field are placed at the end. ### limit Limit the number of results returned. ```md ::pagelist{source="href(/elements/.*)" limit="5"} ``` ## Query Language Examples ### Basic Queries ```md ::pagelist{source="href(/elements/.*)"} ::pagelist{source="name(.*IDE.*)"} ::pagelist{source="keyword(tutorial)"} ::pagelist{source="keyword(^.*tutorial.*$)"} ``` ### AND - Both conditions must match ```md ::pagelist{source="href(/elements/.*) AND keyword(media)"} ::pagelist{source="name(.*IDE.*) AND href(.*sql.*)"} ``` ### OR - Either condition matches ```md ::pagelist{source="name(Video) OR name(Audio)"} ::pagelist{source="keyword(beginner) OR keyword(tutorial)"} ``` ### NOT - Exclude matching pages ```md ::pagelist{source="href(/elements/.*) AND NOT name(Video)"} ::pagelist{source="NOT keyword(deprecated)"} ``` ### Parentheses - Group conditions ```md ::pagelist{source="href(/elements/.*) AND (name(Video) OR name(Audio))"} ::pagelist{source="(href(/elements/.*) OR href(/advanced/.*)) AND keyword(test)"} ``` ### Custom Frontmatter If your pages have custom frontmatter fields: ```yaml --- name: My Page difficulty: beginner tags: - tutorial - video --- ``` You can query them: ```md ::pagelist{source="difficulty(beginner)"} ::pagelist{source="tags(tutorial)"} ::pagelist{source="difficulty(beginner) OR difficulty(intermediate)"} ::pagelist{source="tags(video) AND NOT difficulty(advanced)"} ``` ## Sorting Examples ### Sort by name ```md ::pagelist{source="href(/elements/.*)" orderBy="name:asc"} ::pagelist{source="href(/elements/.*)" orderBy="name:desc"} ``` ### Sort by custom frontmatter ```md ::pagelist{source="tags(tutorial)" orderBy="difficulty:asc"} ::pagelist{source="href(/.*)" orderBy="priority:desc"} ``` ## Format Examples ### Glossary ```md ::pagelist{format="glossary" source="href(/glossary/.*)"} ``` ::pagelist{format="glossary" source="href(/glossary/.*)"} ### A Glossary of all Elements ```md ::pagelist{format="glossary" source="href(/elements/.*)"} ``` ::pagelist{format="glossary" source="href(/elements/.*)"} ### Unordered List by href ```md ::pagelist{format="ul" source="href(/elements/.*)" orderBy="name:desc"} ``` ::pagelist{format="ul" source="href(/elements/.*)" orderBy="name:desc"} ### Ordered List with IDE in the name and additionally with SQL in the URL ```md ::pagelist{format="ol" source="name(.*IDE.*)"} ::pagelist{format="ol" source="href(.*sql.*) AND name(.*IDE.*)"} ``` ::pagelist{format="ol" source="name(.*IDE.*)"} ::pagelist{format="ol" source="href(.*sql.*) AND name(.*IDE.*)"} ### Using OR to list multiple specific pages ```md ::pagelist{format="ul" source="name(Video) OR name(Audio) OR name(YouTube)"} ``` ::pagelist{format="ul" source="name(Video) OR name(Audio) OR name(YouTube)"} ### Excluding pages with NOT ```md ::pagelist{format="ul" source="href(/elements/.*) AND NOT name(.*IDE.*)" orderBy="name:asc" limit="10"} ``` ::pagelist{format="ul" source="href(/elements/.*) AND NOT name(.*IDE.*)" orderBy="name:asc" limit="10"} ### Custom snippet The snippet is located in the `snippets` folder with the name `list.md.hbs` ```hbs {{#each pages}} - {{{ name }}}: [{{ href }}]({{ href }}) {{/each}} ``` ```md ::pagelist{format="#list" source="name(^V.*)"} ``` ::pagelist{format="#list" source="name(^V.*)"} :::alert{warn} When using custom snippets with pagelist, the file-related helpers are **not available**: - `file` - `rfile` - `base64` - `rbase64` All other helpers (like `dateformat`, `truncate`, `truncateWords`, etc.) work normally. ::: # PlantUML :::alert{warn} **Requires a network connection.** Diagrams are rendered server-side by the [Kroki](https://kroki.io/) service (`kroki.io`) at build/render time. Kroki is not bundled with the hyperbook build output. This element will not work if Kroki is unreachable. Kroki is open source and can be [self-hosted](https://docs.kroki.io/kroki/setup/install/) for air-gapped deployments. ::: [PlantUML](https://www.plantuml.com/) lets you create diagrams and visualizations using text and code. Unlike the [Mermaid](/elements/mermaid) element, this element requires the external service Kroki for rendering the diagrams. ```markdown :::plantuml @startuml Bob -> Alice : hello @enduml ::: ``` :::plantuml @startuml Bob -> Alice : hello @enduml ::: ## Attributes | Attribute | Description | Default | |---|---|---| | `width` | Width of the generated diagram image | - | | `alt` | Alternative text for the diagram image | - | # Protect # Protect If you want to protect an area of your Hyperbook with a password, you can use the protect-block. ## Attributes | Attribute | Description | Default | |---|---|---| | `password` | Password required to unlock the protected content | - | | `description` | Description shown above the password input | - | | `id` | Optional shared id for syncing multiple protected blocks | auto-generated | ```md :::protect{password="hyperbook" description="The password is the name of this project."} :smiley: ::: ``` :::protect{password="hyperbook" description="The password is the name of this project."} :smiley: ::: :::alert{info} Be sure to use a higher number of `:` if you want to protect something which also uses `:`. ::: You can sync protect-block by using the id property. ```md :::protect{id="1" password="hyperbook" description="The password is the name of this project."} :smiley: ::: :::protect{id="1" password="hyperbook" description="The password is the name of this project."} :apple: ::: ``` :::protect{id="1" password="hyperbook" description="The password is the name of this project."} :smiley: ::: :::protect{id="1" password="hyperbook" description="The password is the name of this project."} :apple: ::: # Pyide # PyIDE :::alert{warn} **Requires a network connection.** The Python runtime ([Pyodide](https://pyodide.org/)) is loaded at runtime from `cdn.jsdelivr.net` and is not bundled with the hyperbook build output. The bootstrap script alone is ~1 MB; additional Python packages are downloaded on demand. This element will not work in offline or network-restricted environments. ::: The `pyide` element represents a Python Integrated Development Environment (IDE) component. It is used to embed a Python coding environment within the hyperbook website. This element allows users to write, edit, and execute Python code directly in the browser. ## Attributes | Attribute | Description | Default | |---|---|---| | `packages` | Comma-separated list of PyPI packages to install with `micropip` before running the code | - | | `canvas` | Enable the canvas view for libraries such as `pygame` or `turtle` | `false` | ````md :::pyide ```python a = 5 + 2 print(a) ``` ::: ```` :::pyide ```python a = 5 + 2 print(a) ``` ::: You can also use any package listed here: https://pyodide.org/en/stable/usage/packages-in-pyodide.html If you need packages from PyPI, use the `packages` attribute with a comma-separated list. Hyperbook loads `micropip` and installs these packages before executing your script. ````md :::pyide{packages="snowballstemmer"} ```python import snowballstemmer stemmer = snowballstemmer.stemmer("english") print(stemmer.stemWords(["running", "runner", "runs"])) ``` ::: ```` ````md :::pyide ```python import numpy as np a = np.arange(15).reshape(3, 5) print(a) ``` ::: ```` :::pyide ```python import numpy as np a = np.arange(15).reshape(3, 5) print(a) ``` ::: ## Add test cases You can add test cases to the code snippets by adding a `test` tag to the code block. The `#SCRIPT#` comment will be replaced by the written code. It can be placed in any part of the code block. ````md :::pyide ```python test #SCRIPT# r = check_palindrom("uhu") if r: print("Pass") else: print("Fail") ``` ```python test #SCRIPT# r = check_palindrom("test") if not r: print("Pass") else: print("Fail") ``` ```python def check_palindrom(s): return True ``` ::: ```` :::pyide ```python test #SCRIPT# r = check_palindrom("uhu") if r: print("Pass") else: print("Fail") ``` ```python test #SCRIPT# r = check_palindrom("test") if not r: print("Pass") else: print("Fail") ``` ```python def check_palindrom(s): return True ``` ::: When your code calls `input()`, the browser shows a prompt dialog for the value. ````md :::pyide ```python a = input("Enter a value: ") print(a) ``` ::: ```` :::pyide ```python a = input("Enter a value: ") print(a) ``` ::: ## Stopping the execution :::alert{warn} Use the **Stop** button in the editor to request an interrupt. For infinite loops or long-running processes, interruption is only reliable when these two headers are set on your server: ``` 'Cross-Origin-Embedder-Policy': 'require-corp' 'Cross-Origin-Opener-Policy': 'same-origin' ``` ::: ## Libraries with SDL ### PyGame Top-level PyGame loops are wrapped automatically for browser execution, so you can write the loop directly without `asyncio.run(...)`. ````hyperbook :::pyide{canvas} ```python import pygame pygame.init() screen = pygame.display.set_mode((400, 300)) clock = pygame.time.Clock() r = 0 g = 0 b = 0 running = True while running: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_r: r = (r + 50) % 256 elif event.key == pygame.K_g: g = (g + 50) % 256 elif event.key == pygame.K_b: b = (b + 50) % 256 elif event.key == pygame.K_ESCAPE: running = False elif event.type == pygame.QUIT: running = False screen.fill((r, g, b)) pygame.display.flip() clock.tick(60) ``` ::: ```` :::pyide{canvas} ```python import pygame pygame.init() screen = pygame.display.set_mode((400, 300)) clock = pygame.time.Clock() r = 0 g = 0 b = 0 running = True while running: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_r: r = (r + 50) % 256 elif event.key == pygame.K_g: g = (g + 50) % 256 elif event.key == pygame.K_b: b = (b + 50) % 256 elif event.key == pygame.K_ESCAPE: running = False elif event.type == pygame.QUIT: running = False screen.fill((r, g, b)) pygame.display.flip() clock.tick(60) ``` ::: ## Pytamaro Use `packages="pytamaro"` to render `show_graphic(...)` and `show_animation(...)` in the output panel. ````hyperbook :::pyide{packages="pytamaro"} ```python from pytamaro import * block_size = 25 num_blocks = 16 line = empty_graphic() for col in range(num_blocks): if col % 2 == 0: color = black else: color = white block = rectangle(block_size, block_size, color) line = beside(line, block) second_line = rotate(180, line) finish_line = above(line, second_line) show_graphic(finish_line) ``` ::: ```` :::pyide{packages="pytamaro"} ```python from pytamaro import * block_size = 25 num_blocks = 16 line = empty_graphic() for col in range(num_blocks): if col % 2 == 0: color = black else: color = white block = rectangle(block_size, block_size, color) line = beside(line, block) second_line = rotate(180, line) finish_line = above(line, second_line) show_graphic(finish_line) ``` ::: ## Turtle The browser turtle API is available via `from turtle import *`. ### Movement | Method | Parameters | Description | | --- | --- | --- | | `forward`, `fd` | `distance` | Move the turtle forward by the given distance. | | `backward`, `bk`, `back` | `distance` | Move the turtle backward by the given distance. | | `left`, `lt` | `angle` | Turn the turtle left by the given angle in degrees. | | `right`, `rt` | `angle` | Turn the turtle right by the given angle in degrees. | | `goto`, `setpos`, `setposition` | `x, y` or `(x, y)` | Move the turtle to an absolute position. | | `setx` | `x` | Set only the x coordinate. | | `sety` | `y` | Set only the y coordinate. | | `setheading`, `seth` | `angle` | Set the turtle heading directly. | | `home` | none | Move back to the center and reset the heading. | | `circle` | `radius, steps=120` | Draw an approximated circle. | ### Pen and drawing | Method | Parameters | Description | | --- | --- | --- | | `penup`, `pu`, `up` | none | Lift the pen so movement no longer draws lines. | | `pendown`, `pd`, `down` | none | Lower the pen so movement draws lines again. | | `pensize`, `width` | `size` | Set the stroke width for new lines. | | `colormode` | `mode=1.0` | Set or return the current color mode. Use `255` before passing RGB tuples with values from 0 to 255. | | `pencolor` | `color` | Set the line color. | | `fillcolor` | `color` | Set the fill color used by `begin_fill` / `end_fill`. | | `color` | `stroke` or `stroke, fill` | Set stroke and fill color together. | | `dot` | `size=5, color=None` | Draw a filled dot at the current position. | | `begin_fill` | none | Start recording a filled shape. | | `end_fill` | none | Finish and fill the current shape. | | `write` | `text, move=False, align=\"left\", font=None` | Draw text at the current turtle position. | | `bgcolor` | `color` | Set the turtle canvas background color. | | `bgpic` | `filename=\"\"` | Load a background image from the Pyodide file system, or clear it with an empty string. | ### State | Method | Parameters | Description | | --- | --- | --- | | `position`, `pos` | none | Return the current turtle position as `(x, y)`. | | `xcor` | none | Return the current x coordinate. | | `ycor` | none | Return the current y coordinate. | | `heading` | none | Return the current heading in degrees. | | `towards` | `x, y` | Return the angle from the turtle to the given point. | | `pen` | `options=None` | Return the current pen state, or update parts of it from a dictionary-like object. | | `isvisible` | none | Return whether the turtle cursor is currently visible. | | `shape` | `name=None` | Set or return the turtle shape. Available shapes: `'classic'` (default), `'arrow'`, `'turtle'`, `'triangle'`, `'square'`, `'circle'`. | ### Screen and reset | Method | Parameters | Description | | --- | --- | --- | | `speed` | `value=0` | Set the turtle animation speed. Higher values draw faster. | | `screensize` | `canvwidth=None, canvheight=None, bg=None` | Return or update the turtle canvas size and optional background color. Scrollbars appear when the canvas is larger than the panel. Default size is 640×480. | | `showturtle`, `st` | none | Show the turtle cursor. | | `hideturtle`, `ht` | none | Hide the turtle cursor. | | `clear` | none | Clear the drawing but keep the current turtle state. | | `reset` | none | Reset the turtle state and clear the drawing. | ### Multiple turtles Use `Turtle()` to create additional independent turtles that share the same canvas. Each turtle has its own position, heading, color, shape, and pen state and is controlled through the object it returns. ````hyperbook :::pyide{canvas} ```python from turtle import * from random import randint speed(0) penup() goto(-140, 140) for step in range(15): write(step, align='center') right(90) for num in range(8): penup() forward(10) pendown() forward(10) penup() backward(160) left(90) forward(20) red = Turtle() red.color('red') red.shape('turtle') red.penup() red.goto(-160, 100) red.pendown() blue = Turtle() blue.color('blue') blue.shape('turtle') blue.penup() blue.goto(-160, 70) blue.pendown() green = Turtle() green.shape('turtle') green.color('green') green.penup() green.goto(-160, 40) green.pendown() for turn in range(100): red.forward(randint(1, 5)) blue.forward(randint(1, 5)) green.forward(randint(1, 5)) ``` ::: ```` :::pyide{canvas} ```python from turtle import * from random import randint speed(0) penup() goto(-140, 140) for step in range(15): write(step, align='center') right(90) for num in range(8): penup() forward(10) pendown() forward(10) penup() backward(160) left(90) forward(20) red = Turtle() red.color('red') red.shape('turtle') red.penup() red.goto(-160, 100) red.pendown() blue = Turtle() blue.color('blue') blue.shape('turtle') blue.penup() blue.goto(-160, 70) blue.pendown() green = Turtle() green.shape('turtle') green.color('green') green.penup() green.goto(-160, 40) green.pendown() for turn in range(100): red.forward(randint(1, 5)) blue.forward(randint(1, 5)) green.forward(randint(1, 5)) ``` ::: ````hyperbook :::pyide{canvas} ```python from turtle import * speed(50) colormode(255) screensize(900, 700, "#0f172a") bgcolor("#0f172a") pensize(2) colors = [ (244, 114, 182), (56, 189, 248), (251, 191, 36), (74, 222, 128), ] for step in range(48): pencolor(colors[step % len(colors)]) circle(160 - step * 2) right(15) penup() goto(0, -20) pencolor("#f8fafc") write("Hyperbook", align="center", font=("Arial", 24, "bold")) hideturtle() ``` ::: ```` :::pyide{canvas} ```python from turtle import * speed(50) colormode(255) screensize(900, 700, "#0f172a") bgcolor("#0f172a") pensize(2) colors = [ (244, 114, 182), (56, 189, 248), (251, 191, 36), (74, 222, 128), ] for step in range(48): pencolor(colors[step % len(colors)]) circle(160 - step * 2) right(15) penup() goto(0, -20) pencolor("#f8fafc") write("Hyperbook", align="center", font=("Arial", 24, "bold")) hideturtle() ``` ::: # QR Code # QR Code You can use the QR code element for showing QR codes in your hyperbook. ## Attributes | Attribute | Description | Default | |---|---|---| | `value` | Value encoded in the QR code | - | | `size` | Size of the QR code: `S`, `M`, `L`, or `XL` | `M` | | `label` | Label shown below the QR code | - | ```md ::qr{value="https://hyperbook.openpatch.org" size="XL"} ::qr{value="https://twitter.com/openpatchorg" size="L" label="Follow Me!"} ::qr{value="mailto:mike@openpatch.org" label="Contact Me!"} ::qr{value="WIFI:T:WPA;S:wlan-kabel;P:top-secret;;" size="S" label="Connect Me!"} ``` ::qr{value="https://hyperbook.openpatch.org" size="XL"} ::qr{value="https://twitter.com/openpatchorg" size="L" label="Follow Me!"} ::qr{value="mailto:mike@openpatch.org" label="Contact Me!"} ::qr{value="WIFI:T:WPA;S:wlan-kabel;P:top-secret;;" size="S" label="Connect Me!"} # SQL IDE # SQL IDE The `sqlide` directive embeds an SQL editor and runner for SQLite databases directly in your book. ## Attributes | Attribute | Description | Default | |---|---|---| | `db` | URL or path to the SQLite database file | bundled demo database | | `height` | Height of the editor | `calc(100dvh - 80px)` | (See: https://github.com/martin-pabst/SQL-IDE). :::sqlide{height="500px"} ```mysql Statements.sql SELECT * from fluss; ``` ::: ````markdown :::sqlide{height="500px"} ```mysql Statements.sql SELECT * from fluss; ``` ::: ```` # Scratchblock # Scratchblock The scratchblock element allows to easier inserting of scratch programs. The programs will be rendered as a svg. As a default your hyperbook language and english are supported. ## Attributes | Attribute | Description | Default | |---|---|---| | `language` | Scratchblocks locale to use, for example `de` | hyperbook language / `en` | ```md :::scratchblock when green flag clicked move (1) steps ::: ``` :::scratchblock when green flag clicked move (1) steps ::: You can add a specific language by providing a language parameter. ```md :::scratchblock{language="de"} Wenn die grüne Flagge angeklickt gehe (1) er Schritt drehe dich nach rechts um (15) Grad ::: ``` :::scratchblock{language="de"} Wenn die grüne Flagge angeklickt gehe (1) er Schritt drehe dich nach rechts um (15) Grad ::: You can find the support languages and the corresponding commands here: https://github.com/scratchblocks/scratchblocks/tree/main/locales # Slideshow # Slideshow The `slideshow` directive creates an image slideshow with navigation controls. ## Attributes | Attribute | Description | Default | |---|---|---| | `height` | Height of the slideshow, for example `500` or `500px` | `300px` | ```md :::slideshow ![Test](/test.jpg "A caption") ![Clouds](/clouds.jpg "Another caption") ::: ``` :::slideshow ![Test](/test.jpg "A caption") ![Clouds](/clouds.jpg "Another caption") ::: ```md :::slideshow{height=500} ![Test](/test.jpg "A caption") ![Clouds](/clouds.jpg "Another caption") ::: ``` :::slideshow{height=500} ![Test](/test.jpg "A caption") ![Clouds](/clouds.jpg "Another caption") ::: # Snippets # Snippets Sometimes you want to repeat a certain element, or you want to create your own element. Snippets allow you to do exactly that. :::alert{warn} Snippet files need to be placed in the `snippets` folder at the root of your hyperbook beside the glossary and book folders. Here we use a capital S for snippet. You need to use a small s. ::: You can access your hyperbook config in your snippets like so: `{{{ hyperbook.name }}}`. ## Examples ### Example 1: Block Here is a simple example of a snippet for using a protect element with the same password and id across your hyperbook. The snippet located in `snippets/password.md.hbs`: ```md :::protect{id="1" password="hyperbook" description="The password is the name of this project."} {{{ content }}} ::: {{#if hint}} :::alert{info} Hyperbook is the password. ::: {{/if}} ``` The markdown you need to place in your hyperbook: ```md :::Snippet{#password} :smiley: ::: ``` The result: :::protect{id="1" password="hyperbook" description="The password is the name of this project."} :smiley: ::: ### Example 2: Dynamic Colon Levels Snippets automatically provide variables for different colon levels to handle proper nesting: The snippet located in `snippets/alert.md.hbs`: ```md {{{ c1 }}}alert{label="Info" color="#3B82F6"} {{{ content }}} {{{ c1 }}} ``` Available colon level variables: - `c` - Same number of colons as the snippet block - `c1`, `c2`, `c3`, `c4` - One to four **more** colons (for deeper nesting) - `l1`, `l2`, `l3`, `l4` - One to four **fewer** colons (for shallower nesting) Example usage with different nesting levels: ```md ::::Snippet{#alert} This will be wrapped in a 5-colon alert block. :::: ::Snippet{#alert} This will be wrapped in a 3-colon alert block. :: ``` Results in: - First case: `:::::alert{...}` (4 + 1 = 5 colons using `c1`) - Second case: `:::alert{...}` (2 + 1 = 3 colons using `c1`) This ensures proper nesting regardless of how deeply your snippet is nested within other markdown blocks. ### Example 3: Block with Parameter You can also pass parameters to your snippet to make them dynamic. For example our password snippet from above allows to pass a hint parameter. If the hint parameter is true, a alert element will be shown. ```md :::Snippet{#password hint=true} ::qr{value="https://hyperbook.openpatch.org" size="XL"} ::: ``` :::protect{id="1" password="hyperbook" description="The password is the name of this project."} ::qr{value="https://hyperbook.openpatch.org" size="XL"} ::: :::alert{info} Hyperbook is the password. ::: ### Example 4: Inline ```hbs {{#times n}} :smiley: {{/times}} ``` ```md :Snippet{#smiley n=10} ``` We are ten smilies: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: ## Parameters You can use the parameters by using curly brackets and the name of the parameter. ```hbs {{{p1}}} ``` Three curly brackets will give you the raw content. Two curly brackets will give the HTML-escaped content. ### Content If your snippet spans across multiple lines, you can use the content parameter to use those. See Example 1. :::alert{warn} You need to use three curly brackets for the content parameter. ::: ## Helpers You can use the following helpers in your snippets ### if You can use the if helper to conditionally render a block. ```hbs {{#if p}} content {{/if}} ``` ### unless You can use the unless helper as the inverse of the if helper. Its block will be rendered if the expression returns a falsy value. ```hbs {{#unless hint}} content {{/unless}} ``` ### times You can use the times helper to repeat block. ```hbs {{#times 10}} Hi {{/times}} ``` ### file You can use the file helper to include the content of a file. ```hbs {{{file "/archives/project-1/main.c"}}} ``` You can also only use a few lines of the file. ```hbs {{{file "/archives/project-1/main.c" "1,3-4"}}} ``` And you can define an ellipsis. ```hbs {{{file "/archives/project-1/main.c" "1,3-4" "// ..."}}} ``` ### rfile rfile work like file, but will read from root of a git repository. This is useful, when your hyperbook is in a subdirectory and you want to reference files from the root of the repository. ```hbs {{{rfile "/path/to/file"}}} ``` ### base64 You can use the bae64 helper to embedded media, even from external folders. ```hbs {{base64 "path/relative/to/root/folder"}} ``` This works best in conjunction with the image block: ```hbs ![]({{base64 "path/relative/to/root/folder"}}) ``` ### concat ```hbs {{concat "Hi" " there"}} ``` Hi there ### camelcase ```hbs {{camcelcase "This is a test"}} ``` thisIsATest ### pascalcase ```hbs {{pascalcase "This is a test"}} ``` ThisIsATest ### dashcase ```hbs {{dashcase "This is a test"}} ``` This-is-a-test ### lowercase ```hbs {{lowercase "This is a test"}} ``` this is a test ### uppercase ```hbs {{lowercase "This is a test"}} ``` THIS IS A TEST ### replace ```hbs {{replace "Give me Banana Banana" "Banana" "Apple"}} ``` Give me Apple Banana ### replaceAll ```hbs {{replaceAll "Give me Banana Banana" "Banana" "Apple"}} ``` Give me Apple Apple ### dateformat Format a date string or Date object using a format pattern. ```hbs {{dateformat "2026-01-09T19:29:01.557Z" "YYYY-MM-DD"}} ``` 2026-01-09 Supported format tokens: - `YYYY` - 4-digit year - `YY` - 2-digit year - `MM` - padded month (01-12) - `M` - month (1-12) - `DD` - padded day (01-31) - `D` - day (1-31) - `HH` - padded hours (00-23) - `H` - hours (0-23) - `mm` - padded minutes (00-59) - `m` - minutes (0-59) - `ss` - padded seconds (00-59) - `s` - seconds (0-59) ```hbs {{dateformat "2026-01-09T19:29:01.557Z" "DD.MM.YYYY HH:mm:ss"}} ``` 09.01.2026 19:29:01 ### truncate Truncate a string to a specified character limit with a suffix. ```hbs {{truncate "Hello World, this is a long string" 11 "..."}} ``` Hello World... Parameters: - `str` - the string to truncate - `limit` - maximum number of characters (default: 100) - `suffix` - string to append when truncated (default: "...") ### truncateWords Truncate a string to a specified word limit with a suffix. ```hbs {{truncateWords "one two three four five six" 3 "..."}} ``` one two three... Parameters: - `str` - the string to truncate - `limit` - maximum number of words (default: 10) - `suffix` - string to append when truncated (default: "...") # Struktog [Deprecated] # Struktog [Deprecated] :::alert{warn} Please use [Struktolab](./struktolab) instead. Struktog integration is no developed anymore. ::: Struktog is a Nashi-Shneidermann-Diagram-Editor. It was originally developed by the [TU Dresden](https://gitlab.com/ddi-tu-dresden/cs-school-tools/struktog). We forked the project and added the ability to store diagrams in the URL. This makes it rather easy to use the diagrams in Hyperbook. You can find our fork at [https://struktog.openpatch.org](https://struktog.openpatch.org). ## Attributes | Attribute | Description | Default | |---|---|---| | `data` | Serialized Struktog state or a full Struktog URL containing that state | - | ``` ::struktog{data="pako:eNqtVMtqG0EQ_Jc9u6HnPaNryAtCHEjIJeTQM91jiawlI61wgtGf5ZYfS8vYEQ6RsXAuw7LMVFdVV_fNcLliGYfZzbDgYTYUH4tNpkCpOYOp1kLhZCAYW5CSDGfD9ONK9Obb5UbW03t9rf_6ahxX1y9HuZTl9Aess2spGoYcbYDYE0JBS0CFihXvDmBvhPjdanV1BzfJd0UZXv_6OU6Li2-ymDZVeLG82C4vjlYLhjrznnBIHnr3FcRhhMaRiq_9adSX23HcnQ1tvhj5AJ2tFRM8YO8O9LNCsdFBNyQtxHKSK66XRFEQZO9Ki4pTDCls447q1gHsfDtdbaeHnnySzXQUOhfulNhDy03A-dZAuheoznLN0Z3E07umzcKiPH0GrJqCWmzVKGCKTvIB7AVt5CHLz7ReUB2Pgwul6hIrZO0eEroIFmNTpmRibu6kZrF02o7T-XKYTeut_Plx--i-YizNN4wdbC8dImEGhyFDSNlGJfF3xb2og6CPq-UjvleLqTA1yJUTBC4JUNgBh6jSfHiq73eXPozUZL4aWdbDbrdPo3LZDLMvd-Va9V7D3jSJpPkOpgN70RhF2zsiPy7lFY3jUSWUQzfG7ZUYB663AjVoOFvWyUL3XCX384TSc65WsZseRjKQWgisJTBjeIYASy6l4BwgRgbLBSHZxOCCtt-WdNqoakpD5QZR3wJmCmBCzVB1cpOn-H8XWCdnvboN5KKydt1CZ9ERLrX58NTd-88FhtEyVimQdfmA76VpRyNBxdQTk31eW3e7r7enlp3L4mKuVy36s-F6wdN8mOWSdr8BJ5f68w"} ::struktog{data="https://struktog.openpatch.org/#pako:eNqtVcuKG0kQ_Jc-T0LWu0pX433AsjZ42YvxIasycyS2LQ2jFrYx-rO97Y85ZcaWXzOM8F6aoqiKjIyKjH4_vd6xzNPq_bThaTW1mJsvrkHrtYLr3kPj4iA535CKTFfT8u5G7OTv273cLn_abdvT3Tzv3jyd5bVsl89gymGU7Bhq9gmyFoSGnoAaNS8xnMF-E-I_drubO7hF3hrK9Ot__87L5vof2Sz7LrzZXh-21_dWS46U-UQ4lQiqsYMEzDA4U4tdH0d9e5jn49U01puZz9DVe3EpAqoGsGWH5nMAdSQj5XaRKkFboSwIclJlZMNpjgx2sKKpdQZ7dlhuDsvXmvwl--Ve6NpYqXCEUYdAiGOAaBTowXOvOVzEM4Zhj4XNeMYK2M0FvfluVsCSg9Qz2BPay9cs_6bbDfX5fnCh0kNhg-waoWDI4DEPY0ou1xEueiwWpcO8PNtOq-X2IJ83Pl76VDG3EQdmBa9NIRNWCJgqpFJ9NhLfVjw1dW7oxW77gO7dY2lMA2rnAolbARQOwClbazE9Vve7Q89nGrLezSy30_F4cqNx2U-rl3flRo_RzD7MiWT-Tk6Bo5iNsldF5Idb-YXm-d5OqCZ1Lpw6cQGCjgY9mTlHtcnCkC5yUJUaGuYCPvUBhuqhOjolQRg6HH0J9p3Rn9q4U3_AQY1i6cOiaQyF5NlipmQD55obar7AQSeNP006itbavXVtbLuTCmSPC2zNY8X0E9J6CqWkEAAxM3huCMUXhpDMmL6Vy0LE5id1HpDtLmA1VV3qFbplSomU_99oVQo-mg-AQjbWQT0oi4VL6yOmx_4VfhitmD1jlwbVYhGitmFeywQdixYm_3Ojczy--vi1smvZXK_tqMd4Nb3Z8LKeVi254wfMXCkO"} ``` ::struktog{data="pako:eNqtVMtqG0EQ_Jc9u6HnPaNryAtCHEjIJeTQM91jiawlI61wgtGf5ZYfS8vYEQ6RsXAuw7LMVFdVV_fNcLliGYfZzbDgYTYUH4tNpkCpOYOp1kLhZCAYW5CSDGfD9ONK9Obb5UbW03t9rf_6ahxX1y9HuZTl9Aess2spGoYcbYDYE0JBS0CFihXvDmBvhPjdanV1BzfJd0UZXv_6OU6Li2-ymDZVeLG82C4vjlYLhjrznnBIHnr3FcRhhMaRiq_9adSX23HcnQ1tvhj5AJ2tFRM8YO8O9LNCsdFBNyQtxHKSK66XRFEQZO9Ki4pTDCls447q1gHsfDtdbaeHnnySzXQUOhfulNhDy03A-dZAuheoznLN0Z3E07umzcKiPH0GrJqCWmzVKGCKTvIB7AVt5CHLz7ReUB2Pgwul6hIrZO0eEroIFmNTpmRibu6kZrF02o7T-XKYTeut_Plx--i-YizNN4wdbC8dImEGhyFDSNlGJfF3xb2og6CPq-UjvleLqTA1yJUTBC4JUNgBh6jSfHiq73eXPozUZL4aWdbDbrdPo3LZDLMvd-Va9V7D3jSJpPkOpgN70RhF2zsiPy7lFY3jUSWUQzfG7ZUYB663AjVoOFvWyUL3XCX384TSc65WsZseRjKQWgisJTBjeIYASy6l4BwgRgbLBSHZxOCCtt-WdNqoakpD5QZR3wJmCmBCzVB1cpOn-H8XWCdnvboN5KKydt1CZ9ERLrX58NTd-88FhtEyVimQdfmA76VpRyNBxdQTk31eW3e7r7enlp3L4mKuVy36s-F6wdN8mOWSdr8BJ5f68w"} ::struktog{data="https://struktog.openpatch.org/#pako:eNqtV8tuW0cM_Ze7DoF5P7xrgqQtUCQFGnRTZMEZcmwhimRI10iDwH_WXX-s5-ZlO4lVC8pGgqQrDnl4eA7n_fRmK7qezt5PK5nOphpSddlWqq0Uss05qpItReuq4azTo2l-d6l48tfNXnfzc_wb343ter19-3Stb3QzfwkWs9WUUyBnnVDtHCjUGsh3M9TbdhPs2dWmz6vt5lO4Wf9GFLztZ3y65B2_0Vl3--nsr_fT5Rbvk__4w3P8gk8i0_Wr-9KoJgcNrOQ4ZrKBG0nsjTqb2DO3o2oSa0cQwOJrsCQ-NIo5CClKKt7zrZq22_m37fbybk0___vPel6dv9bVvG8qq8351eb83tPYtxCs8-RCdeQKOqHacG4JnUfoR6WuPSPBHCh2YfJWK7meBioZSdneCvaS96_vpv3T5q2u9odSLbX0KhopJl3y7Y6amkTVNO-9HIfyEN9zskIluUhpZIM4jokrV6fB3wT7RVlORTlaHrL0tKKVNAZ6qh6pd0lcQxsPS31ztV5fP5r6xWotN6GLc2pjIDMGUFHbqLrkaVjWHlM9CpWQxRs7OvXEnTinSimkTKMoJ-duBXu8402_uIvJ4__FQWPKxWigbgF4GT1QidGQ5Jpd0XzcoLhmpIUCrmLka5WFtclREW99kFvBnvBevzv493TLxWo1K40GtrkcI-UkSs0jSy_pqCz9qJmTGtKFaD2hNdUyOtVlGBDwJtiLq_nyar6b58tDeZYqg7MAzdKVfOiddIQlTyetJH_c7Bb2ZlRDWYChQIModDtIax0hya1gL3fvnvD8dfP3wnu5f9xqFu51EXyIi8nI10AbyUcnPdpxHEsh745NBaTovmnQrFYdZNKanLyWQ43_k3crbusDOHBuPoNLrQGCbHwiZ1IHqGxT6f6oURUdfLWeX2yms3l3pV---PCnzyem2kM3kEk36qDEppA3sUD1i0tI4usTl6JuCvpjuzlAkeZMrkCeSpNMUWomo-JhTwmlhfhQ3D899Puau15s16K76fp60SLk8tE0PxzXFzNh6IeDXtDSWJIAsa7JjWGMHC7lGa_X9xtVicNav1RiPfnRK7WIOeoFump8PIpBBU5aDYTNxdYJUaEalhcf8H10y7eDfTOTTyFy3A4wqHLIrWOl6X0QCA6TyQnBoU7VjHQEgxaMP4uS0VFKg-fFxfisFmI0lwTFm2LiCdA69jlH7zGSScjJIgIuC4YTxHT1OFWG_7fYpMM4kKcpQNXGVqhB_nLg9GONdbB3ATwg9glZ--FoiEIHa-shPnSb_K6xmuTENKwwBeJFYVS4YklMzeQBMXOnjc719atlfmZo6d1TtfrMAE01QHx98AtHI2xyAMEjNb3YkqAgTALzodjgDW4IlrLSxXY3Du1jBx0y2N6khkw9L6uYM4wXQbt9YyhcPIrii4rATu7i4HKJJg7YRIiDejOYHzNwYHc5mMSnov9Qcfa2RyMW6NUCcbZ5EYkGWatwbwntFHF2uS_yjBq1MwXuhqJirrHOJVhc-MHinK0fpaFprFhCOYJf1uDuNCAgHPo4QUEidk0LGhAsy4JjAVeAhXdaahpo2YmVfDbKBMQMGyzQRSlXqKsYxk7h88AyVU8owBfBrEFOLa4u6LXDFpUqZh_DP0o90Sc_jfqV3uV4ROTa4ULRYMxjh0cEaCK0JmppI510KJ7j9f6rExVXyBEcNqeclFDXsje7AK2uOlqOJ0_V9Tcy2rX7WDxYJyOB3zlTwqZPCd8uK-BRgla9WMbUIWG_3PIhjICNKWL3Da6Mw8v0_Y4tA9cPrEXG6rKYIltcpDCNFvRlF45z7G8gqC1pC97iZtIweN4lwmUbupk7NLmc6iSPpgtdnV_gkRzto-ntSuaL6QyXwOv_AL3HY-s"} # Struktolab # Struktolab Struktolab allows you to display structograms in your hyperbook. You can use it in view mode to display structograms or in edit mode to create and edit structograms directly in your hyperbook. For more information on how to use Struktolab, see the [Struktolab documentation](https://struktolab.openpatch.org/documentation.html). ## Attributes | Attribute | Description | Default | |---|---|---| | `mode` | Display mode: `preview` or `edit` | `preview` | | `scale` | Scale factor for the diagram | `1` | | `fontSize` | Font size used by Struktolab | `14` | | `lang` | UI language: `de` or `en` | hyperbook language | | `colorMode` | Color mode: `color`, `greyscale`, or `bw` | `color` | ## View :::struktolab{fontSize=16} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ````md :::struktolab{fontSize=16} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ```` ## Edit :::struktolab{fontSize=16 mode="edit" scale=2} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ````md :::struktolab{fontSize=16 mode="edit" scale=2} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ```` ## Color Modes ### Color :::struktolab{colorMode="color"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ````md :::struktolab{colorMode="color"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ```` ### Greyscale :::struktolab{colorMode="greyscale"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ````md :::struktolab{colorMode="greyscale"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ```` ### Black and White :::struktolab{colorMode="bw"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ````md :::struktolab{colorMode="bw"} ``` if x > 0: output("positiv") else: output("not positiv") ``` ::: ```` # Tabs # Tabs Tabs are a great way to display alternative content. In Hyperbook you can use the `::::tabs` and `:::tab{title="A Tab Title"}`. ## `tabs` Attributes | Attribute | Description | Default | |---|---|---| | `id` | Optional shared id for linking multiple tab groups together | auto-generated | ## `tab` Attributes | Attribute | Description | Default | |---|---|---| | `title` | Label shown in the tab bar | - | | `id` | Optional id used for syncing tabs across groups | auto-generated | ```hyperbook ::::tabs :::tab{title="Hi"} Here is a tab Another Test ::: :::tab{title="Huh"} Another tab with a [link](#) Other ::: :::tab{title="Third"} Wow ::: :::: ``` ::::tabs :::tab{title="Hi"} Here is a tab Another Test ::: :::tab{title="Huh"} Another tab with a [link](#) Other ::: :::tab{title="Third"} Wow ::: :::: You can link tabs together by using the same id or the same titles. ```hyperbook ::::tabs{id="code"} :::tab{title="Java" id="java"} Java ::: :::tab{title="Python" id="python"} Python :::: Another tabs cluster with the same ids. ::::tabs{id="code"} :::tab{title="Java"} Java ::: :::tab{title="Python" id="python"} Python :::: :::tab{title="C" id="c"} C :::: ``` ::::tabs{id="code"} :::tab{title="Java"} Java ::: :::tab{title="Python" id="python"} Python :::: Another tabs cluster with the same ids. ::::tabs{id="code"} :::tab{title="Java"} Java ::: :::tab{title="Python" id="python"} Python ::: :::tab{title="C" id="c"} C :::: # Text Input # Text Input The text input directive allows users to input and save text directly in the page. The input is automatically saved to the browser's local storage using Dexie, so it persists across page reloads. ## Attributes | Attribute | Description | Default | |---|---|---| | `id` | Custom id used for local storage persistence | auto-generated | | `placeholder` | Placeholder text shown in the textarea | - | | `height` | Height of the textarea | `200px` | ## Basic Usage ```md ::textinput ``` ::textinput ## With Placeholder You can add a placeholder to guide users on what to input: ```md ::textinput{placeholder="Your solution"} ``` ::textinput{placeholder="Your solution"} ## With Custom Height Adjust the height of the text input area: ```md ::textinput{height="400px"} ``` ::textinput{height="400px"} ## With Both Options Combine placeholder and height for a fully customized text input: ```md ::textinput{placeholder="Enter text here" height="300px"} ``` ::textinput{placeholder="Enter text here" height="300px"} ## With Custom ID You can specify a custom ID to have multiple independent text inputs on the same page: ```md ::textinput{id="answer-1" placeholder="First answer"} ::textinput{id="answer-2" placeholder="Second answer"} ``` ::textinput{id="answer-1" placeholder="First answer"} ::textinput{id="answer-2" placeholder="Second answer"} :::alert{info} The text you enter is automatically saved to your browser's local storage and will persist even after refreshing the page. Each text input is identified by a unique ID, so you can have multiple independent inputs on the same page. ::: # Tiles # Tiles Tiles are a nice visual structure for landing pages or hubs, which should give your users an overview. The `:::tiles` container has no attributes. ## `tile` Attributes | Attribute | Description | Default | |---|---|---| | `title` | Title shown inside the tile | - | | `href` | Optional URL or path the tile links to | - | | `size` | Tile size: `S`, `M`, or `L` | `M` | | `icon` | Optional icon URL or emoji such as `:rocket:` | - | ```md :::tiles ::tile{title="Hallo"} ::tile{title="A tile with a link" href="openpatch.org"} ::tile{title="A large tile" size="L"} ::tile{title="A small tile" size="S"} ::tile{title="A icon tile" icon="https://www.inf-schule.de/assets/img/icons/icon_algorithmen.png"} ::tile{title="An emoji tile" icon=":rocket:"} ::: ``` :::tiles ::tile{title="Hallo"} ::tile{title="A tile with a link" href="openpatch.org"} ::tile{title="A large tile" size="L"} ::tile{title="A small tile" size="S"} ::tile{title="A icon tile" icon="https://www.inf-schule.de/assets/img/icons/icon_algorithmen.png"} ::tile{title="An emoji tile" icon=":rocket:"} ::: # Typst # Typst :::alert{warn} **Requires a network connection.** The Typst WebAssembly compiler (~28 MB), renderer (~1 MB), and JavaScript bundle (~1.4 MB) are loaded at runtime from `cdn.jsdelivr.net` and are not bundled with the hyperbook build output. This element will not work in offline or network-restricted environments. ::: The Typst directive allows you to render [Typst](https://typst.app/) documents directly in your hyperbook. Typst is a modern markup-based typesetting system that is easy to learn and produces beautiful documents. ## Attributes | Attribute | Description | Default | |---|---|---| | `id` | Unique identifier for the Typst block | Auto-generated | | `mode` | Display mode: `preview` (view only) or `edit` (with editor) | `preview` | | `height` | Height of the preview container | `auto` | ## Important Notes - **Multiple Typst Blocks**: When multiple Typst blocks are present on the same page, they render sequentially (one at a time) to ensure file isolation. Each block maintains its own independent file system during rendering. - **File Isolation**: Files loaded in one Typst block (via `@source`) are completely isolated from other blocks on the same page. This means you can use the same filename (e.g., `other.typ`) in different blocks without conflicts. - **Project Download**: When downloading a Typst project as ZIP, all referenced assets are automatically included: - Source files defined with named code blocks (e.g., `main.typ`, `helper.typ`) - Images referenced in `image()` calls that are loaded from relative URLs - User-uploaded files (stored as data URLs) The downloaded ZIP contains a complete, standalone Typst project that can be opened and compiled locally. ## Usage To use the Typst directive, wrap your Typst code in a `:::typst` block with a code block using the `typ` or `typst` language. ### Preview Mode In preview mode, only the rendered output is shown with a download button for exporting to PDF. ````md :::typst{mode="preview"} ```typ = Hello World! This is a simple Typst document. - First item - Second item - Third item ``` ::: ```` :::typst{mode="preview"} ```typ = Hello World! This is a simple Typst document. - First item - Second item - Third item ``` ::: ### Edit Mode In edit mode, an editor is shown alongside the preview, allowing users to modify the Typst code and see live updates. ````md :::typst{mode="edit"} ```typ = Interactive Document You can edit this text and see the changes live! $ sum_(i=1)^n i = (n(n+1))/2 $ ``` ::: ```` :::typst{mode="edit"} ```typ = Interactive Document You can edit this text and see the changes live! $ sum_(i=1)^n i = (n(n+1))/2 $ ``` ::: ### Loading from External Files You can load Typst source files from external sources using special directives. Assets like images are automatically fetched from the server or from remote addresses. #### Loading Source Files Use the `@source` directive to load Typst source files that can be included in your main document: ````md :::typst{mode="preview"} @source dest="other.typ" src="typst-doc.typ" ```typ = Main Document #include "/other.typ" ``` ::: ```` :::typst{mode="preview"} @source dest="other.typ" src="typst-doc.typ" ```typ = Main Document #include "/other.typ" ``` ::: #### Loading Images Images referenced in your Typst code are automatically loaded from the server or from remote URLs: ````md :::typst{mode="preview"} ```typ = Document with Image #figure( image("my-image.jpg", width: 80%), caption: "My image" ) ``` ::: ```` #### File Search Locations Files referenced in `src` attributes (for `@source` and `@font` directives) and images referenced in `image()` calls are resolved as follows: - **Absolute paths** (starting with `/`): The `basePath` is prepended to the path - **Relative paths**: Resolved relative to the current page's directory (`pagePath`) ### Loading Fonts Use the `@font` directive to load custom fonts for your Typst documents. Only `.otf` (OpenType) and `.ttf` (TrueType) fonts are supported. ````md :::typst{mode="preview"} @font src="/fonts/FiraSans-Regular.otf" ```typ #set text(font: "Fira Sans") = Custom Font Example This text uses the Fira Sans font. ``` ::: ```` #### Emoji Support For emoji support in Typst, you need to load the NotoColorEmoji font: ````md :::typst{mode="preview"} @font src="/fonts/NotoColorEmoji.ttf" ```typ = Emoji Example Hello World! 🎉🚀✨ ``` ::: ```` #### Reusing Fonts with Snippets For reusing fonts across many Typst instances, it is recommended to create a snippet. Create a file `fonts.md.hbs` in the `snippets` folder: ```hbs @font src="/fonts/NotoColorEmoji.ttf" @font src="/fonts/FiraSans-Regular.otf" ``` Then use the snippet in your Typst blocks: ````md :::typst @font src="/fonts/NotoColorEmoji.ttf" @font src="/fonts/FiraSans-Regular.otf" ```typ #set text(font: "Fira Sans") = Document with Custom Fonts This text uses Fira Sans and supports emojis! 🎉 ``` ::: ```` #### Multiple Source Files You can define multiple source files by using named code blocks: ````md :::typst{mode="preview"} ```typ main.typ = Main Document #include "/helper.typ" ``` ```typ helper.typ = Helper Content This content is in a separate file. ``` ::: ```` ## Examples ### Mathematical Formulas ````md :::typst{mode="preview" height="300px"} ```typ = Mathematical Formulas Typst supports beautiful mathematical typesetting: $ integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2 $ The quadratic formula: $ x = (-b plus.minus sqrt(b^2 - 4a c)) / (2a) $ ``` ::: ```` :::typst{mode="preview" height="300px"} ```typ = Mathematical Formulas Typst supports beautiful mathematical typesetting: $ integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2 $ The quadratic formula: $ x = (-b plus.minus sqrt(b^2 - 4a c)) / (2a) $ ``` ::: ### Tables ````md :::typst{mode="preview" height="250px"} ```typ = Data Table #table( columns: (auto, auto, auto), [*Name*], [*Age*], [*City*], [Alice], [25], [Berlin], [Bob], [30], [Munich], [Carol], [28], [Hamburg], ) ``` ::: ```` :::typst{mode="preview" height="250px"} ```typ = Data Table #table( columns: (auto, auto, auto), [*Name*], [*Age*], [*City*], [Alice], [25], [Berlin], [Bob], [30], [Munich], [Carol], [28], [Hamburg], ) ``` ::: ### Complex Example with Multiple Files This example demonstrates loading source files with multiple named Typst files: ````md :::typst{mode="preview" height="250px"} ```typ main.typ = Code Example Here is some inline `code` and a code block: #raw(block: true, lang: "python", "def hello(): print('Hello, World!')") #figure( image("/test.jpg", width: 80%), caption: "A complex figure with an image." ) #include "/other.typ" ``` ```typ other.typ = Additional Content #figure( image("/test.jpg", width: 80%), caption: "Another view of the image." ) ``` ::: ```` :::typst{mode="preview" height="250px"} ```typ main.typ = Code Example Here is some inline `code` and a code block: #raw(block: true, lang: "python", "def hello(): print('Hello, World!')") #figure( image("/test.jpg", width: 80%), caption: "A complex figure with an image." ) #include "/other.typ" ``` ```typ other.typ = Another Code Block #figure( image("/test.jpg", width: 80%), caption: "A complex figure with an image." ) ``` ::: # Video # Video Video element to allow easy display of videos on Hyperbook via HTML's [_The Video Embed element_](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video). The `