
Ljg Present
- 4.6k installs
- 6.7k repo stars
- Updated August 4, 2026
- lijigang/ljg-skills
ljg-present renders orgmode or markdown outlines into slogan-style fullscreen HTML slides without changing content text.
About
The ljg-present skill is an outline-faithful visual renderer that converts orgmode or markdown hierarchies into single-file HTML presentations saved to ~/Downloads. It does not extract manifestos, rewrite sentences, or reorder content: outline structure is preserved one-to-one with only physical pagination when blocks are long. Visual language uses one theme color per deck (black, red, yellow, or --cyber terminal style), left-aligned ultra-bold typography from 70vmin single characters to 10vmin long lines, staggered indent by outline depth, and automatic highlight for org emphasis and code markers. Mapping rules turn level-one headings into emphasis cover pages, level-two plus into theme pages, lists into indented lines, tables retain structure, and example blocks become monospace pre pages. Theme inference follows explicit -r -b -y flags, then filetags like :share: or :essay:, defaulting black. Workflow reads assets/slogan_template.html, injects title subtitle theme and slides JSON, and reports navigation keys. Iron rule: never change title, paragraph, list, or table text.
- Outline-faithful: no rewriting, reordering, or manifesto extraction; visual renderer only.
- Themes black red yellow or --cyber with filetags inference and explicit flag override.
- Org and markdown mapping: headings, lists, tables, emphasis, example blocks to slide types.
- Pagination rules split long paragraphs and lists while keeping visual consistency per block.
- Outputs single HTML to ~/Downloads with arrow space and touch navigation.
Ljg Present by the numbers
- 4,627 all-time installs (skills.sh)
- +91 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #70 of 688 Office & Documents skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
ljg-present capabilities & compatibility
- Capabilities
- orgmode and markdown outline parsing · theme selection with filetags inference · 1:1 structure mapping to emphasis and theme slid · automatic pagination with visual consistency · html template injection to ~/downloads
- Use cases
- presentations · copywriting
npx skills add https://github.com/lijigang/ljg-skills --skill ljg-presentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4.6k |
|---|---|
| repo stars | ★ 6.7k |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | lijigang/ljg-skills ↗ |
How do I turn an orgmode or markdown outline into a bold visual presentation without rewriting or reordering content?
Render orgmode or markdown outlines into slogan-style fullscreen HTML slides with black/red/yellow or cyber themes without rewriting content.
Who is it for?
Presenters who want manifesto-style visual decks from existing outlines in Chinese or English.
Skip if: Skip when you need editable PowerPoint PPTX or corporate deck templates with charts.
When should I use this skill?
User says present, 讲这个, 做成演讲, slogan, manifesto slides, or outline beautification.
What you get
Single-file HTML in ~/Downloads with theme-colored slides preserving original outline structure.
- Single-file HTML presentation
- Theme-styled slide JSON render
- Navigation-ready fullscreen deck
By the numbers
- 4 themes: black red yellow cyber
- 5 fontsize tiers by character count
- Iron rule: no content text changes
Files
ljg-present: 演讲铸造器
把 outline 铸成色块——视觉化渲染器,把舞台还给讲的人。
这不是什么
- 不是 manifesto 提炼器——不抽"那句话",不写"完整断言句",不重组顺序
- 不是高桥流——不削词到单字
- 不是 deck-style——不是企业 PPT 那种规整版式
这是什么
Outline → 视觉化渲染器:
- 输入 = orgmode 文件(
***层级 + 列表 + 表格 + 强调) - 输出 = 视觉美化的 slogan-style HTML,1:1 保留 outline 结构
- 不抽提、不重写、不浓缩——只决定怎么把这一行/这一节渲染为页面
视觉语言(参考审美:Felipe Franco / BIG STUDIOS 的 manifesto 美学):
- 整篇一个主题色——red/black/yellow 三选一
- left-aligned 舞台美学——文字左对齐,超大字号自然撑屏
- 超大字 ultra-bold——单字 70vmin、长句 11vmin
- 多行错位——按 outline 嵌套深度自动 indent 0/1/2
- 关键词自动换色——
*强调*~code~自动 hl - 章节切换打节拍——一级标题
*→ emphasis 封面页,其余 → theme 页
核心哲学
Outline 是真理。Skill 是渲染器。
不动内容是一条铁律:
- 标题不改字
- 段落不改字
- 列表项不改字
- 表格不改结构
- 顺序不重排
唯一允许的"动"是:物理分页(一段太长拆成多页),并保持视觉一致性。
Orgmode → 页面映射规则
标题层级
| Org 元素 | 页面 |
|---|---|
* 一级标题 | 独占 emphasis 封面页(accent 底色) |
** 二级标题 | 独占 theme 页(大字标题独占一页) |
*** 三级标题+ | 独占 theme 页(字号降一档) |
内容元素
| Org 元素 | 页面行为 |
|---|---|
| 段落 | theme 页,按句号/换行/字数分页 |
- 列表项 | theme 页,每项一行,indent 按嵌套深度(0/1/2) |
1. 编号列表 | 同上,保留序号前缀 |
| 嵌套列表 | 子项 indent +1(最多 indent=2) |
| `\ | 表格 \ |
*强调* | 自动 hl: true |
~code~ 或 =verbatim= | 自动 hl: true |
「」 内的关键词 | 视觉单元(保留括号,不强制 hl) |
引用 > ... | theme 页,indent 1 显示 |
分隔符 ----- | 独立 emphasis 休止页(无内容,纯色块) |
#+begin_example 块 | 独立 pre 页(monospace 渲染 ASCII art) |
文件级元数据
| Org 元素 | 用途 |
|---|---|
#+title: | → JSON title(浏览器 tab) |
#+author: 或 #+date: | → JSON subtitle(页脚右下) |
#+filetags: | 用于推断 theme(见下) |
#+identifier: | 忽略 |
Theme 推断
优先级:显式参数 > filetags 推断 > 默认 black
显式覆盖(参数):
-r/--theme=red→ red-b/--theme=black→ black-y/--theme=yellow→ yellow--cyber→ cyber-hacker(黑底绿字 + CRT 扫描线 + HUD + 终端光标)
filetags 自动推断:
| filetags 含 | theme | 调性 |
|---|---|---|
:share: :talk: :manifesto: :keynote: | red | 宣言、号召 |
:essay: :think: :learn: :note: | black | 沉思、论证 |
:critique: :warn: :rant: | yellow | 反讽、警觉 |
| 都没有 | black | 默认沉思调 |
分页规则(内容多时)
铁律:拆分后保持视觉一致性。同一逻辑块的页用同样的字号档位/底色/缩进规则。
| 情形 | 拆法 |
|---|---|
| 段落 ≤ 30 字 | 单页 |
| 段落 30-80 字,含多句号 | 每句一页(每页 medium 档字号) |
| 段落 > 80 字 | 按 ~30 字一页拆,加 ⋯ 续标 |
| 列表 ≤ 4 项 | 单页全部展示(错位 indent) |
| 列表 5-8 项 | 拆 2 页,每页 3-4 项(保持每页项数接近) |
| 列表 > 8 项 | 拆多页,每页 4 项 |
| 嵌套列表(如 4 革命×4 属性) | 父项 1 页 + 每个子项独立成组(标题 1 页 + 子项 1 页) |
| 表格 ≤ 6 行 | 单页 |
| 表格 > 6 行 | 拆多页,每页保留表头 |
一致性检查:拆完后扫一遍——同源拆分的页要长得像同一种东西,字号/缩进/底色都对齐。
自动 emphasis(节拍)
- 所有
* 一级标题→ emphasis 封面页 - 文件首页(标题或第一行非空文本)→ emphasis 开场页(如已是一级标题则合并)
- 文件末页(最后一段或最后一项)→ emphasis 收束页
-----分隔符 → emphasis 休止页- 其他全是 theme 页
不要为了凑节奏强行加 emphasis——一级标题就是天然的章节断点。
自动 hl(高亮)
- org
*强调*→hl: true - org
~code~=verbatim=→hl: true - emphasis 页内的 hl 自动忽略(CSS
color: inherit)
映射举例
输入(org 节选):
#+title: 美团分享
#+filetags: :share:
* AI
** 为什么说 AI 是一次革命?
人类革命:能力让渡的层级跃迁
- 「人之为人」重新定义
- 社会组织重排映射结果:
| # | 类型 | 内容 | 来源 |
|---|---|---|---|
| 1 | emphasis | 「AI」 | * AI(一级标题封面) |
| 2 | theme | 「为什么说 AI 是一次革命?」 | ** ... 二级标题独占页 |
| 3 | theme | 「人类革命:能力让渡的层级跃迁」 | 段落,单句 |
| 4 | theme | 两行错位:「『人之为人』重新定义」/「社会组织重排」 | 列表 ≤4 项一页 |
theme 自动选 red(filetags :share:),title=美团分享。
视觉规范
色板(仅 4 色)
--c-black: #1A1A1A
--c-red: #E63956
--c-yellow: #FFD400
--c-white: #FFFFFF
--c-gold: #FFE082主题映射(一篇只用 ≤3 色)
| theme | 默认页 | emphasis 页 | hl 色(仅 theme 页) |
|---|---|---|---|
| black 沉思 | 黑底白字 | 红底白字 | 红色 #E63956 |
| red 宣言 | 红底白字 | 黑底白字 | 柔金黄 #FFE082 |
| yellow 反讽 | 黄底黑字 | 黑底白字 | 红色 #E63956 |
| cyber 终端 | 黑底矩阵绿 | 绿底黑字 | 白色 #FFFFFF(带绿光 + CRT 扫描线 + 顶部 HUD) |
字体栈
"Helvetica Neue", "Arial Black", "Inter", "PingFang SC", "Heiti SC", -apple-system, sans-serif
font-weight: 900
letter-spacing: -0.05emcyber 主题额外字体(用于 HUD/footer/pre):
"JetBrains Mono", "Fira Code", "IBM Plex Mono", "Source Code Pro", "Menlo", monospace字号自适应
按本页"最长那一行"的字符数(CJK 字符按 1.8 计权)自动分档:
| 档位 | 字符数 | 字号 |
|---|---|---|
| single | ≤ 2 | 70vmin |
| short | 3-6 | 48vmin |
| medium | 7-14 | 28vmin |
| long | 15-26 | 16vmin |
| xlong | 27+ | 10vmin |
多行页自动降一档。
排版
- 内容区域 padding 6vmin 7vmin(贴近边缘,让大字有撑满感)
- lines 块水平居中 + 行内左对齐——
align-items: center让 lines 块整体在屏幕水平居中(消除 16:9 右侧空白),但每一行的文字仍是 left-aligned 起始,indent 0/1/2 在块内制造错位 - letter-spacing
-0.05em——ultra-bold 应有的字字挤压感 - line-height
1.05、行间 gap0.15em——多行折行也有呼吸空间 - 文字垂直方向:居中
- 页脚:左下页码 + 右下副标题,13px monospace,opacity 0.5
JSON Schema
{
"theme": "black|red|yellow|cyber", // 主题色(必选,决定整篇调性)
"title": "演讲标题(浏览器 tab)",
"subtitle": "副标题/品牌(页脚右下,可选)",
"slides": [
// 默认 theme 页
{
"lines": [ // 1-N 行
{
"indent": 0, // 0/1/2 缩进档(按 outline 嵌套深度)
"align": "left|center|right", // 可选,默认 left
"chunks": [ // 行内片段
{"t": "句子前段"},
{"t": "高亮词", "hl": true}, // 仅 theme 页生效
{"t": "句子后段"}
]
}
]
},
// emphasis 页(accent 底色,整页就是高亮,不允许 inline hl)
{ "emphasis": true, "lines": [...] },
// pre 页(ASCII art / 预格式化块)
{ "preTitle": "diagram_name", "pre": "...preformatted text..." }
]
}字段省略约定:
- 不写
emphasis= 默认 theme 页 - emphasis 页内
chunks[].hl: true会被忽略 - 写
pre字段则该页为 ASCII art 页(monospace 渲染)
调用流程
1. 获取内容(文件 → Read / 粘贴 → 直接用 / URL → WebFetch) 2. 解析 outline:
- org:识别
***标题层级、-1.列表、|...|表格、*强调*/~code~、#+begin_example块 - markdown(兼容):
###标题、-*列表、|表格、**强调**、`代码块 - 纯文本(fallback):按空行分段,每段一页
3. 推断 theme:显式参数 > #+filetags: > 默认 black 4. 应用映射规则生成 slides 数组:
*标题 → emphasis 封面**+ 标题 → theme 独占页- 段落 → theme 页(按分页规则)
- 列表 → theme 页(错位 indent + 分页规则)
- 表格 → theme 页(保留结构 + 分页规则)
- 强调 → 自动 hl
- example 块 → 独立 pre 页
5. Read assets/slogan_template.html(cyber 主题需在模板基础上注入扫描线/HUD/光标 CSS) 6. 替换占位符:
{{TITLE}}→ 文件#+title:或显式参数{{SUBTITLE}}→#+author:#+date:拼接,或留空{{THEME}}→ 推断或显式参数(black|red|yellow|cyber){{SLIDES_JSON}}→ JSON.stringify(slides)
7. 写文件到 ~/Downloads/{name}.html({name} 取自 #+title: 或文件名,去标点,≤ 20 字) 8. 报告路径 + 翻页键 → ← Space F Home End
品味准则
- outline 是真理——不动字、不抽提、不重写、不重排
- 一级标题 = emphasis 封面——天然的章节断点,自动节拍
- 二级标题 = 独占 theme 页——给标题应有的重量
- 列表错位——靠 indent 0/1/2 体现 outline 嵌套深度
- *`强调` 自动 hl*——尊重作者的标记意图
- 拆页保持一致——同一逻辑块的视觉处理一致(字号档位/缩进/底色)
- 页脚保留——页码 + 副标题不要删,那是品牌的冷气
- 左对齐不居中——VACAT 美学的灵魂
禁区
- 不抽 manifesto——不要"找钉子",作者已经写好了 outline
- 不写新句子——不要"完整断言句"重组
- 不重排顺序——按 outline 顺序输出,作者怎么排就怎么呈现
- 不删内容——所有列表项/段落都要呈现,不挑挑拣拣
- 不放图片/图标——色块就是图(cyber 主题的 HUD/扫描线除外,那是主题的一部分)
- 不用过渡动画——硬切
- 不在 emphasis 页用 inline hl——emphasis 整页就是高亮,再 hl 就乱了
- 不混用多个 theme——一篇一个气质,不切换
- 不要副标题字过大——页脚 13px,气场不能抢主标
- 不擅自加 emphasis——只有一级标题、首末页、
-----是 emphasis,别的不要
中文默认
默认输出中文。除非原文是英文且用户要求保留英文。
通用交互
→SpaceEnterjPageDown:下一页(含蓝牙翻页笔)←kPageUp:上一页(含蓝牙翻页笔)Home/End:跳首末f/F:全屏切换- 触屏左右滑:翻页
- 点击右半屏:下一页;点击左半屏:上一页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{TITLE}}</title>
<style>
:root {
--c-black: #1A1A1A;
--c-red: #E63956;
--c-yellow: #FFD400;
--c-white: #FFFFFF;
--c-gold: #FFE082;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%; height: 100%;
font-family: "Helvetica Neue", "Arial Black", "Inter", "PingFang SC", "Heiti SC", "STHeiti", -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 900;
overflow: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
letter-spacing: -0.05em;
}
/* Theme drives every color via single attribute on body.
Each theme defines: bg/fg pair (default slide), acc-bg/acc-fg pair (emphasis), hl color. */
body[data-theme="black"] { --bg: var(--c-black); --fg: var(--c-white); --acc-bg: var(--c-red); --acc-fg: var(--c-white); --hl: var(--c-red); }
body[data-theme="red"] { --bg: var(--c-red); --fg: var(--c-white); --acc-bg: var(--c-black); --acc-fg: var(--c-white); --hl: var(--c-gold); }
body[data-theme="yellow"] { --bg: var(--c-yellow); --fg: var(--c-black); --acc-bg: var(--c-black); --acc-fg: var(--c-white); --hl: var(--c-red); }
body { background: var(--bg); }
.stage { position: fixed; inset: 0; }
/* Stage: VACAT-style left-aligned + vertical center.
Big type itself fills the visual weight — no need to center horizontally. */
.slide {
position: absolute;
inset: 0;
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 6vmin 7vmin;
background: var(--bg);
color: var(--fg);
}
.slide.active { display: flex; }
.slide[data-emphasis="true"] { background: var(--acc-bg); color: var(--acc-fg); }
/* HL: only meaningful on theme slides — emphasis slides ARE the highlight */
.slide .hl { color: var(--hl); }
.slide[data-emphasis="true"] .hl { color: inherit; /* emphasis slide ignores hl */ }
/* Lines block: width fits the longest line. Each line stays left within the block. */
.lines {
display: flex;
flex-direction: column;
gap: 0.15em;
line-height: 1.05;
max-width: 100%;
align-items: flex-start;
}
.line { white-space: nowrap; text-align: left; }
.line[data-indent="0"] { padding-left: 0; }
.line[data-indent="1"] { padding-left: 7vmin; }
.line[data-indent="2"] { padding-left: 16vmin; }
/* Font-size tiers — v2.1.1 上调 ~30% 适配 16:9 横屏,消除右侧空白。
Single chars dominate the screen; long lines sweep across with full weight. */
.slide[data-len="single"] .lines { font-size: clamp(320px, 80vmin, 1100px); }
.slide[data-len="short"] .lines { font-size: clamp(240px, 55vmin, 780px); }
.slide[data-len="medium"] .lines { font-size: clamp(150px, 35vmin, 480px); }
.slide[data-len="long"] .lines { font-size: clamp(100px, 22vmin, 320px); }
.slide[data-len="xlong"] .lines { font-size: clamp(64px, 14vmin, 200px); }
/* Footers — subtle, monospace, never compete with main text */
.pager, .subtitle {
position: fixed;
bottom: 2.5vmin;
font-family: "Menlo", "Monaco", "OCR A Std", monospace;
font-size: 13px;
font-weight: 400;
letter-spacing: 0.12em;
user-select: none;
z-index: 10;
text-transform: uppercase;
opacity: 0.5;
color: var(--fg);
}
.pager { left: 3vmin; }
.subtitle { right: 3vmin; }
body[data-current="emphasis"] .pager,
body[data-current="emphasis"] .subtitle { color: var(--acc-fg); }
:fullscreen .pager, :fullscreen .subtitle, :fullscreen .first-guide { display: none; }
:-webkit-full-screen .pager, :-webkit-full-screen .subtitle, :-webkit-full-screen .first-guide { display: none; }
.first-guide {
position: fixed;
top: 3vmin;
left: 50%;
transform: translateX(-50%);
font-family: "Menlo", "Monaco", monospace;
font-size: 12px;
font-weight: 400;
color: var(--fg);
opacity: 0.4;
letter-spacing: 0.18em;
user-select: none;
transition: opacity 0.6s ease;
z-index: 10;
text-transform: uppercase;
}
.first-guide.hide { opacity: 0; pointer-events: none; }
</style>
</head>
<body data-theme="{{THEME}}">
<div class="stage" id="stage"></div>
<div class="first-guide" id="firstGuide">→ space next · F fullscreen</div>
<div class="pager" id="pager">01 / 01</div>
<div class="subtitle" id="subtitle">{{SUBTITLE}}</div>
<script>
// {{SLIDES_JSON}} replaced by skill — each slide is one of:
// {lines: [{indent, chunks: [{t, hl?}]}]} — default theme slide
// {emphasis: true, lines: [...]} — accent slide (no inline hl)
const SLIDES = {{SLIDES_JSON}};
const stage = document.getElementById('stage');
const pager = document.getElementById('pager');
const subtitle = document.getElementById('subtitle');
const firstGuide = document.getElementById('firstGuide');
const body = document.body;
// CJK chars are ~1.8x wider than Latin at the same font-size
function lineCharLen(chunks) {
const CJK = /[ -〿㐀-䶿一-鿿豈--]/;
return chunks.reduce((acc, c) => {
const t = (c.t || '').replace(/\s+/g, ' ');
let len = 0;
for (const ch of t) len += CJK.test(ch) ? 1.8 : 1;
return acc + len;
}, 0);
}
function maxLineLen(lines) {
if (!lines || !lines.length) return 0;
return Math.max(...lines.map(l => lineCharLen(l.chunks || [])));
}
// Tier picks font-size class. Multi-line slides shift one tier smaller.
function lengthTier(maxLen, lineCount) {
const adj = maxLen + Math.max(0, (lineCount - 1)) * 4;
if (adj <= 2) return 'single';
if (adj <= 6) return 'short';
if (adj <= 14) return 'medium';
if (adj <= 26) return 'long';
return 'xlong';
}
function escapeHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
}
SLIDES.forEach((s) => {
const el = document.createElement('div');
el.className = 'slide';
if (s.emphasis) el.setAttribute('data-emphasis', 'true');
if (s.lines && s.lines.length) {
el.setAttribute('data-len', lengthTier(maxLineLen(s.lines), s.lines.length));
const linesEl = document.createElement('div');
linesEl.className = 'lines';
s.lines.forEach(line => {
const lineEl = document.createElement('div');
lineEl.className = 'line';
lineEl.setAttribute('data-indent', String(line.indent || 0));
if (line.align) lineEl.style.textAlign = line.align;
const chunks = line.chunks || [];
lineEl.innerHTML = chunks.map(c => {
const t = escapeHtml(c.t);
return c.hl ? '<span class="hl">' + t + '</span>' : t;
}).join('');
linesEl.appendChild(lineEl);
});
el.appendChild(linesEl);
}
stage.appendChild(el);
});
const slides = stage.querySelectorAll('.slide');
let idx = 0;
function show(i) {
if (i < 0) i = 0;
if (i >= slides.length) i = slides.length - 1;
slides[idx].classList.remove('active');
idx = i;
slides[idx].classList.add('active');
pager.textContent = String(idx + 1).padStart(2, '0') + ' / ' + String(slides.length).padStart(2, '0');
// Track current slide kind so footer color follows accent bg correctly
const current = slides[idx].getAttribute('data-emphasis') === 'true' ? 'emphasis' : 'theme';
body.setAttribute('data-current', current);
if (idx === 0) firstGuide.classList.remove('hide');
else firstGuide.classList.add('hide');
}
function next() { show(idx + 1); }
function prev() { show(idx - 1); }
function first() { show(0); }
function last() { show(slides.length - 1); }
function toggleFullscreen() {
if (!document.fullscreenElement) document.documentElement.requestFullscreen?.();
else document.exitFullscreen?.();
}
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowRight': case ' ': case 'Enter': case 'j': case 'PageDown':
e.preventDefault(); next(); break;
case 'ArrowLeft': case 'k': case 'PageUp':
e.preventDefault(); prev(); break;
case 'Home': e.preventDefault(); first(); break;
case 'End': e.preventDefault(); last(); break;
case 'f': case 'F': e.preventDefault(); toggleFullscreen(); break;
}
});
let touchX = null;
document.addEventListener('touchstart', (e) => { touchX = e.touches[0].clientX; });
document.addEventListener('touchend', (e) => {
if (touchX == null) return;
const dx = e.changedTouches[0].clientX - touchX;
if (dx < -40) next();
else if (dx > 40) prev();
touchX = null;
});
document.addEventListener('click', (e) => {
if (e.target.closest('.pager,.subtitle,.first-guide')) return;
const mid = window.innerWidth / 2;
if (e.clientX > mid) next(); else prev();
});
show(0);
</script>
</body>
</html>
Related skills
How it compares
ljg-present renders outlines to HTML without rewriting, not PowerPoint or manifesto extraction tools.
FAQ
Who is ljg-present for?
Presenters converting orgmode or markdown outlines into fullscreen HTML slogan-style slides.
When should I use ljg-present?
When visualizing an outline for talks without changing words, order, or structure.
Is ljg-present safe to install?
Review the Security Audits panel; outputs local HTML files to ~/Downloads.