@taloswork/ose-shared 0.1.10 → 0.1.12

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 (126) hide show
  1. package/package.json +3 -3
  2. package/skills/document/SKILL.md +143 -215
  3. package/skills/document/art-directions/base.css +27 -2
  4. package/skills/document/art-directions/cinematic-depth.css +3 -1
  5. package/skills/document/art-directions/collage-paper.css +5 -1
  6. package/skills/document/art-directions/editorial-cut.css +5 -1
  7. package/skills/document/art-directions/geometric-pop.css +6 -0
  8. package/skills/document/art-directions/gradient-mesh.css +5 -1
  9. package/skills/document/art-directions/illustrated-doodle.css +5 -0
  10. package/skills/document/art-directions/luxury-ornament.css +4 -1
  11. package/skills/document/art-directions/maximalist-blocks.css +6 -0
  12. package/skills/document/art-directions/technical-grid.css +4 -0
  13. package/skills/document/assets/fonts/fonts.json +2 -2
  14. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
  15. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
  16. package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
  17. package/skills/document/assets/pdf-fonts/README.md +35 -0
  18. package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
  19. package/skills/document/assets/tessdata/README.md +21 -0
  20. package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
  21. package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
  22. package/skills/document/bin/deck-build.cjs +2311 -41
  23. package/skills/document/bin/deck-chart.cjs +762 -0
  24. package/skills/document/bin/deck-plan.cjs +802 -0
  25. package/skills/document/bin/deck-qa.cjs +984 -249
  26. package/skills/document/bin/deck-scenario.cjs +1346 -0
  27. package/skills/document/bin/doc-convert.cjs +130 -39
  28. package/skills/document/bin/doc-fill.cjs +73121 -170
  29. package/skills/document/bin/doc-gen.cjs +625 -68
  30. package/skills/document/bin/doc-run.cjs +162 -24
  31. package/skills/document/bin/doc-scan.cjs +13 -0
  32. package/skills/document/bin/doc-text.cjs +13 -0
  33. package/skills/document/bin/doc-validate.cjs +718 -23
  34. package/skills/document/bin/doc-xml.cjs +9 -5
  35. package/skills/document/bin/docx-annotate.cjs +95 -19
  36. package/skills/document/bin/fontkit.cjs +1 -0
  37. package/skills/document/bin/html2pptx.cjs +1372 -121
  38. package/skills/document/bin/ocr-worker.cjs +1 -0
  39. package/skills/document/bin/ocr.cjs +131 -40
  40. package/skills/document/bin/pdf-form.cjs +674 -177
  41. package/skills/document/bin/pdf-images.cjs +13 -0
  42. package/skills/document/bin/pdf-ops.cjs +634 -199
  43. package/skills/document/bin/pdf-render.cjs +19 -3
  44. package/skills/document/bin/pdf-tables.cjs +13 -0
  45. package/skills/document/bin/pptx-clone.cjs +3 -3
  46. package/skills/document/bin/pptx-slide.cjs +3 -3
  47. package/skills/document/bin/xlsx-query.cjs +192 -61
  48. package/skills/document/bin-reference.md +56 -33
  49. package/skills/document/content-pipeline.md +129 -242
  50. package/skills/document/deck-design.md +355 -638
  51. package/skills/document/layouts/README.md +46 -33
  52. package/skills/document/layouts/capacity.json +1 -0
  53. package/skills/document/layouts/slots.json +1 -1
  54. package/skills/document/layouts/toc-panel.tpl +1 -1
  55. package/skills/document/motion/README.md +3 -2
  56. package/skills/document/office.md +3 -3
  57. package/skills/document/packs/board-brief/backdrop.html +2 -0
  58. package/skills/document/packs/board-brief/pack.css +2 -1
  59. package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
  60. package/skills/document/packs/mckinsey-photo/pack.css +2 -1
  61. package/skills/document/packs/press-room/backdrop.html +2 -0
  62. package/skills/document/packs/press-room/pack.css +2 -1
  63. package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
  64. package/skills/document/packs/prismatic-3d/pack.css +4 -1
  65. package/skills/document/pdf.md +53 -49
  66. package/skills/document/rewrite.md +241 -308
  67. package/skills/document/scenarios.json +1290 -0
  68. package/skills/document/scenarios.schema.json +103 -0
  69. package/skills/document/styles/README.md +15 -25
  70. package/skills/document/styles/_contract-defaults.css +25 -1
  71. package/skills/document/styles/anthropic/skin.css +6 -3
  72. package/skills/document/styles/apple/skin.css +4 -3
  73. package/skills/document/styles/bistro-warm/backdrop.html +2 -0
  74. package/skills/document/styles/bistro-warm/skin.css +4 -1
  75. package/skills/document/styles/black-gold/skin.css +6 -5
  76. package/skills/document/styles/bold-clash/backdrop.html +2 -0
  77. package/skills/document/styles/bold-clash/skin.css +2 -1
  78. package/skills/document/styles/branding/skin.css +4 -4
  79. package/skills/document/styles/campus-teal/backdrop.html +2 -0
  80. package/skills/document/styles/campus-teal/skin.css +2 -1
  81. package/skills/document/styles/cinereel/skin.css +7 -3
  82. package/skills/document/styles/clinic-care/backdrop.html +2 -0
  83. package/skills/document/styles/clinic-care/skin.css +2 -1
  84. package/skills/document/styles/cool/skin.css +6 -5
  85. package/skills/document/styles/dark-glass/backdrop.html +2 -0
  86. package/skills/document/styles/dark-glass/skin.css +2 -1
  87. package/skills/document/styles/gov-brief/backdrop.html +2 -0
  88. package/skills/document/styles/gov-brief/skin.css +2 -1
  89. package/skills/document/styles/grid-tech/backdrop.html +2 -0
  90. package/skills/document/styles/grid-tech/skin.css +2 -1
  91. package/skills/document/styles/hand-drawn/skin.css +3 -3
  92. package/skills/document/styles/ivory-gold/skin.css +7 -6
  93. package/skills/document/styles/legal-slate/backdrop.html +2 -0
  94. package/skills/document/styles/legal-slate/skin.css +2 -1
  95. package/skills/document/styles/magazine-ink/skin.css +4 -3
  96. package/skills/document/styles/mckinsey/skin.css +2 -1
  97. package/skills/document/styles/memphis/backdrop.html +2 -0
  98. package/skills/document/styles/memphis/skin.css +2 -1
  99. package/skills/document/styles/mono/skin.css +6 -2
  100. package/skills/document/styles/neon-cyber/backdrop.html +2 -0
  101. package/skills/document/styles/neon-cyber/skin.css +2 -1
  102. package/skills/document/styles/olive-gold/skin.css +7 -6
  103. package/skills/document/styles/organic-earth/backdrop.html +2 -0
  104. package/skills/document/styles/organic-earth/skin.css +2 -1
  105. package/skills/document/styles/pastel-saas/backdrop.html +2 -0
  106. package/skills/document/styles/pastel-saas/skin.css +2 -1
  107. package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
  108. package/skills/document/styles/portfolio-noir/skin.css +2 -1
  109. package/skills/document/styles/retro-press/backdrop.html +2 -0
  110. package/skills/document/styles/retro-press/skin.css +2 -1
  111. package/skills/document/styles/sport-arena/backdrop.html +2 -0
  112. package/skills/document/styles/sport-arena/skin.css +4 -1
  113. package/skills/document/styles/swiss-grid/skin.css +10 -5
  114. package/skills/document/styles/wedding-blush/backdrop.html +2 -0
  115. package/skills/document/styles/wedding-blush/skin.css +4 -1
  116. package/skills/document/theme-system.md +85 -77
  117. package/skills/document/themes/README.md +7 -5
  118. package/skills/document/vision.md +8 -5
  119. package/skills/document/xlsx.md +15 -25
  120. package/skills/cozy-static-svg/SKILL.md +0 -76
  121. package/skills/cozy-static-svg/references/approved-preview.png +0 -0
  122. package/skills/cozy-static-svg/references/cross-platform.md +0 -73
  123. package/skills/cozy-static-svg/references/moon_keeps_watch.svg +0 -229
  124. package/skills/cozy-static-svg/references/style-anchor.md +0 -271
  125. package/skills/cozy-static-svg/scripts/artkit.py +0 -321
  126. package/skills/cozy-static-svg/scripts/to_png.py +0 -333
@@ -108,6 +108,7 @@ function radarChart(metrics, warnings) {
108
108
  });
109
109
  return { type: "radar", labels, series: [{ name: "", values }], colors: ["__ACCENT__"], axisColor: "__AXIS__" };
110
110
  }
111
+ var DERIVED_CHART_OPTIONS = /* @__PURE__ */ new Set(["max", "unit", "showValues", "render"]);
111
112
  function deriveGantt(rows, warnings, errors) {
112
113
  const total = rows.cols.length;
113
114
  rows.tasks.forEach((task, i) => {
@@ -164,7 +165,735 @@ function escapeHtml(value) {
164
165
  return String(value).replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
165
166
  }
166
167
 
168
+ // packages/core/dist/document/deck-chart-svg.js
169
+ var SVG_CHART_TYPES = ["bar", "barH", "line", "area", "pie", "doughnut", "radar", "scatter"];
170
+ var RADAR_MAX_DIMS = 8;
171
+ var RADAR_MAX_SERIES = 3;
172
+ var SINGLE = "var(--chart-1, var(--accent))";
173
+ var RAMP = [
174
+ "var(--series-1, var(--c1, var(--accent)))",
175
+ "var(--series-2, var(--c2, var(--muted)))",
176
+ "var(--series-3, var(--c3, var(--title)))",
177
+ "var(--series-4, var(--c4, var(--body)))"
178
+ ];
179
+ var MAX_SERIES = RAMP.length;
180
+ var ACCENT = "var(--chart-accent, var(--accent))";
181
+ var SURFACE = "var(--chart-surface, var(--slide-bg, #fff))";
182
+ var TXT = "var(--chart-ink, var(--body, currentColor))";
183
+ var MUTED = "var(--chart-muted, var(--muted, currentColor))";
184
+ var TITLE = "var(--chart-title, var(--title, currentColor))";
185
+ var GRID = "var(--chart-grid, var(--hair, var(--muted)))";
186
+ var r1 = (n) => String(Math.round(n * 10) / 10);
187
+ var esc = (s) => escapeHtml(s);
188
+ function textW(s, size) {
189
+ let w = 0;
190
+ for (const ch of s) {
191
+ const cp = ch.codePointAt(0);
192
+ w += cp >= 11904 ? 1 : ch === " " ? 0.3 : 0.56;
193
+ }
194
+ return w * size;
195
+ }
196
+ function formatValue(v) {
197
+ const rounded = Math.abs(v) >= 100 ? Math.round(v) : Math.round(v * 100) / 100;
198
+ const [int, frac] = String(Math.abs(rounded)).split(".");
199
+ const grouped = int.length > 4 ? int.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : int;
200
+ return `${rounded < 0 ? "-" : ""}${grouped}${frac ? `.${frac}` : ""}`;
201
+ }
202
+ function niceNum(x, round) {
203
+ const exp = Math.floor(Math.log10(x));
204
+ const f = x / 10 ** exp;
205
+ const nf = round ? f < 1.5 ? 1 : f < 3 ? 2 : f < 7 ? 5 : 10 : f <= 1 ? 1 : f <= 2 ? 2 : f <= 5 ? 5 : 10;
206
+ return nf * 10 ** exp;
207
+ }
208
+ function niceScale(min, max, target = 5) {
209
+ if (!Number.isFinite(min) || !Number.isFinite(max))
210
+ return { lo: 0, hi: 1, ticks: [0, 1] };
211
+ if (min === max) {
212
+ if (min === 0)
213
+ return { lo: 0, hi: 1, ticks: [0, 0.25, 0.5, 0.75, 1] };
214
+ if (min > 0)
215
+ min = 0;
216
+ else
217
+ max = 0;
218
+ }
219
+ const step = niceNum(niceNum(max - min, false) / (target - 1), true);
220
+ const lo = Math.floor(min / step) * step;
221
+ const hi = Math.ceil(max / step) * step;
222
+ const ticks = [];
223
+ for (let t = lo; t <= hi + step / 2; t += step)
224
+ ticks.push(Math.round(t / step) * step);
225
+ return { lo, hi, ticks };
226
+ }
227
+ function barPath(x, y, w, h, end, radius = 4) {
228
+ if (w <= 0 || h <= 0)
229
+ return "";
230
+ const r = Math.min(radius, w / 2, h / 2);
231
+ const R = (n) => r1(n);
232
+ switch (end) {
233
+ case "top":
234
+ return `M${R(x)},${R(y + h)}V${R(y + r)}Q${R(x)},${R(y)} ${R(x + r)},${R(y)}H${R(x + w - r)}Q${R(x + w)},${R(y)} ${R(x + w)},${R(y + r)}V${R(y + h)}Z`;
235
+ case "bottom":
236
+ return `M${R(x)},${R(y)}V${R(y + h - r)}Q${R(x)},${R(y + h)} ${R(x + r)},${R(y + h)}H${R(x + w - r)}Q${R(x + w)},${R(y + h)} ${R(x + w)},${R(y + h - r)}V${R(y)}Z`;
237
+ case "right":
238
+ return `M${R(x)},${R(y)}H${R(x + w - r)}Q${R(x + w)},${R(y)} ${R(x + w)},${R(y + r)}V${R(y + h - r)}Q${R(x + w)},${R(y + h)} ${R(x + w - r)},${R(y + h)}H${R(x)}Z`;
239
+ case "left":
240
+ return `M${R(x + w)},${R(y)}H${R(x + r)}Q${R(x)},${R(y)} ${R(x)},${R(y + r)}V${R(y + h - r)}Q${R(x)},${R(y + h)} ${R(x + r)},${R(y + h)}H${R(x + w)}Z`;
241
+ }
242
+ }
243
+ function normalize(spec, warnings, errors) {
244
+ let type = String(spec?.type ?? "bar");
245
+ if (type === "bar" && spec.horizontal === true)
246
+ type = "barH";
247
+ if (type === "column")
248
+ type = "bar";
249
+ if (!SVG_CHART_TYPES.includes(type)) {
250
+ errors.push(`chart type "${type}" has no SVG renderer (${SVG_CHART_TYPES.join(" | ")}) \u2014 keep it native (render:"native")`);
251
+ return null;
252
+ }
253
+ if (type === "radar") {
254
+ const dims = Array.isArray(spec.labels) ? spec.labels.length : 0;
255
+ if (dims && dims < 3)
256
+ errors.push(`radar needs at least 3 dimensions (got ${dims}) \u2014 use a bar chart`);
257
+ if (dims > RADAR_MAX_DIMS)
258
+ errors.push(`radar takes at most ${RADAR_MAX_DIMS} dimensions (got ${dims}) \u2014 use barH, or group the dimensions`);
259
+ const raw = Array.isArray(spec.series) ? spec.series.filter((s) => s && Array.isArray(s.values)) : [];
260
+ if (raw.length > RADAR_MAX_SERIES)
261
+ errors.push(`radar overlays at most ${RADAR_MAX_SERIES} series (got ${raw.length}) \u2014 more polygons are unreadable; split the chart`);
262
+ raw.forEach((s, i) => {
263
+ if (!s.values.length)
264
+ errors.push(`radar series ${i + 1}${s.name ? ` "${s.name}"` : ""} has no values`);
265
+ });
266
+ }
267
+ const labels = Array.isArray(spec.labels) ? spec.labels.map((l) => String(l ?? "")) : [];
268
+ const rawSeries = Array.isArray(spec.series) ? spec.series.filter((s) => s && Array.isArray(s.values)) : [];
269
+ if (!labels.length)
270
+ errors.push("chart needs labels (one per category / slice)");
271
+ if (!rawSeries.length)
272
+ errors.push("chart needs at least one series with values");
273
+ if (errors.length)
274
+ return null;
275
+ const series = rawSeries.map((s, i) => {
276
+ const name = String(s.name ?? "");
277
+ const at = `series ${i + 1}${name ? ` "${name}"` : ""}`;
278
+ const vals = s.values.map((v) => typeof v === "string" ? Number(v.replace(/[,%\s]/g, "")) : Number(v));
279
+ if (vals.length !== labels.length) {
280
+ warnings.push(`${at} has ${vals.length} value(s) for ${labels.length} label(s) \u2014 ${vals.length < labels.length ? "missing ones drawn as 0" : "extras ignored"}`);
281
+ }
282
+ const bad = vals.filter((v) => !Number.isFinite(v)).length;
283
+ if (bad)
284
+ warnings.push(`${at} has ${bad} non-numeric value(s) \u2014 drawn as 0`);
285
+ return { name, values: labels.map((_, j) => Number.isFinite(vals[j]) ? vals[j] : 0) };
286
+ });
287
+ const pie = type === "pie" || type === "doughnut";
288
+ if (!pie && series.length > MAX_SERIES) {
289
+ errors.push(`${series.length} series \u2014 a chart carries at most ${MAX_SERIES} (the skin's --c1..--c4); fold the rest into "\u5176\u4ED6" or split the chart`);
290
+ return null;
291
+ }
292
+ if (pie && series.length > 1)
293
+ warnings.push(`${type} draws only the first series (${series.length} given)`);
294
+ return { type, labels, series };
295
+ }
296
+ function chartDataMissing(spec) {
297
+ if (!spec || typeof spec !== "object" || Array.isArray(spec))
298
+ return "the chart is not a {type, labels, series} object";
299
+ const c = spec;
300
+ const series = Array.isArray(c.series) ? c.series.filter((s) => s && typeof s === "object") : [];
301
+ const labels = Array.isArray(c.labels) ? c.labels : [];
302
+ const withValues = series.some((s) => Array.isArray(s.values) && s.values.length > 0);
303
+ if (c.type === "scatter" && series.some((s) => Array.isArray(s.points) && s.points.length > 0))
304
+ return null;
305
+ if (!labels.length)
306
+ return "the chart has no labels";
307
+ if (!withValues)
308
+ return "the chart has no series with values";
309
+ return null;
310
+ }
311
+ function renderChartSvg(spec, opts) {
312
+ const warnings = [];
313
+ const errors = [];
314
+ const W = Math.max(120, Math.round(opts.width));
315
+ const H = Math.max(80, Math.round(opts.height));
316
+ const base = Math.max(12, Math.min(18, Math.round(Math.min(W / 2, H) / 22)));
317
+ const unit = typeof spec?.unit === "string" ? spec.unit : "";
318
+ const label = (v) => `${formatValue(v)}${unit}`;
319
+ let type;
320
+ let body;
321
+ let names;
322
+ if (String(spec?.type ?? "") === "scatter") {
323
+ const sc = normalizeScatter(spec, warnings, errors);
324
+ if (!sc)
325
+ return { svg: "", warnings, errors };
326
+ type = "scatter";
327
+ body = scatterBody(sc, W, H, base, spec);
328
+ names = sc.series.map((s) => s.name).filter(Boolean);
329
+ } else {
330
+ const n = normalize(spec, warnings, errors);
331
+ if (!n)
332
+ return { svg: "", warnings, errors };
333
+ type = n.type;
334
+ names = n.labels;
335
+ body = n.type === "radar" ? radarBody(n, W, H, base, spec, label, warnings) : n.type === "pie" || n.type === "doughnut" ? pieBody(n, W, H, base, spec, label, warnings) : n.type === "barH" ? barHBody(n, W, H, base, spec, label, warnings) : n.type === "bar" ? barBody(n, W, H, base, spec, label, warnings) : lineBody(n, W, H, base, spec, label);
336
+ }
337
+ const size = opts.fluid === false ? `width="${W}" height="${H}"` : `width="100%" height="100%"`;
338
+ const aria = esc(`${type} chart: ${names.slice(0, 12).join(", ")}`);
339
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" class="talos-chart-svg" data-chart-type="${type}" ${size} viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${aria}" style="display:block;overflow:visible;font-family:var(--font-body, inherit);font-size:${base}px">${body}</svg>`;
340
+ return { svg, warnings, errors };
341
+ }
342
+ function legendRow(names, colors, x, y, base, maxW) {
343
+ let cx = x;
344
+ let cy = y;
345
+ const size = Math.round(base * 0.75);
346
+ const rowH = Math.round(base * 1.6);
347
+ const parts = [];
348
+ names.forEach((name, i) => {
349
+ const w = size + 6 + textW(name, base) + 18;
350
+ if (cx > x && cx + w > x + maxW) {
351
+ cx = x;
352
+ cy += rowH;
353
+ }
354
+ parts.push(`<rect x="${r1(cx)}" y="${r1(cy + (rowH - size) / 2 - 2)}" width="${size}" height="${size}" rx="2" style="fill:${colors[i]}"/>`);
355
+ parts.push(`<text x="${r1(cx + size + 6)}" y="${r1(cy + rowH / 2 + base * 0.35 - 2)}" style="fill:${TXT}">${esc(name)}</text>`);
356
+ cx += w;
357
+ });
358
+ return [parts.join(""), cy - y + rowH];
359
+ }
360
+ function seriesColors(n) {
361
+ return n.series.length === 1 ? [SINGLE] : n.series.map((_, i) => RAMP[i]);
362
+ }
363
+ function highlightSet(spec, count, warnings) {
364
+ if (spec.highlight === void 0)
365
+ return null;
366
+ const list = (Array.isArray(spec.highlight) ? spec.highlight : [spec.highlight]).map(Number);
367
+ const ok = list.filter((i) => Number.isInteger(i) && i >= 0 && i < count);
368
+ if (ok.length !== list.length)
369
+ warnings.push(`highlight indexes must be 0..${count - 1} \u2014 ignoring the rest`);
370
+ return ok.length ? new Set(ok) : null;
371
+ }
372
+ function labelled(values, showValues, few) {
373
+ if (showValues === false)
374
+ return /* @__PURE__ */ new Set();
375
+ if (showValues === true || values.length <= few)
376
+ return new Set(values.map((_, i) => i));
377
+ let hi = 0;
378
+ values.forEach((v, i) => {
379
+ if (v > values[hi])
380
+ hi = i;
381
+ });
382
+ return /* @__PURE__ */ new Set([hi, values.length - 1]);
383
+ }
384
+ function yAxis(ticks, toY, x0, x1, base, unit) {
385
+ return ticks.map((t) => {
386
+ const y = r1(toY(t));
387
+ return `<line x1="${r1(x0)}" x2="${r1(x1)}" y1="${y}" y2="${y}" style="stroke:${GRID};stroke-width:1" shape-rendering="crispEdges"/><text x="${r1(x0 - 8)}" y="${r1(toY(t) + base * 0.35)}" text-anchor="end" style="fill:${MUTED};font-size:${base - 1}px">${esc(formatValue(t) + (t === ticks[ticks.length - 1] ? unit : ""))}</text>`;
388
+ }).join("");
389
+ }
390
+ function barBody(n, W, H, base, spec, label, warnings) {
391
+ const stacked = spec.stacked === true && n.series.length > 1;
392
+ const colors = seriesColors(n);
393
+ const parts = [];
394
+ let top = 8;
395
+ if (n.series.length > 1) {
396
+ const [lg, h] = legendRow(n.series.map((s) => s.name || "\u2014"), colors, 0, 0, base, W);
397
+ parts.push(lg);
398
+ top = h + 8;
399
+ }
400
+ const totals = n.labels.map((_, j) => n.series.reduce((a, s) => a + Math.max(0, s.values[j]), 0));
401
+ const all = stacked ? totals : n.series.flatMap((s) => s.values);
402
+ if (stacked && n.series.some((s) => s.values.some((v) => v < 0)))
403
+ warnings.push("stacked bars cannot show negative values \u2014 drawn as 0");
404
+ const sc = niceScale(Math.min(0, ...all), Math.max(0, ...all));
405
+ const tickW = Math.max(...sc.ticks.map((t, i) => textW(formatValue(t) + (i === sc.ticks.length - 1 ? spec.unit ?? "" : ""), base - 1)));
406
+ const x0 = Math.ceil(tickW + 14);
407
+ const x1 = W - 4;
408
+ const y0 = top + base * 1.6;
409
+ const y1 = H - base * 2;
410
+ const toY = (v) => y1 - (v - sc.lo) / (sc.hi - sc.lo) * (y1 - y0);
411
+ parts.push(yAxis(sc.ticks, toY, x0, x1, base, spec.unit ?? ""));
412
+ const band = (x1 - x0) / n.labels.length;
413
+ const groupW = Math.min(band * 0.64, stacked ? 72 : 72 * n.series.length);
414
+ const barW = stacked ? groupW : (groupW - 2 * (n.series.length - 1)) / n.series.length;
415
+ const hl = n.series.length === 1 && !stacked ? highlightSet(spec, n.labels.length, warnings) : null;
416
+ const tooLong = n.labels.some((l) => textW(l, base) > band - 4);
417
+ if (tooLong)
418
+ warnings.push(`category labels are wider than their ${Math.round(band)}px column \u2014 use type "barH" or shorter labels`);
419
+ n.labels.forEach((lab, j) => {
420
+ const gx = x0 + band * j + (band - groupW) / 2;
421
+ if (stacked) {
422
+ let acc = 0;
423
+ n.series.forEach((s, i) => {
424
+ const v = Math.max(0, s.values[j]);
425
+ if (v <= 0)
426
+ return;
427
+ const yTop = toY(acc + v);
428
+ const yBot = toY(acc);
429
+ const isTop = n.series.slice(i + 1).every((t) => Math.max(0, t.values[j]) === 0);
430
+ const h = yBot - yTop - (acc > 0 ? 2 : 0);
431
+ parts.push(`<path d="${barPath(gx, yTop, barW, h, "top", isTop ? 4 : 0)}" style="fill:${colors[i]}"/>`);
432
+ acc += v;
433
+ });
434
+ } else {
435
+ n.series.forEach((s, i) => {
436
+ const v = s.values[j];
437
+ const x = gx + i * (barW + 2);
438
+ const yv = toY(v);
439
+ const yz = toY(0);
440
+ const fill = hl ? hl.has(j) ? ACCENT : MUTED : colors[i];
441
+ const op = hl && !hl.has(j) ? ";fill-opacity:.45" : "";
442
+ if (v >= 0)
443
+ parts.push(`<path d="${barPath(x, yv, barW, yz - yv, "top")}" style="fill:${fill}${op}"/>`);
444
+ else
445
+ parts.push(`<path d="${barPath(x, yz, barW, yv - yz, "bottom")}" style="fill:${fill}${op}"/>`);
446
+ });
447
+ }
448
+ parts.push(`<text x="${r1(x0 + band * j + band / 2)}" y="${r1(y1 + base * 1.4)}" text-anchor="middle" style="fill:${MUTED}">${esc(lab)}</text>`);
449
+ });
450
+ if (stacked || n.series.length === 1) {
451
+ const vals = stacked ? totals : n.series[0].values;
452
+ for (const j of labelled(vals, spec.showValues, 12)) {
453
+ const v = vals[j];
454
+ const y = v >= 0 ? toY(v) - 6 : toY(v) + base + 2;
455
+ parts.push(`<text x="${r1(x0 + band * j + band / 2)}" y="${r1(y)}" text-anchor="middle" style="fill:${TXT};font-weight:600">${esc(label(v))}</text>`);
456
+ }
457
+ } else if (spec.showValues === true) {
458
+ n.labels.forEach((_, j) => {
459
+ const gx = x0 + band * j + (band - groupW) / 2;
460
+ n.series.forEach((s, i) => {
461
+ const v = s.values[j];
462
+ const y = v >= 0 ? toY(v) - 5 : toY(v) + base;
463
+ parts.push(`<text x="${r1(gx + i * (barW + 2) + barW / 2)}" y="${r1(y)}" text-anchor="middle" style="fill:${TXT};font-size:${base - 2}px">${esc(label(v))}</text>`);
464
+ });
465
+ });
466
+ }
467
+ parts.push(`<line x1="${r1(x0)}" x2="${r1(x1)}" y1="${r1(toY(0))}" y2="${r1(toY(0))}" style="stroke:${MUTED};stroke-width:1" shape-rendering="crispEdges"/>`);
468
+ return parts.join("");
469
+ }
470
+ function barHBody(n, W, H, base, spec, label, warnings) {
471
+ const stacked = spec.stacked === true && n.series.length > 1;
472
+ const colors = seriesColors(n);
473
+ const parts = [];
474
+ let top = 4;
475
+ if (n.series.length > 1) {
476
+ const [lg, h] = legendRow(n.series.map((s) => s.name || "\u2014"), colors, 0, 0, base, W);
477
+ parts.push(lg);
478
+ top = h + 6;
479
+ }
480
+ const totals = n.labels.map((_, j) => n.series.reduce((a, s) => a + Math.max(0, s.values[j]), 0));
481
+ const vals = stacked ? totals : n.series.flatMap((s) => s.values);
482
+ if (vals.some((v) => v < 0))
483
+ warnings.push("barH draws negative values as 0 \u2014 use a vertical bar for a diverging series");
484
+ const sc = niceScale(0, Math.max(0, ...vals));
485
+ const labelW = Math.min(W * 0.38, Math.max(...n.labels.map((l) => textW(l, base))) + 12);
486
+ const valueW = Math.max(...(stacked ? totals : n.series.flatMap((s) => s.values)).map((v) => textW(label(Math.max(0, v)), base))) + 12;
487
+ const x0 = labelW;
488
+ const x1 = W - valueW;
489
+ const toX = (v) => x0 + Math.max(0, v) / (sc.hi || 1) * (x1 - x0);
490
+ const band = (H - top) / n.labels.length;
491
+ const groupH = Math.min(band * 0.62, stacked ? 44 : 44 * n.series.length);
492
+ const barH = stacked ? groupH : (groupH - 2 * (n.series.length - 1)) / n.series.length;
493
+ const hl = n.series.length === 1 && !stacked ? highlightSet(spec, n.labels.length, warnings) : null;
494
+ const shown = labelled(stacked ? totals : n.series[0].values, n.series.length === 1 || stacked ? spec.showValues : spec.showValues === true, 16);
495
+ n.labels.forEach((lab, j) => {
496
+ const gy = top + band * j + (band - groupH) / 2;
497
+ parts.push(`<text x="${r1(x0 - 12)}" y="${r1(top + band * j + band / 2 + base * 0.35)}" text-anchor="end" style="fill:${TXT}">${esc(lab)}</text>`);
498
+ if (stacked) {
499
+ let acc = 0;
500
+ n.series.forEach((s, i) => {
501
+ const v = Math.max(0, s.values[j]);
502
+ if (v <= 0)
503
+ return;
504
+ const xa = toX(acc) + (acc > 0 ? 2 : 0);
505
+ const isEnd = n.series.slice(i + 1).every((t) => Math.max(0, t.values[j]) === 0);
506
+ parts.push(`<path d="${barPath(xa, gy, toX(acc + v) - xa, barH, "right", isEnd ? 4 : 0)}" style="fill:${colors[i]}"/>`);
507
+ acc += v;
508
+ });
509
+ if (shown.has(j))
510
+ parts.push(`<text x="${r1(toX(totals[j]) + 8)}" y="${r1(gy + barH / 2 + base * 0.35)}" style="fill:${TXT};font-weight:600">${esc(label(totals[j]))}</text>`);
511
+ } else {
512
+ n.series.forEach((s, i) => {
513
+ const v = s.values[j];
514
+ const y = gy + i * (barH + 2);
515
+ const fill = hl ? hl.has(j) ? ACCENT : MUTED : colors[i];
516
+ const op = hl && !hl.has(j) ? ";fill-opacity:.45" : "";
517
+ parts.push(`<path d="${barPath(x0, y, toX(v) - x0, barH, "right")}" style="fill:${fill}${op}"/>`);
518
+ if (shown.has(j) && (n.series.length === 1 || spec.showValues === true)) {
519
+ parts.push(`<text x="${r1(toX(v) + 8)}" y="${r1(y + barH / 2 + base * 0.35)}" style="fill:${TXT};font-weight:600">${esc(label(v))}</text>`);
520
+ }
521
+ });
522
+ }
523
+ });
524
+ parts.push(`<line x1="${r1(x0)}" x2="${r1(x0)}" y1="${r1(top)}" y2="${r1(H)}" style="stroke:${MUTED};stroke-width:1" shape-rendering="crispEdges"/>`);
525
+ return parts.join("");
526
+ }
527
+ function lineBody(n, W, H, base, spec, label) {
528
+ const colors = seriesColors(n);
529
+ const parts = [];
530
+ let top = 8;
531
+ if (n.series.length > 1) {
532
+ const [lg, h] = legendRow(n.series.map((s) => s.name || "\u2014"), colors, 0, 0, base, W);
533
+ parts.push(lg);
534
+ top = h + 8;
535
+ }
536
+ const all = n.series.flatMap((s) => s.values);
537
+ const min = Math.min(...all);
538
+ const max = Math.max(...all);
539
+ const zoom = n.type === "line" && spec.zeroBaseline !== true && min > 0 && min > max * 0.6;
540
+ const sc = niceScale(zoom ? min : Math.min(0, min), Math.max(0, max));
541
+ const tickW = Math.max(...sc.ticks.map((t, i) => textW(formatValue(t) + (i === sc.ticks.length - 1 ? spec.unit ?? "" : ""), base - 1)));
542
+ const endW = Math.max(...n.series.map((s) => textW(label(s.values[s.values.length - 1]), base))) + 14;
543
+ const x0 = Math.ceil(tickW + 14);
544
+ const x1 = W - endW;
545
+ const y0 = top + base;
546
+ const y1 = H - base * 2;
547
+ const toY = (v) => y1 - (v - sc.lo) / (sc.hi - sc.lo) * (y1 - y0);
548
+ const inset = Math.min(24, (x1 - x0) / (2 * n.labels.length));
549
+ const toX = (j) => n.labels.length === 1 ? (x0 + x1) / 2 : x0 + inset + j * (x1 - x0 - 2 * inset) / (n.labels.length - 1);
550
+ parts.push(yAxis(sc.ticks, toY, x0, x1, base, spec.unit ?? ""));
551
+ const every = Math.max(1, Math.ceil(n.labels.reduce((m, l) => Math.max(m, textW(l, base)), 0) / Math.max(1, (x1 - x0) / n.labels.length - 6)));
552
+ n.labels.forEach((lab, j) => {
553
+ if (j % every !== 0 && j !== n.labels.length - 1)
554
+ return;
555
+ parts.push(`<text x="${r1(toX(j))}" y="${r1(y1 + base * 1.4)}" text-anchor="middle" style="fill:${MUTED}">${esc(lab)}</text>`);
556
+ });
557
+ n.series.forEach((s, i) => {
558
+ const pts = s.values.map((v, j) => `${r1(toX(j))},${r1(toY(v))}`);
559
+ if (n.type === "area") {
560
+ parts.push(`<path d="M${r1(toX(0))},${r1(toY(Math.max(sc.lo, 0)))}L${pts.join("L")}L${r1(toX(s.values.length - 1))},${r1(toY(Math.max(sc.lo, 0)))}Z" style="fill:${colors[i]};fill-opacity:.12"/>`);
561
+ }
562
+ parts.push(`<path d="M${pts.join("L")}" style="fill:none;stroke:${colors[i]};stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round"/>`);
563
+ const marks = s.values.length <= 12 || spec.showValues === true ? s.values.map((_, j) => j) : [s.values.length - 1];
564
+ for (const j of marks) {
565
+ parts.push(`<circle cx="${r1(toX(j))}" cy="${r1(toY(s.values[j]))}" r="4.5" style="fill:${colors[i]};stroke:${SURFACE};stroke-width:2"/>`);
566
+ }
567
+ const lab = spec.showValues === false ? [] : spec.showValues === true ? s.values.map((_, j) => j) : [s.values.length - 1];
568
+ for (const j of lab) {
569
+ const end = j === s.values.length - 1;
570
+ parts.push(`<text x="${r1(toX(j) + (end ? 10 : 0))}" y="${r1(toY(s.values[j]) + (end ? base * 0.35 : -10))}" text-anchor="${end ? "start" : "middle"}" style="fill:${TXT};font-weight:600">${esc(label(s.values[j]))}</text>`);
571
+ }
572
+ });
573
+ return parts.join("");
574
+ }
575
+ function arc(cx, cy, r, a0, a1) {
576
+ const p = (a) => [cx + r * Math.sin(a), cy - r * Math.cos(a)];
577
+ const [xa, ya] = p(a0);
578
+ const [xb, yb] = p(a1);
579
+ return `${r1(xa)},${r1(ya)} A${r1(r)},${r1(r)} 0 ${a1 - a0 > Math.PI ? 1 : 0} 1 ${r1(xb)},${r1(yb)}`;
580
+ }
581
+ function pieBody(n, W, H, base, spec, label, warnings) {
582
+ let slices = n.labels.map((name, j) => ({ name, value: n.series[0].values[j] }));
583
+ if (slices.some((s) => s.value < 0))
584
+ warnings.push("pie/doughnut slices cannot be negative \u2014 drawn as 0");
585
+ slices = slices.map((s) => ({ ...s, value: Math.max(0, s.value) }));
586
+ let other = false;
587
+ if (slices.length > MAX_SERIES) {
588
+ const isOther = (name) => /^(其他|其它|others?)$/i.test(name.trim());
589
+ const sorted = [...slices].sort((a2, b) => Number(isOther(a2.name)) - Number(isOther(b.name)) || b.value - a2.value);
590
+ const rest = sorted.slice(MAX_SERIES - 1);
591
+ slices = [...sorted.slice(0, MAX_SERIES - 1), { name: "\u5176\u4ED6", value: rest.reduce((a2, s) => a2 + s.value, 0) }];
592
+ other = true;
593
+ warnings.push(`${n.labels.length} slices \u2014 the ${rest.length} smallest are folded into "\u5176\u4ED6" (a chart carries ${MAX_SERIES} colors)`);
594
+ }
595
+ const total = slices.reduce((a2, s) => a2 + s.value, 0);
596
+ const colors = slices.map((_, i) => other && i === slices.length - 1 ? MUTED : RAMP[i]);
597
+ const percentData = (spec.unit ?? "").trim() === "%";
598
+ const pctOf = (v) => total > 0 ? Math.round(v / total * 1e3) / 10 : 0;
599
+ const legendText = (v) => percentData ? label(v) : spec.showValues === false ? `${pctOf(v)}%` : `${label(v)} \xB7 ${pctOf(v)}%`;
600
+ const legendW = Math.min(W * 0.45, Math.max(...slices.map((s) => textW(`${s.name} ${legendText(s.value)}`, base))) + base + 16);
601
+ const r = Math.max(20, Math.min((W - legendW - 32) / 2, H / 2 - 4));
602
+ const left = Math.max(0, (W - (2 * r + 32 + legendW)) / 2);
603
+ const cx = left + r + 4;
604
+ const cy = H / 2;
605
+ const parts = [];
606
+ const hole = n.type === "doughnut" ? r * 0.58 : 0;
607
+ let a = 0;
608
+ slices.forEach((s, i) => {
609
+ if (s.value <= 0 || total <= 0)
610
+ return;
611
+ const sweep = s.value / total * Math.PI * 2;
612
+ const a1 = a + sweep;
613
+ if (sweep >= Math.PI * 2 - 1e-6) {
614
+ parts.push(`<circle cx="${r1(cx)}" cy="${r1(cy)}" r="${r1(r)}" style="fill:${colors[i]}"/>`);
615
+ } else if (hole) {
616
+ const p = (rad, ang) => `${r1(cx + rad * Math.sin(ang))},${r1(cy - rad * Math.cos(ang))}`;
617
+ parts.push(`<path d="M${arc(cx, cy, r, a, a1)} L${p(hole, a1)} A${r1(hole)},${r1(hole)} 0 ${sweep > Math.PI ? 1 : 0} 0 ${p(hole, a)}Z" style="fill:${colors[i]};stroke:${SURFACE};stroke-width:2;stroke-linejoin:round"/>`);
618
+ } else {
619
+ parts.push(`<path d="M${r1(cx)},${r1(cy)} L${arc(cx, cy, r, a, a1)}Z" style="fill:${colors[i]};stroke:${SURFACE};stroke-width:2;stroke-linejoin:round"/>`);
620
+ }
621
+ a = a1;
622
+ });
623
+ const center = spec.centerLabel ?? (percentData ? "" : label(total));
624
+ if (hole && center) {
625
+ parts.push(`<text x="${r1(cx)}" y="${r1(cy + base * 0.6)}" text-anchor="middle" style="fill:${TITLE};font-weight:700;font-size:${Math.round(Math.min(hole * 0.55, base * 2))}px">${esc(center)}</text>`);
626
+ }
627
+ const lx = cx + r + 28;
628
+ const rowH = Math.round(base * 1.9);
629
+ const ly = cy - slices.length * rowH / 2;
630
+ slices.forEach((s, i) => {
631
+ const y = ly + i * rowH;
632
+ parts.push(`<rect x="${r1(lx)}" y="${r1(y + rowH / 2 - base * 0.4)}" width="${r1(base * 0.8)}" height="${r1(base * 0.8)}" rx="2" style="fill:${colors[i]}"/>`);
633
+ parts.push(`<text x="${r1(lx + base + 8)}" y="${r1(y + rowH / 2 + base * 0.35)}" style="fill:${TXT}">${esc(s.name)}<tspan dx="8" style="fill:${MUTED}">${esc(legendText(s.value))}</tspan></text>`);
634
+ });
635
+ return parts.join("");
636
+ }
637
+ function radarBody(n, W, H, base, spec, label, warnings) {
638
+ const colors = seriesColors(n);
639
+ const parts = [];
640
+ let top = 0;
641
+ if (n.series.length > 1) {
642
+ const [lg, h] = legendRow(n.series.map((s) => s.name || "\u2014"), colors, 0, 0, base, W);
643
+ parts.push(lg);
644
+ top = h + 4;
645
+ }
646
+ const dataMax = Math.max(0, ...n.series.flatMap((s) => s.values));
647
+ const given = Number(spec.max);
648
+ const explicit = spec.max !== void 0 && Number.isFinite(given) && given > 0;
649
+ if (spec.max !== void 0 && !explicit)
650
+ warnings.push(`radar max must be a positive number \u2014 using a scale over the data`);
651
+ const sc = niceScale(0, explicit ? given : dataMax || 1);
652
+ const max = explicit ? given : sc.hi;
653
+ const ticks = explicit ? [0.25, 0.5, 0.75, 1].map((f) => f * max) : sc.ticks.filter((t) => t > 0);
654
+ let low = 0;
655
+ let high = 0;
656
+ const series = n.series.map((s) => ({
657
+ name: s.name,
658
+ values: s.values.map((v) => {
659
+ if (v < 0) {
660
+ low++;
661
+ return 0;
662
+ }
663
+ if (v > max) {
664
+ high++;
665
+ return max;
666
+ }
667
+ return v;
668
+ })
669
+ }));
670
+ if (low)
671
+ warnings.push(`radar: ${low} negative value(s) drawn at the centre (0) \u2014 a radar's scale starts at 0`);
672
+ if (high)
673
+ warnings.push(`radar: ${high} value(s) above max ${formatValue(max)} drawn on the outer ring \u2014 raise "max" or fix the data`);
674
+ const single = n.series.length === 1;
675
+ const showVal = single && spec.showValues !== false;
676
+ const dimText = (j) => n.labels[j] + (showVal ? ` ${label(n.series[0].values[j])}` : "");
677
+ const labelW = Math.max(...n.labels.map((_, j) => textW(dimText(j), base)));
678
+ const dims = n.labels.length;
679
+ const ang = (j) => j * 2 * Math.PI / dims;
680
+ const sideW = Math.max(...n.labels.map((_, j) => Math.abs(Math.sin(ang(j))) > 0.2 ? textW(dimText(j), base) : textW(dimText(j), base) / 2));
681
+ const r = Math.max(30, Math.min((H - top) / 2 - base * 1.8, W / 2 - Math.min(sideW, labelW) - 14));
682
+ const cx = W / 2;
683
+ const cy = top + (H - top) / 2;
684
+ const pt = (j, rad) => [cx + rad * Math.sin(ang(j)), cy - rad * Math.cos(ang(j))];
685
+ const poly = (rad) => n.labels.map((_, j) => pt(j, rad(j)).map(r1).join(",")).join(" ");
686
+ for (const t of ticks) {
687
+ parts.push(`<polygon points="${poly(() => t / max * r)}" style="fill:none;stroke:${GRID};stroke-width:1"/>`);
688
+ }
689
+ n.labels.forEach((_, j) => {
690
+ const [x, y] = pt(j, r);
691
+ parts.push(`<line x1="${r1(cx)}" y1="${r1(cy)}" x2="${r1(x)}" y2="${r1(y)}" style="stroke:${GRID};stroke-width:1"/>`);
692
+ });
693
+ parts.push(radarTicks(series, ticks, max, r, cx, cy, dims, base, spec.unit ?? ""));
694
+ series.forEach((s, i) => {
695
+ const pts = poly((j) => s.values[j] / max * r);
696
+ parts.push(`<polygon points="${pts}" style="fill:${colors[i]};fill-opacity:${single ? ".18" : ".1"};stroke:${colors[i]};stroke-width:2.5;stroke-linejoin:round"/>`);
697
+ s.values.forEach((v, j) => {
698
+ const [x, y] = pt(j, v / max * r);
699
+ parts.push(`<circle cx="${r1(x)}" cy="${r1(y)}" r="4" style="fill:${colors[i]};stroke:${SURFACE};stroke-width:2"/>`);
700
+ });
701
+ });
702
+ n.labels.forEach((name, j) => {
703
+ const sin = Math.sin(ang(j));
704
+ const cos = Math.cos(ang(j));
705
+ const [x, y] = pt(j, r + 14);
706
+ const anchor = sin > 0.2 ? "start" : sin < -0.2 ? "end" : "middle";
707
+ const dy = cos > 0.2 ? -2 : cos < -0.2 ? base * 0.9 : base * 0.35;
708
+ const value = showVal ? `<tspan dx="6" style="fill:${TITLE};font-weight:700">${esc(label(n.series[0].values[j]))}</tspan>` : "";
709
+ parts.push(`<text x="${r1(x)}" y="${r1(y + dy)}" text-anchor="${anchor}" style="fill:${TXT}">${esc(name)}${value}</text>`);
710
+ });
711
+ return parts.join("");
712
+ }
713
+ function segHitsBox(ax, ay, bx, by, box) {
714
+ const dx = bx - ax;
715
+ const dy = by - ay;
716
+ let t0 = 0;
717
+ let t1 = 1;
718
+ for (const [p, q] of [[-dx, ax - box.x0], [dx, box.x1 - ax], [-dy, ay - box.y0], [dy, box.y1 - ay]]) {
719
+ if (p === 0) {
720
+ if (q < 0)
721
+ return false;
722
+ continue;
723
+ }
724
+ const t = q / p;
725
+ if (p < 0) {
726
+ if (t > t1)
727
+ return false;
728
+ if (t > t0)
729
+ t0 = t;
730
+ } else {
731
+ if (t < t0)
732
+ return false;
733
+ if (t < t1)
734
+ t1 = t;
735
+ }
736
+ }
737
+ return true;
738
+ }
739
+ function radarTicks(series, ticks, max, r, cx, cy, dims, base, unit) {
740
+ const size = base - 3;
741
+ const ang = (j) => j * 2 * Math.PI / dims;
742
+ const at = (j, rad) => [cx + rad * Math.sin(ang(j)), cy - rad * Math.cos(ang(j))];
743
+ const edge = Math.cos(Math.PI / dims);
744
+ const gap = ticks.length > 1 ? (ticks[1] - ticks[0]) / max * r * edge : Infinity;
745
+ const step = gap < size * 1.6 ? 2 : 1;
746
+ const shown = ticks.map((t, i) => ({ t, i })).filter(({ i }) => (ticks.length - 1 - i) % step === 0);
747
+ const segs = [];
748
+ const dots = [];
749
+ for (const s of series) {
750
+ const pts = s.values.map((v, j) => at(j, v / max * r));
751
+ pts.forEach((p, j) => {
752
+ const q = pts[(j + 1) % pts.length];
753
+ segs.push([p[0], p[1], q[0], q[1]]);
754
+ dots.push(p);
755
+ });
756
+ }
757
+ const DOT = 6;
758
+ const PAD = 2;
759
+ const place = (wedge, t, last) => {
760
+ const mid = ang(wedge) + Math.PI / dims;
761
+ const sin = Math.sin(mid);
762
+ const cos = Math.cos(mid);
763
+ const rad = t / max * r * edge;
764
+ const px = cx + rad * sin;
765
+ const py = cy - rad * cos;
766
+ const text = formatValue(t) + (last ? unit : "");
767
+ const w = textW(text, size);
768
+ const anchor = sin < -0.2 ? "end" : sin > 0.2 ? "start" : "middle";
769
+ const x = px + (anchor === "end" ? -3 : anchor === "start" ? 3 : 0);
770
+ const y = cos > 0.2 ? py - 3 : cos < -0.2 ? py + size + 1 : py + size * 0.35;
771
+ const x0 = anchor === "end" ? x - w : anchor === "start" ? x : x - w / 2;
772
+ const box = { x0: x0 - PAD, y0: y - size * 0.8 - PAD, x1: x0 + w + PAD, y1: y + size * 0.2 + PAD };
773
+ const hit = segs.some(([ax, ay, bx, by]) => segHitsBox(ax, ay, bx, by, box)) || dots.some(([dx, dy]) => Math.hypot(Math.max(box.x0 - dx, 0, dx - box.x1), Math.max(box.y0 - dy, 0, dy - box.y1)) < DOT);
774
+ return { x, y, anchor, text, hit };
775
+ };
776
+ const order = [dims - 1, ...Array.from({ length: dims - 1 }, (_, j) => j)];
777
+ let best = order[0];
778
+ let bestHits = Infinity;
779
+ for (const wedge of order) {
780
+ const hits = shown.reduce((n, { t, i }) => n + (place(wedge, t, i === ticks.length - 1).hit ? 1 : 0), 0);
781
+ if (hits < bestHits) {
782
+ best = wedge;
783
+ bestHits = hits;
784
+ }
785
+ if (!hits)
786
+ break;
787
+ }
788
+ return shown.map(({ t, i }) => ({ last: i === ticks.length - 1, ...place(best, t, i === ticks.length - 1) })).filter((l) => l.last || !l.hit).map((l) => `<text x="${r1(l.x)}" y="${r1(l.y)}" text-anchor="${l.anchor}" style="fill:${MUTED};font-size:${size}px;paint-order:stroke;stroke:${SURFACE};stroke-width:3px;stroke-linejoin:round">${esc(l.text)}</text>`).join("");
789
+ }
790
+ var toNum = (v) => typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v.replace(/[,%\s]/g, "")) : NaN;
791
+ function normalizeScatter(spec, warnings, errors) {
792
+ const raw = Array.isArray(spec.series) ? spec.series.filter((s) => s && typeof s === "object") : [];
793
+ if (!raw.length) {
794
+ errors.push("scatter needs at least one series with points ([x, y] or {x, y, label})");
795
+ return null;
796
+ }
797
+ if (raw.length > MAX_SERIES) {
798
+ errors.push(`${raw.length} series \u2014 a chart carries at most ${MAX_SERIES} (the skin's --c1..--c4); merge or split the chart`);
799
+ return null;
800
+ }
801
+ const labels = Array.isArray(spec.labels) ? spec.labels : [];
802
+ const series = raw.map((s, i) => {
803
+ const name = String(s.name ?? "");
804
+ const at = `scatter series ${i + 1}${name ? ` "${name}"` : ""}`;
805
+ let rows = Array.isArray(s.points) ? s.points : [];
806
+ if (!Array.isArray(s.points) && Array.isArray(s.values))
807
+ rows = s.values.map((y, j) => [labels[j], y]);
808
+ let bad = 0;
809
+ const points = [];
810
+ for (const p of rows) {
811
+ const [x, y, lab] = Array.isArray(p) ? [p[0], p[1], p[2]] : p && typeof p === "object" ? [p.x, p.y, p.label] : [NaN, NaN, ""];
812
+ const nx = toNum(x);
813
+ const ny = toNum(y);
814
+ if (!Number.isFinite(nx) || !Number.isFinite(ny)) {
815
+ bad++;
816
+ continue;
817
+ }
818
+ points.push({ x: nx, y: ny, label: lab == null ? "" : String(lab) });
819
+ }
820
+ if (bad)
821
+ warnings.push(`${at}: ${bad} point(s) without a numeric x and y \u2014 dropped`);
822
+ if (!rows.length)
823
+ warnings.push(`${at} has no points`);
824
+ return { name, points };
825
+ });
826
+ if (!series.some((s) => s.points.length)) {
827
+ errors.push("scatter has no plottable point \u2014 give series[].points as [x, y] or {x, y, label}");
828
+ return null;
829
+ }
830
+ return { series };
831
+ }
832
+ function axisScale(vals) {
833
+ const min = Math.min(...vals);
834
+ const max = Math.max(...vals);
835
+ const zoom = min > 0 && min > max * 0.6 || max < 0 && max < min * 0.6;
836
+ return niceScale(zoom ? min : Math.min(0, min), zoom ? max : Math.max(0, max));
837
+ }
838
+ function scatterBody(d, W, H, base, spec) {
839
+ const colors = d.series.length === 1 ? [SINGLE] : d.series.map((_, i) => RAMP[i]);
840
+ const parts = [];
841
+ let top = 8;
842
+ if (d.series.length > 1) {
843
+ const [lg, h] = legendRow(d.series.map((s) => s.name || "\u2014"), colors, 0, 0, base, W);
844
+ parts.push(lg);
845
+ top = h + 8;
846
+ }
847
+ const all = d.series.flatMap((s) => s.points);
848
+ const xs = axisScale(all.map((p) => p.x));
849
+ const ys = axisScale(all.map((p) => p.y));
850
+ const unit = spec.unit ?? "";
851
+ const xUnit = spec.xUnit ?? "";
852
+ if (spec.yLabel) {
853
+ parts.push(`<text x="0" y="${r1(top + base * 0.8)}" style="fill:${MUTED};font-size:${base - 1}px">${esc(spec.yLabel)}</text>`);
854
+ top += base * 1.4;
855
+ }
856
+ const tickW = Math.max(...ys.ticks.map((t, i) => textW(formatValue(t) + (i === ys.ticks.length - 1 ? unit : ""), base - 1)));
857
+ const lastX = formatValue(xs.ticks[xs.ticks.length - 1]) + xUnit;
858
+ const x0 = Math.ceil(tickW + 14);
859
+ const x1 = W - Math.max(8, textW(lastX, base - 1) / 2 + 2);
860
+ const y0 = top + base * 0.6;
861
+ const y1 = H - base * (spec.xLabel ? 3.2 : 2);
862
+ const toX = (v) => x0 + (v - xs.lo) / (xs.hi - xs.lo) * (x1 - x0);
863
+ const toY = (v) => y1 - (v - ys.lo) / (ys.hi - ys.lo) * (y1 - y0);
864
+ parts.push(yAxis(ys.ticks, toY, x0, x1, base, unit));
865
+ xs.ticks.forEach((t, i) => {
866
+ const x = r1(toX(t));
867
+ parts.push(`<line x1="${x}" x2="${x}" y1="${r1(y0)}" y2="${r1(y1)}" style="stroke:${GRID};stroke-width:1" shape-rendering="crispEdges"/>`);
868
+ parts.push(`<text x="${x}" y="${r1(y1 + base * 1.4)}" text-anchor="middle" style="fill:${MUTED};font-size:${base - 1}px">${esc(formatValue(t) + (i === xs.ticks.length - 1 ? xUnit : ""))}</text>`);
869
+ });
870
+ if (spec.xLabel) {
871
+ parts.push(`<text x="${r1(x1)}" y="${r1(H - base * 0.4)}" text-anchor="end" style="fill:${MUTED};font-size:${base - 1}px">${esc(spec.xLabel)}</text>`);
872
+ }
873
+ const ax = r1(toX(xs.lo <= 0 && xs.hi >= 0 ? 0 : xs.lo));
874
+ const ay = r1(toY(ys.lo <= 0 && ys.hi >= 0 ? 0 : ys.lo));
875
+ parts.push(`<line x1="${r1(x0)}" x2="${r1(x1)}" y1="${ay}" y2="${ay}" style="stroke:${MUTED};stroke-width:1" shape-rendering="crispEdges"/>`);
876
+ parts.push(`<line x1="${ax}" x2="${ax}" y1="${r1(y0)}" y2="${r1(y1)}" style="stroke:${MUTED};stroke-width:1" shape-rendering="crispEdges"/>`);
877
+ const dense = all.length > 60;
878
+ d.series.forEach((s, i) => {
879
+ for (const p of s.points) {
880
+ parts.push(`<circle cx="${r1(toX(p.x))}" cy="${r1(toY(p.y))}" r="${dense ? 4 : 6}" style="fill:${colors[i]};fill-opacity:.85;stroke:${SURFACE};stroke-width:1.5"/>`);
881
+ }
882
+ });
883
+ if (spec.showValues !== false) {
884
+ d.series.forEach((s) => {
885
+ for (const p of s.points) {
886
+ if (!p.label)
887
+ continue;
888
+ parts.push(`<text x="${r1(toX(p.x) + 9)}" y="${r1(toY(p.y) + base * 0.35 - 1)}" style="fill:${TXT};font-size:${base - 2}px">${esc(p.label)}</text>`);
889
+ }
890
+ });
891
+ }
892
+ return parts.join("");
893
+ }
894
+
167
895
  // packages/core/dist/document/deck-build.js
896
+ var DEFAULT_CHART_BOX = [700, 370];
168
897
  var SCENES = /* @__PURE__ */ new Set(["hero", "content", "data", "media", "closing"]);
169
898
  var DECOR_DENSITIES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
170
899
  var SCENE_VARIANTS = ["a", "b", "c"];
@@ -226,6 +955,17 @@ function templateSlots(tpl) {
226
955
  names.add(slotName(m[1]));
227
956
  return [...names];
228
957
  }
958
+ function templateDefaults(tpl) {
959
+ const out = {};
960
+ for (const m of withoutBlocks(withoutComments(tpl)).matchAll(SLOT_RE)) {
961
+ const name = slotName(m[1]);
962
+ if (name.startsWith("_") || Object.hasOwn(out, name))
963
+ continue;
964
+ const bar = m[1].indexOf("|");
965
+ out[name] = bar >= 0 ? m[1].slice(bar + 1) : "";
966
+ }
967
+ return out;
968
+ }
229
969
  function fillSlots(tpl, values) {
230
970
  return tpl.replace(SLOT_RE, (_full, inner) => {
231
971
  const bar = inner.indexOf("|");
@@ -276,21 +1016,46 @@ function expandRepeats(tpl, rows, layout, problems, figureInputs = {}) {
276
1016
  }
277
1017
  return html;
278
1018
  }
279
- function injectCharts(html, chart, layout, problems) {
1019
+ function injectCharts(html, chart, layout, problems, render) {
280
1020
  const specs = Array.isArray(chart) ? chart : [chart];
281
1021
  let i = 0;
282
- const out = html.replace(/data-chart=(['"])[\s\S]*?\1/g, (full) => {
1022
+ const out = html.replace(/<div\b[^>]*\bdata-chart=(['"])[\s\S]*?\1[^>]*>\s*<\/div>/g, (full) => {
1023
+ const at = i;
283
1024
  const spec = specs[i++];
284
1025
  if (spec === void 0)
285
1026
  return full;
286
- return `data-chart='${JSON.stringify(spec).replace(/'/g, "&#39;")}'`;
1027
+ const { render: own, ...data } = spec && typeof spec === "object" ? spec : {};
1028
+ const mode = own === "svg" || own === "native" ? own : render.fallback;
1029
+ if (own !== void 0 && own !== "svg" && own !== "native")
1030
+ problems.errors.push(`${layout}: chart render must be "svg" or "native"`);
1031
+ const missing = chartDataMissing(data);
1032
+ if (missing) {
1033
+ problems.errors.push(`${layout}: chart ${at + 1}: ${missing} \u2014 give it {type, labels, series:[{values}]}`);
1034
+ return full;
1035
+ }
1036
+ if (mode === "svg") {
1037
+ const box = render.boxes?.[at];
1038
+ if (!box)
1039
+ problems.warnings.push(`${layout}: chart ${at + 1} has no measured box \u2014 SVG drawn for ${DEFAULT_CHART_BOX.join("\xD7")}`);
1040
+ const [w, h] = box ?? DEFAULT_CHART_BOX;
1041
+ const svgSpec = render.derived && data.showValues === void 0 ? { ...data, showValues: false } : data;
1042
+ const r = renderChartSvg(svgSpec, { width: w, height: h, fluid: true });
1043
+ problems.warnings.push(...r.warnings.map((m) => `${layout}: chart ${at + 1}: ${m}`));
1044
+ if (r.errors.length) {
1045
+ problems.errors.push(...r.errors.map((m) => `${layout}: chart ${at + 1}: ${m}`));
1046
+ return full;
1047
+ }
1048
+ return r.svg;
1049
+ }
1050
+ const json = JSON.stringify(data).replace(/'/g, "&#39;");
1051
+ return full.replace(/data-chart=(['"])[\s\S]*?\1/, () => `data-chart='${json}'`);
287
1052
  });
288
1053
  if (i === 0)
289
1054
  problems.errors.push(`${layout}: a chart was supplied but this layout has no data-chart`);
290
1055
  else if (specs.length > i)
291
1056
  problems.errors.push(`${layout}: ${specs.length} charts supplied but the layout has ${i}`);
292
1057
  else if (specs.length < i)
293
- problems.warnings.push(`${layout}: ${i} charts in the layout, only ${specs.length} supplied \u2014 the rest keep their demo data`);
1058
+ problems.errors.push(`${layout}: ${i} charts in the layout, only ${specs.length} supplied \u2014 the rest would ship the template's demo data`);
294
1059
  return out;
295
1060
  }
296
1061
  function stripLeadComment(tpl) {
@@ -301,7 +1066,7 @@ function injectVisualScene(html, slide) {
301
1066
  return html;
302
1067
  return html.replace(/<section\b/, `<section data-scene="${slide.scene}" data-decor-density="${slide.decorDensity}" data-scene-variant="${slide.sceneVariant}"`);
303
1068
  }
304
- function assembleDeck(spec, readLayout) {
1069
+ function assembleDeck(spec, readLayout, opts = {}) {
305
1070
  const problems = { warnings: [], errors: [] };
306
1071
  const sections = [];
307
1072
  const slides = Array.isArray(spec?.slides) ? spec.slides : [];
@@ -327,12 +1092,27 @@ function assembleDeck(spec, readLayout) {
327
1092
  if (!local.errors.length)
328
1093
  html = expandRepeats(html, figures.rows, where, local, FIGURE_SPECS[slide.layout] ?? {});
329
1094
  html = fillLayout(html, slide.slots ?? {}, where, local);
1095
+ let chart = figures.chart ?? slide.chart;
330
1096
  if (slide.chart !== void 0 && DERIVED_CHART_LAYOUTS.has(slide.layout)) {
331
- local.errors.push(`${where}: this layout builds its chart from rows \u2014 drop "chart" from the spec`);
1097
+ const opts2 = slide.chart && typeof slide.chart === "object" && !Array.isArray(slide.chart) ? slide.chart : null;
1098
+ const data = opts2 ? Object.keys(opts2).filter((k) => !DERIVED_CHART_OPTIONS.has(k)) : [];
1099
+ if (!opts2 || data.length) {
1100
+ local.errors.push(`${where}: this layout builds its chart from rows \u2014 "chart" may only set ${[...DERIVED_CHART_OPTIONS].join(" / ")}` + (data.length ? ` (got ${data.join(", ")})` : ""));
1101
+ } else if (figures.chart && typeof figures.chart === "object") {
1102
+ chart = { ...figures.chart, ...opts2 };
1103
+ }
1104
+ }
1105
+ const charts = (withoutComments(html).match(/\bdata-chart=/g) ?? []).length;
1106
+ if (chart === void 0 && charts && !local.errors.length) {
1107
+ local.errors.push(`${where}: this layout draws ${charts > 1 ? `${charts} charts` : "a chart"} \u2014 give "chart" {type, labels, series} (without it the slide ships the template's demo chart), or pick a layout without one`);
1108
+ }
1109
+ if (chart !== void 0 && !local.errors.length) {
1110
+ html = injectCharts(html, chart, where, local, {
1111
+ fallback: spec.chartRender === "svg" ? "svg" : "native",
1112
+ boxes: opts.chartBoxes?.(slide.layout),
1113
+ derived: figures.chart !== void 0
1114
+ });
332
1115
  }
333
- const chart = figures.chart ?? slide.chart;
334
- if (chart !== void 0 && !local.errors.length)
335
- html = injectCharts(html, chart, where, local);
336
1116
  if (!local.errors.length)
337
1117
  html = injectVisualScene(html, slide);
338
1118
  problems.warnings.push(...local.warnings);
@@ -343,6 +1123,178 @@ function assembleDeck(spec, readLayout) {
343
1123
  return { html: sections.join("\n"), warnings: problems.warnings, errors: problems.errors };
344
1124
  }
345
1125
 
1126
+ // packages/core/dist/document/ooxml-util.js
1127
+ function visualWidth(text) {
1128
+ let w = 0;
1129
+ for (const ch of text) {
1130
+ const cp = ch.codePointAt(0);
1131
+ if (ch === " " || ch === " ")
1132
+ w += 0.35;
1133
+ else if (cp >= 4352 && cp <= 4447 || // Hangul Jamo
1134
+ cp >= 11904 && cp <= 40959 || // CJK radicals … unified ideographs
1135
+ cp >= 40960 && cp <= 42191 || // Yi
1136
+ cp >= 44032 && cp <= 55203 || // Hangul syllables
1137
+ cp >= 63744 && cp <= 64255 || // CJK compat ideographs
1138
+ cp >= 65072 && cp <= 65103 || // CJK compat forms
1139
+ cp >= 65280 && cp <= 65376 || // fullwidth forms
1140
+ cp >= 65504 && cp <= 65510 || cp >= 131072 && cp <= 262141)
1141
+ w += 1;
1142
+ else
1143
+ w += 0.5;
1144
+ }
1145
+ return w;
1146
+ }
1147
+
1148
+ // packages/core/dist/document/deck-capacity.js
1149
+ function parseCapacityDoc(raw) {
1150
+ const d = raw;
1151
+ if (!d || typeof d !== "object" || !Array.isArray(d.styles) || !d.layouts || typeof d.layouts !== "object")
1152
+ return null;
1153
+ return d;
1154
+ }
1155
+ function pick(v, idx, ceiling) {
1156
+ if (v === void 0)
1157
+ return void 0;
1158
+ let n;
1159
+ if (typeof v === "number")
1160
+ n = v;
1161
+ else if (idx >= 0 && typeof v[idx] === "number")
1162
+ n = v[idx];
1163
+ else {
1164
+ const present = v.filter((x) => typeof x === "number");
1165
+ n = present.length ? Math.min(...present) : void 0;
1166
+ }
1167
+ if (n === void 0)
1168
+ return void 0;
1169
+ return Math.min(n, ceiling);
1170
+ }
1171
+ function capOf(v, idx, ceiling) {
1172
+ if (!v)
1173
+ return void 0;
1174
+ const max = pick(v.max, idx, ceiling);
1175
+ if (max === void 0)
1176
+ return void 0;
1177
+ const one = pick(v.one, idx, ceiling);
1178
+ return one !== void 0 && one < max ? { max, one } : { max };
1179
+ }
1180
+ function resolveLayoutCapacity(doc, layout, ctx = {}) {
1181
+ const packKey = ctx.pack ? `${ctx.pack}/${layout}` : null;
1182
+ const key = packKey && doc.layouts[packKey] ? packKey : layout;
1183
+ const raw = doc.layouts[key];
1184
+ if (!raw)
1185
+ return null;
1186
+ const styleId = key === packKey ? ctx.pack : ctx.style;
1187
+ const idx = styleId ? doc.styles.indexOf(styleId) : -1;
1188
+ const ceiling = doc.maxUnits ?? 160;
1189
+ const slots = {};
1190
+ for (const [s, v] of Object.entries(raw.slots ?? {})) {
1191
+ const c = capOf(v, idx, ceiling);
1192
+ if (c)
1193
+ slots[s] = c;
1194
+ }
1195
+ const rows = {};
1196
+ for (const [k, r] of Object.entries(raw.rows ?? {})) {
1197
+ const fields = {};
1198
+ for (const [f, fv] of Object.entries(r.fields ?? {})) {
1199
+ const lo = capOf(fv.lo, idx, ceiling);
1200
+ const hi = capOf(fv.hi, idx, ceiling);
1201
+ if (lo && hi)
1202
+ fields[f] = { lo, hi };
1203
+ }
1204
+ rows[k] = { max: pick(r.max, idx, Number.POSITIVE_INFINITY) ?? 1, fields };
1205
+ }
1206
+ return { key, slots, numeric: new Set(raw.numeric ?? []), rows, charts: raw.charts ?? [] };
1207
+ }
1208
+ function rowFieldCap(field, n, max) {
1209
+ const loN = Math.min(3, max);
1210
+ if (n <= loN || max <= loN)
1211
+ return field.lo;
1212
+ if (n >= max)
1213
+ return field.hi;
1214
+ const t = (n - loN) / (max - loN);
1215
+ const mix = (a, b) => Math.floor(a + (b - a) * t);
1216
+ const one = field.lo.one !== void 0 || field.hi.one !== void 0 ? mix(field.lo.one ?? field.lo.max, field.hi.one ?? field.hi.max) : void 0;
1217
+ const out = { max: mix(field.lo.max, field.hi.max) };
1218
+ if (one !== void 0 && one < out.max)
1219
+ out.one = one;
1220
+ return out;
1221
+ }
1222
+ var ONE_LINE_SLOT = /_(?:title|name|label|head|date|dur|unit|tag|initials|role|key)$|^(?:eyebrow|badge|tag|round|kicker|featured|brand\d*|col\d+|name|label)$/;
1223
+ var NUMBERED_SLOT = /\d/;
1224
+ var CHROME_SLOT = /^(?:eyebrow|footer|pageno|meta|date|presenter|brand\d+|refs|image_label|media_label|source_label|index)$/;
1225
+ var fmtW = (n) => String(Math.round(n * 10) / 10);
1226
+ function checkSlideCapacity(slide, cap, defaults) {
1227
+ const errors = [];
1228
+ const warnings = [];
1229
+ const slots = slide.slots ?? {};
1230
+ const checkText = (where, value, c, numeric, oneLine) => {
1231
+ const w = visualWidth(value);
1232
+ if (w > c.max) {
1233
+ errors.push({
1234
+ slot: where,
1235
+ code: "overflow",
1236
+ message: numeric ? `${where} "${value}" will not stay on one line \u2014 it holds \u2264${fmtW(c.max)} (\u2248${Math.floor(c.max * 2)} digits); shorten the number (\u4E07/\u4EBF/K/M) or move the unit into its unit slot` : `${where} is ~${fmtW(w)} wide but holds \u2264${fmtW(c.max)} \u2014 cut ~${fmtW(w - c.max)} (CJK=1, Latin\u22480.5), or pick a roomier layout (deck-plan)`
1237
+ });
1238
+ } else if (oneLine && c.one !== void 0 && w > c.one) {
1239
+ warnings.push({ slot: where, code: "wrap", message: `${where} wraps (~${fmtW(w)} wide; one line holds ${fmtW(c.one)})` });
1240
+ }
1241
+ };
1242
+ for (const [name, raw] of Object.entries(slots)) {
1243
+ const c = cap.slots[name];
1244
+ if (!c || raw === void 0 || raw === null)
1245
+ continue;
1246
+ checkText(name, String(raw), c, cap.numeric.has(name), ONE_LINE_SLOT.test(name));
1247
+ }
1248
+ for (const [key, list] of Object.entries(slide.rows ?? {})) {
1249
+ const r = cap.rows[key];
1250
+ if (!r || !Array.isArray(list))
1251
+ continue;
1252
+ if (list.length > r.max) {
1253
+ errors.push({
1254
+ slot: `rows.${key}`,
1255
+ code: "rows",
1256
+ message: `rows.${key} has ${list.length} rows but the layout holds \u2264${r.max} \u2014 split the slide or merge rows`
1257
+ });
1258
+ }
1259
+ list.forEach((row, i) => {
1260
+ if (!row || typeof row !== "object")
1261
+ return;
1262
+ for (const [f, v] of Object.entries(row)) {
1263
+ const fc = r.fields[f];
1264
+ if (!fc || v === void 0 || v === null)
1265
+ continue;
1266
+ checkText(`rows.${key}[${i}].${f}`, String(v), rowFieldCap(fc, list.length, r.max), false, ONE_LINE_SLOT.test(f));
1267
+ }
1268
+ });
1269
+ }
1270
+ if (defaults) {
1271
+ const missingNumbered = [];
1272
+ for (const [name, def] of Object.entries(defaults)) {
1273
+ if (name.startsWith("_") || Object.hasOwn(slots, name))
1274
+ continue;
1275
+ if (!def.trim())
1276
+ continue;
1277
+ if (NUMBERED_SLOT.test(name) && !CHROME_SLOT.test(name))
1278
+ missingNumbered.push(name);
1279
+ else {
1280
+ warnings.push({
1281
+ slot: name,
1282
+ code: "default",
1283
+ message: `${name} is not filled \u2014 it renders the template text "${def.replace(/<[^>]+>/g, " ").trim().slice(0, 24)}"; pass "" to leave it empty`
1284
+ });
1285
+ }
1286
+ }
1287
+ if (missingNumbered.length) {
1288
+ errors.push({
1289
+ slot: missingNumbered[0],
1290
+ code: "underfill",
1291
+ message: `${missingNumbered.join(", ")} not filled \u2014 the slide would show the template's demo copy there. Give every item this layout has, or pick a layout with fewer slots (deck-plan)`
1292
+ });
1293
+ }
1294
+ }
1295
+ return { errors, warnings };
1296
+ }
1297
+
346
1298
  // packages/core/dist/document/deck-catalog.js
347
1299
  var KINDS = ["skin", "pack"];
348
1300
  var MODES = ["light", "dark"];
@@ -557,6 +1509,1314 @@ function resolveThemeLayers(system, styleId, mediaOverride) {
557
1509
  };
558
1510
  }
559
1511
 
1512
+ // packages/core/dist/document/deck-scenarios.data.js
1513
+ var SCENARIO_MATRIX = {
1514
+ "scenarios": [
1515
+ {
1516
+ "id": "pitch",
1517
+ "subtitle": "\u878D\u8D44/\u8DEF\u6F14",
1518
+ "tone": [
1519
+ "\u81EA\u4FE1",
1520
+ "\u6709\u5F20\u529B",
1521
+ "\u6545\u4E8B\u9A71\u52A8"
1522
+ ],
1523
+ "density": [
1524
+ "\u7A00\u758F",
1525
+ "\u5927\u6570\u5B57"
1526
+ ],
1527
+ "requiredSections": [
1528
+ {
1529
+ "terms": [
1530
+ "Problem",
1531
+ "\u95EE\u9898"
1532
+ ],
1533
+ "content": "\u8C01\u75DB\u3001\u591A\u75DB\uFF1A\u91CF\u5316\u7684\u75DB\u70B9\u8BC1\u636E\uFF08\u65F6\u95F4/\u91D1\u94B1\u635F\u5931\uFF09\uFF1B\u5B9C \u5927\u6570\u5B57"
1534
+ },
1535
+ {
1536
+ "terms": [
1537
+ "Solution",
1538
+ "\u89E3\u51B3\u65B9\u6848"
1539
+ ],
1540
+ "content": "\u600E\u4E48\u89E3\u51B3\u3001\u4E3A\u4EC0\u4E48\u73B0\u5728\u80FD\u89E3\u51B3\uFF1B\u5B9C \u524D\u540E\u5BF9\u6BD4\u6216\u6D41\u7A0B"
1541
+ },
1542
+ {
1543
+ "terms": [
1544
+ "Market",
1545
+ "\u5E02\u573A"
1546
+ ],
1547
+ "content": "TAM/SAM/SOM \u4E0E\u589E\u901F\uFF0C\u6CE8\u660E\u6765\u6E90\u5E74\u4EFD\uFF1B\u5B9C \u67F1\u72B6\u56FE"
1548
+ },
1549
+ {
1550
+ "terms": [
1551
+ "Product",
1552
+ "Traction",
1553
+ "\u4EA7\u54C1",
1554
+ "\u8FDB\u5C55"
1555
+ ],
1556
+ "content": "\u4EA7\u54C1\u5F62\u6001 + \u589E\u957F\u8BC1\u636E\uFF08\u5BA2\u6237\u6570\u3001\u6536\u5165\u3001\u7559\u5B58\uFF09\uFF1B\u5B9C \u589E\u957F\u6298\u7EBF"
1557
+ },
1558
+ {
1559
+ "terms": [
1560
+ "Team",
1561
+ "\u56E2\u961F"
1562
+ ],
1563
+ "content": "\u6838\u5FC3\u6210\u5458\u4E0E\u8FD9\u4EF6\u4E8B\u76F8\u5173\u7684\u7ECF\u5386\uFF1B\u5B9C \u5E76\u5217\u5361\u7247"
1564
+ },
1565
+ {
1566
+ "terms": [
1567
+ "Ask",
1568
+ "\u878D\u8D44\u9700\u6C42"
1569
+ ],
1570
+ "key": true,
1571
+ "content": "\u878D\u8D44\u91D1\u989D\u3001\u8D44\u91D1\u7528\u9014\u5360\u6BD4\u3001\u5230\u4E0B\u4E00\u8F6E\u7684\u91CC\u7A0B\u7891\uFF1B\u5B9C \u997C\u56FE\u6216\u62C6\u5206"
1572
+ }
1573
+ ],
1574
+ "narrative": "SCQA / \u95EE\u9898-\u65B9\u6848",
1575
+ "pages": {
1576
+ "min": 10,
1577
+ "max": 12
1578
+ },
1579
+ "researchFocus": [
1580
+ "\u5E02\u573A\u89C4\u6A21\u4E0E\u589E\u901F(TAM/SAM)",
1581
+ "\u53EF\u6BD4\u516C\u53F8\u878D\u8D44\u4E0E\u4F30\u503C",
1582
+ "\u540C\u7C7B\u4EA7\u54C1\u4E0E\u5DEE\u5F02",
1583
+ "\u76EE\u6807\u5BA2\u7FA4\u75DB\u70B9\u8BC1\u636E"
1584
+ ],
1585
+ "motion": {
1586
+ "delivery": "presented",
1587
+ "keywords": [
1588
+ "pitch",
1589
+ "\u8DEF\u6F14"
1590
+ ]
1591
+ }
1592
+ },
1593
+ {
1594
+ "id": "\u5185\u5BA1",
1595
+ "tone": [
1596
+ "\u5BA2\u89C2",
1597
+ "\u76F4\u63A5",
1598
+ "\u7ED3\u8BBA\u5148\u884C"
1599
+ ],
1600
+ "density": [
1601
+ "\u7565\u5BC6(\u5BB9\u660E\u7EC6/\u6570\u636E)"
1602
+ ],
1603
+ "requiredSections": [
1604
+ {
1605
+ "terms": [
1606
+ "\u7ED3\u8BBA",
1607
+ "\u8FDB\u5C55"
1608
+ ],
1609
+ "content": "\u4E00\u53E5\u8BDD\u7ED3\u8BBA\uFF1A\u8FBE\u6210/\u672A\u8FBE\u6210\u4E0E\u504F\u5DEE\u5E45\u5EA6"
1610
+ },
1611
+ {
1612
+ "terms": [
1613
+ "\u5173\u952E\u6307\u6807"
1614
+ ],
1615
+ "content": "\u6838\u5FC3\u6307\u6807 \u5B9E\u9645 vs \u76EE\u6807 vs \u4E0A\u671F\uFF0C\u53E3\u5F84\u4E00\u81F4\uFF1B\u5B9C \u8868\u683C\u6216\u5BF9\u6BD4\u67F1"
1616
+ },
1617
+ {
1618
+ "terms": [
1619
+ "\u95EE\u9898\u4E0E\u98CE\u9669"
1620
+ ],
1621
+ "content": "\u95EE\u9898\u3001\u5F71\u54CD\u7A0B\u5EA6\u3001\u8D23\u4EFB\u65B9\uFF1B\u5B9C \u5217\u8868\u6216\u77E9\u9635"
1622
+ },
1623
+ {
1624
+ "terms": [
1625
+ "\u4E0B\u4E00\u6B65",
1626
+ "\u51B3\u7B56\u9879"
1627
+ ],
1628
+ "content": "\u9700\u62CD\u677F\u7684\u51B3\u7B56\u9879\u3001\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u65F6\u95F4"
1629
+ }
1630
+ ],
1631
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
1632
+ "pages": {
1633
+ "min": 6,
1634
+ "max": 8
1635
+ },
1636
+ "researchFocus": [
1637
+ "\u884C\u4E1A\u5BF9\u6807\u6307\u6807",
1638
+ "\u53E3\u5F84\u5B9A\u4E49",
1639
+ "\u5916\u90E8\u98CE\u9669\u4E8B\u4EF6"
1640
+ ],
1641
+ "motion": {
1642
+ "delivery": "read",
1643
+ "keywords": [
1644
+ "\u5185\u5BA1"
1645
+ ]
1646
+ }
1647
+ },
1648
+ {
1649
+ "id": "\u9500\u552E",
1650
+ "tone": [
1651
+ "\u5229\u76CA\u9A71\u52A8",
1652
+ "\u5BA2\u6237\u4E2D\u5FC3",
1653
+ "\u8BF4\u670D"
1654
+ ],
1655
+ "density": [
1656
+ "\u4E2D",
1657
+ "\u91CD\u4EF7\u503C\u4E0E\u4F50\u8BC1"
1658
+ ],
1659
+ "requiredSections": [
1660
+ {
1661
+ "terms": [
1662
+ "\u75DB\u70B9"
1663
+ ],
1664
+ "content": "\u5BA2\u6237\u7684\u4E1A\u52A1\u75DB\u70B9\u4E0E\u4EE3\u4EF7\uFF0C\u7528\u5BA2\u6237\u884C\u4E1A\u7684\u6570\u636E"
1665
+ },
1666
+ {
1667
+ "terms": [
1668
+ "\u65B9\u6848",
1669
+ "\u4EF7\u503C\u4E3B\u5F20"
1670
+ ],
1671
+ "content": "\u65B9\u6848\u5982\u4F55\u89E3\u51B3\u3001\u5BF9\u5BA2\u6237\u7684\u4EF7\u503C\u4E3B\u5F20\u4E00\u53E5\u8BDD"
1672
+ },
1673
+ {
1674
+ "terms": [
1675
+ "\u5DEE\u5F02\u5316"
1676
+ ],
1677
+ "content": "\u4E0E\u66FF\u4EE3\u65B9\u6848\u9010\u9879\u5BF9\u6BD4\u7684\u5DEE\u5F02\u70B9\uFF1B\u5B9C \u5BF9\u6BD4\u8868"
1678
+ },
1679
+ {
1680
+ "terms": [
1681
+ "\u5BA2\u6237\u6848\u4F8B"
1682
+ ],
1683
+ "content": "\u540C\u884C\u4E1A\u5BA2\u6237\u3001\u505A\u4E86\u4EC0\u4E48\u3001\u91CF\u5316\u7ED3\u679C"
1684
+ },
1685
+ {
1686
+ "terms": [
1687
+ "ROI",
1688
+ "\u5B9A\u4EF7"
1689
+ ],
1690
+ "content": "\u6295\u5165\u3001\u56DE\u62A5\u3001\u56DE\u672C\u5468\u671F\u6216\u5B9A\u4EF7\u65B9\u6848\uFF1B\u5B9C \u5927\u6570\u5B57"
1691
+ },
1692
+ {
1693
+ "terms": [
1694
+ "CTA",
1695
+ "\u884C\u52A8\u53F7\u53EC"
1696
+ ],
1697
+ "key": true,
1698
+ "content": "\u4E0B\u4E00\u6B65\u5177\u4F53\u52A8\u4F5C\uFF1A\u8BD5\u7528\u3001\u4F1A\u8BAE\u3001\u7B7E\u7EA6\u65F6\u95F4"
1699
+ }
1700
+ ],
1701
+ "narrative": "\u95EE\u9898-\u65B9\u6848",
1702
+ "pages": {
1703
+ "min": 8,
1704
+ "max": 12
1705
+ },
1706
+ "researchFocus": [
1707
+ "\u5BA2\u6237\u6240\u5728\u884C\u4E1A\u75DB\u70B9",
1708
+ "\u7ADE\u54C1\u65B9\u6848\u4E0E\u5DEE\u5F02",
1709
+ "\u540C\u884C\u843D\u5730\u6848\u4F8B\u4E0E ROI",
1710
+ "\u516C\u5F00\u5B9A\u4EF7"
1711
+ ],
1712
+ "motion": {
1713
+ "delivery": "presented",
1714
+ "keywords": [
1715
+ "\u9500\u552E",
1716
+ "sales"
1717
+ ]
1718
+ }
1719
+ },
1720
+ {
1721
+ "id": "\u8BB2\u5EA7",
1722
+ "subtitle": "\u5B66\u672F",
1723
+ "tone": [
1724
+ "\u8BB2\u89E3",
1725
+ "\u5FAA\u5E8F\u6E10\u8FDB"
1726
+ ],
1727
+ "density": [
1728
+ "\u4E2D",
1729
+ "\u6982\u5FF5+\u4F8B\u5B50"
1730
+ ],
1731
+ "requiredSections": [
1732
+ {
1733
+ "terms": [
1734
+ "\u5B66\u4E60\u76EE\u6807"
1735
+ ],
1736
+ "content": "\u542C\u5B8C\u80FD\u505A\u5230\u7684 3 \u4EF6\u4E8B"
1737
+ },
1738
+ {
1739
+ "terms": [
1740
+ "\u80CC\u666F",
1741
+ "\u57FA\u7840"
1742
+ ],
1743
+ "content": "\u7406\u89E3\u6838\u5FC3\u6982\u5FF5\u5FC5\u987B\u5148\u77E5\u9053\u7684\u524D\u7F6E\u77E5\u8BC6"
1744
+ },
1745
+ {
1746
+ "terms": [
1747
+ "\u6838\u5FC3\u6982\u5FF5"
1748
+ ],
1749
+ "content": "\u5B9A\u4E49\u3001\u5173\u952E\u6027\u8D28\uFF0C\u914D\u4E00\u4E2A\u76F4\u89C2\u4F8B\u5B50"
1750
+ },
1751
+ {
1752
+ "terms": [
1753
+ "\u6848\u4F8B",
1754
+ "\u63A8\u5BFC"
1755
+ ],
1756
+ "content": "\u5B8C\u6574\u7684\u6848\u4F8B\u6216\u63A8\u5BFC\u6B65\u9AA4\uFF1B\u5B9C \u6B65\u9AA4"
1757
+ },
1758
+ {
1759
+ "terms": [
1760
+ "\u5C0F\u7ED3"
1761
+ ],
1762
+ "content": "\u8981\u70B9\u56DE\u987E + \u5EF6\u4F38\u9605\u8BFB"
1763
+ }
1764
+ ],
1765
+ "narrative": "\u7531\u6D45\u5165\u6DF1",
1766
+ "pages": {
1767
+ "min": 10,
1768
+ "max": 15
1769
+ },
1770
+ "researchFocus": [
1771
+ "\u6982\u5FF5\u5B9A\u4E49\u4E0E\u6743\u5A01\u6559\u6750",
1772
+ "\u5173\u952E\u8BBA\u6587\u4E0E\u7EFC\u8FF0",
1773
+ "\u7ECF\u5178\u6848\u4F8B/\u63A8\u5BFC",
1774
+ "\u5E38\u89C1\u8BEF\u89E3"
1775
+ ],
1776
+ "motion": {
1777
+ "delivery": "presented",
1778
+ "keywords": [
1779
+ "\u8BB2\u5EA7",
1780
+ "lecture"
1781
+ ]
1782
+ }
1783
+ },
1784
+ {
1785
+ "id": "\u53D1\u5E03\u4F1A",
1786
+ "subtitle": "keynote",
1787
+ "tone": [
1788
+ "\u60C5\u7EEA\u5316",
1789
+ "\u620F\u5267\u6027",
1790
+ "\u63ED\u6653\u611F"
1791
+ ],
1792
+ "density": [
1793
+ "\u6781\u7A00\u758F",
1794
+ "\u4E00\u9875\u4E00\u4FE1\u606F",
1795
+ "\u8D85\u5927\u89C6\u89C9"
1796
+ ],
1797
+ "densityEmphasis": true,
1798
+ "requiredSections": [
1799
+ {
1800
+ "terms": [
1801
+ "\u94A9\u5B50"
1802
+ ],
1803
+ "content": "\u4E00\u53E5\u8BDD\u6216\u4E00\u4E2A\u753B\u9762\u6293\u4F4F\u6CE8\u610F\u529B\uFF1B\u5B9C \u5927\u5B57\u6216\u5927\u56FE"
1804
+ },
1805
+ {
1806
+ "terms": [
1807
+ "\u80CC\u666F",
1808
+ "\u75DB\u70B9"
1809
+ ],
1810
+ "content": "\u7528\u6237\u4ECA\u5929\u7684\u56F0\u6270\uFF0C\u4E00\u4E2A\u5177\u4F53\u573A\u666F"
1811
+ },
1812
+ {
1813
+ "terms": [
1814
+ "\u4EA7\u54C1\u63ED\u6653"
1815
+ ],
1816
+ "content": "\u4EA7\u54C1\u540D\u4E0E\u4E00\u53E5\u8BDD\u5B9A\u4F4D\uFF0C\u5355\u72EC\u4E00\u9875\uFF1B\u5B9C \u5927\u56FE"
1817
+ },
1818
+ {
1819
+ "terms": [
1820
+ "\u4EAE\u70B9\u7279\u6027"
1821
+ ],
1822
+ "content": "3 \u4E2A\u4EE5\u5185\u7684\u6838\u5FC3\u5356\u70B9\uFF0C\u5404\u914D\u4E00\u4E2A\u53EF\u611F\u77E5\u7684\u6570\u5B57"
1823
+ },
1824
+ {
1825
+ "terms": [
1826
+ "Demo",
1827
+ "\u6F14\u793A"
1828
+ ],
1829
+ "content": "\u771F\u5B9E\u4F7F\u7528\u6D41\u7A0B\u6F14\u793A\uFF1B\u5B9C \u6B65\u9AA4\u6216\u622A\u56FE"
1830
+ },
1831
+ {
1832
+ "terms": [
1833
+ "\u4EF7\u683C\u4E0A\u5E02"
1834
+ ],
1835
+ "content": "\u4EF7\u683C\u3001\u7248\u672C\u3001\u4E0A\u5E02\u65F6\u95F4\u4E0E\u6E20\u9053"
1836
+ },
1837
+ {
1838
+ "terms": [
1839
+ "\u884C\u52A8\u53F7\u53EC"
1840
+ ],
1841
+ "key": true,
1842
+ "content": "\u73B0\u5728\u5C31\u80FD\u505A\u7684\u4E8B\uFF1A\u9884\u8BA2\u3001\u4E0B\u8F7D\u3001\u4F53\u9A8C"
1843
+ }
1844
+ ],
1845
+ "narrative": "\u60C5\u7EEA\u7EBF",
1846
+ "pages": {
1847
+ "min": 15,
1848
+ "max": 20
1849
+ },
1850
+ "researchFocus": [
1851
+ "\u540C\u7C7B\u4EA7\u54C1\u75DB\u70B9\u4E0E\u5DEE\u8BC4",
1852
+ "\u7ADE\u54C1\u53C2\u6570\u5BF9\u6BD4",
1853
+ "\u7ADE\u54C1\u5B9A\u4EF7\u4E0E\u4E0A\u5E02\u8282\u594F",
1854
+ "\u53EF\u5F15\u7528\u7684\u7528\u6237\u573A\u666F"
1855
+ ],
1856
+ "motion": {
1857
+ "delivery": "presented",
1858
+ "keywords": [
1859
+ "\u53D1\u5E03\u4F1A",
1860
+ "launch",
1861
+ "keynote"
1862
+ ]
1863
+ }
1864
+ },
1865
+ {
1866
+ "id": "\u57F9\u8BAD",
1867
+ "tone": [
1868
+ "\u6559\u5B66",
1869
+ "\u6E05\u6670",
1870
+ "\u53EF\u64CD\u4F5C"
1871
+ ],
1872
+ "density": [
1873
+ "\u4E2D",
1874
+ "\u5206\u6A21\u5757"
1875
+ ],
1876
+ "requiredSections": [
1877
+ {
1878
+ "terms": [
1879
+ "Objectives",
1880
+ "\u5B66\u4E60\u76EE\u6807"
1881
+ ],
1882
+ "content": "\u5B66\u5B8C\u80FD\u505A\u5230\u4EC0\u4E48\uFF0C\u53EF\u68C0\u9A8C"
1883
+ },
1884
+ {
1885
+ "terms": [
1886
+ "Module",
1887
+ "\u8BFE\u7A0B\u6A21\u5757"
1888
+ ],
1889
+ "content": "\u6A21\u5757\u5212\u5206\u4E0E\u6BCF\u5757\u8981\u70B9\uFF1B\u5B9C \u5206\u5C42\u6216\u6B65\u9AA4"
1890
+ },
1891
+ {
1892
+ "terms": [
1893
+ "\u7EC3\u4E60",
1894
+ "\u793A\u4F8B",
1895
+ "Exercise"
1896
+ ],
1897
+ "content": "\u53EF\u7167\u505A\u7684\u793A\u4F8B\u6216\u7EC3\u4E60\u9898"
1898
+ },
1899
+ {
1900
+ "terms": [
1901
+ "Quiz",
1902
+ "\u968F\u5802\u6D4B\u9A8C"
1903
+ ],
1904
+ "content": "\u68C0\u9A8C\u7406\u89E3\u7684\u9898\u76EE\u4E0E\u7B54\u6848"
1905
+ },
1906
+ {
1907
+ "terms": [
1908
+ "Summary",
1909
+ "\u5C0F\u7ED3"
1910
+ ],
1911
+ "content": "\u8981\u70B9\u56DE\u987E\u4E0E\u8BFE\u540E\u884C\u52A8"
1912
+ }
1913
+ ],
1914
+ "narrative": "\u7531\u6D45\u5165\u6DF1",
1915
+ "pages": {
1916
+ "min": 8,
1917
+ "max": 15
1918
+ },
1919
+ "researchFocus": [
1920
+ "\u6743\u5A01\u6559\u7A0B\u4E0E\u6807\u51C6",
1921
+ "\u5206\u6B65\u64CD\u4F5C\u793A\u4F8B",
1922
+ "\u5E38\u89C1\u9519\u8BEF\u4E0E\u6392\u9519",
1923
+ "\u7EC3\u4E60\u9898\u4E0E\u8003\u70B9"
1924
+ ],
1925
+ "motion": {
1926
+ "delivery": "read",
1927
+ "keywords": [
1928
+ "\u57F9\u8BAD",
1929
+ "training"
1930
+ ]
1931
+ }
1932
+ },
1933
+ {
1934
+ "id": "\u5927\u4F1A",
1935
+ "subtitle": "talk",
1936
+ "tone": [
1937
+ "\u89C2\u70B9\u9C9C\u660E",
1938
+ "\u542F\u53D1",
1939
+ "\u8BB0\u5FC6\u70B9"
1940
+ ],
1941
+ "density": [
1942
+ "\u7A00\u758F",
1943
+ "\u4E00\u56FE\u4E00\u53E5"
1944
+ ],
1945
+ "requiredSections": [
1946
+ {
1947
+ "terms": [
1948
+ "\u5F00\u573A\u94A9\u5B50"
1949
+ ],
1950
+ "content": "\u53CD\u5E38\u8BC6\u7684\u95EE\u9898\u3001\u6570\u636E\u6216\u6545\u4E8B\u5F00\u573A"
1951
+ },
1952
+ {
1953
+ "terms": [
1954
+ "\u6838\u5FC3\u8BBA\u70B9"
1955
+ ],
1956
+ "content": "\u4E00\u53E5\u8BDD\u89C2\u70B9\uFF0C\u5168\u573A\u53EA\u6B64\u4E00\u4E2A"
1957
+ },
1958
+ {
1959
+ "terms": [
1960
+ "\u8BBA\u636E",
1961
+ "\u6848\u4F8B"
1962
+ ],
1963
+ "content": "\u652F\u6491\u89C2\u70B9\u7684\u6848\u4F8B\u4E0E\u6570\u636E\uFF0C\u6CE8\u660E\u51FA\u5904"
1964
+ },
1965
+ {
1966
+ "terms": [
1967
+ "\u53CD\u76F4\u89C9\u6D1E\u5BDF"
1968
+ ],
1969
+ "content": "\u4E0E\u542C\u4F17\u76F4\u89C9\u76F8\u53CD\u7684\u53D1\u73B0\u53CA\u5176\u539F\u56E0"
1970
+ },
1971
+ {
1972
+ "terms": [
1973
+ "\u6536\u675F\u8BB0\u5FC6\u70B9"
1974
+ ],
1975
+ "content": "\u4E00\u53E5\u53EF\u88AB\u590D\u8FF0\u7684\u8BDD\u6536\u5C3E"
1976
+ }
1977
+ ],
1978
+ "narrative": "\u60C5\u7EEA\u7EBF / SCQA",
1979
+ "pages": {
1980
+ "min": 15,
1981
+ "max": 20
1982
+ },
1983
+ "researchFocus": [
1984
+ "\u53CD\u76F4\u89C9\u6570\u636E\u4E0E\u7814\u7A76",
1985
+ "\u4E89\u8BAE\u89C2\u70B9\u4E0E\u53CD\u4F8B",
1986
+ "\u4E00\u624B\u6848\u4F8B/\u5931\u8D25\u6559\u8BAD",
1987
+ "\u53EF\u5F15\u7528\u91D1\u53E5"
1988
+ ],
1989
+ "motion": {
1990
+ "delivery": "presented",
1991
+ "keywords": [
1992
+ "\u5927\u4F1A",
1993
+ "conference",
1994
+ "talk"
1995
+ ]
1996
+ }
1997
+ },
1998
+ {
1999
+ "id": "\u5DE5\u4F5C\u6C47\u62A5",
2000
+ "subtitle": "\u5468\u6708\u5B63\u62A5",
2001
+ "tone": [
2002
+ "\u5BA2\u89C2",
2003
+ "\u7B80\u6D01",
2004
+ "\u7ED3\u8BBA\u5148\u884C"
2005
+ ],
2006
+ "density": [
2007
+ "\u4E2D"
2008
+ ],
2009
+ "requiredSections": [
2010
+ {
2011
+ "terms": [
2012
+ "\u6982\u8981"
2013
+ ],
2014
+ "content": "\u672C\u671F\u7ED3\u8BBA\u4E00\u53E5\u8BDD + 2\u20133 \u4E2A\u5173\u952E\u6570"
2015
+ },
2016
+ {
2017
+ "terms": [
2018
+ "\u8FDB\u5C55",
2019
+ "\u6210\u679C"
2020
+ ],
2021
+ "content": "\u5B8C\u6210\u4E8B\u9879\u4E0E\u4EA7\u51FA\uFF0C\u5BF9\u7167\u8BA1\u5212"
2022
+ },
2023
+ {
2024
+ "terms": [
2025
+ "\u6570\u636E\u6307\u6807"
2026
+ ],
2027
+ "content": "\u5173\u952E\u6307\u6807\u73AF\u6BD4/\u540C\u6BD4\uFF0C\u53E3\u5F84\u4E00\u81F4\uFF1B\u5B9C \u6298\u7EBF\u6216\u67F1\u72B6"
2028
+ },
2029
+ {
2030
+ "terms": [
2031
+ "\u95EE\u9898"
2032
+ ],
2033
+ "content": "\u672A\u8FBE\u6210\u9879\u3001\u539F\u56E0\u3001\u9700\u8981\u7684\u652F\u6301"
2034
+ },
2035
+ {
2036
+ "terms": [
2037
+ "\u4E0B\u4E00\u6B65\u8BA1\u5212"
2038
+ ],
2039
+ "content": "\u4E0B\u671F\u76EE\u6807\u3001\u5173\u952E\u52A8\u4F5C\u3001\u65F6\u95F4\u70B9"
2040
+ }
2041
+ ],
2042
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2043
+ "pages": {
2044
+ "min": 5,
2045
+ "max": 10
2046
+ },
2047
+ "researchFocus": [
2048
+ "\u884C\u4E1A\u57FA\u51C6\u4E0E\u540C\u884C\u6570\u636E",
2049
+ "\u6307\u6807\u53E3\u5F84\u5B9A\u4E49",
2050
+ "\u5916\u90E8\u73AF\u5883\u53D8\u5316"
2051
+ ],
2052
+ "motion": {
2053
+ "delivery": "read",
2054
+ "keywords": [
2055
+ "\u6C47\u62A5",
2056
+ "\u5468\u62A5",
2057
+ "\u6708\u62A5"
2058
+ ]
2059
+ }
2060
+ },
2061
+ {
2062
+ "id": "\u8FF0\u804C/\u664B\u5347",
2063
+ "tone": [
2064
+ "\u81EA\u4FE1",
2065
+ "\u6709\u636E",
2066
+ "\u4E2A\u4EBA\u89C6\u89D2"
2067
+ ],
2068
+ "density": [
2069
+ "\u4E2D"
2070
+ ],
2071
+ "requiredSections": [
2072
+ {
2073
+ "terms": [
2074
+ "\u804C\u8D23\u6982\u8FF0"
2075
+ ],
2076
+ "content": "\u5C97\u4F4D\u804C\u8D23\u4E0E\u8D1F\u8D23\u8303\u56F4"
2077
+ },
2078
+ {
2079
+ "terms": [
2080
+ "\u5173\u952E\u6210\u679C",
2081
+ "STAR"
2082
+ ],
2083
+ "content": "2\u20133 \u4E2A\u4EE3\u8868\u6027\u6210\u679C\uFF0C\u6309\u60C5\u5883-\u4EFB\u52A1-\u884C\u52A8-\u7ED3\u679C\u5199"
2084
+ },
2085
+ {
2086
+ "terms": [
2087
+ "\u6570\u636E\u4F50\u8BC1"
2088
+ ],
2089
+ "content": "\u6210\u679C\u7684\u91CF\u5316\u6570\u636E\u4E0E\u5BF9\u6BD4\u57FA\u51C6"
2090
+ },
2091
+ {
2092
+ "terms": [
2093
+ "\u80FD\u529B\u6210\u957F"
2094
+ ],
2095
+ "content": "\u76F8\u6BD4\u4E0A\u671F\u65B0\u589E\u7684\u80FD\u529B\uFF0C\u914D\u4E8B\u4F8B"
2096
+ },
2097
+ {
2098
+ "terms": [
2099
+ "\u89C4\u5212"
2100
+ ],
2101
+ "content": "\u4E0B\u4E00\u9636\u6BB5\u76EE\u6807\u4E0E\u8BA1\u5212"
2102
+ }
2103
+ ],
2104
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2105
+ "pages": {
2106
+ "min": 8,
2107
+ "max": 12
2108
+ },
2109
+ "researchFocus": [
2110
+ "\u540C\u5C97\u4F4D\u80FD\u529B\u6A21\u578B",
2111
+ "\u884C\u4E1A\u5BF9\u6807\u6570\u636E",
2112
+ "\u8BE5\u9886\u57DF\u516C\u8BA4\u7684\u6210\u679C\u8861\u91CF\u53E3\u5F84"
2113
+ ],
2114
+ "motion": {
2115
+ "delivery": "read",
2116
+ "keywords": [
2117
+ "\u8FF0\u804C"
2118
+ ]
2119
+ }
2120
+ },
2121
+ {
2122
+ "id": "\u9879\u76EE\u7ACB\u9879/\u8BC4\u5BA1",
2123
+ "tone": [
2124
+ "\u4E25\u8C28",
2125
+ "\u8BF4\u670D"
2126
+ ],
2127
+ "density": [
2128
+ "\u4E2D\u504F\u5BC6"
2129
+ ],
2130
+ "requiredSections": [
2131
+ {
2132
+ "terms": [
2133
+ "\u80CC\u666F",
2134
+ "\u75DB\u70B9"
2135
+ ],
2136
+ "content": "\u4E3A\u4EC0\u4E48\u8981\u505A\uFF1A\u73B0\u72B6\u95EE\u9898\u4E0E\u4E0D\u505A\u7684\u4EE3\u4EF7"
2137
+ },
2138
+ {
2139
+ "terms": [
2140
+ "\u76EE\u6807"
2141
+ ],
2142
+ "content": "\u53EF\u8861\u91CF\u7684\u76EE\u6807\u4E0E\u9A8C\u6536\u6807\u51C6"
2143
+ },
2144
+ {
2145
+ "terms": [
2146
+ "\u65B9\u6848"
2147
+ ],
2148
+ "content": "\u65B9\u6848\u8981\u70B9\u4E0E\u5907\u9009\u65B9\u6848\u5BF9\u6BD4\uFF1B\u5B9C \u5BF9\u6BD4\u8868"
2149
+ },
2150
+ {
2151
+ "terms": [
2152
+ "\u8BA1\u5212",
2153
+ "\u91CC\u7A0B\u7891"
2154
+ ],
2155
+ "content": "\u9636\u6BB5\u5212\u5206\u3001\u91CC\u7A0B\u7891\u3001\u65F6\u95F4\uFF1B\u5B9C \u65F6\u95F4\u8F74"
2156
+ },
2157
+ {
2158
+ "terms": [
2159
+ "\u8D44\u6E90",
2160
+ "\u9884\u7B97"
2161
+ ],
2162
+ "content": "\u4EBA\u529B\u3001\u9884\u7B97\u3001\u5916\u90E8\u4F9D\u8D56\uFF1B\u5B9C \u8868\u683C"
2163
+ },
2164
+ {
2165
+ "terms": [
2166
+ "\u98CE\u9669"
2167
+ ],
2168
+ "content": "\u4E3B\u8981\u98CE\u9669\u3001\u6982\u7387\u5F71\u54CD\u3001\u5E94\u5BF9\u63AA\u65BD\uFF1B\u5B9C \u77E9\u9635"
2169
+ }
2170
+ ],
2171
+ "narrative": "\u95EE\u9898-\u65B9\u6848",
2172
+ "pages": {
2173
+ "min": 8,
2174
+ "max": 14
2175
+ },
2176
+ "researchFocus": [
2177
+ "\u540C\u7C7B\u9879\u76EE\u6295\u5165\u4EA7\u51FA",
2178
+ "\u6280\u672F\u65B9\u6848\u4E0E\u9009\u578B\u5BF9\u6BD4",
2179
+ "\u5408\u89C4\u4E0E\u98CE\u9669\u6848\u4F8B",
2180
+ "\u4EBA\u529B/\u9884\u7B97\u57FA\u51C6"
2181
+ ],
2182
+ "motion": {
2183
+ "delivery": "read",
2184
+ "keywords": [
2185
+ "\u8BC4\u5BA1",
2186
+ "review"
2187
+ ]
2188
+ }
2189
+ },
2190
+ {
2191
+ "id": "\u5E74\u5EA6\u603B\u7ED3/\u5E74\u4F1A",
2192
+ "tone": [
2193
+ "\u632F\u594B",
2194
+ "\u4EEA\u5F0F\u611F"
2195
+ ],
2196
+ "density": [
2197
+ "\u7A00\u758F",
2198
+ "\u5927\u89C6\u89C9"
2199
+ ],
2200
+ "requiredSections": [
2201
+ {
2202
+ "terms": [
2203
+ "\u56DE\u987E",
2204
+ "\u9AD8\u5149"
2205
+ ],
2206
+ "content": "\u5E74\u5EA6\u9AD8\u5149\u65F6\u523B\uFF0C\u914D\u753B\u9762"
2207
+ },
2208
+ {
2209
+ "terms": [
2210
+ "\u5173\u952E\u6570\u636E"
2211
+ ],
2212
+ "content": "\u5168\u5E74\u6838\u5FC3\u6570\u636E\u4E0E\u589E\u957F\uFF1B\u5B9C \u5927\u6570\u5B57"
2213
+ },
2214
+ {
2215
+ "terms": [
2216
+ "\u91CC\u7A0B\u7891"
2217
+ ],
2218
+ "content": "\u6309\u65F6\u95F4\u6392\u5217\u7684\u5173\u952E\u8282\u70B9\uFF1B\u5B9C \u65F6\u95F4\u8F74"
2219
+ },
2220
+ {
2221
+ "terms": [
2222
+ "\u56E2\u961F"
2223
+ ],
2224
+ "content": "\u81F4\u8C22\u7684\u56E2\u961F\u4E0E\u4EBA\u7269\uFF0C\u914D\u6545\u4E8B"
2225
+ },
2226
+ {
2227
+ "terms": [
2228
+ "\u6765\u5E74\u5C55\u671B"
2229
+ ],
2230
+ "content": "\u6765\u5E74\u76EE\u6807\u4E0E\u65B9\u5411"
2231
+ }
2232
+ ],
2233
+ "narrative": "\u60C5\u7EEA\u7EBF",
2234
+ "pages": {
2235
+ "min": 12,
2236
+ "max": 20
2237
+ },
2238
+ "researchFocus": [
2239
+ "\u884C\u4E1A\u5E74\u5EA6\u5927\u4E8B",
2240
+ "\u540C\u884C\u91CC\u7A0B\u7891\u5BF9\u7167",
2241
+ "\u6765\u5E74\u8D8B\u52BF\u9884\u6D4B"
2242
+ ]
2243
+ },
2244
+ {
2245
+ "id": "\u5546\u4E1A\u8BA1\u5212\u4E66",
2246
+ "subtitle": "BP",
2247
+ "tone": [
2248
+ "\u81EA\u4FE1",
2249
+ "\u5168\u9762",
2250
+ "\u6570\u636E\u652F\u6491"
2251
+ ],
2252
+ "density": [
2253
+ "\u4E2D\u504F\u5BC6"
2254
+ ],
2255
+ "requiredSections": [
2256
+ {
2257
+ "terms": [
2258
+ "\u95EE\u9898"
2259
+ ],
2260
+ "content": "\u76EE\u6807\u7528\u6237\u7684\u75DB\u70B9\u4E0E\u89C4\u6A21"
2261
+ },
2262
+ {
2263
+ "terms": [
2264
+ "\u65B9\u6848"
2265
+ ],
2266
+ "content": "\u4EA7\u54C1\u5982\u4F55\u89E3\u51B3\uFF0C\u4E3A\u4EC0\u4E48\u662F\u73B0\u5728"
2267
+ },
2268
+ {
2269
+ "terms": [
2270
+ "\u5E02\u573A"
2271
+ ],
2272
+ "content": "\u5E02\u573A\u89C4\u6A21\u5206\u5C42\u4E0E\u589E\u901F\uFF0C\u6CE8\u660E\u6765\u6E90\uFF1B\u5B9C \u67F1\u72B6\u56FE"
2273
+ },
2274
+ {
2275
+ "terms": [
2276
+ "\u4EA7\u54C1"
2277
+ ],
2278
+ "content": "\u4EA7\u54C1\u5F62\u6001\u3001\u6838\u5FC3\u529F\u80FD\u3001\u5F53\u524D\u8FDB\u5C55"
2279
+ },
2280
+ {
2281
+ "terms": [
2282
+ "\u5546\u4E1A\u6A21\u5F0F"
2283
+ ],
2284
+ "content": "\u600E\u4E48\u8D5A\u94B1\uFF1A\u6536\u5165\u6765\u6E90\u3001\u5B9A\u4EF7\u3001\u5355\u4F4D\u7ECF\u6D4E"
2285
+ },
2286
+ {
2287
+ "terms": [
2288
+ "\u7ADE\u4E89"
2289
+ ],
2290
+ "content": "\u7ADE\u54C1\u683C\u5C40\u4E0E\u5DEE\u5F02\u5316\uFF1B\u5B9C \u77E9\u9635\u6216\u5BF9\u6BD4\u8868"
2291
+ },
2292
+ {
2293
+ "terms": [
2294
+ "\u8D22\u52A1\u9884\u6D4B"
2295
+ ],
2296
+ "content": "\u672A\u6765 3\u20135 \u5E74\u6536\u5165\u3001\u6210\u672C\u3001\u5229\u6DA6\u9884\u6D4B\u4E0E\u5173\u952E\u5047\u8BBE\uFF1B\u5B9C \u8868\u683C"
2297
+ },
2298
+ {
2299
+ "terms": [
2300
+ "\u56E2\u961F"
2301
+ ],
2302
+ "content": "\u6838\u5FC3\u56E2\u961F\u4E0E\u76F8\u5173\u7ECF\u5386"
2303
+ },
2304
+ {
2305
+ "terms": [
2306
+ "\u878D\u8D44"
2307
+ ],
2308
+ "key": true,
2309
+ "content": "\u878D\u8D44\u91D1\u989D\u3001\u51FA\u8BA9\u6BD4\u4F8B\u3001\u8D44\u91D1\u7528\u9014\u5360\u6BD4"
2310
+ }
2311
+ ],
2312
+ "narrative": "SCQA",
2313
+ "pages": {
2314
+ "min": 15,
2315
+ "max": 25
2316
+ },
2317
+ "researchFocus": [
2318
+ "\u5E02\u573A\u89C4\u6A21\u4E0E\u5206\u5C42",
2319
+ "\u7ADE\u4E89\u683C\u5C40\u4E0E\u4EFD\u989D",
2320
+ "\u5546\u4E1A\u6A21\u5F0F\u4E0E\u5355\u4F4D\u7ECF\u6D4E",
2321
+ "\u53EF\u6BD4\u516C\u53F8\u8D22\u52A1\u4E0E\u878D\u8D44"
2322
+ ]
2323
+ },
2324
+ {
2325
+ "id": "\u6295\u6807",
2326
+ "subtitle": "\u62DB\u6295\u6807",
2327
+ "tone": [
2328
+ "\u6B63\u5F0F",
2329
+ "\u8981\u7D20\u9F50\u5168",
2330
+ "\u5F97\u5206\u5BFC\u5411"
2331
+ ],
2332
+ "density": [
2333
+ "\u5BC6"
2334
+ ],
2335
+ "requiredSections": [
2336
+ {
2337
+ "terms": [
2338
+ "\u9700\u6C42\u7406\u89E3"
2339
+ ],
2340
+ "content": "\u5BF9\u62DB\u6807\u9700\u6C42\u4E0E\u8BC4\u5206\u70B9\u7684\u9010\u6761\u56DE\u5E94"
2341
+ },
2342
+ {
2343
+ "terms": [
2344
+ "\u65B9\u6848"
2345
+ ],
2346
+ "content": "\u6280\u672F\u65B9\u6848\u8981\u70B9\uFF0C\u5BF9\u5E94\u9700\u6C42\u6761\u76EE"
2347
+ },
2348
+ {
2349
+ "terms": [
2350
+ "\u56E2\u961F",
2351
+ "\u8D44\u8D28"
2352
+ ],
2353
+ "content": "\u9879\u76EE\u56E2\u961F\u914D\u7F6E\u4E0E\u8D44\u8D28\u8BC1\u4E66"
2354
+ },
2355
+ {
2356
+ "terms": [
2357
+ "\u6848\u4F8B\u4E1A\u7EE9"
2358
+ ],
2359
+ "content": "\u540C\u7C7B\u9879\u76EE\u4E1A\u7EE9\u3001\u5BA2\u6237\u3001\u89C4\u6A21"
2360
+ },
2361
+ {
2362
+ "terms": [
2363
+ "\u5B9E\u65BD\u8BA1\u5212"
2364
+ ],
2365
+ "content": "\u9636\u6BB5\u3001\u91CC\u7A0B\u7891\u3001\u5DE5\u671F\uFF1B\u5B9C \u65F6\u95F4\u8F74"
2366
+ },
2367
+ {
2368
+ "terms": [
2369
+ "\u62A5\u4EF7"
2370
+ ],
2371
+ "key": true,
2372
+ "content": "\u62A5\u4EF7\u603B\u989D\u4E0E\u5206\u9879\u660E\u7EC6\u3001\u4ED8\u6B3E\u65B9\u5F0F\uFF1B\u5B9C \u8868\u683C"
2373
+ },
2374
+ {
2375
+ "terms": [
2376
+ "\u552E\u540E"
2377
+ ],
2378
+ "content": "\u552E\u540E\u670D\u52A1\u5185\u5BB9\u3001\u54CD\u5E94\u65F6\u95F4\u3001\u8D28\u4FDD\u671F"
2379
+ }
2380
+ ],
2381
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2382
+ "pages": {
2383
+ "min": 15,
2384
+ "max": 30
2385
+ },
2386
+ "researchFocus": [
2387
+ "\u62DB\u6807\u65B9\u80CC\u666F\u4E0E\u65E2\u5F80\u91C7\u8D2D",
2388
+ "\u8BC4\u5206\u529E\u6CD5\u4E0E\u8D44\u8D28\u8981\u6C42",
2389
+ "\u540C\u7C7B\u4E2D\u6807\u516C\u544A\u4E0E\u62A5\u4EF7\u533A\u95F4",
2390
+ "\u6807\u6746\u6848\u4F8B"
2391
+ ]
2392
+ },
2393
+ {
2394
+ "id": "\u653F\u52A1/\u515A\u653F\u6C47\u62A5",
2395
+ "tone": [
2396
+ "\u4E25\u8083",
2397
+ "\u89C4\u8303"
2398
+ ],
2399
+ "density": [
2400
+ "\u4E2D\u504F\u5BC6"
2401
+ ],
2402
+ "requiredSections": [
2403
+ {
2404
+ "terms": [
2405
+ "\u80CC\u666F",
2406
+ "\u4F9D\u636E"
2407
+ ],
2408
+ "content": "\u653F\u7B56\u4F9D\u636E\u4E0E\u4E0A\u7EA7\u90E8\u7F72\uFF0C\u5F15\u539F\u6587\u51FA\u5904"
2409
+ },
2410
+ {
2411
+ "terms": [
2412
+ "\u5DE5\u4F5C\u4E3E\u63AA"
2413
+ ],
2414
+ "content": "\u91C7\u53D6\u7684\u4E3B\u8981\u63AA\u65BD\uFF0C\u5206\u6761\u5217\u51FA"
2415
+ },
2416
+ {
2417
+ "terms": [
2418
+ "\u6210\u6548\u6570\u636E"
2419
+ ],
2420
+ "content": "\u91CF\u5316\u6210\u6548\u4E0E\u540C\u6BD4\uFF0C\u6CE8\u660E\u53E3\u5F84\uFF1B\u5B9C \u8868\u683C"
2421
+ },
2422
+ {
2423
+ "terms": [
2424
+ "\u5B58\u5728\u95EE\u9898"
2425
+ ],
2426
+ "content": "\u5B58\u5728\u7684\u95EE\u9898\u4E0E\u539F\u56E0\u5206\u6790"
2427
+ },
2428
+ {
2429
+ "terms": [
2430
+ "\u4E0B\u6B65\u90E8\u7F72"
2431
+ ],
2432
+ "content": "\u4E0B\u4E00\u6B65\u5DE5\u4F5C\u5B89\u6392\u4E0E\u65F6\u95F4\u8282\u70B9"
2433
+ }
2434
+ ],
2435
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2436
+ "pages": {
2437
+ "min": 8,
2438
+ "max": 15
2439
+ },
2440
+ "researchFocus": [
2441
+ "\u653F\u7B56\u6587\u4EF6\u4E0E\u4F9D\u636E\u539F\u6587",
2442
+ "\u4E0A\u7EA7\u90E8\u7F72\u53E3\u5F84",
2443
+ "\u540C\u7EA7\u5730\u533A\u505A\u6CD5",
2444
+ "\u516C\u5F00\u7EDF\u8BA1\u6570\u636E"
2445
+ ],
2446
+ "motion": {
2447
+ "delivery": "read",
2448
+ "keywords": [
2449
+ "\u6C47\u62A5"
2450
+ ]
2451
+ }
2452
+ },
2453
+ {
2454
+ "id": "\u516C\u53F8/\u4EA7\u54C1\u4ECB\u7ECD",
2455
+ "tone": [
2456
+ "\u54C1\u724C",
2457
+ "\u81EA\u4FE1",
2458
+ "\u5229\u76CA\u5BFC\u5411"
2459
+ ],
2460
+ "density": [
2461
+ "\u4E2D",
2462
+ "\u91CD\u89C6\u89C9"
2463
+ ],
2464
+ "requiredSections": [
2465
+ {
2466
+ "terms": [
2467
+ "\u516C\u53F8",
2468
+ "\u613F\u666F"
2469
+ ],
2470
+ "content": "\u516C\u53F8\u662F\u8C01\u3001\u613F\u666F\u4E00\u53E5\u8BDD\u3001\u5173\u952E\u80CC\u666F\u6570\u636E"
2471
+ },
2472
+ {
2473
+ "terms": [
2474
+ "\u6838\u5FC3\u80FD\u529B"
2475
+ ],
2476
+ "content": "\u6838\u5FC3\u6280\u672F\u6216\u80FD\u529B\uFF0C\u4E0E\u540C\u884C\u7684\u5DEE\u5F02"
2477
+ },
2478
+ {
2479
+ "terms": [
2480
+ "\u4EA7\u54C1",
2481
+ "\u65B9\u6848"
2482
+ ],
2483
+ "content": "\u4EA7\u54C1\u6216\u65B9\u6848\u7EC4\u5408\u4E0E\u9002\u7528\u573A\u666F"
2484
+ },
2485
+ {
2486
+ "terms": [
2487
+ "\u6807\u6746\u6848\u4F8B"
2488
+ ],
2489
+ "content": "\u6807\u6746\u5BA2\u6237\u4E0E\u91CF\u5316\u6210\u679C"
2490
+ },
2491
+ {
2492
+ "terms": [
2493
+ "\u6570\u636E"
2494
+ ],
2495
+ "content": "\u89C4\u6A21\u3001\u5BA2\u6237\u6570\u3001\u589E\u957F\u7B49\u53EF\u516C\u5F00\u6570\u636E\uFF1B\u5B9C \u5927\u6570\u5B57"
2496
+ },
2497
+ {
2498
+ "terms": [
2499
+ "\u5408\u4F5C\u9080\u8BF7"
2500
+ ],
2501
+ "content": "\u5408\u4F5C\u65B9\u5F0F\u4E0E\u8054\u7CFB\u65B9\u5F0F"
2502
+ }
2503
+ ],
2504
+ "narrative": "\u95EE\u9898-\u65B9\u6848",
2505
+ "pages": {
2506
+ "min": 8,
2507
+ "max": 15
2508
+ },
2509
+ "researchFocus": [
2510
+ "\u884C\u4E1A\u5730\u4F4D\u4E0E\u7B2C\u4E09\u65B9\u62A5\u544A",
2511
+ "\u7ADE\u54C1\u5BF9\u6BD4",
2512
+ "\u53EF\u516C\u5F00\u5F15\u7528\u7684\u5BA2\u6237\u6848\u4F8B",
2513
+ "\u884C\u4E1A\u8D8B\u52BF"
2514
+ ]
2515
+ },
2516
+ {
2517
+ "id": "\u4EA7\u54C1\u8C03\u7814",
2518
+ "subtitle": "\u7ADE\u54C1\u5206\u6790 \u5E02\u573A\u7814\u7A76",
2519
+ "tone": [
2520
+ "\u5BA2\u89C2",
2521
+ "\u6709\u636E",
2522
+ "\u4E0D\u66FF\u7ED3\u8BBA\u4E0B\u5224\u65AD"
2523
+ ],
2524
+ "density": [
2525
+ "\u4E2D"
2526
+ ],
2527
+ "requiredSections": [
2528
+ {
2529
+ "terms": [
2530
+ "\u5E02\u573A\u89C4\u6A21"
2531
+ ],
2532
+ "content": "\u5E02\u573A\u89C4\u6A21\u4E0E\u589E\u901F\uFF0C\u6CE8\u660E\u6765\u6E90\u5E74\u4EFD\uFF1B\u5B9C \u67F1\u72B6\u56FE"
2533
+ },
2534
+ {
2535
+ "terms": [
2536
+ "\u7ADE\u54C1\u683C\u5C40"
2537
+ ],
2538
+ "content": "\u4E3B\u8981\u7ADE\u54C1\u7684\u5B9A\u4F4D\u3001\u529F\u80FD\u3001\u5B9A\u4EF7\u3001\u4EFD\u989D\uFF1B\u5B9C \u5BF9\u6BD4\u8868"
2539
+ },
2540
+ {
2541
+ "terms": [
2542
+ "\u7528\u6237\u9700\u6C42"
2543
+ ],
2544
+ "content": "\u7528\u6237\u7684\u6838\u5FC3\u9700\u6C42\u4E0E\u75DB\u70B9\uFF0C\u5F15\u7528\u8BC4\u4EF7\u539F\u8BDD"
2545
+ },
2546
+ {
2547
+ "terms": [
2548
+ "\u5DEE\u8DDD\u4E0E\u673A\u4F1A"
2549
+ ],
2550
+ "content": "\u7ADE\u54C1\u672A\u6EE1\u8DB3\u7684\u9700\u6C42\u4E0E\u6211\u4EEC\u7684\u673A\u4F1A"
2551
+ },
2552
+ {
2553
+ "terms": [
2554
+ "\u7ED3\u8BBA\u5EFA\u8BAE"
2555
+ ],
2556
+ "content": "\u57FA\u4E8E\u4E0A\u8FF0\u4E8B\u5B9E\u7684\u5224\u65AD\u4E0E\u5EFA\u8BAE"
2557
+ }
2558
+ ],
2559
+ "narrative": "\u5148\u6446\u4E8B\u5B9E\uFF0C\u518D\u7ED9\u5224\u65AD",
2560
+ "pages": {
2561
+ "min": 10,
2562
+ "max": 15
2563
+ },
2564
+ "researchFocus": [
2565
+ "\u5E02\u573A\u89C4\u6A21\u4E0E\u589E\u901F",
2566
+ "\u7ADE\u54C1\u529F\u80FD\u4E0E\u5B9A\u4EF7\u5BF9\u6BD4",
2567
+ "\u7528\u6237\u8BC4\u4EF7\u4E0E\u5DEE\u8BC4",
2568
+ "\u6E20\u9053\u4E0E\u4EFD\u989D\u6570\u636E"
2569
+ ],
2570
+ "motion": {
2571
+ "delivery": "read",
2572
+ "keywords": [
2573
+ "\u8C03\u7814",
2574
+ "research"
2575
+ ]
2576
+ }
2577
+ },
2578
+ {
2579
+ "id": "\u4F1A\u8BAE\u7EAA\u8981",
2580
+ "subtitle": "\u4F1A\u540E\u540C\u6B65",
2581
+ "tone": [
2582
+ "\u7B80\u6D01",
2583
+ "\u53EF\u6267\u884C",
2584
+ "\u4E0D\u590D\u8FF0\u8FC7\u7A0B"
2585
+ ],
2586
+ "density": [
2587
+ "\u4F4E"
2588
+ ],
2589
+ "requiredSections": [
2590
+ {
2591
+ "terms": [
2592
+ "\u8BAE\u9898"
2593
+ ],
2594
+ "content": "\u8BA8\u8BBA\u7684\u8BAE\u9898\u6E05\u5355"
2595
+ },
2596
+ {
2597
+ "terms": [
2598
+ "\u7ED3\u8BBA"
2599
+ ],
2600
+ "content": "\u6BCF\u4E2A\u8BAE\u9898\u7684\u7ED3\u8BBA"
2601
+ },
2602
+ {
2603
+ "terms": [
2604
+ "\u5F85\u529E"
2605
+ ],
2606
+ "content": "\u5F85\u529E\u4E8B\u9879\u3001\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u65F6\u95F4\uFF1B\u5B9C \u8868\u683C"
2607
+ },
2608
+ {
2609
+ "terms": [
2610
+ "\u5F85\u51B3\u4E8B\u9879"
2611
+ ],
2612
+ "content": "\u672A\u51B3\u4E8B\u9879\u4E0E\u5F85\u51B3\u4EBA"
2613
+ }
2614
+ ],
2615
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2616
+ "pages": {
2617
+ "min": 4,
2618
+ "max": 8
2619
+ },
2620
+ "researchFocus": [
2621
+ "\u8BAE\u9898\u6D89\u53CA\u7684\u5916\u90E8\u4E8B\u5B9E",
2622
+ "\u76F8\u5173\u53E3\u5F84\u4E0E\u5B9A\u4E49",
2623
+ "\u540C\u7C7B\u51B3\u7B56\u7684\u516C\u5F00\u5148\u4F8B"
2624
+ ],
2625
+ "motion": {
2626
+ "delivery": "read",
2627
+ "keywords": [
2628
+ "\u7EAA\u8981",
2629
+ "minutes"
2630
+ ]
2631
+ }
2632
+ },
2633
+ {
2634
+ "id": "\u6570\u636E\u5206\u6790",
2635
+ "subtitle": "\u6570\u636E\u89E3\u8BFB \u6307\u6807\u590D\u76D8",
2636
+ "tone": [
2637
+ "\u7ED3\u8BBA\u5148\u884C",
2638
+ "\u53EF\u8FFD\u6EAF"
2639
+ ],
2640
+ "density": [
2641
+ "\u4E2D"
2642
+ ],
2643
+ "requiredSections": [
2644
+ {
2645
+ "terms": [
2646
+ "\u7ED3\u8BBA"
2647
+ ],
2648
+ "content": "\u4E00\u53E5\u8BDD\u7ED3\u8BBA\u4E0E\u5173\u952E\u6570\u5B57\uFF1B\u5B9C \u5927\u6570\u5B57"
2649
+ },
2650
+ {
2651
+ "terms": [
2652
+ "\u53E3\u5F84\u4E0E\u6570\u636E\u6E90"
2653
+ ],
2654
+ "content": "\u6307\u6807\u5B9A\u4E49\u3001\u6570\u636E\u6765\u6E90\u3001\u65F6\u95F4\u8303\u56F4"
2655
+ },
2656
+ {
2657
+ "terms": [
2658
+ "\u7EF4\u5EA6\u62C6\u89E3"
2659
+ ],
2660
+ "content": "\u6309\u7EF4\u5EA6\u62C6\u89E3\u7684\u5206\u5E03\u4E0E\u53D8\u5316\uFF1B\u5B9C \u67F1\u72B6\u6216\u6298\u7EBF"
2661
+ },
2662
+ {
2663
+ "terms": [
2664
+ "\u5F52\u56E0"
2665
+ ],
2666
+ "content": "\u53D8\u5316\u7684\u4E3B\u8981\u539F\u56E0\u53CA\u5176\u8D21\u732E"
2667
+ },
2668
+ {
2669
+ "terms": [
2670
+ "\u5EFA\u8BAE"
2671
+ ],
2672
+ "content": "\u57FA\u4E8E\u5F52\u56E0\u7684\u53EF\u6267\u884C\u5EFA\u8BAE"
2673
+ }
2674
+ ],
2675
+ "narrative": "\u7ED3\u8BBA\u5148\u884C",
2676
+ "pages": {
2677
+ "min": 6,
2678
+ "max": 10
2679
+ },
2680
+ "researchFocus": [
2681
+ "\u6307\u6807\u53E3\u5F84\u5B9A\u4E49",
2682
+ "\u884C\u4E1A\u57FA\u51C6\u503C",
2683
+ "\u540C\u7C7B\u5F52\u56E0\u6848\u4F8B\u4E0E\u65B9\u6CD5"
2684
+ ],
2685
+ "motion": {
2686
+ "delivery": "read",
2687
+ "keywords": [
2688
+ "\u5206\u6790",
2689
+ "analysis"
2690
+ ]
2691
+ }
2692
+ }
2693
+ ],
2694
+ "narratives": [
2695
+ {
2696
+ "deckType": "\u62A5\u544A/\u603B\u7ED3",
2697
+ "structure": "\u7ED3\u8BBA\u5148\u884C",
2698
+ "flow": [
2699
+ "\u7ED3\u8BBA",
2700
+ "\u8BC1\u636E",
2701
+ "\u7EC6\u8282",
2702
+ "\u4E0B\u4E00\u6B65"
2703
+ ]
2704
+ },
2705
+ {
2706
+ "deckType": "\u4EA7\u54C1/\u9500\u552E",
2707
+ "structure": "\u95EE\u9898-\u65B9\u6848",
2708
+ "flow": [
2709
+ "\u75DB\u70B9",
2710
+ "\u65B9\u6848",
2711
+ "\u4EF7\u503C",
2712
+ "\u4F50\u8BC1",
2713
+ "CTA"
2714
+ ]
2715
+ },
2716
+ {
2717
+ "deckType": "\u57F9\u8BAD/\u6559\u5B66",
2718
+ "structure": "\u7531\u6D45\u5165\u6DF1",
2719
+ "flow": [
2720
+ "\u57FA\u7840",
2721
+ "\u6838\u5FC3",
2722
+ "\u8FDB\u9636",
2723
+ "\u7EC3\u4E60"
2724
+ ]
2725
+ },
2726
+ {
2727
+ "deckType": "\u878D\u8D44/\u8DEF\u6F14",
2728
+ "structure": "SCQA",
2729
+ "flow": [
2730
+ "\u60C5\u5883",
2731
+ "\u51B2\u7A81",
2732
+ "\u95EE\u9898",
2733
+ "\u89E3\u7B54"
2734
+ ]
2735
+ },
2736
+ {
2737
+ "deckType": "\u6D3B\u52A8/\u5BA3\u4F20",
2738
+ "structure": "\u60C5\u7EEA\u7EBF",
2739
+ "flow": [
2740
+ "\u94A9\u5B50",
2741
+ "\u5171\u9E23",
2742
+ "\u9AD8\u6F6E",
2743
+ "\u884C\u52A8"
2744
+ ]
2745
+ }
2746
+ ],
2747
+ "styleBias": [
2748
+ {
2749
+ "tones": [
2750
+ "\u5F20\u529B",
2751
+ "\u60C5\u7EEA"
2752
+ ],
2753
+ "scenarios": [
2754
+ "pitch",
2755
+ "\u53D1\u5E03\u4F1A",
2756
+ "\u5927\u4F1A"
2757
+ ],
2758
+ "look": "\u6DF1\u8272\u9177\u70AB",
2759
+ "styles": [
2760
+ "cool",
2761
+ "apple",
2762
+ "black-gold"
2763
+ ]
2764
+ },
2765
+ {
2766
+ "tones": [
2767
+ "\u52A1\u5B9E"
2768
+ ],
2769
+ "scenarios": [
2770
+ "\u5185\u5BA1",
2771
+ "\u9500\u552E"
2772
+ ],
2773
+ "look": "\u767D\u5E95\u54A8\u8BE2",
2774
+ "styles": [
2775
+ "mckinsey"
2776
+ ]
2777
+ },
2778
+ {
2779
+ "tones": [
2780
+ "\u6E05\u6670"
2781
+ ],
2782
+ "scenarios": [
2783
+ "\u8BB2\u5EA7",
2784
+ "\u57F9\u8BAD"
2785
+ ],
2786
+ "look": "\u4E2D\u6027/\u6559\u80B2\u98CE",
2787
+ "styles": []
2788
+ }
2789
+ ],
2790
+ "motionExtraKeywords": {
2791
+ "presented": [
2792
+ "\u4E3B\u9898\u6F14\u8BB2"
2793
+ ],
2794
+ "read": [
2795
+ "\u62A5\u544A",
2796
+ "report",
2797
+ "\u8BB2\u4E49",
2798
+ "handout"
2799
+ ]
2800
+ }
2801
+ };
2802
+
2803
+ // packages/core/dist/document/deck-scenarios.js
2804
+ function motionKeywords(m) {
2805
+ const out = { presented: [], read: [] };
2806
+ const add = (d, k) => {
2807
+ if (!out[d].includes(k))
2808
+ out[d].push(k);
2809
+ };
2810
+ for (const s of m.scenarios)
2811
+ if (s.motion)
2812
+ for (const k of s.motion.keywords)
2813
+ add(s.motion.delivery, k);
2814
+ for (const d of ["presented", "read"])
2815
+ for (const k of m.motionExtraKeywords[d])
2816
+ add(d, k);
2817
+ return out;
2818
+ }
2819
+
560
2820
  // packages/core/dist/document/deck-motion.js
561
2821
  var ANIM_EFFECTS = [
562
2822
  "fade",
@@ -686,36 +2946,9 @@ function applyMotion(html, layoutId, preset) {
686
2946
  }
687
2947
  return out;
688
2948
  }
689
- var STAGE_SCENARIOS = [
690
- "pitch",
691
- "\u8DEF\u6F14",
692
- "\u53D1\u5E03\u4F1A",
693
- "launch",
694
- "keynote",
695
- "\u4E3B\u9898\u6F14\u8BB2",
696
- "\u5927\u4F1A",
697
- "conference",
698
- "\u8BB2\u5EA7",
699
- "lecture",
700
- "talk",
701
- "\u9500\u552E",
702
- "sales"
703
- ];
704
- var READ_SCENARIOS = [
705
- "\u5185\u5BA1",
706
- "\u8BC4\u5BA1",
707
- "review",
708
- "\u62A5\u544A",
709
- "\u6C47\u62A5",
710
- "report",
711
- "\u8BB2\u4E49",
712
- "handout",
713
- "\u57F9\u8BAD",
714
- "training",
715
- "\u8FF0\u804C",
716
- "\u5468\u62A5",
717
- "\u6708\u62A5"
718
- ];
2949
+ var MOTION_KEYWORDS = motionKeywords(SCENARIO_MATRIX);
2950
+ var STAGE_SCENARIOS = MOTION_KEYWORDS.presented;
2951
+ var READ_SCENARIOS = MOTION_KEYWORDS.read;
719
2952
  function motionForDeck(spec) {
720
2953
  if (typeof spec.motion === "boolean") {
721
2954
  return { on: spec.motion, reason: `motion: ${spec.motion} in the spec` };
@@ -759,6 +2992,8 @@ async function main() {
759
2992
  if (spec?.layoutPack !== void 0 && typeof spec.layoutPack !== "string") die('layoutPack must be a pack id or "none"', 64);
760
2993
  if (spec?.scenario !== void 0 && typeof spec.scenario !== "string") die('scenario must be a string (the confirmed scenario, e.g. "\u5185\u5BA1" or "\u53D1\u5E03\u4F1A")', 64);
761
2994
  if (spec?.motion !== void 0 && typeof spec.motion !== "boolean") die("motion must be true or false", 64);
2995
+ if (spec?.chartRender !== void 0 && !["svg", "native"].includes(spec.chartRender)) die('chartRender must be "svg" or "native"', 64);
2996
+ if (spec?.capacityCheck !== void 0 && !["strict", "error", "warn", "off"].includes(spec.capacityCheck)) die('capacityCheck must be "strict", "error", "warn" or "off"', 64);
762
2997
  if (spec?.decorRhythm !== void 0 && (spec.themeVersion !== 2 || !["auto", "steady"].includes(spec.decorRhythm))) {
763
2998
  die("decorRhythm must be auto|steady and requires themeVersion: 2", 64);
764
2999
  }
@@ -809,6 +3044,13 @@ async function main() {
809
3044
  }
810
3045
  }
811
3046
  let fromPack = 0, fromShared = 0;
3047
+ const peekLayout = (id) => {
3048
+ if (!/^[a-z0-9-]+$/i.test(id)) return null;
3049
+ const pp = packLayoutsDir ? (0, import_node_path.join)(packLayoutsDir, `${id}.tpl`) : null;
3050
+ if (pp && (0, import_node_fs.existsSync)(pp)) return (0, import_node_fs.readFileSync)(pp, "utf8");
3051
+ const p = (0, import_node_path.join)(layoutsDir, `${id}.tpl`);
3052
+ return (0, import_node_fs.existsSync)(p) ? (0, import_node_fs.readFileSync)(p, "utf8") : null;
3053
+ };
812
3054
  const readLayout = (id) => {
813
3055
  if (!/^[a-z0-9-]+$/i.test(id)) return null;
814
3056
  if (packLayoutsDir) {
@@ -832,8 +3074,35 @@ async function main() {
832
3074
  buildSpec = { ...spec, slides: planned.slides };
833
3075
  rhythmErrors = planned.errors;
834
3076
  }
835
- const { html, warnings, errors } = assembleDeck(buildSpec, readLayout);
3077
+ let capacityDoc = null;
3078
+ const capacityPath = (0, import_node_path.join)(layoutsDir, "capacity.json");
3079
+ if ((0, import_node_fs.existsSync)(capacityPath)) {
3080
+ try {
3081
+ capacityDoc = parseCapacityDoc(JSON.parse((0, import_node_fs.readFileSync)(capacityPath, "utf8")));
3082
+ } catch {
3083
+ }
3084
+ }
3085
+ const capCtx = { style: styleId ?? void 0, pack: layoutPackEntry?.id ?? null };
3086
+ const capOf2 = (id) => capacityDoc ? resolveLayoutCapacity(capacityDoc, id, capCtx) : null;
3087
+ const { html, warnings, errors } = assembleDeck(buildSpec, readLayout, { chartBoxes: (id) => capOf2(id)?.charts });
836
3088
  errors.push(...rhythmErrors);
3089
+ const capacityMode = spec?.capacityCheck ?? "error";
3090
+ let capacityChecked = 0;
3091
+ if (capacityMode !== "off" && capacityDoc) {
3092
+ for (const [i, slide] of (Array.isArray(spec?.slides) ? spec.slides : []).entries()) {
3093
+ const cap = typeof slide?.layout === "string" ? capOf2(slide.layout) : null;
3094
+ const tpl = cap ? peekLayout(slide.layout) : null;
3095
+ if (!cap || !tpl) continue;
3096
+ capacityChecked++;
3097
+ const r = checkSlideCapacity(slide, cap, templateDefaults(tpl));
3098
+ const at = `slide ${i + 1} (${slide.layout}): `;
3099
+ for (const f of r.warnings) warnings.push(at + f.message);
3100
+ for (const f of r.errors) {
3101
+ const fatal = capacityMode === "strict" || capacityMode === "error" && f.code !== "underfill";
3102
+ (fatal ? errors : warnings).push(at + f.message);
3103
+ }
3104
+ }
3105
+ }
837
3106
  if (profiles) {
838
3107
  for (const [i, slide] of (Array.isArray(spec?.slides) ? spec.slides : []).entries()) {
839
3108
  if (profiles.themeV2Layouts?.includes(slide?.layout) && spec?.themeVersion !== 2) {
@@ -852,7 +3121,7 @@ async function main() {
852
3121
  if (errors.length) {
853
3122
  for (const e of errors) process.stderr.write(`deck-build: ERROR ${e}
854
3123
  `);
855
- die(`${errors.length} spec error(s) \u2014 nothing written (see layouts/slots.json for each layout's slots + rows)`, 1);
3124
+ die(`${errors.length} spec error(s) \u2014 nothing written (see layouts/slots.json for each layout's slots + rows; \`deck-plan\` picks layouts that fit the content)`, 1);
856
3125
  }
857
3126
  const resolved = fromPack + fromShared;
858
3127
  if (packLayoutsDir && resolved > 0) {
@@ -897,6 +3166,7 @@ async function main() {
897
3166
  out: outPath,
898
3167
  slides: Array.isArray(spec?.slides) ? spec.slides.length : 0,
899
3168
  warnings,
3169
+ capacity: capacityDoc ? { checked: capacityChecked, mode: capacityMode } : { checked: 0, mode: "unavailable" },
900
3170
  motion: catalogEntry?.motion ? motion : { on: false, reason: "the family has no motion preset" },
901
3171
  next: `html2pptx ${outPath} out.pptx${styleId ? ` --style ${styleId}` : " --style <skin>"}${spec?.themeVersion === 2 ? " --theme-v2" : ""}${spec?.mediaStyle ? ` --media-style ${spec.mediaStyle}` : ""}`
902
3172
  }, null, 1) + "\n");