gnab/remark
A simple, in-browser, markdown-driven slideshow tool.
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓24+ active contributors
- ✓Distributed ownership (top contributor 29% of recent commits)
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
- ⚠Stale — last commit 2y ago
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the “Healthy” badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/gnab/remark)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/gnab/remark on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: gnab/remark
Generated by RepoPilot · 2026-05-06 · Source
Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/gnab/remark shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
Verdict
GO — Healthy across all four use cases
- 24+ active contributors
- Distributed ownership (top contributor 29% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live gnab/remark
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/gnab/remark.
What it runs against: a local clone of gnab/remark — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in gnab/remark | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch develop exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 716 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of gnab/remark. If you don't
# have one yet, run these first:
#
# git clone https://github.com/gnab/remark.git
# cd remark
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of gnab/remark and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "gnab/remark(\\.git)?\\b" \\
&& ok "origin remote is gnab/remark" \\
|| miss "origin remote is not gnab/remark (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify develop >/dev/null 2>&1 \\
&& ok "default branch develop exists" \\
|| miss "default branch develop no longer exists"
# 4. Critical files exist
test -f "src/remark.js" \\
&& ok "src/remark.js" \\
|| miss "missing critical file: src/remark.js"
test -f "src/remark/models/slideshow.js" \\
&& ok "src/remark/models/slideshow.js" \\
|| miss "missing critical file: src/remark/models/slideshow.js"
test -f "src/remark/parser.js" \\
&& ok "src/remark/parser.js" \\
|| miss "missing critical file: src/remark/parser.js"
test -f "src/remark/converter.js" \\
&& ok "src/remark/converter.js" \\
|| miss "missing critical file: src/remark/converter.js"
test -f "src/remark/views/slideshowView.js" \\
&& ok "src/remark/views/slideshowView.js" \\
|| miss "missing critical file: src/remark/views/slideshowView.js"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 716 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~686d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/gnab/remark"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
TL;DR
remark is a browser-based, markdown-driven slideshow tool that renders presentations directly from markdown text in an HTML textarea, with no external dependencies beyond marked.js. It features presenter mode with speaker notes, syntax highlighting, slide scaling across devices, and touch/keyboard navigation—all built for developers comfortable editing HTML/CSS. Monolithic single-file build structure: src/remark.js is the main engine, bundled with src/remark.html as self-contained distribution. Components live in src/remark/components/ (printing, slide-number, styler, timer), controllers in src/remark/controllers/ (keyboard, mouse, touch, location inputs), and models in src/remark/models/ (slide, slideshow with navigation/events). Built via Browserify (Gruntfile.js) into a single distributable.
Who it's for
Technical presenters and developers who want to write slides in markdown, version control their presentations in Git, and customize styling with plain CSS—primarily those giving tech talks, conference presentations, or internal engineering talks who value simplicity and code-centric workflows.
Maturity & risk
Production-ready and stable: version 0.15.0 with decades-old architectural patterns, active CI/CD via Travis CI, comprehensive test suite using Mocha/mocha-chrome, and visible use in real presentations (remarkjs.com). However, commit recency data is not provided; check the git log to assess ongoing maintenance.
Low dependency risk: only marked (v0.3.9) as a runtime dependency, though that version is 4+ years old and may have security concerns—consider pinning to a recent marked version. Single entry point (src/remark.js) with no package maintainer activity timeline visible. Large HTML/JavaScript file counts (1.3M+ each) suggest monolithic codebase that could be difficult to modularize.
Active areas of work
No recent activity visible in provided data. Repo appears in maintenance mode with stable, widely-adopted codebase. Check HISTORY.md for last documented changes and GitHub issues/PRs for current work.
Get running
git clone https://github.com/gnab/remark.git && cd remark && npm install && make
Daily commands: make (runs Grunt tasks); make serve (via grunt-contrib-connect per Gruntfile.js); make test (runs mocha via make.js); open boilerplate-local.html in browser after building.
Map of the codebase
src/remark.js— Main entry point that initializes the slideshow engine and exposes the public API.src/remark/models/slideshow.js— Core slideshow model managing slides, navigation state, and event emission.src/remark/parser.js— Parses markdown syntax into slide and presenter note structures; essential for content rendering.src/remark/converter.js— Converts parsed markdown into HTML; critical for visual presentation.src/remark/views/slideshowView.js— Main view layer rendering slides to the DOM; handles visual state synchronization.src/remark/controllers/defaultController.js— Input event handler routing keyboard, mouse, touch, and navigation events to slideshow actions.src/remark/lexer.js— Tokenizes markdown input before parsing; foundational for content processing pipeline.
How to make changes
Add a new keyboard shortcut
- Define the key binding in src/remark/controllers/inputs/keyboard.js by adding a handler function that maps keys to slideshow methods (e.g., show(), hide(), prev(), next()) (
src/remark/controllers/inputs/keyboard.js) - Call the appropriate slideshow method from src/remark/models/slideshow.js (e.g., navigation.next()) in your handler (
src/remark/models/slideshow.js) - Document the shortcut in README.md with key code and description
Add a new markdown syntax feature
- Add tokenization rules in src/remark/lexer.js to recognize the new syntax pattern (
src/remark/lexer.js) - Extend the parser in src/remark/parser.js to handle new token types and build appropriate slide properties (
src/remark/parser.js) - Update src/remark/converter.js to transform the parsed structure into appropriate HTML/CSS classes (
src/remark/converter.js) - Add LESS styling in src/remark.less to visually style the new feature (
src/remark.less)
Add a new UI component (e.g., custom slide counter)
- Create a new component directory and module following the pattern in src/remark/components/ with initialization and update methods (
src/remark/components) - Subscribe to slideshow events (change, hidePresenter, showPresenter) in src/remark/models/slideshow/events.js (
src/remark/models/slideshow/events.js) - Render DOM elements and update them on event callbacks; inject styles via src/remark/resources.js if needed (
src/remark/resources.js) - Instantiate the component in src/remark/views/slideshowView.js during slideshow initialization (
src/remark/views/slideshowView.js)
Add support for a new input method (e.g., gamepad controls)
- Create a new input handler module following the pattern of src/remark/controllers/inputs/keyboard.js (
src/remark/controllers/inputs) - Implement event listeners and map input actions to slideshow navigation calls (prev(), next(), etc.) (
src/remark/models/slideshow.js) - Register the input handler in src/remark/controllers/defaultController.js by instantiating and binding it (
src/remark/controllers/defaultController.js)
Why these technologies
- Markdown (via marked.js) — Primary content format; human-friendly syntax for creating presentations without HTML/CSS knowledge
- DOM API (vanilla JS) — Avoids framework overhead; direct control over rendering and animation for in-browser slideshow efficiency
- LESS CSS preprocessor — Embedded into source for easy theming and maintainability; compiled and injected at runtime
- Highlight.js integration — Client-side syntax highlighting for code blocks without server dependency
Trade-offs already made
-
All rendering in browser, no server
- Why: Simplicity and portability; presentations work offline and in any environment
- Consequence: Larger JavaScript bundle; initial parse/render on client may be slow for very long presentations
-
Single markdown file as content source
- Why: Minimal setup; easy to version control and share
- Consequence: No built-in support for multi-file presentations or dynamic content fetching (must extend via macros)
-
Presenter mode via cloned DOM view
- Why: Works across any browser/OS without server; synchronized state between windows
- Consequence: Requires manual window management; limited to same-origin content
-
Vanilla JS instead of framework
- Why: Minimal dependencies; predictable performance and bundle size
- Consequence: More manual DOM manipulation and event handling; harder to extend without establishing patterns
Non-goals (don't propose these)
- Server-side rendering or real-time collaboration
- Mobile-native app support (web-only)
- Built-in speaker camera/video integration
- Dynamic content updates without page reload
- Animation timeline or keyframe editor
- Exporting to video or proprietary formats (PDF/print only)
Traps & gotchas
marked.js v0.3.9 is outdated; newer versions have different APIs—check marked CHANGELOG before upgrading. Presenter mode uses window.open() to clone slideshow; browser popup blocking may interfere. Slide scaling via CSS transforms assumes fixed viewport dimensions (see src/remark/scaler.js). Touch events hardcoded to left/right swipe; no pinch-zoom support. CSS class names in markdown (e.g., .left, .center) must match src/remark/remark.less definitions or won't style correctly.
Architecture
Concepts to learn
- Markdown-driven UI — remark's core innovation—parsing markdown syntax extensions (class names, layout templates) to declaratively configure slide styling without touching HTML, reducing cognitive load for presenters
- Presenter mode / speaker notes — remark clones the slideshow into a separate browser window and uses keyboard shortcuts to sync; understanding window.open() and postMessage() cross-window communication is essential for extending presenter features
- CSS transform-based scaling — src/remark/scaler.js scales slides proportionally via CSS transforms (scale-x, scale-y) rather than viewport resizing, ensuring consistent rendering across devices; critical for understanding responsive behavior
- Browserify module bundling — remark uses Browserify (not webpack/rollup) to bundle CommonJS modules from src/ into a single browser-runnable script; build pipeline knowledge is necessary for local development
- Touch gesture recognition — src/remark/controllers/inputs/touch.js detects swipe gestures (touchstart/touchmove/touchend events) for mobile slide navigation; understanding touch event coordinate deltas is needed to extend mobile support
- Syntax highlighting via language-specific lexers — src/remark/highlighter.js wraps language-specific highlighting libraries; adding support for new languages requires understanding how remark maps code block languages to tokenizers
- State machine for slideshow navigation — src/remark/models/slideshow/navigation.js implements slide state transitions (next, previous, go-to); modifying navigation behavior (e.g., looping, jumping) requires understanding this state flow
Related repos
hakimel/reveal.js— Direct competitor: full-featured HTML5 presentation framework with more plugins/themes but heavier than remark's markdown-first approachsinatra/sinatra— Inspired minimal design philosophy; remark rejects feature bloat in favor of markdown simplicity like Sinatra rejects Rails overheadimpress/impress.js— Alternative in-browser slideshow with 3D transforms and different UX; similar use case but different presentation modelgnab/remarkise— Official companion tool: web UI for rendering remark slideshows from markdown URLs without local buildmarked-js/marked— Direct dependency: markdown parser remark uses; fork or patch if syntax extensions needed
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test coverage for src/remark/views/ components
The test directory shows no test files for slideView.js, notesView.js, or slideshowView.js despite these being critical view components. These handle the rendering and presentation logic. Adding unit tests would ensure view behavior remains stable across refactors and new features.
- [ ] Create test/remark/views/slideView_test.js with tests for slide rendering, scaling, and DOM manipulation
- [ ] Create test/remark/views/notesView_test.js with tests for notes display and synchronization
- [ ] Create test/remark/views/slideshowView_test.js with tests for overall view orchestration
- [ ] Mock DOM elements and test interaction with src/remark/dom.js utilities
- [ ] Run tests via 'npm test' and verify mocha-chrome integration
Add missing tests for input controllers in src/remark/controllers/inputs/
The inputs directory (keyboard.js, mouse.js, touch.js, location.js, message.js) has no corresponding test files, yet these handle user interactions which are critical to the slideshow experience. These are high-risk areas for regressions.
- [ ] Create test/remark/controllers/inputs/keyboard_test.js to test key event handling and navigation
- [ ] Create test/remark/controllers/inputs/mouse_test.js to test click and drag interactions
- [ ] Create test/remark/controllers/inputs/touch_test.js to test swipe and touch gesture handling
- [ ] Mock keyboard/mouse/touch events and verify correct slideshow navigation calls
- [ ] Ensure tests integrate with sinon for stubbing event listeners
Add missing test coverage for src/remark/components/ (printing, timer, styler)
Only slide-number_test.js and timer_test.js exist. The printing.js and styler.js components lack test files despite being important for presentation features. Printing affects PDF export and styler affects CSS generation—both deserve explicit test coverage.
- [ ] Create test/remark/components/printing_test.js with tests for print stylesheet injection and page break handling
- [ ] Create test/remark/components/styler_test.js with tests for CSS class application, theme switching, and custom style injection
- [ ] Verify printing tests handle window.print() interactions and media query simulation
- [ ] Verify styler tests validate style transformations and DOM updates via src/remark/dom.js
- [ ] Add tests to test/remark/components/ directory structure
Good first issues
- Add unit tests for src/remark/macros.js (macro expansion logic) and src/remark/highlighter.js (syntax highlighting integration)—currently untested based on test file absence
- Document keyboard shortcuts and input controller architecture in README.md or a CONTRIBUTING.md (src/remark/controllers/inputs/ contains 5 input handlers but no contributor guide)
- Add TypeScript type definitions for the public API (src/remark/api.js) to improve IDE autocomplete and reduce onboarding friction for TypeScript users
Top contributors
- @gnab — 29 commits
- @peterj — 25 commits
- @dvberkel — 16 commits
- @tripu — 8 commits
- @mschilli87 — 2 commits
Recent commits
be58d65— Merge pull request #693 from ryanrolds/highlight_update (tripu)1d05759— Updated highlighter (ryanrolds)c526fb8— Merge pull request #690 from wolterhv/patch-1 (tripu)8ce67b1— Update README.md (wolterhv)1bbce13— Merge pull request #675 from adamkusmirek/Fix(#674) (tripu)6af4fd3— Fix for Issue #674: broken gh pages links (adamkusmirek)f32c421— Merge pull request #666 from gnab/fix/665 (peterj)c16c76a— Fix #665 (broken link in README) (tripu)49f47c8— Revert "Merge pull request #490 from joel-porquet/font_size" (gnab)b7e9483— Merge branch 'develop' of https://github.com/gnab/remark into develop (gnab)
Security observations
The remark slideshow tool has several notable security concerns, primarily centered around the severely outdated marked dependency (0.3.9) which contains known XSS vulnerabilities. Combined
- High · Outdated Dependency - marked 0.3.9 —
package.json - dependencies.marked. The project uses marked 0.3.9, which is severely outdated (released in 2015). This version contains multiple known security vulnerabilities including XSS issues. Current versions are 4.x+. Given that remark is a markdown processor that renders user-provided markdown content, XSS vulnerabilities in the markdown parser are critical. Fix: Upgrade marked to the latest stable version (4.3.0 or higher). Review breaking changes and test thoroughly with existing markdown slides to ensure compatibility. - High · Potential XSS via Markdown Rendering —
src/remark/converter.js, src/remark/parser.js, src/remark/views/. The codebase appears to process and render markdown content in the browser without clear evidence of sanitization. Files like src/remark/converter.js and src/remark/parser.js process markdown, and views render the output. Combined with the outdated marked dependency, this creates XSS vulnerability risks when rendering untrusted markdown content. Fix: Implement comprehensive output sanitization using a library like DOMPurify. Ensure all HTML output from markdown is properly escaped. Review and test all markdown rendering paths with XSS payloads. - Medium · Outdated Build and Development Dependencies —
package.json - devDependencies. Several dev dependencies are outdated: browserify 13.0.0 (2016), grunt 1.0.3 (2016), mocha 5.2.0 (2018). While not direct production issues, outdated build tools may have known vulnerabilities that could be exploited during development or in the build pipeline. Fix: Update browserify to ^16.0.0+, grunt to ^1.3.0+, and mocha to ^10.0.0+. Review and test the build process after updates to ensure no breaking changes. - Medium · Missing Content Security Policy (CSP) Headers —
boilerplate-local.html, boilerplate-remote.html, boilerplate-single.html. The boilerplate HTML files (boilerplate-local.html, boilerplate-remote.html, boilerplate-single.html) do not appear to specify CSP headers. For a tool that renders user-provided markdown in the browser, CSP is a critical defense against XSS attacks. Fix: Add strict Content-Security-Policy headers to prevent inline script execution and restrict script sources. Example: Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' - Medium · No Input Validation for Markdown Source —
src/remark/controllers/inputs/. No evidence of validation or sanitization for markdown source input from various controllers (keyboard.js, location.js, message.js, mouse.js, touch.js). If remote markdown URLs are loaded, SSRF or other injection attacks may be possible. Fix: Implement strict input validation for all markdown sources. Validate URLs using a whitelist approach. Implement size limits on markdown input to prevent DoS attacks. - Low · Missing Security Headers Configuration —
Gruntfile.js, grunt-contrib-connect configuration. The Grunt configuration (Gruntfile.js) for the connect server likely doesn't specify security headers like X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security, etc. Fix: Configure the development server to serve security headers. For production deployments, ensure web server configuration includes headers like: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Strict-Transport-Security: max-age=31536000 - Low · No HTTPS Enforcement in Boilerplate —
boilerplate-remote.html. Boilerplate files do not enforce HTTPS, which could be problematic if remote markdown sources are loaded over HTTP. Fix: Ensure remote markdown sources are always loaded over HTTPS. Consider adding protocol-relative URLs or explicit HTTPS requirements.
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.