@taloswork/ose-shared 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/package.json +3 -3
  2. package/skills/document/SKILL.md +32 -17
  3. package/skills/document/art-directions/base.css +23 -0
  4. package/skills/document/art-directions/cinematic-depth.css +6 -0
  5. package/skills/document/art-directions/collage-paper.css +8 -0
  6. package/skills/document/art-directions/editorial-cut.css +8 -0
  7. package/skills/document/art-directions/geometric-pop.css +9 -0
  8. package/skills/document/art-directions/gradient-mesh.css +6 -0
  9. package/skills/document/art-directions/illustrated-doodle.css +8 -0
  10. package/skills/document/art-directions/luxury-ornament.css +6 -0
  11. package/skills/document/art-directions/maximalist-blocks.css +9 -0
  12. package/skills/document/art-directions/technical-grid.css +6 -0
  13. package/skills/document/assets/fonts/fonts.json +2 -2
  14. package/skills/document/bin/deck-build.cjs +266 -10
  15. package/skills/document/bin/doc-convert.cjs +66 -28
  16. package/skills/document/bin/doc-gen.cjs +6 -4
  17. package/skills/document/bin/html2pptx.cjs +231 -20
  18. package/skills/document/catalog.json +11 -0
  19. package/skills/document/components/base.css +45 -0
  20. package/skills/document/components/profiles/clean.css +2 -0
  21. package/skills/document/components/profiles/editorial.css +3 -0
  22. package/skills/document/components/profiles/glass.css +2 -0
  23. package/skills/document/components/profiles/luxury.css +3 -0
  24. package/skills/document/components/profiles/organic.css +2 -0
  25. package/skills/document/components/profiles/poster.css +3 -0
  26. package/skills/document/content-pipeline.md +55 -48
  27. package/skills/document/deck-design.md +24 -15
  28. package/skills/document/layouts/README.md +33 -12
  29. package/skills/document/layouts/business-model.tpl +6 -0
  30. package/skills/document/layouts/customer-proof.tpl +6 -0
  31. package/skills/document/layouts/funding-ask.tpl +5 -0
  32. package/skills/document/layouts/market-rings.tpl +5 -0
  33. package/skills/document/layouts/pitch-problem.tpl +6 -0
  34. package/skills/document/layouts/pitch-solution.tpl +5 -0
  35. package/skills/document/layouts/pricing-tiers.tpl +5 -0
  36. package/skills/document/layouts/product-device.tpl +6 -0
  37. package/skills/document/layouts/profiles.json +24 -0
  38. package/skills/document/layouts/slots.json +1 -1
  39. package/skills/document/layouts/team-founders.tpl +5 -0
  40. package/skills/document/layouts/traction-milestones.tpl +6 -0
  41. package/skills/document/media-styles/cinematic.css +2 -0
  42. package/skills/document/media-styles/clean-product.css +2 -0
  43. package/skills/document/media-styles/cutout-collage.css +3 -0
  44. package/skills/document/media-styles/device-stage.css +3 -0
  45. package/skills/document/media-styles/editorial-photo.css +3 -0
  46. package/skills/document/media-styles/illustrated.css +2 -0
  47. package/skills/document/media-styles/spatial-3d.css +3 -0
  48. package/skills/document/motion/mckinsey-photo.json +22 -0
  49. package/skills/document/motion/prismatic-3d.json +31 -0
  50. package/skills/document/packs/README.md +7 -3
  51. package/skills/document/packs/mckinsey-photo/backdrop.html +6 -0
  52. package/skills/document/packs/mckinsey-photo/layouts/cover-image.tpl +5 -0
  53. package/skills/document/packs/mckinsey-photo/layouts/cover.tpl +5 -0
  54. package/skills/document/packs/mckinsey-photo/layouts/image-left.tpl +5 -0
  55. package/skills/document/packs/mckinsey-photo/layouts/numbered-brief.tpl +5 -0
  56. package/skills/document/packs/mckinsey-photo/pack.css +27 -0
  57. package/skills/document/packs/prismatic-3d/backdrop.html +4 -0
  58. package/skills/document/packs/prismatic-3d/layouts/big-stat.tpl +6 -0
  59. package/skills/document/packs/prismatic-3d/layouts/cover.tpl +9 -0
  60. package/skills/document/packs/prismatic-3d/layouts/section-image.tpl +6 -0
  61. package/skills/document/packs/prismatic-3d/pack.css +28 -0
  62. package/skills/document/recipes/agency-pitch.json +13 -0
  63. package/skills/document/recipes/product-launch.json +13 -0
  64. package/skills/document/recipes/startup-fundraising.json +19 -0
  65. package/skills/document/styles/README.md +13 -6
  66. package/skills/document/styles/_contract-defaults.css +1 -1
  67. package/skills/document/styles/_samples/_groups/biz-premium.webp +0 -0
  68. package/skills/document/styles/_samples/_groups/creative.webp +0 -0
  69. package/skills/document/styles/_samples/_groups/editorial.webp +0 -0
  70. package/skills/document/styles/_samples/_groups/minimal.webp +0 -0
  71. package/skills/document/styles/_samples/_groups/natural.webp +0 -0
  72. package/skills/document/styles/_samples/_groups/tech-future.webp +0 -0
  73. package/skills/document/styles/_samples/bistro-warm.webp +0 -0
  74. package/skills/document/styles/_samples/board-brief.webp +0 -0
  75. package/skills/document/styles/_samples/bold-clash.webp +0 -0
  76. package/skills/document/styles/_samples/campus-teal.webp +0 -0
  77. package/skills/document/styles/_samples/clinic-care.webp +0 -0
  78. package/skills/document/styles/_samples/dark-glass.webp +0 -0
  79. package/skills/document/styles/_samples/gov-brief.webp +0 -0
  80. package/skills/document/styles/_samples/grid-tech.webp +0 -0
  81. package/skills/document/styles/_samples/legal-slate.webp +0 -0
  82. package/skills/document/styles/_samples/mckinsey-photo.webp +0 -0
  83. package/skills/document/styles/_samples/memphis.webp +0 -0
  84. package/skills/document/styles/_samples/neon-cyber.webp +0 -0
  85. package/skills/document/styles/_samples/organic-earth.webp +0 -0
  86. package/skills/document/styles/_samples/pastel-saas.webp +0 -0
  87. package/skills/document/styles/_samples/portfolio-noir.webp +0 -0
  88. package/skills/document/styles/_samples/press-room.webp +0 -0
  89. package/skills/document/styles/_samples/prismatic-3d.webp +0 -0
  90. package/skills/document/styles/_samples/retro-press.webp +0 -0
  91. package/skills/document/styles/_samples/sport-arena.webp +0 -0
  92. package/skills/document/styles/_samples/wedding-blush.webp +0 -0
  93. package/skills/document/styles/dark-glass/backdrop.html +7 -3
  94. package/skills/document/styles/dark-glass/skin.css +1 -0
  95. package/skills/document/styles/portfolio-noir/skin.css +3 -0
  96. package/skills/document/theme-system.json +68 -0
  97. package/skills/document/theme-system.md +101 -0
@@ -69412,7 +69412,7 @@ var require_dir_writer = __commonJS({
69412
69412
  module2.exports = DirWriter;
69413
69413
  var Writer = require_writer();
69414
69414
  var inherits2 = require_inherits();
69415
- var mkdir = require_mkdirp();
69415
+ var mkdir2 = require_mkdirp();
69416
69416
  var path = require("path");
69417
69417
  var collect = require_collect();
69418
69418
  inherits2(DirWriter, Writer);
@@ -69428,7 +69428,7 @@ var require_dir_writer = __commonJS({
69428
69428
  }
69429
69429
  DirWriter.prototype._create = function() {
69430
69430
  var self2 = this;
69431
- mkdir(self2._path, Writer.dirmode, function(er) {
69431
+ mkdir2(self2._path, Writer.dirmode, function(er) {
69432
69432
  if (er) return self2.error(er);
69433
69433
  self2.ready = true;
69434
69434
  self2.emit("ready");
@@ -69773,7 +69773,7 @@ var require_writer = __commonJS({
69773
69773
  var fs = require_graceful_fs();
69774
69774
  var inherits2 = require_inherits();
69775
69775
  var rimraf = require_rimraf();
69776
- var mkdir = require_mkdirp();
69776
+ var mkdir2 = require_mkdirp();
69777
69777
  var path = require("path");
69778
69778
  var umask = process.platform === "win32" ? 0 : process.umask();
69779
69779
  var getType = require_get_type();
@@ -69879,7 +69879,7 @@ var require_writer = __commonJS({
69879
69879
  }
69880
69880
  };
69881
69881
  function create(self2) {
69882
- mkdir(path.dirname(self2._path), Writer.dirmode, function(er, made) {
69882
+ mkdir2(path.dirname(self2._path), Writer.dirmode, function(er, made) {
69883
69883
  if (er) return self2.error(er);
69884
69884
  self2._madeDir = made;
69885
69885
  return self2._create();
@@ -147823,8 +147823,14 @@ var LAYOUT_WALKER_JS = `
147823
147823
  })()
147824
147824
  `;
147825
147825
 
147826
+ // packages/core/dist/shared/async.js
147827
+ function sleep(ms) {
147828
+ return new Promise((resolve) => setTimeout(resolve, ms));
147829
+ }
147830
+
147826
147831
  // packages/core/dist/document/html-pptx-headless.js
147827
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
147832
+ var configuredSettleMs = Number(process.env.TALOS_CAPTURE_SETTLE_MS ?? 16);
147833
+ var CAPTURE_SETTLE_MS = Number.isFinite(configuredSettleMs) ? Math.max(0, Math.min(250, configuredSettleMs)) : 16;
147828
147834
  var pngDataUri = (bytes) => "data:image/png;base64," + Buffer.from(bytes).toString("base64");
147829
147835
  var isolateSlide = (si) => `new Promise((done) => { const secs = document.querySelectorAll("section.slide"); secs.forEach((s, j) => { s.style.display = j === ${si} ? "" : "none"; }); window.scrollTo(0, 0); requestAnimationFrame(() => requestAnimationFrame(() => done(true))); })`;
147830
147836
  var showAllSlides = `document.querySelectorAll("section.slide").forEach((s) => { s.style.display = ""; }); document.getElementById("__talos_hide")?.remove(); true`;
@@ -147839,7 +147845,7 @@ async function snapshotHeadless(page, opts = {}) {
147839
147845
  await page.evaluate(`(() => { document.querySelectorAll("*").forEach((el) => { const bake = getComputedStyle(el).getPropertyValue("--talos-bake").trim() === "1" || (el.closest && el.closest("[data-bake]")); if (bake) { const c = getComputedStyle(el).color; el.style.setProperty("color", c, "important"); el.style.setProperty("-webkit-text-fill-color", c, "important"); } }); const s = document.createElement("style"); s.id = "__talos_hide"; s.textContent = "*{color:transparent !important;-webkit-text-fill-color:transparent !important;text-shadow:none !important}"; document.head.appendChild(s); return true; })()`);
147840
147846
  for (let si = 0; si < snapshot.slides.length; si++) {
147841
147847
  await page.evaluate(isolateSlide(si));
147842
- await sleep(90);
147848
+ await sleep(CAPTURE_SETTLE_MS);
147843
147849
  const png = await page.screenshot({ x: 0, y: 0, width: 1280, height: 720 });
147844
147850
  const backdropEl = { kind: "image", frame: { x: 0, y: 0, w: 1280, h: 720 }, data: pngDataUri(png) };
147845
147851
  const keepEls = snapshot.slides[si].elements.filter((e) => e && (e.kind === "text" || e.kind === "chart"));
@@ -147854,7 +147860,7 @@ async function snapshotHeadless(page, opts = {}) {
147854
147860
  if (!Array.isArray(els) || !els.some((e) => e && e.kind === "capture"))
147855
147861
  continue;
147856
147862
  await page.evaluate(isolateSlide(si));
147857
- await sleep(90);
147863
+ await sleep(CAPTURE_SETTLE_MS);
147858
147864
  for (const el of els) {
147859
147865
  if (!el || el.kind !== "capture")
147860
147866
  continue;
@@ -147883,12 +147889,16 @@ async function snapshotHeadless(page, opts = {}) {
147883
147889
  }
147884
147890
  return snapshot;
147885
147891
  }
147886
- async function rasterizeHeadless(page) {
147892
+ async function rasterizeHeadless(page, slideIndexes) {
147887
147893
  const count = Number(await page.evaluate('document.querySelectorAll("section.slide").length'));
147888
147894
  if (!count)
147889
147895
  throw new Error('no <section class="slide"> found');
147896
+ const indexes = slideIndexes ?? Array.from({ length: count }, (_2, i) => i);
147897
+ if (indexes.length === 0 || indexes.some((i) => !Number.isInteger(i) || i < 0 || i >= count)) {
147898
+ throw new Error(`preview page is out of bounds (deck has ${count} slide${count === 1 ? "" : "s"})`);
147899
+ }
147890
147900
  const shots = [];
147891
- for (let i = 0; i < count; i++) {
147901
+ for (const i of indexes) {
147892
147902
  await page.evaluate(`new Promise((done) => { window.scrollTo(0, document.querySelectorAll("section.slide")[${i}].offsetTop); requestAnimationFrame(() => requestAnimationFrame(() => done(true))); })`);
147893
147903
  const png = await page.screenshot({ x: 0, y: 0, width: 1280, height: 720 });
147894
147904
  shots.push(Buffer.from(png).toString("base64"));
@@ -147948,7 +147958,9 @@ async function inlineHtmlImages(html, resolve) {
147948
147958
  // packages/core/dist/document/deck-catalog.js
147949
147959
  var KINDS = ["skin", "pack"];
147950
147960
  var MODES = ["light", "dark"];
147961
+ var RENDER_MODES = ["classic-v1", "theme-v2"];
147951
147962
  var ID_RE = /^[a-z0-9][a-z0-9-]*$/;
147963
+ var COMMIT_RE = /^[0-9a-f]{7,40}$/;
147952
147964
  function isStr(v) {
147953
147965
  return typeof v === "string" && v.length > 0;
147954
147966
  }
@@ -147978,6 +147990,35 @@ function parseCatalog(raw) {
147978
147990
  groups.push({ id: g.id, name_cn: g.name_cn, sample: g.sample, scenarios: g.scenarios });
147979
147991
  }
147980
147992
  });
147993
+ const collections = [];
147994
+ const rawCollections = o.collections === void 0 ? [] : Array.isArray(o.collections) ? o.collections : [];
147995
+ if (o.collections !== void 0 && !Array.isArray(o.collections))
147996
+ errors.push("catalog.collections must be an array when present");
147997
+ rawCollections.forEach((c, i) => {
147998
+ const at = `collection[${i}]`;
147999
+ const before = errors.length;
148000
+ if (!isStr(c?.id) || !ID_RE.test(c.id))
148001
+ errors.push(`${at}: bad id`);
148002
+ if (!isStr(c?.name_cn))
148003
+ errors.push(`${at}: missing name_cn`);
148004
+ if (!isStr(c?.renderMode) || !RENDER_MODES.includes(c.renderMode))
148005
+ errors.push(`${at}: bad renderMode`);
148006
+ if (!isStrArr(c?.entryIds) || c.entryIds.length === 0)
148007
+ errors.push(`${at}: entryIds must be a non-empty string array`);
148008
+ if (c?.sourceCommit !== void 0 && (!isStr(c.sourceCommit) || !COMMIT_RE.test(c.sourceCommit)))
148009
+ errors.push(`${at}: bad sourceCommit`);
148010
+ if (c?.renderMode === "classic-v1" && c?.sourceCommit === void 0)
148011
+ errors.push(`${at}: classic-v1 requires sourceCommit`);
148012
+ if (errors.length === before) {
148013
+ collections.push({
148014
+ id: c.id,
148015
+ name_cn: c.name_cn,
148016
+ renderMode: c.renderMode,
148017
+ entryIds: c.entryIds,
148018
+ ...c.sourceCommit !== void 0 ? { sourceCommit: c.sourceCommit } : {}
148019
+ });
148020
+ }
148021
+ });
147981
148022
  const entries = [];
147982
148023
  const rawEntries = Array.isArray(o.entries) ? o.entries : [];
147983
148024
  if (!Array.isArray(o.entries))
@@ -148017,11 +148058,14 @@ function parseCatalog(raw) {
148017
148058
  });
148018
148059
  }
148019
148060
  });
148020
- return { catalog: errors.length ? null : { groups, entries }, errors };
148061
+ return { catalog: errors.length ? null : { groups, ...collections.length ? { collections } : {}, entries }, errors };
148021
148062
  }
148022
148063
  function resolveEntry(catalog, id) {
148023
148064
  return catalog.entries.find((e) => e.id === id) ?? null;
148024
148065
  }
148066
+ function renderModeOf(catalog, id) {
148067
+ return catalog.collections?.find((c) => c.entryIds.includes(id))?.renderMode ?? "theme-v2";
148068
+ }
148025
148069
  function assetDirOf(entry) {
148026
148070
  return entry.kind === "skin" ? `styles/${entry.id}` : `packs/${entry.id}`;
148027
148071
  }
@@ -148029,6 +148073,108 @@ function cssFileOf(entry) {
148029
148073
  return entry.kind === "skin" ? "skin.css" : "pack.css";
148030
148074
  }
148031
148075
 
148076
+ // packages/core/dist/shared/guards.js
148077
+ function isRecord(value) {
148078
+ return value !== null && typeof value === "object" && !Array.isArray(value);
148079
+ }
148080
+
148081
+ // packages/core/dist/document/deck-theme-system.js
148082
+ var ID_RE2 = /^[a-z0-9][a-z0-9-]*$/;
148083
+ var PATH_RE = /^[a-z0-9_./-]+\.css$/i;
148084
+ function isSafeCssPath(v) {
148085
+ return typeof v === "string" && PATH_RE.test(v) && !v.startsWith("/") && !v.includes("..");
148086
+ }
148087
+ function parsePathMap(value, at, errors) {
148088
+ if (!isRecord(value)) {
148089
+ errors.push(`${at} must be an object`);
148090
+ return {};
148091
+ }
148092
+ const out = {};
148093
+ for (const [id, path] of Object.entries(value)) {
148094
+ if (!ID_RE2.test(id))
148095
+ errors.push(`${at}.${id}: bad id`);
148096
+ if (!isSafeCssPath(path)) {
148097
+ errors.push(`${at}.${id}: must be a safe relative .css path`);
148098
+ continue;
148099
+ }
148100
+ out[id] = path;
148101
+ }
148102
+ return out;
148103
+ }
148104
+ function parseThemeSystem(raw) {
148105
+ const errors = [];
148106
+ if (!isRecord(raw))
148107
+ return { system: null, errors: ["theme system is not an object"] };
148108
+ if (raw.version !== 2)
148109
+ errors.push("theme system version must be 2");
148110
+ if (!isSafeCssPath(raw.componentBase)) {
148111
+ errors.push("componentBase must be a safe relative .css path");
148112
+ }
148113
+ if (!isSafeCssPath(raw.artBase)) {
148114
+ errors.push("artBase must be a safe relative .css path");
148115
+ }
148116
+ const componentProfiles = parsePathMap(raw.componentProfiles, "componentProfiles", errors);
148117
+ const mediaStyles = parsePathMap(raw.mediaStyles, "mediaStyles", errors);
148118
+ const artDirections = parsePathMap(raw.artDirections, "artDirections", errors);
148119
+ const bindings = {};
148120
+ if (!isRecord(raw.bindings))
148121
+ errors.push("bindings must be an object");
148122
+ else {
148123
+ for (const [id, value] of Object.entries(raw.bindings)) {
148124
+ const at = `bindings.${id}`;
148125
+ if (!ID_RE2.test(id))
148126
+ errors.push(`${at}: bad id`);
148127
+ if (!isRecord(value)) {
148128
+ errors.push(`${at} must be an object`);
148129
+ continue;
148130
+ }
148131
+ const componentProfile = value.componentProfile;
148132
+ const mediaStyle = value.mediaStyle;
148133
+ const artDirection = value.artDirection;
148134
+ if (typeof componentProfile !== "string" || !ID_RE2.test(componentProfile))
148135
+ errors.push(`${at}: bad componentProfile`);
148136
+ if (typeof mediaStyle !== "string" || !ID_RE2.test(mediaStyle))
148137
+ errors.push(`${at}: bad mediaStyle`);
148138
+ if (typeof artDirection !== "string" || !ID_RE2.test(artDirection))
148139
+ errors.push(`${at}: bad artDirection`);
148140
+ if (typeof componentProfile === "string" && typeof mediaStyle === "string" && typeof artDirection === "string") {
148141
+ bindings[id] = { componentProfile, mediaStyle, artDirection };
148142
+ }
148143
+ }
148144
+ }
148145
+ if (errors.length)
148146
+ return { system: null, errors };
148147
+ return {
148148
+ system: {
148149
+ version: 2,
148150
+ componentBase: raw.componentBase,
148151
+ artBase: raw.artBase,
148152
+ componentProfiles,
148153
+ mediaStyles,
148154
+ artDirections,
148155
+ bindings
148156
+ },
148157
+ errors
148158
+ };
148159
+ }
148160
+ function resolveThemeLayers(system, styleId, mediaOverride) {
148161
+ const binding = system.bindings[styleId];
148162
+ if (!binding)
148163
+ return null;
148164
+ const mediaStyle = mediaOverride ?? binding.mediaStyle;
148165
+ const componentPath = system.componentProfiles[binding.componentProfile];
148166
+ const mediaPath = system.mediaStyles[mediaStyle];
148167
+ const artPath = system.artDirections[binding.artDirection];
148168
+ if (!componentPath || !mediaPath || !artPath)
148169
+ return null;
148170
+ return {
148171
+ paths: [system.componentBase, componentPath, mediaPath, system.artBase, artPath],
148172
+ componentProfile: binding.componentProfile,
148173
+ mediaStyle,
148174
+ artDirection: binding.artDirection
148175
+ };
148176
+ }
148177
+
148032
148178
  // apps/admin/data/skills.lib/document/bin-src/html2pptx.mjs
148033
148179
  function imageResolver(baseDir) {
148034
148180
  return async (src) => {
@@ -148047,6 +148193,19 @@ function die(msg, code = 1) {
148047
148193
  `);
148048
148194
  process.exit(code);
148049
148195
  }
148196
+ function parsePreviewPages(spec) {
148197
+ if (!spec || spec.trim().toLowerCase() === "all") return null;
148198
+ const pages = [];
148199
+ for (const part of spec.split(",")) {
148200
+ const match = /^(\d+)(?:-(\d+))?$/.exec(part.trim());
148201
+ if (!match) die(`bad preview page spec: ${spec} (use all or N,N-M)`, 64);
148202
+ const from = Number(match[1]);
148203
+ const to = match[2] ? Number(match[2]) : from;
148204
+ if (from < 1 || to < from) die(`bad preview page range: ${part}`, 64);
148205
+ for (let p = from; p <= to; p++) if (!pages.includes(p - 1)) pages.push(p - 1);
148206
+ }
148207
+ return pages;
148208
+ }
148050
148209
  var _tailwindCss;
148051
148210
  async function tailwindCss() {
148052
148211
  if (_tailwindCss !== void 0) return _tailwindCss;
@@ -148197,19 +148356,33 @@ async function main() {
148197
148356
  const themePath = themeIdx >= 0 ? args[themeIdx + 1] : void 0;
148198
148357
  const styleIdx = args.indexOf("--style");
148199
148358
  const styleArg = styleIdx >= 0 ? args[styleIdx + 1] : void 0;
148359
+ const themeV2 = args.includes("--theme-v2");
148360
+ const mediaIdx = args.indexOf("--media-style");
148361
+ const mediaStyleArg = mediaIdx >= 0 ? args[mediaIdx + 1] : void 0;
148200
148362
  const bdIdx = args.indexOf("--backdrop");
148201
148363
  const bdVariant = bdIdx >= 0 ? args[bdIdx + 1] : void 0;
148202
148364
  const lineIdx = args.indexOf("--line");
148203
148365
  const lineArg = lineIdx >= 0 ? args[lineIdx + 1] : void 0;
148204
148366
  const notesIdx = args.indexOf("--notes-doc");
148205
148367
  const notesDocPath = notesIdx >= 0 ? args[notesIdx + 1] : void 0;
148206
- const consumed = /* @__PURE__ */ new Set([themeIdx, themeIdx >= 0 ? themeIdx + 1 : -1, styleIdx, styleIdx >= 0 ? styleIdx + 1 : -1, bdIdx, bdIdx >= 0 ? bdIdx + 1 : -1, lineIdx, lineIdx >= 0 ? lineIdx + 1 : -1, modeIdx, modeIdx >= 0 ? modeIdx + 1 : -1, notesIdx, notesIdx >= 0 ? notesIdx + 1 : -1]);
148207
- const positional = args.filter((a, i) => a !== "--rasterize" && a !== "--editable" && !consumed.has(i));
148368
+ const previewIdx = args.indexOf("--preview-dir");
148369
+ const previewDir = previewIdx >= 0 ? args[previewIdx + 1] : void 0;
148370
+ const previewPagesIdx = args.indexOf("--preview-pages");
148371
+ const previewPagesArg = previewPagesIdx >= 0 ? args[previewPagesIdx + 1] : void 0;
148372
+ const previewIndexes = parsePreviewPages(previewPagesArg);
148373
+ const consumed = /* @__PURE__ */ new Set([themeIdx, themeIdx >= 0 ? themeIdx + 1 : -1, styleIdx, styleIdx >= 0 ? styleIdx + 1 : -1, mediaIdx, mediaIdx >= 0 ? mediaIdx + 1 : -1, bdIdx, bdIdx >= 0 ? bdIdx + 1 : -1, lineIdx, lineIdx >= 0 ? lineIdx + 1 : -1, modeIdx, modeIdx >= 0 ? modeIdx + 1 : -1, notesIdx, notesIdx >= 0 ? notesIdx + 1 : -1, previewIdx, previewIdx >= 0 ? previewIdx + 1 : -1, previewPagesIdx, previewPagesIdx >= 0 ? previewPagesIdx + 1 : -1]);
148374
+ const positional = args.filter((a, i) => a !== "--rasterize" && a !== "--editable" && a !== "--theme-v2" && !consumed.has(i));
148208
148375
  const [input, out] = positional;
148209
- if (!input || !out) die("usage: html2pptx <in.html|in.layout.json> <out.pptx> [--theme <theme.json>] [--style <name|dir>] [--backdrop <variant>] [--line <name|none>] [--editable] [--rasterize] [--notes-doc <out.md>]", 64);
148210
- if (!/\.pptx$/i.test(out)) die("output must be a .pptx path", 64);
148376
+ if (!input || !out && !previewDir) die("usage: html2pptx <in.html|in.layout.json> [out.pptx] [--theme <theme.json>] [--style <name|dir>] [--theme-v2] [--media-style <id>] [--backdrop <variant>] [--line <name|none>] [--editable] [--rasterize] [--notes-doc <out.md>] [--preview-dir <dir>] [--preview-pages <all|N,N-M>]", 64);
148377
+ if (previewPagesArg && !previewDir) die("--preview-pages requires --preview-dir", 64);
148378
+ if (previewDir && out) die("--preview-dir is preview-only; omit the .pptx output", 64);
148379
+ if (previewDir && (rasterize || forceEditable || notesDocPath)) die("--preview-dir cannot be combined with --rasterize, --editable/--mode, or --notes-doc", 64);
148380
+ if (themeV2 && !styleArg) die("--theme-v2 requires --style <catalog-id>", 64);
148381
+ if (mediaStyleArg && !themeV2) die("--media-style requires --theme-v2", 64);
148382
+ if (!previewDir && !/\.pptx$/i.test(out)) die("output must be a .pptx path", 64);
148211
148383
  const raw = await (0, import_promises.readFile)(input, "utf8").catch((e) => die(`cannot read ${input}: ${e.message}`, 66));
148212
148384
  if (/\.json$/i.test(input)) {
148385
+ if (previewDir) die("--preview-dir requires HTML input", 64);
148213
148386
  let snapshot2;
148214
148387
  try {
148215
148388
  snapshot2 = JSON.parse(raw);
@@ -148249,18 +148422,17 @@ async function main() {
148249
148422
  let skinCss = "", backdropHtml = "", skinAccent = "", skinAxis = "", skinChart1 = "";
148250
148423
  if (styleArg) {
148251
148424
  const skillDir = (0, import_node_path.dirname)((0, import_node_path.dirname)(process.argv[1] || ""));
148252
- let styleDir, cssName = "skin.css";
148425
+ let styleDir, cssName = "skin.css", entry = null, catalog = null;
148253
148426
  if ((0, import_node_fs.existsSync)((0, import_node_path.join)(styleArg, "skin.css"))) {
148254
148427
  styleDir = styleArg;
148255
148428
  } else if ((0, import_node_fs.existsSync)((0, import_node_path.join)(styleArg, "pack.css"))) {
148256
148429
  styleDir = styleArg;
148257
148430
  cssName = "pack.css";
148258
148431
  } else {
148259
- let entry = null;
148260
148432
  const catalogPath = (0, import_node_path.join)(skillDir, "catalog.json");
148261
148433
  if ((0, import_node_fs.existsSync)(catalogPath)) {
148262
148434
  try {
148263
- const { catalog } = parseCatalog(JSON.parse((0, import_node_fs.readFileSync)(catalogPath, "utf8")));
148435
+ ({ catalog } = parseCatalog(JSON.parse((0, import_node_fs.readFileSync)(catalogPath, "utf8"))));
148264
148436
  if (catalog) entry = resolveEntry(catalog, styleArg);
148265
148437
  } catch {
148266
148438
  }
@@ -148275,7 +148447,27 @@ async function main() {
148275
148447
  die(`cannot read style ${skinPath}: ${e.message}`, 66);
148276
148448
  }
148277
148449
  const defaultsPath = (0, import_node_path.join)(skillDir, "styles", "_contract-defaults.css");
148278
- if ((0, import_node_fs.existsSync)(defaultsPath)) skinCss = await (0, import_promises.readFile)(defaultsPath, "utf8") + "\n" + skinCss;
148450
+ let v2Css = "";
148451
+ if (themeV2) {
148452
+ if (!entry) die("--theme-v2 requires a catalog style id (explicit style directories are V1 only)", 64);
148453
+ if (catalog && renderModeOf(catalog, entry.id) === "classic-v1") {
148454
+ die(`style "${entry.id}" is frozen in collection "original-13" as classic-v1; omit --theme-v2 and --media-style`, 64);
148455
+ }
148456
+ const registryPath = (0, import_node_path.join)(skillDir, "theme-system.json");
148457
+ let parsed;
148458
+ try {
148459
+ parsed = parseThemeSystem(JSON.parse(await (0, import_promises.readFile)(registryPath, "utf8")));
148460
+ } catch (e) {
148461
+ die(`cannot read Theme V2 registry ${registryPath}: ${e.message}`, 66);
148462
+ }
148463
+ if (!parsed.system) die(`invalid Theme V2 registry: ${parsed.errors.join("; ")}`, 66);
148464
+ const layers = resolveThemeLayers(parsed.system, entry.id, mediaStyleArg);
148465
+ if (!layers) die(`Theme V2 cannot resolve style "${entry.id}"${mediaStyleArg ? ` with media style "${mediaStyleArg}"` : ""}`, 66);
148466
+ for (const rel of layers.paths) v2Css += await (0, import_promises.readFile)((0, import_node_path.join)(skillDir, rel), "utf8") + "\n";
148467
+ process.stderr.write(`html2pptx: Theme V2 ${entry.id} = components:${layers.componentProfile} + media:${layers.mediaStyle} + art:${layers.artDirection}
148468
+ `);
148469
+ }
148470
+ skinCss = ((0, import_node_fs.existsSync)(defaultsPath) ? await (0, import_promises.readFile)(defaultsPath, "utf8") + "\n" : "") + v2Css + skinCss;
148279
148471
  const sharedBackdropsDir = (0, import_node_path.join)(skillDir, "styles", "_backdrops");
148280
148472
  const NO_AUTO_BACKDROP_VARIETY = /* @__PURE__ */ new Set(["mckinsey", "mono", "cinereel"]);
148281
148473
  let bdPath;
@@ -148289,7 +148481,7 @@ async function main() {
148289
148481
  `);
148290
148482
  bdPath = (0, import_node_path.join)(styleDir, "backdrop.html");
148291
148483
  }
148292
- } else if (NO_AUTO_BACKDROP_VARIETY.has((0, import_node_path.basename)(styleDir))) {
148484
+ } else if (themeV2 || NO_AUTO_BACKDROP_VARIETY.has((0, import_node_path.basename)(styleDir))) {
148293
148485
  bdPath = (0, import_node_path.join)(styleDir, "backdrop.html");
148294
148486
  } else {
148295
148487
  const ownVariants = (0, import_node_fs.existsSync)(styleDir) ? (0, import_node_fs.readdirSync)(styleDir).filter((f) => /^backdrop.*\.html$/.test(f)).map((f) => (0, import_node_path.join)(styleDir, f)) : [];
@@ -148323,7 +148515,8 @@ async function main() {
148323
148515
  for (const s of skipped) process.stderr.write(`html2pptx: <img src="${s}"> not found/unfetchable \u2014 the render will drop it
148324
148516
  `);
148325
148517
  if (styleArg) {
148326
- if (backdropHtml) rawInlined = rawInlined.replace(/<div class="slide-backdrop">\s*<\/div>/g, `<div class="slide-backdrop">${backdropHtml}</div>`);
148518
+ const artLayer = themeV2 ? '<div class="theme-art-layer" aria-hidden="true"><i class="art-shape art-a"></i><i class="art-shape art-b"></i><i class="art-shape art-c"></i><i class="art-pattern"></i></div>' : "";
148519
+ if (backdropHtml || artLayer) rawInlined = rawInlined.replace(/<div class="slide-backdrop">\s*<\/div>/g, `<div class="slide-backdrop">${backdropHtml}${artLayer}</div>`);
148327
148520
  if (skinAccent) rawInlined = rawInlined.replaceAll("__ACCENT__", skinAccent);
148328
148521
  if (skinAxis) rawInlined = rawInlined.replaceAll("__AXIS__", skinAxis);
148329
148522
  if (skinChart1) rawInlined = rawInlined.replaceAll("__CHART1__", skinChart1);
@@ -148333,6 +148526,24 @@ async function main() {
148333
148526
  const latin = await latinFontFaceCss(rawInlined + skinCss);
148334
148527
  const fontCss = cjk + latin;
148335
148528
  const html = injectHead(rawInlined, `${fontCss ? `<style>${fontCss}</style>` : ""}${tw ? `<style>${tw}</style>` : ""}<style>${themeCss}${SKELETON_CSS}${skinCss}</style>`);
148529
+ if (previewDir) {
148530
+ let images;
148531
+ if (hasService) ({ images } = await callService("/shot", html, previewIndexes ? { pages: previewIndexes } : {}).catch((e) => die(e.message, 1)));
148532
+ else {
148533
+ images = await withHeadlessPage(html, (p) => rasterizeHeadless(p, previewIndexes ?? void 0)).catch((e) => die(e.message, 1));
148534
+ if (images == null) die(NO_BROWSER, 69);
148535
+ }
148536
+ await (0, import_promises.mkdir)(previewDir, { recursive: true });
148537
+ const pageNumbers = previewIndexes ? previewIndexes.map((i) => i + 1) : images.map((_2, i) => i + 1);
148538
+ const files = [];
148539
+ for (let i = 0; i < images.length; i++) {
148540
+ const path = (0, import_node_path.join)(previewDir, `slide-${String(pageNumbers[i]).padStart(3, "0")}.png`);
148541
+ await (0, import_promises.writeFile)(path, Buffer.from(images[i], "base64"));
148542
+ files.push(path);
148543
+ }
148544
+ process.stdout.write(JSON.stringify({ ok: true, engine: hasService ? "preview" : "preview (headless)", slides: images.length, pages: pageNumbers, files }, null, 1) + "\n");
148545
+ return;
148546
+ }
148336
148547
  if (rasterize) {
148337
148548
  let images;
148338
148549
  if (hasService) ({ images } = await callService("/shot", html).catch((e) => die(e.message, 1)));
@@ -7,13 +7,24 @@
7
7
  {"id":"creative","name_cn":"创意手作","sample":"styles/_samples/_groups/creative.webp","scenarios":["创意","教学","工作坊","头脑风暴"]},
8
8
  {"id":"natural","name_cn":"自然温和","sample":"styles/_samples/_groups/natural.webp","scenarios":["品牌手册","可持续","美妆","零售"]}
9
9
  ],
10
+ "collections": [
11
+ {
12
+ "id": "original-13",
13
+ "name_cn": "初代经典",
14
+ "renderMode": "classic-v1",
15
+ "sourceCommit": "7306d8e5",
16
+ "entryIds": ["cool", "swiss-grid", "black-gold", "olive-gold", "mckinsey", "apple", "mono", "ivory-gold", "magazine-ink", "anthropic", "hand-drawn", "cinereel", "branding"]
17
+ }
18
+ ],
10
19
  "entries": [
11
20
  {"id":"cool","kind":"skin","group":"tech-future","name_cn":"酷炫深色","oneliner":"深蓝辉光 · 科技发布","scenarios":["科技","发布","pitch","主题演讲"],"mode":"dark","sample":"styles/_samples/cool.webp","motion":"motion/cool.json"},
12
21
  {"id":"neon-cyber","kind":"skin","group":"tech-future","name_cn":"霓虹赛博","oneliner":"霓虹品红 · 赛博夜场","scenarios":["科技","游戏","Web3","夜场发布"],"mode":"dark","sample":"styles/_samples/neon-cyber.webp","motion":"motion/neon-cyber.json"},
22
+ {"id":"prismatic-3d","kind":"pack","group":"tech-future","name_cn":"棱镜空间","oneliner":"紫黑棱镜 · 立体发布","scenarios":["科技发布","AI 产品","创意提案","未来趋势"],"mode":"dark","sample":"styles/_samples/prismatic-3d.webp","motion":"motion/prismatic-3d.json"},
13
23
  {"id":"swiss-grid","kind":"skin","group":"tech-future","name_cn":"瑞士国际","oneliner":"克莱因蓝白 · 瑞士国际","scenarios":["科技产品","数据汇报","设计","年度总结"],"mode":"light","sample":"styles/_samples/swiss-grid.webp","motion":"motion/swiss-grid.json"},
14
24
  {"id":"black-gold","kind":"skin","group":"biz-premium","name_cn":"黑金","oneliner":"纯黑金辉 · 高端商务","scenarios":["金融","年报","路演","奢侈品"],"mode":"dark","sample":"styles/_samples/black-gold.webp","motion":"motion/black-gold.json"},
15
25
  {"id":"olive-gold","kind":"skin","group":"biz-premium","name_cn":"橄榄金","oneliner":"橄榄亮金 · 高端招商","scenarios":["高端","证书","招商","品牌"],"mode":"dark","sample":"styles/_samples/olive-gold.webp","motion":"motion/olive-gold.json"},
16
26
  {"id":"mckinsey","kind":"skin","group":"biz-premium","name_cn":"白底咨询","oneliner":"白底藏青 · 咨询评审","scenarios":["咨询","战略汇报","评审","董事会"],"mode":"light","sample":"styles/_samples/mckinsey.webp","motion":"motion/mckinsey.json"},
27
+ {"id":"mckinsey-photo","kind":"pack","group":"biz-premium","name_cn":"咨询影像","oneliner":"藏青影像 · 企业叙事","scenarios":["咨询","企业战略","案例研究","年度回顾"],"mode":"light","sample":"styles/_samples/mckinsey-photo.webp","motion":"motion/mckinsey-photo.json"},
17
28
  {"id":"apple","kind":"skin","group":"minimal","name_cn":"纯黑极简","oneliner":"纯黑极简 · 产品发布","scenarios":["产品发布","主题演讲","极简演示"],"mode":"dark","sample":"styles/_samples/apple.webp","motion":"motion/apple.json"},
18
29
  {"id":"mono","kind":"skin","group":"minimal","name_cn":"纯白黑灰","oneliner":"纯白黑灰 · 极简设计","scenarios":["极简","高端","设计","单色报告"],"mode":"light","sample":"styles/_samples/mono.webp","motion":"motion/mono.json"},
19
30
  {"id":"ivory-gold","kind":"skin","group":"minimal","name_cn":"奶油青铜","oneliner":"奶油青铜 · 奢华极简","scenarios":["奢华极简","品牌手册","路演","高端零售"],"mode":"light","sample":"styles/_samples/ivory-gold.webp","motion":"motion/ivory-gold.json"},
@@ -0,0 +1,45 @@
1
+ /* Theme V2 semantic component contract. Enabled only by --theme-v2. */
2
+ :root{
3
+ --cmp-radius:var(--card-radius,4px);--cmp-border:var(--hair);--cmp-surface:transparent;
4
+ --cmp-surface-strong:var(--title);--cmp-on-strong:var(--slide-bg);--cmp-shadow:none;
5
+ --cmp-pad:24px;--cmp-gap:18px;--cmp-line:1px;--cmp-media-radius:var(--cmp-radius);
6
+ --cmp-media-fit:cover;--cmp-media-filter:none;--cmp-media-shadow:none;
7
+ }
8
+ .component-card,.metric-card,.proof-card,.price-card,.team-card,.feature-card,.fund-card{
9
+ position:relative;background:var(--cmp-surface);border:var(--cmp-line) solid var(--cmp-border);
10
+ border-radius:var(--cmp-radius);box-shadow:var(--cmp-shadow);padding:var(--cmp-pad);overflow:hidden;
11
+ }
12
+ .component-card--strong,.metric-card--hero,.price-card--featured{
13
+ background:var(--cmp-surface-strong);border-color:var(--cmp-surface-strong);color:var(--cmp-on-strong);
14
+ }
15
+ .component-card--strong :is(h2,h3,p,span),.metric-card--hero :is(h2,h3,p,span),.price-card--featured :is(h2,h3,p,span){color:inherit}
16
+ .metric-value{font-family:var(--font-head);font-size:calc(44px * var(--type-scale,1));font-weight:800;letter-spacing:-.04em;line-height:1}
17
+ .metric-label,.component-label,.media-caption{color:var(--muted);font-size:calc(13px * var(--type-scale,1));letter-spacing:.08em;text-transform:uppercase}
18
+ .component-title{font-family:var(--font-head);color:var(--title);font-weight:750;line-height:1.15}
19
+ .component-copy{color:var(--body);line-height:1.55}
20
+ .badge,.tag,.pill{display:inline-flex;align-items:center;width:max-content;border:var(--cmp-line) solid var(--cmp-border);border-radius:999px;padding:5px 11px;font-size:calc(12px * var(--type-scale,1));font-weight:700}
21
+ .badge--accent,.tag--accent{background:var(--accent);border-color:var(--accent);color:var(--slide-bg)}
22
+ .media-frame,.media-cutout,.device-frame{position:relative;overflow:hidden;border:var(--cmp-line) solid var(--cmp-border);border-radius:var(--cmp-media-radius);box-shadow:var(--cmp-media-shadow);background:var(--card-bg,transparent)}
23
+ .media-frame>img,.media-cutout>img,.device-frame>img{width:100%;height:100%;object-fit:var(--cmp-media-fit);filter:var(--cmp-media-filter)}
24
+ .quote-mark{font-family:var(--font-head);color:var(--accent);font-size:calc(64px * var(--type-scale,1));line-height:.6}
25
+ .quote-copy{font-family:var(--font-head);font-size:calc(26px * var(--type-scale,1));line-height:1.32;color:var(--title)}
26
+ .logo-tile{display:flex;align-items:center;justify-content:center;min-height:62px;border:var(--cmp-line) solid var(--cmp-border);border-radius:var(--cmp-radius);color:var(--muted);font-weight:700}
27
+ .progress-track,.fund-track{height:10px;background:var(--hair);border-radius:999px;overflow:hidden}
28
+ .progress-fill,.fund-fill{height:100%;background:var(--accent);border-radius:inherit}
29
+ .timeline-node{width:14px;height:14px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 5px var(--cmp-surface)}
30
+ .avatar-frame{overflow:hidden;border-radius:50%;border:var(--cmp-line) solid var(--cmp-border)}
31
+ .avatar-frame>img{width:100%;height:100%;object-fit:cover}
32
+ .data-chip{font-family:var(--font-head);font-variant-numeric:tabular-nums;color:var(--accent);font-weight:800}
33
+ .component-grid{display:grid;gap:var(--cmp-gap)}
34
+ .chart-shell,.table-shell,.callout,.workflow-node{background:var(--cmp-surface);border:var(--cmp-line) solid var(--cmp-border);border-radius:var(--cmp-radius);box-shadow:var(--cmp-shadow)}
35
+ .chart-shell,.table-shell{padding:var(--cmp-pad)}
36
+ .chart-shell .chart{background:transparent}
37
+ .legend-item{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:calc(13px * var(--type-scale,1))}
38
+ .legend-item:before{content:"";width:9px;height:9px;border-radius:50%;background:var(--accent)}
39
+ .table-shell .dt-head{background:color-mix(in srgb,var(--accent) 10%,transparent);border-bottom:var(--cmp-line) solid var(--cmp-border)}
40
+ .table-shell .dt-row{border-bottom:var(--cmp-line) solid var(--hair)}
41
+ .callout{padding:16px 18px;border-left:4px solid var(--accent)}
42
+ .workflow-node{padding:18px;text-align:center}
43
+ .workflow-connector{height:var(--cmp-line);background:var(--cmp-border)}
44
+ .cta-block{background:var(--accent);color:var(--slide-bg);border-radius:var(--cmp-radius);padding:var(--cmp-pad)}
45
+ .cta-block :is(h2,h3,p,span){color:inherit}
@@ -0,0 +1,2 @@
1
+ :root{--cmp-radius:6px;--cmp-line:1px;--cmp-pad:24px;--cmp-gap:16px;--cmp-surface:var(--card-bg,transparent);--cmp-border:var(--hair);--cmp-shadow:none}
2
+ .component-card:before,.metric-card:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent)}
@@ -0,0 +1,3 @@
1
+ :root{--cmp-radius:0px;--cmp-line:1px;--cmp-pad:22px;--cmp-gap:14px;--cmp-surface:transparent;--cmp-border:var(--title);--cmp-shadow:none}
2
+ .component-card,.metric-card,.proof-card,.price-card,.team-card{border-left-width:5px;border-top:0;border-right:0;border-bottom:0}
3
+ .component-label{font-family:var(--font-head);letter-spacing:.16em}
@@ -0,0 +1,2 @@
1
+ :root{--cmp-radius:18px;--cmp-line:1px;--cmp-pad:26px;--cmp-gap:18px;--cmp-surface:color-mix(in srgb,var(--slide-bg) 58%,transparent);--cmp-border:color-mix(in srgb,var(--accent) 28%,var(--hair));--cmp-shadow:0 18px 44px rgba(0,0,0,.18)}
2
+ .component-card,.metric-card,.proof-card,.price-card,.team-card{backdrop-filter:blur(16px)}
@@ -0,0 +1,3 @@
1
+ :root{--cmp-radius:0px;--cmp-line:1px;--cmp-pad:28px;--cmp-gap:20px;--cmp-surface:transparent;--cmp-border:var(--accent);--cmp-shadow:none}
2
+ .component-card,.metric-card,.proof-card,.price-card,.team-card{border-top:0;border-left:0;border-right:0}
3
+ .component-label,.metric-label{font-family:var(--font-head);letter-spacing:.2em}
@@ -0,0 +1,2 @@
1
+ :root{--cmp-radius:24px;--cmp-line:1px;--cmp-pad:26px;--cmp-gap:20px;--cmp-surface:var(--card-bg,transparent);--cmp-border:var(--hair);--cmp-shadow:0 14px 34px rgba(0,0,0,.08)}
2
+ .component-card:nth-child(even),.feature-card:nth-child(even){border-radius:40px 16px 40px 16px}
@@ -0,0 +1,3 @@
1
+ :root{--cmp-radius:2px;--cmp-line:2px;--cmp-pad:24px;--cmp-gap:18px;--cmp-surface:var(--card-bg,transparent);--cmp-border:var(--title);--cmp-shadow:8px 8px 0 var(--title)}
2
+ .component-card--strong,.metric-card--hero,.price-card--featured{transform:rotate(-1deg)}
3
+ .badge,.tag,.pill{border-radius:2px;text-transform:uppercase}