{"id":"deck-ljg-present","name":"deck-ljg-present","summary":"アウトラインを大きなカラーブロック宣言デッキに1:1で鋳込みし、元のテキストは変えず美しく仕上げます。","body":"【模板: 宣言式演讲（Outline-Faithful）】\n\n【意图】把用户的 outline / markdown 1:1 视觉化为色块大字 manifesto deck。**不抽提、不重写、不重排、不浓缩**——只决定每一行/每一节渲染为哪一页。审美参考：Felipe Franco / BIG STUDIOS 的 manifesto 大字海报。\n\n【铁律 — 全部违反必须重做】\n- 标题不改字, 段落不改字, 列表不改字, 顺序不重排\n- 唯一允许的\"动\"是**物理分页**（一段太长拆成多页）\n- 不抽 manifesto / 不写新句子 / 不删内容 / 不放图片图标 / 不用过渡动画\n- 一篇只用一个主题色（black / red / yellow 三选一）\n\n【outline → 页面映射】\n\n| 输入元素 | 输出页 |\n|---|---|\n| `# 一级标题` | 独占 **emphasis** 封面页（accent 底色, 通常单字/单短词） |\n| `## 二级标题` | 独占 **theme** 页（大字标题独占一页） |\n| `### 三级标题`+ | 独占 theme 页（字号自动降一档） |\n| 段落（≤30字） | 单 theme 页 |\n| 段落（30-80字, 多句号） | 每句一页（medium 档） |\n| 段落（>80字） | 按 ~30 字一页拆, 末尾加 `⋯` |\n| `- 列表项`（≤4） | 一页全展示, indent 按嵌套深度 0/1/2 |\n| 列表 5-8 项 | 拆 2 页, 每页 3-4 项, 项数接近 |\n| 列表 >8 项 | 拆多页, 每页 4 项 |\n| 表格 ≤6 行 | 单页 |\n| 表格 >6 行 | 拆多页, 每页保留表头 |\n| `**强调**` / `` `code` `` | 自动 `hl: true` |\n| `---` 分隔符 | 独立 **emphasis 休止页**（空 emphasis, 纯色块） |\n\n**首末页自动 emphasis**：文档首段（如已是 `#` 则合并）+ 文档末段 = emphasis 开场 / 收束页。一级标题就是天然的章节断点, 不要为了凑节奏强行加 emphasis。\n\n【主题色推断 — 一篇只能一个】\n\n| 文档调性 / 标签 | theme | 默认页 | emphasis 页 | hl 色（仅 theme 页生效） |\n|---|---|---|---|---|\n| 沉思 / 论证 / 笔记（默认） | **black** | 黑底白字 | 红底白字 | 红 `#E63956` |\n| 宣言 / 号召 / 演讲（含 `share` / `manifesto` / `keynote` / `talk` 标签或语气） | **red** | 红底白字 | 黑底白字 | 柔金黄 `#FFE082` |\n| 反讽 / 警觉 / 批判（含 `critique` / `warn` / `rant`） | **yellow** | 黄底黑字 | 黑底白字 | 红 `#E63956` |\n\n显式覆盖：用户写\"用 red / 用 yellow / 用黑底\"即按指令。无任何线索时默认 black。\n\n【视觉规范 — 数值锁死】\n\n色板（仅 4 色, 不许改 hex）：\n```\n--c-black:  #1A1A1A\n--c-red:    #E63956\n--c-yellow: #FFD400\n--c-white:  #FFFFFF\n--c-gold:   #FFE082\n```\n\n字体栈（必须用 ultra-bold 900, letter-spacing `-0.05em`）：\n```\n\"Helvetica Neue\", \"Arial Black\", \"Inter\", \"PingFang SC\", \"Heiti SC\", \"STHeiti\", -apple-system, sans-serif\nfont-weight: 900\n```\n\n字号档位（按本页**最长那一行**字符数, CJK 按 1.8 计权, 多行页降一档）：\n\n| 档位 | 字符数 | font-size |\n|---|---|---|\n| single | ≤2 | `clamp(320px, 80vmin, 1100px)` |\n| short | 3-6 | `clamp(240px, 55vmin, 780px)` |\n| medium | 7-14 | `clamp(150px, 35vmin, 480px)` |\n| long | 15-26 | `clamp(100px, 22vmin, 320px)` |\n| xlong | 27+ | `clamp(64px, 14vmin, 200px)` |\n\n排版：\n- padding `6vmin 7vmin`, 让大字撑满边缘\n- `.lines` 块在屏幕内水平居中, 但块内每行 **left-aligned**（消除右侧空白同时保 indent 错位）\n- line-height `1.05`, 行间 gap `0.15em`\n- 内容垂直居中\n- 页脚：左下页码（`01 / 08`）+ 右下副标题, 13px monospace, opacity 0.5, uppercase, letter-spacing `0.12em`\n- emphasis 页：背景换 `--acc-bg`, 字色换 `--acc-fg`, 行内 `.hl` 自动 `color: inherit`（emphasis 整页就是高亮）\n- indent 档位：0 = `0`, 1 = `7vmin`, 2 = `16vmin`\n\n【输出契约】\n\n输出**单文件 HTML**, 完全自包含, inline CSS + inline JS, 直接在 iframe sandbox 里能跑。骨架照下面这个模板, 把 `SLIDES` 数组、`<title>`、`{{SUBTITLE}}`、`body[data-theme]` 填好即可。**不要外链 CDN, 不要外部资源**。\n\nSLIDES 数组每项形态：\n```js\n// 默认 theme 页\n{ lines: [ { indent: 0, chunks: [ {t: \"前段\"}, {t: \"高亮词\", hl: true}, {t: \"后段\"} ] } ] }\n// emphasis 页（accent 底色, inline hl 自动忽略）\n{ emphasis: true, lines: [ { indent: 0, chunks: [ {t: \"AI\"} ] } ] }\n// 休止页 = emphasis + 空 lines\n{ emphasis: true, lines: [] }\n```\n\n完整 HTML 骨架（agent 应**复用 CSS 与 JS 不要改**, 只填 SLIDES / title / subtitle / data-theme）：\n\n```html\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title><!-- 文档标题 --></title>\n<style>\n  :root {\n    --c-black: #1A1A1A; --c-red: #E63956; --c-yellow: #FFD400;\n    --c-white: #FFFFFF; --c-gold: #FFE082;\n  }\n  * { margin: 0; padding: 0; box-sizing: border-box; }\n  html, body {\n    width: 100%; height: 100%;\n    font-family: \"Helvetica Neue\", \"Arial Black\", \"Inter\", \"PingFang SC\", \"Heiti SC\", \"STHeiti\", -apple-system, sans-serif;\n    font-weight: 900; overflow: hidden;\n    -webkit-font-smoothing: antialiased;\n    letter-spacing: -0.05em;\n  }\n  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); }\n  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); }\n  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); }\n  body { background: var(--bg); }\n  .stage { position: fixed; inset: 0; }\n  .slide {\n    position: absolute; inset: 0; display: none;\n    flex-direction: column; justify-content: center; align-items: center;\n    padding: 6vmin 7vmin;\n    background: var(--bg); color: var(--fg);\n  }\n  .slide.active { display: flex; }\n  .slide[data-emphasis=\"true\"] { background: var(--acc-bg); color: var(--acc-fg); }\n  .slide .hl { color: var(--hl); }\n  .slide[data-emphasis=\"true\"] .hl { color: inherit; }\n  .lines { display: flex; flex-direction: column; gap: 0.15em; line-height: 1.05; max-width: 100%; align-items: flex-start; }\n  .line { white-space: nowrap; text-align: left; }\n  .line[data-indent=\"0\"] { padding-left: 0; }\n  .line[data-indent=\"1\"] { padding-left: 7vmin; }\n  .line[data-indent=\"2\"] { padding-left: 16vmin; }\n  .slide[data-len=\"single\"] .lines { font-size: clamp(320px, 80vmin, 1100px); }\n  .slide[data-len=\"short\"]  .lines { font-size: clamp(240px, 55vmin, 780px); }\n  .slide[data-len=\"medium\"] .lines { font-size: clamp(150px, 35vmin, 480px); }\n  .slide[data-len=\"long\"]   .lines { font-size: clamp(100px, 22vmin, 320px); }\n  .slide[data-len=\"xlong\"]  .lines { font-size: clamp(64px,  14vmin, 200px); }\n  .pager, .subtitle {\n    position: fixed; bottom: 2.5vmin;\n    font-family: \"Menlo\", \"Monaco\", monospace;\n    font-size: 13px; font-weight: 400; letter-spacing: 0.12em;\n    user-select: none; z-index: 10; text-transform: uppercase;\n    opacity: 0.5; color: var(--fg);\n  }\n  .pager { left: 3vmin; }\n  .subtitle { right: 3vmin; }\n  body[data-current=\"emphasis\"] .pager,\n  body[data-current=\"emphasis\"] .subtitle { color: var(--acc-fg); }\n</style>\n</head>\n<body data-theme=\"red\"><!-- black|red|yellow -->\n<div class=\"stage\" id=\"stage\"></div>\n<div class=\"pager\" id=\"pager\">01 / 01</div>\n<div class=\"subtitle\" id=\"subtitle\"><!-- 副标题 / 品牌, 可空 --></div>\n<script>\n  const SLIDES = [ /* 填入按 outline 映射出的 slides 数组 */ ];\n  const stage = document.getElementById('stage');\n  const pager = document.getElementById('pager');\n  const body = document.body;\n  function lineCharLen(chunks) {\n    const CJK = /[　-〿㐀-䶿一-鿿豈-﫿＀-￯]/;\n    return chunks.reduce((acc, c) => {\n      let len = 0;\n      for (const ch of (c.t || '')) len += CJK.test(ch) ? 1.8 : 1;\n      return acc + len;\n    }, 0);\n  }\n  function maxLineLen(lines) { return lines && lines.length ? Math.max(...lines.map(l => lineCharLen(l.chunks || []))) : 0; }\n  function lengthTier(maxLen, lineCount) {\n    const adj = maxLen + Math.max(0, lineCount - 1) * 4;\n    if (adj <= 2)  return 'single';\n    if (adj <= 6)  return 'short';\n    if (adj <= 14) return 'medium';\n    if (adj <= 26) return 'long';\n    return 'xlong';\n  }\n  function escapeHtml(s) {\n    return String(s == null ? '' : s)\n      .replace(/&/g, '&amp;').replace(/</g, '&lt;')\n      .replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#39;');\n  }\n  SLIDES.forEach((s) => {\n    const el = document.createElement('div');\n    el.className = 'slide';\n    if (s.emphasis) el.setAttribute('data-emphasis', 'true');\n    if (s.lines && s.lines.length) {\n      el.setAttribute('data-len', lengthTier(maxLineLen(s.lines), s.lines.length));\n      const linesEl = document.createElement('div');\n      linesEl.className = 'lines';\n      s.lines.forEach(line => {\n        const lineEl = document.createElement('div');\n        lineEl.className = 'line';\n        lineEl.setAttribute('data-indent', String(line.indent || 0));\n        lineEl.innerHTML = (line.chunks || []).map(c => {\n          const t = escapeHtml(c.t);\n          return c.hl ? '<span class=\"hl\">' + t + '</span>' : t;\n        }).join('');\n        linesEl.appendChild(lineEl);\n      });\n      el.appendChild(linesEl);\n    }\n    stage.appendChild(el);\n  });\n  const slides = stage.querySelectorAll('.slide');\n  let idx = 0;\n  function show(i) {\n    if (i < 0) i = 0; if (i >= slides.length) i = slides.length - 1;\n    slides[idx].classList.remove('active');\n    idx = i;\n    slides[idx].classList.add('active');\n    pager.textContent = String(idx + 1).padStart(2, '0') + ' / ' + String(slides.length).padStart(2, '0');\n    body.setAttribute('data-current', slides[idx].getAttribute('data-emphasis') === 'true' ? 'emphasis' : 'theme');\n  }\n  function next() { show(idx + 1); }\n  function prev() { show(idx - 1); }\n  document.addEventListener('keydown', (e) => {\n    switch (e.key) {\n      case 'ArrowRight': case ' ': case 'Enter': case 'j': case 'PageDown': e.preventDefault(); next(); break;\n      case 'ArrowLeft': case 'k': case 'PageUp': e.preventDefault(); prev(); break;\n      case 'Home': e.preventDefault(); show(0); break;\n      case 'End': e.preventDefault(); show(slides.length - 1); break;\n      case 'f': case 'F': e.preventDefault(); document.fullscreenElement ? document.exitFullscreen?.() : document.documentElement.requestFullscreen?.(); break;\n    }\n  });\n  let touchX = null;\n  document.addEventListener('touchstart', (e) => { touchX = e.touches[0].clientX; });\n  document.addEventListener('touchend', (e) => {\n    if (touchX == null) return;\n    const dx = e.changedTouches[0].clientX - touchX;\n    if (dx < -40) next(); else if (dx > 40) prev();\n    touchX = null;\n  });\n  document.addEventListener('click', (e) => {\n    if (e.target.closest('.pager,.subtitle')) return;\n    const mid = window.innerWidth / 2;\n    if (e.clientX > mid) next(); else prev();\n  });\n  show(0);\n</script>\n</body>\n</html>\n```\n\n【调用流程 — agent 内部】\n1. 读用户内容（markdown / outline / 纯文本）\n2. 按上面表格做 **outline → slides 数组** 映射, 不抽提不重写\n3. 推断 theme（标签 > 语气 > 默认 black）\n4. 复用骨架, 替换 `<title>` / `data-theme` / `<div id=\"subtitle\">` 内容 / `SLIDES` 数组\n5. 一次性输出整个 HTML 文档\n\n【品味准则】\n- outline 是真理, skill 是渲染器\n- 一级标题 = emphasis 封面（天然章节断点）\n- 二级标题 = 独占 theme 页（给标题应有的重量）\n- 列表错位靠 indent 0/1/2 体现嵌套深度\n- `**强调**` 自动 hl\n- 拆页保持视觉一致性（同源块字号/缩进对齐）\n- 左对齐不居中——这是 manifesto 美学的灵魂\n\n【禁区】\n- 不抽 manifesto（不要\"找钉子\", 作者已经写好了 outline）\n- 不写新句子、不重组顺序、不删内容\n- 不放图片 / 不放图标 / 不加过渡动画\n- 不在 emphasis 页用 inline hl（emphasis 整页就是高亮）\n- 不混用多个 theme（一篇一个气质）\n- 不擅自加 emphasis（只有一级标题 / 首末页 / 分隔符）\n\n【致谢】\n本 skill 改编自 [lijigang/ljg-skills · ljg-present](https://github.com/lijigang/ljg-skills/tree/md/skills/ljg-present)（v3.0.0）。原版输出多主题包含 cyber-hacker 模式与 PNG 投影; html-anything 版只保留 3 主题 + 单文件 HTML 输出。审美灵感继续指向 Felipe Franco / BIG STUDIOS 的 manifesto 字体海报。","author":"@nexu-io","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nexu-io/html-anything/tree/main/next/src/lib/templates/skills/deck-ljg-present","license":"Apache-2.0","category":"slides","lang":"en","tokens":4087,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"example.html","size":7512,"sha256":"b7053740676412ff04ccbcfb488537f7c623c7b3426daae8f9d01cb1af29cdbb"},{"path":"example.md","size":294,"sha256":"7e1749decd7aa127ca70f768df6b4bdcb04ae08df5d84c8e9a04d728ab75ca00"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":[]}}