@openpresentation/opf 0.20.5 → 0.22.0

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 (61) hide show
  1. package/dist/browser.js +16 -16
  2. package/dist/{chunk-2M7U4WL3.js → chunk-23MOKE4B.js} +292 -104
  3. package/dist/{chunk-TCBEF5OY.js → chunk-2SENTA2R.js} +1 -1
  4. package/dist/{chunk-7PQZDC2H.js → chunk-3T7MNRBY.js} +1 -1
  5. package/dist/{chunk-VFMHM2IL.js → chunk-3UAVON3O.js} +28 -2
  6. package/dist/{chunk-IDMQWS6O.js → chunk-7XNAMCGT.js} +6 -6
  7. package/dist/{chunk-URUNMZFO.js → chunk-BULGAKBY.js} +2 -2
  8. package/dist/{chunk-WD4W7KKK.js → chunk-DY7H4RG6.js} +54 -11
  9. package/dist/{chunk-V66KJA3H.js → chunk-KUNXFN7B.js} +1 -1
  10. package/dist/{chunk-52GKVP3I.js → chunk-MAJHCGOW.js} +22 -12
  11. package/dist/{chunk-OIGCC6VA.js → chunk-MG34DCDB.js} +2 -2
  12. package/dist/{chunk-5SQ6CIWR.js → chunk-MLMTXXM4.js} +5 -5
  13. package/dist/{chunk-Z75YB54S.js → chunk-SBWIZSPB.js} +5 -5
  14. package/dist/{chunk-TZ5IZETE.js → chunk-SPCONMIY.js} +1 -1
  15. package/dist/{chunk-XAYMPBNE.js → chunk-VKNFZEMG.js} +403 -189
  16. package/dist/{chunk-S4FR7ATF.js → chunk-X3PQMMAY.js} +1 -1
  17. package/dist/{chunk-YOLDSF7U.js → chunk-YSJRBRHB.js} +1 -1
  18. package/dist/{chunk-DYGYMBBW.js → chunk-YUIEWE5B.js} +2 -2
  19. package/dist/{composition-QtS1rnhv.d.ts → composition-CkHn6JrM.d.ts} +139 -5
  20. package/dist/composition.d.ts +1 -1
  21. package/dist/composition.js +1 -1
  22. package/dist/conversion-3TTBY7OY.js +19 -0
  23. package/dist/{conversion-Cqwu0Ds9.d.ts → conversion-DS4OiVls.d.ts} +6 -5
  24. package/dist/convert.d.ts +3 -3
  25. package/dist/convert.js +7 -7
  26. package/dist/data.d.ts +1 -1
  27. package/dist/data.js +2 -2
  28. package/dist/{deck-report-BdG5H3Qj.d.ts → deck-report-vnWmXBjq.d.ts} +2 -2
  29. package/dist/deck.d.ts +6 -6
  30. package/dist/deck.js +10 -10
  31. package/dist/diff.d.ts +1 -1
  32. package/dist/diff.js +5 -5
  33. package/dist/docs.js +18 -12
  34. package/dist/examples.d.ts +1 -1
  35. package/dist/{finding-DqgwzUmr.d.ts → finding-l9dlQ2NF.d.ts} +4 -0
  36. package/dist/format.js +2 -2
  37. package/dist/index.d.ts +21 -15
  38. package/dist/index.js +20 -20
  39. package/dist/markdown.d.ts +4 -4
  40. package/dist/markdown.js +7 -7
  41. package/dist/node-engine.d.ts +30 -9
  42. package/dist/node-engine.js +20 -20
  43. package/dist/{pagination-CAX6mHG6.d.ts → pagination-C4jzAtFy.d.ts} +2 -2
  44. package/dist/pagination.d.ts +2 -2
  45. package/dist/pagination.js +4 -4
  46. package/dist/patch.d.ts +1 -1
  47. package/dist/patch.js +4 -4
  48. package/dist/{presentation-BG4QCcdB.d.ts → presentation-fN6tORPZ.d.ts} +58 -8
  49. package/dist/repo-readme.js +1 -1
  50. package/dist/schemas.js +1 -1
  51. package/dist/spec/schemas/finding.schema.json +6 -1
  52. package/dist/spec/schemas/opf.schema.json +14 -2
  53. package/dist/types.d.ts +2 -2
  54. package/dist/{validation-types-DRaNnieo.d.ts → validation-types-B21fZfb2.d.ts} +3 -3
  55. package/dist/{validator-BJtXHJKh.d.ts → validator-Cflv4F9e.d.ts} +4 -10
  56. package/dist/validator.d.ts +5 -5
  57. package/dist/validator.js +3 -3
  58. package/dist/yaml.d.ts +4 -4
  59. package/dist/yaml.js +7 -7
  60. package/package.json +1 -1
  61. package/dist/conversion-DRCJIMWS.js +0 -19
@@ -1875,6 +1875,159 @@ function tableCellDisplayValue(cell, columnFormat2) {
1875
1875
  return formatDataNumber(cell, columnFormat2);
1876
1876
  }
1877
1877
 
1878
+ // src/chart-axis.ts
1879
+ var CHART_TEXT_PX = 20;
1880
+ function chartTextSize(geometry) {
1881
+ const { width, height, scale } = geometry;
1882
+ const minimum = geometry.minFontSize ?? 16;
1883
+ if (![width, height, scale].every(Number.isFinite) || width <= 0 || height <= 0 || scale <= 0) return Math.max(CHART_TEXT_PX, minimum);
1884
+ return Math.max(Math.floor(Math.min(CHART_TEXT_PX, height / scale / 18, width / scale / 30)), minimum);
1885
+ }
1886
+ function clean(value) {
1887
+ const rounded = Number(value.toPrecision(12));
1888
+ return Object.is(rounded, -0) ? 0 : rounded;
1889
+ }
1890
+ function scaleFrom(min, max, step) {
1891
+ const ticks = [];
1892
+ const count = Math.round((max - min) / step);
1893
+ for (let index = 0; index <= count; index++) ticks.push(clean(min + index * step));
1894
+ return { min, max, step, ticks };
1895
+ }
1896
+ function pickStep(min, max, limit, steps) {
1897
+ return steps.find((step) => Math.round((max - min) / step) <= limit) ?? max - min;
1898
+ }
1899
+ function niceScale(dataMin, dataMax, maxIntervals = 10, { percent = false } = {}) {
1900
+ const limit = Number.isFinite(maxIntervals) ? Math.max(1, Math.floor(maxIntervals)) : 10;
1901
+ if (percent) {
1902
+ const min = dataMin < -1e-9 ? -1 : 0;
1903
+ const max = dataMax > 1e-9 || min === 0 ? 1 : 0;
1904
+ return scaleFrom(min, max, pickStep(min, max, limit, [0.1, 0.2, 0.25, 0.5, 1]));
1905
+ }
1906
+ if (!Number.isFinite(dataMin) || !Number.isFinite(dataMax)) {
1907
+ dataMin = 0;
1908
+ dataMax = 1;
1909
+ }
1910
+ let lo = Math.min(dataMin, 0);
1911
+ let hi = Math.max(dataMax, 0);
1912
+ if (dataMin > 0 && dataMax - dataMin < dataMax / 6) lo = dataMin;
1913
+ if (dataMax < 0 && dataMax - dataMin < -dataMin / 6) hi = dataMax;
1914
+ if (hi === lo) {
1915
+ if (hi === 0) hi = 1;
1916
+ else if (hi > 0) lo = 0;
1917
+ else hi = 0;
1918
+ }
1919
+ const span = hi - lo;
1920
+ const top = hi > 0 ? hi + span / 20 : hi;
1921
+ const bottom = lo < 0 ? lo - span / 20 : lo > 0 ? Math.max(0, lo - span / 20) : lo;
1922
+ const raw = (top - bottom) / limit;
1923
+ if (!(raw > 0) || !Number.isFinite(raw)) return normalizedScale(dataMin, dataMax, limit);
1924
+ let exponent = Math.floor(Math.log10(raw)) - 1;
1925
+ for (; exponent <= 308; exponent++) {
1926
+ for (const mantissa of [1, 2, 5]) {
1927
+ const step = mantissa * 10 ** exponent;
1928
+ if (!(step > 0) || !Number.isFinite(step)) continue;
1929
+ const min = Math.floor(bottom / step + 1e-9) * step;
1930
+ const max = Math.ceil(top / step - 1e-9) * step;
1931
+ if (Number.isFinite(min) && Number.isFinite(max) && Math.round((max - min) / step) <= limit) return scaleFrom(clean(min), clean(max), step);
1932
+ }
1933
+ }
1934
+ return normalizedScale(dataMin, dataMax, limit);
1935
+ }
1936
+ function normalizedScale(dataMin, dataMax, limit) {
1937
+ const magnitude = Math.max(Math.abs(dataMin), Math.abs(dataMax));
1938
+ const unit = 10 ** Math.floor(Math.log10(magnitude)) || Number.MIN_VALUE;
1939
+ const normalized = niceScale(dataMin / unit, dataMax / unit, limit);
1940
+ const restore = (value) => {
1941
+ const scaled = value * unit;
1942
+ return Number.isFinite(scaled) ? scaled : Math.sign(value) * Number.MAX_VALUE;
1943
+ };
1944
+ const min = Math.min(restore(normalized.min), dataMin);
1945
+ const max = Math.max(restore(normalized.max), dataMax);
1946
+ const ticks = [.../* @__PURE__ */ new Set([min, ...normalized.ticks.map(restore), max])].sort((a, b) => a - b);
1947
+ return { min, max, step: restore(normalized.step) || Number.MIN_VALUE, ticks };
1948
+ }
1949
+ function maxIntervalsFor(length, labelExtent) {
1950
+ return Math.max(1, Math.min(10, Math.floor(length / Math.max(1, labelExtent))));
1951
+ }
1952
+ var bar = (horizontal, grouping) => ({ kind: "bar", horizontal, grouping });
1953
+ var line = (grouping) => ({ kind: "line", horizontal: false, grouping });
1954
+ var area = (grouping) => ({ kind: "area", horizontal: false, grouping });
1955
+ var CATEGORY_CHARTS = {
1956
+ column: bar(false, "clustered"),
1957
+ "stacked-column": bar(false, "stacked"),
1958
+ "100pct-stacked-column": bar(false, "percentStacked"),
1959
+ bar: bar(true, "clustered"),
1960
+ "stacked-bar": bar(true, "stacked"),
1961
+ "100pct-stacked-bar": bar(true, "percentStacked"),
1962
+ line: line("standard"),
1963
+ "line-with-markers": line("standard"),
1964
+ "stacked-line": line("stacked"),
1965
+ "stacked-line-with-markers": line("stacked"),
1966
+ area: area("standard"),
1967
+ "stacked-area": area("stacked"),
1968
+ "100pct-stacked-area": area("percentStacked")
1969
+ };
1970
+ function stackCategoryValues(series, count, kind2, grouping, path = "chart") {
1971
+ const percent = grouping === "percentStacked";
1972
+ const stacked = grouping === "stacked" || percent;
1973
+ const fail = (aggregate) => new RangeError(`Cannot render ${path}: ${aggregate} exceeds the finite numeric range. Rescale the chart values before rendering.`);
1974
+ const totals = Array.from({ length: count }, (_, i) => series.reduce((sum, entry) => sum + Math.abs(entry.values[i] ?? 0), 0));
1975
+ if (percent && totals.some((total) => !Number.isFinite(total))) throw fail("percentage total");
1976
+ const positive2 = new Array(count).fill(0);
1977
+ const negative = new Array(count).fill(0);
1978
+ const running = new Array(count).fill(0);
1979
+ return series.map(
1980
+ (entry) => entry.values.map((raw, i) => {
1981
+ if (raw === null && kind2 !== "area") return null;
1982
+ let value = raw ?? 0;
1983
+ if (percent) value = totals[i] ? value / totals[i] : 0;
1984
+ if (!stacked) return { value, from: 0, to: value };
1985
+ if (kind2 === "bar") {
1986
+ const base = value >= 0 ? positive2 : negative;
1987
+ const from2 = base[i];
1988
+ base[i] += value;
1989
+ if (!Number.isFinite(base[i])) throw fail("stacked total");
1990
+ return { value, from: from2, to: base[i] };
1991
+ }
1992
+ const from = running[i];
1993
+ running[i] += value;
1994
+ if (!Number.isFinite(running[i])) throw fail("stacked total");
1995
+ return { value, from, to: running[i] };
1996
+ })
1997
+ );
1998
+ }
1999
+ function chartValueAxis(chart, resolved, geometry) {
2000
+ const type = typeof chart === "object" && chart !== null ? chart.type : void 0;
2001
+ const id = typeof type === "string" ? type.trim().toLowerCase() : "";
2002
+ const spec = Object.hasOwn(CATEGORY_CHARTS, id) ? CATEGORY_CHARTS[id] : void 0;
2003
+ if (!spec || !resolved.ok || resolved.combo) return void 0;
2004
+ const { scale, width, height } = geometry;
2005
+ if (![width, height, scale].every(Number.isFinite) || width <= 0 || height <= 0 || scale <= 0) return void 0;
2006
+ const series = resolved.columns.slice(1).map((_, offset) => ({ values: resolved.rows.map((row) => chartNumber(row[offset + 1])) }));
2007
+ if (!series.length || !resolved.rows.length) return void 0;
2008
+ const percent = spec.grouping === "percentStacked";
2009
+ let stacks;
2010
+ try {
2011
+ stacks = stackCategoryValues(series, resolved.rows.length, spec.kind, spec.grouping);
2012
+ } catch {
2013
+ return void 0;
2014
+ }
2015
+ const stackedGrouping = spec.grouping === "stacked" || percent;
2016
+ const extents = stacks.flat().flatMap((point) => point === null ? [] : spec.kind === "line" || !stackedGrouping ? [point.to] : [point.from, point.to]);
2017
+ const anchor = spec.kind === "bar" || spec.kind === "area" ? [0] : [];
2018
+ const dataMin = extents.length ? Math.min(...extents, ...anchor) : 0;
2019
+ const dataMax = extents.length ? Math.max(...extents, ...anchor) : 1;
2020
+ const pad = 10 * scale;
2021
+ const fontPx = chartTextSize(geometry) * scale;
2022
+ const lineHeight = fontPx * 1.5;
2023
+ const options = resolveChartOptions(chart, chartOptionTarget(id));
2024
+ const legendBand = options.legend === "top" || options.legend === "bottom" ? lineHeight + pad : 0;
2025
+ const titleBand = options.axisTitles.category ? lineHeight : 0;
2026
+ const length = spec.horizontal ? width * 0.7 - 2 * pad - fontPx : height - 2 * pad - lineHeight * 1.5 - legendBand - titleBand;
2027
+ const fitted = niceScale(dataMin, dataMax, maxIntervalsFor(Math.max(1, length), spec.horizontal ? 4 * fontPx : lineHeight * 1.2), { percent });
2028
+ return { min: fitted.min, max: fitted.max, majorUnit: fitted.step, ...percent ? { percent: true } : {} };
2029
+ }
2030
+
1878
2031
  // src/image-aspect.ts
1879
2032
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1880
2033
  function sourceOf(value) {
@@ -2170,20 +2323,20 @@ function parse(layout) {
2170
2323
  if (reserved.has(name)) region(`/regions/${pointerSegment(name)}`, `'${name}' is reserved and cannot name a region (reserved: ${RESERVED_REGION_NAMES.join(", ")})`);
2171
2324
  else if (!bounds.has(name)) region(`/regions/${pointerSegment(name)}`, `region '${name}' has no area in areas`);
2172
2325
  }
2173
- for (const area of list) {
2174
- if (area.heading) continue;
2175
- if (reserved.has(area.name)) {
2176
- if (!Object.hasOwn(entries, area.name)) region("/areas", `'${area.name}' is reserved and cannot name a body area (reserved: ${RESERVED_REGION_NAMES.join(", ")})`);
2326
+ for (const area2 of list) {
2327
+ if (area2.heading) continue;
2328
+ if (reserved.has(area2.name)) {
2329
+ if (!Object.hasOwn(entries, area2.name)) region("/areas", `'${area2.name}' is reserved and cannot name a body area (reserved: ${RESERVED_REGION_NAMES.join(", ")})`);
2177
2330
  continue;
2178
2331
  }
2179
- if (!Object.hasOwn(entries, area.name)) region("/regions", `body area '${area.name}' has no entry in regions`);
2332
+ if (!Object.hasOwn(entries, area2.name)) region("/regions", `body area '${area2.name}' has no entry in regions`);
2180
2333
  }
2181
2334
  if (bounds.has("subtitle") && !bounds.has("title")) region("/areas", "a 'subtitle' area needs a 'title' area");
2182
2335
  for (const [name, entry] of Object.entries(entries)) {
2183
2336
  const raw = record4(entry);
2184
2337
  if (raw.flow === "none" && raw.max !== void 0 && raw.max !== 1) region(`/regions/${pointerSegment(name)}/max`, `a region with flow "none" holds one block; max must be 1, not ${JSON.stringify(raw.max)}`);
2185
2338
  }
2186
- const regions = list.filter((area) => !area.heading && !reserved.has(area.name) && Object.hasOwn(entries, area.name)).map((area) => layoutRegion(area.name, entries[area.name]));
2339
+ const regions = list.filter((area2) => !area2.heading && !reserved.has(area2.name) && Object.hasOwn(entries, area2.name)).map((area2) => layoutRegion(area2.name, entries[area2.name]));
2187
2340
  for (const entry of regions) if (!entry.accepts.length) region(`/regions/${pointerSegment(entry.name)}/accepts`, `region '${entry.name}' accepts no content kind`);
2188
2341
  if (value.overflowRegion !== void 0 && !regions.some((entry) => entry.name === value.overflowRegion)) region("/overflowRegion", `overflowRegion ${JSON.stringify(value.overflowRegion)} is not a region of this layout`);
2189
2342
  if (issues.length) return { issues };
@@ -3406,6 +3559,8 @@ function labWithLightness(l, a, b) {
3406
3559
  function labDifference(a, b) {
3407
3560
  return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
3408
3561
  }
3562
+ var CHART_SERIES_TIER_SIZE = 6;
3563
+ var tierOf = (index) => Math.floor(index / CHART_SERIES_TIER_SIZE);
3409
3564
  var CHART_SERIES_SHORTFALL_WEIGHT = 8;
3410
3565
  function chartPaletteForFill(fill, palette) {
3411
3566
  const opaqueFill = colorContrast(fill, fill) !== void 0;
@@ -3426,7 +3581,7 @@ function chartPaletteForFill(fill, palette) {
3426
3581
  const baseLab = labOf(opaqueChannels(base));
3427
3582
  const needs = palette.flatMap((_2, other) => {
3428
3583
  const before = preferredLab[other], lab = placedLab[other];
3429
- return other !== index && before && lab && placed[other] !== void 0 ? [{ lab, adjacent: Math.abs(other - index) === 1, wide: Math.min(labDifference(before, wanted), CHART_SERIES_MIN_DIFFERENCE), step: Math.min(Math.abs(before[0] - wanted[0]), CHART_SERIES_MIN_LIGHTNESS_STEP) }] : [];
3584
+ return other !== index && tierOf(other) <= tierOf(index) && before && lab && placed[other] !== void 0 ? [{ lab, adjacent: Math.abs(other - index) === 1, wide: Math.min(labDifference(before, wanted), CHART_SERIES_MIN_DIFFERENCE), step: Math.min(Math.abs(before[0] - wanted[0]), CHART_SERIES_MIN_LIGHTNESS_STEP) }] : [];
3430
3585
  });
3431
3586
  const shortfall = (candidate) => needs.reduce((worst, need) => {
3432
3587
  const lightness = Math.abs(need.lab[0] - candidate[0]) - need.step, difference = labDifference(need.lab, candidate) - need.wide;
@@ -3452,6 +3607,28 @@ function chartPaletteForFill(fill, palette) {
3452
3607
  });
3453
3608
  return placed;
3454
3609
  }
3610
+ var CHART_SERIES_TINT_MIX = 0.4;
3611
+ function chartSeriesPalette(colorScheme = {}, colors, context = {}) {
3612
+ const accents = [1, 2, 3, 4, 5, 6].map((number) => {
3613
+ const slot = `accent${number}`;
3614
+ return normalizeHexColor(colorScheme?.[slot]) ?? normalizeHexColor(ENGINE_DEFAULT_COLOR_SCHEME[slot]) ?? "#000000";
3615
+ });
3616
+ const palette = [...accents, ...accents.map((accent) => mixChannels(opaqueChannels(accent), [255, 255, 255], CHART_SERIES_TINT_MIX))];
3617
+ resolveChartColors(colors, colorScheme, context).forEach((color, index) => {
3618
+ if (color === void 0) return;
3619
+ while (palette.length < index) palette.push(palette[palette.length % 12]);
3620
+ palette[index] = color;
3621
+ });
3622
+ return palette;
3623
+ }
3624
+ function resolveChartColors(colors, colorScheme = {}, context = {}) {
3625
+ if (!Array.isArray(colors)) return [];
3626
+ return colors.map((entry) => {
3627
+ if (typeof entry !== "string") return void 0;
3628
+ const resolved = resolveColorRef(entry.trim(), { colorScheme, roles: context.roles, variables: context.variables, fallback: "" });
3629
+ return normalizeHexColor(resolved);
3630
+ });
3631
+ }
3455
3632
  var CHART_HIGHLIGHT_MUTED_MIX = 0.3;
3456
3633
  var CHART_HIGHLIGHT_MUTED_MIN_CONTRAST = 1.6;
3457
3634
  var mixChannels = (from, to, amount) => hexOfChannels(from.map((channel, index) => channel + (to[index] - channel) * amount));
@@ -4258,10 +4435,10 @@ function metricTrendMark(layout, options) {
4258
4435
  if (!part || !part.visible || !part.fit || part.fit.sourceLines.length === 0) return void 0;
4259
4436
  const trend = part.text.trim();
4260
4437
  if (!Object.hasOwn(METRIC_TREND_SHAPES, trend)) return void 0;
4261
- const line = part.fit.sourceLines[0], origin = part.linePositions[0];
4262
- if (!origin || line.width <= 0) return void 0;
4438
+ const line2 = part.fit.sourceLines[0], origin = part.linePositions[0];
4439
+ if (!origin || line2.width <= 0) return void 0;
4263
4440
  const size = part.fit.fontSize * 0.72, gap = part.fit.fontSize * 0.3, left = part.box.x, right = part.box.x + part.box.width;
4264
- const after = origin.x + line.width + gap, before = origin.x - gap - size;
4441
+ const after = origin.x + line2.width + gap, before = origin.x - gap - size;
4265
4442
  const fitsAfter = after + size <= right + 0.01, fitsBefore = before >= left - 0.01;
4266
4443
  const x = layout.alignment === "left" ? fitsAfter ? after : fitsBefore ? before : void 0 : fitsBefore ? before : fitsAfter ? after : void 0;
4267
4444
  if (x === void 0) return void 0;
@@ -4321,7 +4498,7 @@ function codeHighlightLines(highlight, source) {
4321
4498
  return;
4322
4499
  }
4323
4500
  if (end > lineCount) issues.push({ code: "code-highlight-out-of-range", index, message: `The highlight ${start === end ? `line ${start}` : `range [${start}, ${end}]`} is past the last line (the code has ${lineCount} ${lineCount === 1 ? "line" : "lines"}); lines beyond it are ignored.` });
4324
- for (let line = start; line <= Math.min(end, lineCount); line++) marked.add(line);
4501
+ for (let line2 = start; line2 <= Math.min(end, lineCount); line2++) marked.add(line2);
4325
4502
  });
4326
4503
  return { lineCount, lines: [...marked].sort((a, b) => a - b), issues };
4327
4504
  }
@@ -4336,10 +4513,10 @@ function codeHighlightList(lines) {
4336
4513
  return out;
4337
4514
  }
4338
4515
  function codeLineNumbers(sourceLines) {
4339
- let line = 1;
4516
+ let line2 = 1;
4340
4517
  return sourceLines.map((entry) => {
4341
- const number = line;
4342
- if (entry.boundary === "hard") line++;
4518
+ const number = line2;
4519
+ if (entry.boundary === "hard") line2++;
4343
4520
  return number;
4344
4521
  });
4345
4522
  }
@@ -4359,7 +4536,7 @@ function codeHighlightSlice(highlight, source, start, end) {
4359
4536
  if (!lines.length) return void 0;
4360
4537
  const breaks = (text2) => (text2.match(/\r\n|\r|\n/g) ?? []).length;
4361
4538
  const before = breaks(source.slice(0, start)), count = codeLineCount(source.slice(start, end));
4362
- const kept = lines.filter((line) => line > before && line <= before + count).map((line) => line - before);
4539
+ const kept = lines.filter((line2) => line2 > before && line2 <= before + count).map((line2) => line2 - before);
4363
4540
  return kept.length ? codeHighlightList(kept) : void 0;
4364
4541
  }
4365
4542
  var CODE_HIGHLIGHT_DIM = 0.35;
@@ -4812,24 +4989,24 @@ function placeTextLines(lines, box, alignment = "left", rasterPadding = 0, direc
4812
4989
  if (!Array.isArray(lines) || !box || ![box.x, box.y, box.width, box.height, rasterPadding].every(Number.isFinite) || box.width <= 0 || box.height <= 0 || rasterPadding < 0 || !["left", "center", "right"].includes(alignment)) throw new RangeError("Text placement requires lines, finite positive dimensions, nonnegative padding and a valid alignment.");
4813
4990
  const placed = [];
4814
4991
  let shift = 0, bottom = box.y, height = 0, overflow = false;
4815
- for (const [lineIndex, line] of lines.entries()) {
4992
+ for (const [lineIndex, line2] of lines.entries()) {
4816
4993
  const lineAlignment = physicalAlignment(alignment, directions?.[lineIndex]), factor = lineAlignment === "right" ? 1 : lineAlignment === "center" ? 0.5 : 0;
4817
- if (!line || ![line.width, line.y, line.baseline, line.height].every(Number.isFinite) || line.width < 0 || line.height <= 0 || line.y < 0 || line.baseline < line.y) throw new RangeError("Text lines require finite coordinates, nonnegative advances and a baseline at or below their top.");
4818
- const ink = line.outline;
4994
+ if (!line2 || ![line2.width, line2.y, line2.baseline, line2.height].every(Number.isFinite) || line2.width < 0 || line2.height <= 0 || line2.y < 0 || line2.baseline < line2.y) throw new RangeError("Text lines require finite coordinates, nonnegative advances and a baseline at or below their top.");
4995
+ const ink = line2.outline;
4819
4996
  if (ink !== null && (!ink || ![ink.x, ink.y, ink.width, ink.height].every(Number.isFinite) || ink.width < 0 || ink.height < 0)) throw new RangeError("Text outlines must be null or finite coordinates with nonnegative dimensions.");
4820
- let x = box.x + (box.width - line.width) * factor;
4997
+ let x = box.x + (box.width - line2.width) * factor;
4821
4998
  if (ink) {
4822
4999
  const low = box.x + rasterPadding - ink.x, high = box.x + box.width - rasterPadding - ink.x - ink.width;
4823
5000
  if (low > high + 0.01) overflow = true;
4824
5001
  else x = Math.max(low, Math.min(x, high));
4825
- shift += Math.max(0, bottom + rasterPadding - (box.y + line.baseline + shift + ink.y));
5002
+ shift += Math.max(0, bottom + rasterPadding - (box.y + line2.baseline + shift + ink.y));
4826
5003
  }
4827
- const y = box.y + line.y + shift, baseline = box.y + line.baseline + shift;
5004
+ const y = box.y + line2.y + shift, baseline = box.y + line2.baseline + shift;
4828
5005
  const outline = ink ? { ...ink, x: x + ink.x, y: baseline + ink.y } : null;
4829
5006
  if (outline) bottom = outline.y + outline.height + rasterPadding;
4830
- height = Math.max(height, y + line.height - box.y, outline ? bottom - box.y : 0);
4831
- if (line.width > box.width + 0.01 || height > box.height + 0.01) overflow = true;
4832
- placed.push({ x, y, baseline, height: line.height, width: line.width, outline, ...directions ? { alignment: lineAlignment } : {} });
5007
+ height = Math.max(height, y + line2.height - box.y, outline ? bottom - box.y : 0);
5008
+ if (line2.width > box.width + 0.01 || height > box.height + 0.01) overflow = true;
5009
+ placed.push({ x, y, baseline, height: line2.height, width: line2.width, outline, ...directions ? { alignment: lineAlignment } : {} });
4833
5010
  }
4834
5011
  return { alignment, rasterPadding, lines: placed, height, overflow };
4835
5012
  }
@@ -4839,11 +5016,11 @@ function translateTextFit(fit, dy) {
4839
5016
  ...fit,
4840
5017
  placement: {
4841
5018
  ...fit.placement,
4842
- lines: fit.placement.lines.map((line) => ({
4843
- ...line,
4844
- y: line.y + dy,
4845
- baseline: line.baseline + dy,
4846
- outline: line.outline ? { ...line.outline, y: line.outline.y + dy } : line.outline
5019
+ lines: fit.placement.lines.map((line2) => ({
5020
+ ...line2,
5021
+ y: line2.y + dy,
5022
+ baseline: line2.baseline + dy,
5023
+ outline: line2.outline ? { ...line2.outline, y: line2.outline.y + dy } : line2.outline
4847
5024
  }))
4848
5025
  }
4849
5026
  };
@@ -5012,13 +5189,14 @@ function resolveSocialProfile(platform, value, owner = "organization") {
5012
5189
  return { text: display(url), href: encodeURI(url), resolved: true };
5013
5190
  }
5014
5191
  var FURNITURE_GAP = 12;
5192
+ var FURNITURE_ZONE_GUTTER = 0.02;
5015
5193
  var FURNITURE_IMAGE_SHARE = 0.4;
5016
5194
  var SPACED_WORD_CHARACTER = /^[\p{L}\p{N}\p{M}]$/u;
5017
5195
  var UNSPACED_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}\p{Script=Tibetan}]/u;
5018
5196
  function breaksInsideWord(text2, fit) {
5019
- return fit.sourceLines.some((line) => {
5020
- if (line.boundary !== "soft" || line.nextStart <= 0 || line.nextStart >= text2.length) return false;
5021
- const before = [...text2.slice(0, line.nextStart)].at(-1) ?? "", after = [...text2.slice(line.nextStart, line.nextStart + 2)][0] ?? "";
5197
+ return fit.sourceLines.some((line2) => {
5198
+ if (line2.boundary !== "soft" || line2.nextStart <= 0 || line2.nextStart >= text2.length) return false;
5199
+ const before = [...text2.slice(0, line2.nextStart)].at(-1) ?? "", after = [...text2.slice(line2.nextStart, line2.nextStart + 2)][0] ?? "";
5022
5200
  return SPACED_WORD_CHARACTER.test(before) && SPACED_WORD_CHARACTER.test(after) && !UNSPACED_SCRIPT.test(before) && !UNSPACED_SCRIPT.test(after);
5023
5201
  });
5024
5202
  }
@@ -5048,9 +5226,25 @@ function layoutFurniture(input, options = {}) {
5048
5226
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Header/footer content must be an object or false.");
5049
5227
  configured = true;
5050
5228
  const zones = [];
5229
+ const inset = Math.min((settings.padding ?? 0.08) * Math.min(width, height), width * 0.25), gutter = width * FURNITURE_ZONE_GUTTER, column = (width - 2 * inset - 2 * gutter) / 3;
5230
+ const slotOf = (index) => furnitureRtl && index !== 1 ? 2 - index : index;
5231
+ const hasContent = (zone) => {
5232
+ const item = record8(record8(value)[zone]);
5233
+ return item.image !== void 0 || (typeof item.text === "string" ? item.text.trim() !== "" : item.text !== void 0) || item.socials === true || item.date === true || typeof item.date === "string";
5234
+ };
5235
+ const slots = ["left", "center", "right"].map((zone, index) => ({ slot: slotOf(index), occupied: hasContent(zone) })).sort((a, b) => a.slot - b.slot);
5236
+ const first = slots.findIndex((entry) => entry.occupied);
5237
+ const interval = (slot) => {
5238
+ const start = inset + slot * (column + gutter), end = start + column;
5239
+ if (!slots[slot].occupied || first < 0) return { x: start, width: column };
5240
+ const before = slots.slice(0, slot).map((entry) => entry.occupied).lastIndexOf(true), after = slots.findIndex((entry, i) => i > slot && entry.occupied);
5241
+ const left = before < 0 ? start - inset : (start - (inset + before * (column + gutter) + column) - gutter) / 2, right = after < 0 ? width - inset - end : (inset + after * (column + gutter) - end - gutter) / 2;
5242
+ const lend = slot === 1 ? Math.min(left, right) : void 0;
5243
+ return { x: start - (lend ?? left), width: column + (lend ?? left) + (lend ?? right) };
5244
+ };
5051
5245
  for (const [index, zone] of ["left", "center", "right"].entries()) {
5052
5246
  const side = furnitureRtl && zone !== "center" ? zone === "left" ? "right" : "left" : zone;
5053
- const content = record8(record8(value)[zone]), path = `${root}.${zone}`, x = width * (0.07 + (furnitureRtl ? 2 - index : index) * 0.3), zoneWidth = width * 0.26;
5247
+ const content = record8(record8(value)[zone]), path = `${root}.${zone}`, { x, width: zoneWidth } = interval(slotOf(index));
5054
5248
  const pending = [];
5055
5249
  const add = (field, text2, generated = false, sourcePath, extras = {}) => {
5056
5250
  if (text2 === void 0) return;
@@ -5101,10 +5295,10 @@ function layoutFurniture(input, options = {}) {
5101
5295
  }
5102
5296
  const fitPart = (part, boxWidth) => {
5103
5297
  const fit = fitText(part.text, { width: Math.max(Number.MIN_VALUE, boxWidth - (outlines ? 2 * padding : 0)), height: Number.MAX_VALUE }, size, size, textWidthMeasurer(part.style, options.textMeasurement));
5104
- const ink = fit.sourceLines.map((line, index2) => {
5298
+ const ink = fit.sourceLines.map((line2, index2) => {
5105
5299
  let outline = null;
5106
5300
  if (outlines) {
5107
- for (const segment2 of line.segments) if (segment2.kind === "text") {
5301
+ for (const segment2 of line2.segments) if (segment2.kind === "text") {
5108
5302
  const bounds = measureTextOutline(part.text.slice(segment2.start, segment2.end), size, part.style, options.textMeasurement);
5109
5303
  if (bounds) {
5110
5304
  const next = { ...bounds, x: bounds.x + segment2.x };
@@ -5119,7 +5313,7 @@ function layoutFurniture(input, options = {}) {
5119
5313
  }
5120
5314
  }
5121
5315
  }
5122
- return { width: line.width, y: index2 * fit.lineHeight, baseline: size + index2 * fit.lineHeight, height: fit.lineHeight, outline };
5316
+ return { width: line2.width, y: index2 * fit.lineHeight, baseline: size + index2 * fit.lineHeight, height: fit.lineHeight, outline };
5123
5317
  });
5124
5318
  const natural = outlines ? placeTextLines(ink, { x: 0, y: 0, width: boxWidth, height: Number.MAX_VALUE }, side, padding) : void 0;
5125
5319
  const partHeight = natural?.height ?? fit.sourceLines.length * fit.lineHeight;
@@ -5127,7 +5321,7 @@ function layoutFurniture(input, options = {}) {
5127
5321
  return { fit, ink, height: Math.max(scale, partHeight) };
5128
5322
  };
5129
5323
  const gap = FURNITURE_GAP * scale, gaps = gap * Math.max(0, pending.length - 1);
5130
- const widths = pending.map((part) => part.type === "image" ? pending.length > 1 ? Math.min(part.width, zoneWidth * FURNITURE_IMAGE_SHARE) : part.width : pending.length === 1 ? zoneWidth : Math.max(scale, ...fitPart(part, 1e7).ink.map((line) => Math.max(line.width + (outlines ? 2 * padding : 0), line.outline ? line.outline.width + 2 * padding : 0))));
5324
+ const widths = pending.map((part) => part.type === "image" ? pending.length > 1 ? Math.min(part.width, zoneWidth * FURNITURE_IMAGE_SHARE) : part.width : pending.length === 1 ? zoneWidth : Math.max(scale, ...fitPart(part, 1e7).ink.map((line2) => Math.max(line2.width + (outlines ? 2 * padding : 0), line2.outline ? line2.outline.width + 2 * padding : 0))));
5131
5325
  if (pending.length > 1 && widths.reduce((sum, entry) => sum + entry, 0) + gaps > zoneWidth + 0.01) {
5132
5326
  const texts = pending.map((_, index2) => index2).filter((index2) => pending[index2].type === "text").sort((a, b) => widths[a] - widths[b]);
5133
5327
  let remaining = zoneWidth - gaps - pending.reduce((sum, part, index2) => sum + (part.type === "image" ? widths[index2] : 0), 0), count = texts.length;
@@ -5170,10 +5364,10 @@ function layoutFurniture(input, options = {}) {
5170
5364
  else footerTop = top;
5171
5365
  for (const zone of zones) for (const part of zone) {
5172
5366
  part.box.y += top;
5173
- if (part.type === "text" && part.fit.placement) for (const line of part.fit.placement.lines) {
5174
- line.y += top;
5175
- line.baseline += top;
5176
- if (line.outline) line.outline.y += top;
5367
+ if (part.type === "text" && part.fit.placement) for (const line2 of part.fit.placement.lines) {
5368
+ line2.y += top;
5369
+ line2.baseline += top;
5370
+ if (line2.outline) line2.outline.y += top;
5177
5371
  }
5178
5372
  if (part.box.y + part.box.height > height - height * 0.04 + 0.01) error(part.path, "Repeated header/footer content extends beyond the usable slide height. Change its content or design before pagination.");
5179
5373
  parts.push(part);
@@ -5262,11 +5456,11 @@ function layoutQuote(value, box, options = {}) {
5262
5456
  const diagnostics = [], parts = [];
5263
5457
  const report = (reason, diagnosticPath, roles, message) => diagnostics.push({ code: "text-overflow", reason, path: diagnosticPath, parts: roles, message });
5264
5458
  const source = (sourcePath, text2, outputStart) => ({ path: sourcePath, start: 0, end: text2.length, outputStart, outputEnd: outputStart + text2.length });
5265
- const add = (role, text2, sources, area, fontSize, fontFamily, fontWeight, partPath, runs) => {
5459
+ const add = (role, text2, sources, area2, fontSize, fontFamily, fontWeight, partPath, runs) => {
5266
5460
  const requestedStyle = { fontFamily, fontWeight, italic: false, path: partPath };
5267
5461
  const style = resolveTextStyle({ ...requestedStyle }, options.textMeasurement);
5268
5462
  const requestedFontSize = gridFontSize(fontSize * scale, minimum);
5269
- const part = { role, path: partPath, text: text2, sources, box: area, requestedFontSize, minFontSize: minimum, requestedStyle, style, ...runs ? { runs } : {} };
5463
+ const part = { role, path: partPath, text: text2, sources, box: area2, requestedFontSize, minFontSize: minimum, requestedStyle, style, ...runs ? { runs } : {} };
5270
5464
  parts.push(part);
5271
5465
  return part;
5272
5466
  };
@@ -5304,8 +5498,8 @@ function layoutQuote(value, box, options = {}) {
5304
5498
  path
5305
5499
  ) : void 0;
5306
5500
  const footerRequested = attribution?.requestedFontSize ?? gridFontSize(17 * scale, minimum);
5307
- const usable2 = (area) => [area.x, area.y, area.width, area.height].every(Number.isFinite) && area.width > 0 && area.height > 0;
5308
- const fit = (part, area, size = part.requestedFontSize, floor = minimum) => !usable2(area) ? void 0 : part.runs ? fitRichText(part.runs, area, size, floor, { style: part.style, textMeasurement: options.textMeasurement, ...options.citationMarker ? { citationMarker: options.citationMarker } : {}, ...options.direction ? { direction: options.direction } : {} }) : fitText(part.text, area, size, floor, textWidthMeasurer(part.style, options.textMeasurement), options.direction);
5501
+ const usable2 = (area2) => [area2.x, area2.y, area2.width, area2.height].every(Number.isFinite) && area2.width > 0 && area2.height > 0;
5502
+ const fit = (part, area2, size = part.requestedFontSize, floor = minimum) => !usable2(area2) ? void 0 : part.runs ? fitRichText(part.runs, area2, size, floor, { style: part.style, textMeasurement: options.textMeasurement, ...options.citationMarker ? { citationMarker: options.citationMarker } : {}, ...options.direction ? { direction: options.direction } : {} }) : fitText(part.text, area2, size, floor, textWidthMeasurer(part.style, options.textMeasurement), options.direction);
5309
5503
  const heightOf = (fit2) => "richLines" in fit2 ? fit2.height : fit2.lines.length * fit2.lineHeight;
5310
5504
  const inner = { x: box.x + 18, y: box.y + 18, width: box.width - 36, height: box.height - 36 };
5311
5505
  let photo, photoFits = true;
@@ -5369,13 +5563,13 @@ function layoutQuote(value, box, options = {}) {
5369
5563
  }
5370
5564
  if (hasPhoto && !photo) photoFits = false;
5371
5565
  for (const part of parts) {
5372
- const area = part.box;
5566
+ const area2 = part.box;
5373
5567
  if (!part.fit) {
5374
5568
  report("invalid-part-box", part.path, [part.role], `Quote ${part.role} has no usable space after its insets; increase the cell size or change the arrangement.`);
5375
5569
  } else if (part.fit.overflow) {
5376
5570
  report("text-fit", part.path, [part.role], `Quote ${part.role} exceeds its available space at the readability floor; increase its space or change the arrangement.`);
5377
5571
  }
5378
- if (usable2(area) && (area.x < box.x || area.y < box.y || area.x + area.width > box.x + box.width + 0.01 || area.y + area.height > box.y + box.height + 0.01)) {
5572
+ if (usable2(area2) && (area2.x < box.x || area2.y < box.y || area2.x + area2.width > box.x + box.width + 0.01 || area2.y + area2.height > box.y + box.height + 0.01)) {
5379
5573
  report("part-outside-cell", part.path, [part.role], `Quote ${part.role} extends outside its cell; increase the cell size or change the arrangement.`);
5380
5574
  }
5381
5575
  }
@@ -5388,7 +5582,7 @@ function layoutQuote(value, box, options = {}) {
5388
5582
  if (diagnostics.length && options.overflow === "error") throw new OPFCompositionError(diagnostics);
5389
5583
  const occupiedParts = parts.filter((part) => part.fit).map((part) => ({ ...part.box, height: part.fit.placement?.height ?? heightOf(part.fit) }));
5390
5584
  if (photo) occupiedParts.push(photo.box);
5391
- const bottom = Math.max(box.y, ...occupiedParts.map((area) => area.y + area.height));
5585
+ const bottom = Math.max(box.y, ...occupiedParts.map((area2) => area2.y + area2.height));
5392
5586
  const bounds = { x: box.x, y: box.y, width: box.width, height: bottom - box.y };
5393
5587
  return { bounds, algorithm: "quote-flow-v1", textMeasurement: options.textMeasurement ? "provided" : "estimated", parts, ...photo ? { photo } : {}, diagnostics, overflow: diagnostics.length > 0 };
5394
5588
  }
@@ -5434,12 +5628,12 @@ function layoutMetric(value, box, options = {}) {
5434
5628
  const primary = parts[0], metadata = parts.filter((part) => part !== primary && part.visible), unit = metadata.find((part) => part.role === "unit");
5435
5629
  const metricDirection = options.direction === "rtl" ? paragraphDirection(["label", "description", "unit"].map((role) => parts.find((part) => part.role === role)?.text).find(Boolean) ?? primary.text, "rtl") : void 0;
5436
5630
  const alignment = physicalAlignment(requestedAlignment, metricDirection);
5437
- const usable2 = (area) => [area.x, area.y, area.width, area.height, area.x + area.width, area.y + area.height].every(Number.isFinite) && area.width > 0 && area.height > 0;
5631
+ const usable2 = (area2) => [area2.x, area2.y, area2.width, area2.height, area2.x + area2.width, area2.y + area2.height].every(Number.isFinite) && area2.width > 0 && area2.height > 0;
5438
5632
  const occupied = (fit) => fit.placement?.height ?? fit.lines.length * fit.lineHeight;
5439
5633
  const gap = 8 * scale, primaryGap = 12 * scale;
5440
- const lineInk = (part, fit) => fit.sourceLines.map((line, index) => {
5634
+ const lineInk = (part, fit) => fit.sourceLines.map((line2, index) => {
5441
5635
  let outline = null;
5442
- for (const segment2 of line.segments) {
5636
+ for (const segment2 of line2.segments) {
5443
5637
  if (segment2.kind !== "text") continue;
5444
5638
  const bounds2 = measureTextOutline(part.text.slice(segment2.start, segment2.end), fit.fontSize, part.style, options.textMeasurement);
5445
5639
  if (!bounds2) continue;
@@ -5453,7 +5647,7 @@ function layoutMetric(value, box, options = {}) {
5453
5647
  outline.height = bottom2 - outline.y;
5454
5648
  }
5455
5649
  }
5456
- return { width: line.width, y: index * fit.lineHeight, baseline: fit.fontSize + index * fit.lineHeight, height: fit.lineHeight, outline };
5650
+ return { width: line2.width, y: index * fit.lineHeight, baseline: fit.fontSize + index * fit.lineHeight, height: fit.lineHeight, outline };
5457
5651
  });
5458
5652
  const place = (part, fit, width) => {
5459
5653
  if (!hasOutlines) return fit;
@@ -5461,8 +5655,8 @@ function layoutMetric(value, box, options = {}) {
5461
5655
  return { ...fit, placement, overflow: placement.overflow };
5462
5656
  };
5463
5657
  const naturalWidth = (part, fit) => {
5464
- if (!hasOutlines) return Math.max(...fit.sourceLines.map((line) => line.width));
5465
- const width = Math.max(...lineInk(part, fit).map((line) => Math.max(line.width, line.outline ? line.outline.x + line.outline.width : 0) - Math.min(0, line.outline?.x ?? 0)));
5658
+ if (!hasOutlines) return Math.max(...fit.sourceLines.map((line2) => line2.width));
5659
+ const width = Math.max(...lineInk(part, fit).map((line2) => Math.max(line2.width, line2.outline ? line2.outline.x + line2.outline.width : 0) - Math.min(0, line2.outline?.x ?? 0)));
5466
5660
  return width > 0 ? width + 2 * rasterPadding : 0;
5467
5661
  };
5468
5662
  const cache2 = /* @__PURE__ */ new Map();
@@ -5477,12 +5671,12 @@ function layoutMetric(value, box, options = {}) {
5477
5671
  }
5478
5672
  return fit;
5479
5673
  };
5480
- const fitValue = (area, singleLine) => {
5481
- if (!usable2(area)) return void 0;
5674
+ const fitValue = (area2, singleLine) => {
5675
+ if (!usable2(area2)) return void 0;
5482
5676
  for (let step = 0; step <= 76; step++) {
5483
5677
  const size = gridFontSize(primary.requestedFontSize - step * scale, minimum);
5484
- const natural = measure(primary, size, area.width);
5485
- const fit = { ...natural, overflow: !!natural.placement?.overflow || singleLine && natural.lines.length !== 1 || occupied(natural) > area.height + 0.01 || natural.sourceLines.some((line) => line.width > area.width + 0.01) };
5678
+ const natural = measure(primary, size, area2.width);
5679
+ const fit = { ...natural, overflow: !!natural.placement?.overflow || singleLine && natural.lines.length !== 1 || occupied(natural) > area2.height + 0.01 || natural.sourceLines.some((line2) => line2.width > area2.width + 0.01) };
5486
5680
  if (!fit.overflow || size === minimum) return fit;
5487
5681
  }
5488
5682
  };
@@ -5499,8 +5693,8 @@ function layoutMetric(value, box, options = {}) {
5499
5693
  const tail = metadata.filter((part) => arrangement !== "inline-unit" || part !== unit);
5500
5694
  const tailHeight = tail.reduce((sum, part) => sum + occupied(measured.get(part)), 0) + Math.max(0, tail.length - 1) * gap;
5501
5695
  if (!Number.isFinite(tailHeight)) throw new RangeError("Metric metadata exceeds finite coordinates.");
5502
- const area = { ...box, width: arrangement === "inline-unit" ? box.width - unitWidth - gap : box.width, height: box.height - tailHeight - (tail.length ? primaryGap : 0) };
5503
- const valueFit = fitValue(area, arrangement === "inline-unit"), valueHeight = valueFit ? occupied(valueFit) : 0;
5696
+ const area2 = { ...box, width: arrangement === "inline-unit" ? box.width - unitWidth - gap : box.width, height: box.height - tailHeight - (tail.length ? primaryGap : 0) };
5697
+ const valueFit = fitValue(area2, arrangement === "inline-unit"), valueHeight = valueFit ? occupied(valueFit) : 0;
5504
5698
  const allocations = [];
5505
5699
  let primaryHeight = valueHeight;
5506
5700
  if (arrangement === "inline-unit" && unit && unitFit && valueFit) {
@@ -5508,18 +5702,18 @@ function layoutMetric(value, box, options = {}) {
5508
5702
  const unitBaseline = unitFit.placement?.lines.at(-1)?.baseline ?? occupied(unitFit) - unitFit.lineHeight + unitFit.fontSize;
5509
5703
  const valueY = box.y + Math.max(0, unitBaseline - valueBaseline), unitY = box.y + Math.max(0, valueBaseline - unitBaseline);
5510
5704
  const measuredWidth = naturalWidth(primary, valueFit);
5511
- const valueWidth = Math.min(area.width, measuredWidth || valueFit.fontSize);
5512
- allocations.push({ part: primary, box: { ...area, y: valueY, width: valueWidth, height: valueHeight }, fit: place(primary, valueFit, valueWidth) });
5705
+ const valueWidth = Math.min(area2.width, measuredWidth || valueFit.fontSize);
5706
+ allocations.push({ part: primary, box: { ...area2, y: valueY, width: valueWidth, height: valueHeight }, fit: place(primary, valueFit, valueWidth) });
5513
5707
  allocations.push({ part: unit, box: { x: box.x + valueWidth + gap, y: unitY, width: unitWidth, height: occupied(unitFit) }, fit: { ...unitFit, overflow: !!unitFit.placement?.overflow } });
5514
5708
  primaryHeight = Math.max(valueY - box.y + valueHeight, unitY - box.y + occupied(unitFit));
5515
- } else allocations.push({ part: primary, box: valueFit ? { ...area, height: valueHeight } : area, fit: valueFit });
5709
+ } else allocations.push({ part: primary, box: valueFit ? { ...area2, height: valueHeight } : area2, fit: valueFit });
5516
5710
  let y = box.y + primaryHeight + (tail.length ? primaryGap : 0);
5517
5711
  for (const part of tail) {
5518
5712
  const natural = measured.get(part), height = occupied(natural);
5519
- allocations.push({ part, box: { ...box, y, height }, fit: { ...natural, overflow: !!natural.placement?.overflow || natural.sourceLines.some((line) => line.width > box.width + 0.01) } });
5713
+ allocations.push({ part, box: { ...box, y, height }, fit: { ...natural, overflow: !!natural.placement?.overflow || natural.sourceLines.some((line2) => line2.width > box.width + 0.01) } });
5520
5714
  y += height + gap;
5521
5715
  }
5522
- const overflow = !valueFit || valueFit.overflow || arrangement === "inline-unit" && valueFit.lines.length !== 1 || primaryHeight > area.height + 0.01 || allocations.some(({ box: area2, fit }) => !fit || fit.overflow || !usable2(area2) || area2.y + area2.height > box.y + box.height + 0.01);
5716
+ const overflow = !valueFit || valueFit.overflow || arrangement === "inline-unit" && valueFit.lines.length !== 1 || primaryHeight > area2.height + 0.01 || allocations.some(({ box: area3, fit }) => !fit || fit.overflow || !usable2(area3) || area3.y + area3.height > box.y + box.height + 0.01);
5523
5717
  const score = allocations.reduce((sum, { part, fit }) => sum + (part.requestedFontSize - (fit?.fontSize ?? minimum)) / scale, 0);
5524
5718
  const candidate = { arrangement, allocations, score, overflow };
5525
5719
  if (!selected || !overflow && (selected.overflow || score < selected.score) || overflow && selected.overflow) selected = candidate;
@@ -5538,10 +5732,10 @@ function layoutMetric(value, box, options = {}) {
5538
5732
  }
5539
5733
  for (const part of parts) if (part.fit) {
5540
5734
  if (part.fit.placement) {
5541
- part.fit = { ...part.fit, placement: { ...part.fit.placement, lines: part.fit.placement.lines.map((line) => ({ ...line, x: line.x + part.box.x, y: line.y + part.box.y, baseline: line.baseline + part.box.y, outline: line.outline ? { ...line.outline, x: line.outline.x + part.box.x, y: line.outline.y + part.box.y } : null })) } };
5735
+ part.fit = { ...part.fit, placement: { ...part.fit.placement, lines: part.fit.placement.lines.map((line2) => ({ ...line2, x: line2.x + part.box.x, y: line2.y + part.box.y, baseline: line2.baseline + part.box.y, outline: line2.outline ? { ...line2.outline, x: line2.outline.x + part.box.x, y: line2.outline.y + part.box.y } : null })) } };
5542
5736
  part.linePositions = part.fit.placement.lines.map(({ x, baseline }) => ({ x, baseline }));
5543
- } else part.linePositions = part.fit.sourceLines.map((line, index) => ({
5544
- x: part.box.x + (part.box.width - line.width) * alignmentFactor,
5737
+ } else part.linePositions = part.fit.sourceLines.map((line2, index) => ({
5738
+ x: part.box.x + (part.box.width - line2.width) * alignmentFactor,
5545
5739
  baseline: part.box.y + part.fit.fontSize + index * part.fit.lineHeight
5546
5740
  }));
5547
5741
  }
@@ -5601,10 +5795,10 @@ function layoutTimeline(value, box, options = {}) {
5601
5795
  let measured = cache2.get(key);
5602
5796
  if (measured) return measured;
5603
5797
  const fit = fitText(part.text, { width: Math.max(Number.MIN_VALUE, width - (outlines ? 2 * padding : 0)), height: Number.MAX_VALUE }, size, size, textWidthMeasurer(part.style, options.textMeasurement), options.direction);
5604
- const ink = fit.sourceLines.map((line, index) => {
5798
+ const ink = fit.sourceLines.map((line2, index) => {
5605
5799
  let left = Infinity, top = Infinity, right = -Infinity, bottom = -Infinity, hasInk = false;
5606
5800
  if (outlines) {
5607
- for (const segment2 of line.segments) if (segment2.kind === "text") {
5801
+ for (const segment2 of line2.segments) if (segment2.kind === "text") {
5608
5802
  const outline = measureTextOutline(part.text.slice(segment2.start, segment2.end), size, part.style, options.textMeasurement);
5609
5803
  if (outline) {
5610
5804
  hasInk = true;
@@ -5615,7 +5809,7 @@ function layoutTimeline(value, box, options = {}) {
5615
5809
  }
5616
5810
  }
5617
5811
  }
5618
- return { width: line.width, y: index * fit.lineHeight, baseline: size + index * fit.lineHeight, height: fit.lineHeight, outline: hasInk ? { x: left, y: top, width: right - left, height: bottom - top } : null };
5812
+ return { width: line2.width, y: index * fit.lineHeight, baseline: size + index * fit.lineHeight, height: fit.lineHeight, outline: hasInk ? { x: left, y: top, width: right - left, height: bottom - top } : null };
5619
5813
  });
5620
5814
  const placement = outlines ? placeTextLines(ink, { x: 0, y: 0, width, height: Number.MAX_VALUE }, alignment, padding, fit.directions) : void 0;
5621
5815
  const height = placement?.height ?? fit.sourceLines.length * fit.lineHeight;
@@ -5639,13 +5833,13 @@ function layoutTimeline(value, box, options = {}) {
5639
5833
  };
5640
5834
  const place = (part, x, y2, width, alignment) => {
5641
5835
  const measured = measure(part, gridFontSize(part.requestedFontSize - reduction * scale, minimum), Math.max(scale, width), alignment);
5642
- const area = { x, y: y2, width: Math.max(scale, width), height: Math.max(scale, measured.height) }, placement = outlines ? placeTextLines(measured.ink, area, alignment, padding, measured.fit.directions) : void 0;
5836
+ const area2 = { x, y: y2, width: Math.max(scale, width), height: Math.max(scale, measured.height) }, placement = outlines ? placeTextLines(measured.ink, area2, alignment, padding, measured.fit.directions) : void 0;
5643
5837
  const fit = { ...measured.fit, ...placement ? { placement } : {}, overflow: measured.fit.overflow || !!placement?.overflow };
5644
- const accepted = { ...part, box: area, alignment, fit };
5838
+ const accepted = { ...part, box: area2, alignment, fit };
5645
5839
  parts.push(accepted);
5646
5840
  if (fit.overflow) report(accepted, "text-fit", "Timeline field exceeds its readable width; increase its space or split the timeline.");
5647
5841
  if (breaksInsideWord(part.text, fit)) report(accepted, "text-fit", "Timeline field would split a word; use a wider vertical arrangement or paginate events.");
5648
- if (area.x < box.x - 0.01 || area.y < box.y - 0.01 || area.x + area.width > box.x + box.width + 0.01 || area.y + area.height > box.y + box.height + 0.01) report(accepted, "part-outside-cell", "Timeline field extends outside its cell; increase the cell or paginate events.", Math.max(area.x + area.width - box.x - box.width, area.y + area.height - box.y - box.height));
5842
+ if (area2.x < box.x - 0.01 || area2.y < box.y - 0.01 || area2.x + area2.width > box.x + box.width + 0.01 || area2.y + area2.height > box.y + box.height + 0.01) report(accepted, "part-outside-cell", "Timeline field extends outside its cell; increase the cell or paginate events.", Math.max(area2.x + area2.width - box.x - box.width, area2.y + area2.height - box.y - box.height));
5649
5843
  return accepted;
5650
5844
  };
5651
5845
  let y = box.y;
@@ -5787,14 +5981,14 @@ function fitSourceText(text2, box, size, minimum, step, measure, prose = false,
5787
5981
  push(end, end, "end");
5788
5982
  const lineHeight = fontSize * 1.22;
5789
5983
  return {
5790
- lines: sourceLines.map((line) => text2.slice(line.start, line.end)),
5984
+ lines: sourceLines.map((line2) => text2.slice(line2.start, line2.end)),
5791
5985
  sourceLines,
5792
5986
  fontSize,
5793
5987
  lineHeight,
5794
5988
  tabSize: 4,
5795
5989
  tabWidth,
5796
- ...directionAt ? { directions: sourceLines.map((line) => directionAt(line.start)) } : {},
5797
- overflow: sourceLines.length * lineHeight > box.height + 0.01 || sourceLines.some((line) => line.width > box.width + 0.01)
5990
+ ...directionAt ? { directions: sourceLines.map((line2) => directionAt(line2.start)) } : {},
5991
+ overflow: sourceLines.length * lineHeight > box.height + 0.01 || sourceLines.some((line2) => line2.width > box.width + 0.01)
5798
5992
  };
5799
5993
  };
5800
5994
  return fitAtSizes(layout, size, minimum, step, snap);
@@ -5832,8 +6026,8 @@ function layoutCode(value, box, options = {}) {
5832
6026
  if (code.language) add("language", code.language, `${path}.language`);
5833
6027
  if (!parts.length) add("language", "code", path, true);
5834
6028
  const body = add("body", code.source, shorthand ? path : `${path}.source`), headers = parts.filter((part) => part !== body);
5835
- const usable2 = (area) => [area.x, area.y, area.width, area.height].every(Number.isFinite) && area.width > 0 && area.height > 0;
5836
- const fit = (part, area, size = part.requestedFontSize, floor = minimum) => usable2(area) ? fitCodeText(part.text, area, size, floor, scale, textWidthMeasurer(part.style, options.textMeasurement)) : void 0;
6029
+ const usable2 = (area2) => [area2.x, area2.y, area2.width, area2.height].every(Number.isFinite) && area2.width > 0 && area2.height > 0;
6030
+ const fit = (part, area2, size = part.requestedFontSize, floor = minimum) => usable2(area2) ? fitCodeText(part.text, area2, size, floor, scale, textWidthMeasurer(part.style, options.textMeasurement)) : void 0;
5837
6031
  if (usable2(inner)) {
5838
6032
  const combinations = headers.reduce((choices, part) => choices.flatMap((choice) => [.../* @__PURE__ */ new Set([part.requestedFontSize, minimum])].map((size) => [...choice, { part, size }])), [[]]);
5839
6033
  let selected;
@@ -5842,12 +6036,12 @@ function layoutCode(value, box, options = {}) {
5842
6036
  const allocations = [];
5843
6037
  for (const [index, { part, size }] of combination.entries()) {
5844
6038
  const measured = fitCodeText(part.text, inner, size, size, scale, textWidthMeasurer(part.style, options.textMeasurement));
5845
- const area2 = { ...inner, y, height: measured.lines.length * measured.lineHeight };
5846
- allocations.push({ part, box: area2, fit: usable2(area2) ? { ...measured, overflow: measured.sourceLines.some((line) => line.width > area2.width + 0.01) } : void 0 });
5847
- y += area2.height + (index < headers.length - 1 ? 8 : 12);
6039
+ const area3 = { ...inner, y, height: measured.lines.length * measured.lineHeight };
6040
+ allocations.push({ part, box: area3, fit: usable2(area3) ? { ...measured, overflow: measured.sourceLines.some((line2) => line2.width > area3.width + 0.01) } : void 0 });
6041
+ y += area3.height + (index < headers.length - 1 ? 8 : 12);
5848
6042
  }
5849
- const area = { ...inner, y, height: inner.y + inner.height - y };
5850
- allocations.push({ part: body, box: area, fit: fit(body, area) });
6043
+ const area2 = { ...inner, y, height: inner.y + inner.height - y };
6044
+ allocations.push({ part: body, box: area2, fit: fit(body, area2) });
5851
6045
  const overflow = allocations.some(({ box: box2, fit: fit2 }) => !fit2 || fit2.overflow || !usable2(box2) || box2.y + box2.height > inner.y + inner.height + 0.01);
5852
6046
  const score = allocations.reduce((sum, { part, fit: fit2 }) => sum + (part.requestedFontSize - (fit2?.fontSize ?? minimum)) / scale, 0);
5853
6047
  if (!selected || !overflow && (selected.overflow || score < selected.score) || overflow && selected.overflow) selected = { allocations, score, overflow };
@@ -5861,8 +6055,8 @@ function layoutCode(value, box, options = {}) {
5861
6055
  for (const part of parts) {
5862
6056
  if (!part.fit) report("invalid-part-box", part, [part.role], `Code ${part.role} has no usable space after metadata and insets; increase the cell or change the arrangement.`);
5863
6057
  else if (part.fit.overflow) report("text-fit", part, [part.role], `Code ${part.role} exceeds its space at the readability floor; increase its space or change the arrangement.`);
5864
- const area = part.box;
5865
- if (usable2(area) && (area.x < box.x || area.y < box.y || area.x + area.width > box.x + box.width + 0.01 || area.y + area.height > box.y + box.height + 0.01)) {
6058
+ const area2 = part.box;
6059
+ if (usable2(area2) && (area2.x < box.x || area2.y < box.y || area2.x + area2.width > box.x + box.width + 0.01 || area2.y + area2.height > box.y + box.height + 0.01)) {
5866
6060
  report("part-outside-cell", part, [part.role], `Code ${part.role} extends outside its cell; increase the cell or change the arrangement.`);
5867
6061
  }
5868
6062
  }
@@ -5990,27 +6184,27 @@ function richTextLayouter(input, box, requestedSize, options, minimum = 0) {
5990
6184
  let y = 0;
5991
6185
  const richLines = ranges.map((range, rangeIndex) => {
5992
6186
  const parts = fragments(range.start, range.end), shown = hung.get(rangeIndex) ?? range.end, ascent = Math.max(fontSize, ...parts.map((part) => part.fontSize - part.baselineShift)), descent = Math.max(fontSize * 0.22, ...parts.map((part) => part.fontSize * 0.22 + part.baselineShift));
5993
- const height = ascent + descent, line = { fragments: parts, width: shown === range.end ? parts.reduce((sum, p) => sum + p.width, 0) : width(range.start, shown), y, baseline: y + ascent, height };
6187
+ const height = ascent + descent, line2 = { fragments: parts, width: shown === range.end ? parts.reduce((sum, p) => sum + p.width, 0) : width(range.start, shown), y, baseline: y + ascent, height };
5994
6188
  y += height;
5995
- return line;
6189
+ return line2;
5996
6190
  });
5997
6191
  if (options.uniformLineHeight) {
5998
- const height = Math.max(...richLines.map((line) => line.height));
6192
+ const height = Math.max(...richLines.map((line2) => line2.height));
5999
6193
  y = 0;
6000
- for (const line of richLines) {
6001
- line.baseline += y - line.y;
6002
- line.y = y;
6003
- line.height = height;
6194
+ for (const line2 of richLines) {
6195
+ line2.baseline += y - line2.y;
6196
+ line2.y = y;
6197
+ line2.height = height;
6004
6198
  y += height;
6005
6199
  }
6006
6200
  }
6007
6201
  return {
6008
6202
  lines: ranges.map((range) => whole.slice(range.start, range.end)),
6009
6203
  fontSize,
6010
- lineHeight: Math.max(fontSize * 1.22, ...richLines.map((line) => line.height)),
6204
+ lineHeight: Math.max(fontSize * 1.22, ...richLines.map((line2) => line2.height)),
6011
6205
  richLines,
6012
6206
  height: y,
6013
- overflow: y > box.height + 0.01 || richLines.some((line) => line.width > box.width + 0.01),
6207
+ overflow: y > box.height + 0.01 || richLines.some((line2) => line2.width > box.width + 0.01),
6014
6208
  ...directionAt ? { directions: ranges.map((range) => directionAt(range.start)) } : {}
6015
6209
  };
6016
6210
  };
@@ -6107,7 +6301,7 @@ ${flatten(entry.description)}` : ""}`;
6107
6301
  return void 0;
6108
6302
  }
6109
6303
  function layoutTable(value, box, options = {}) {
6110
- const scale = options.scale ?? 1, minimum = snapFontSizeUp((options.minFontSize ?? 16) * scale), requested = gridFontSize(15 * scale, minimum);
6304
+ const scale = options.scale ?? 1, minimum = snapFontSizeUp((options.minFontSize ?? 16) * scale), requested = gridFontSize(contentNominalSize("table") * scale, minimum);
6111
6305
  if (![box.x, box.y, box.width, box.height, scale, minimum].every(Number.isFinite) || box.width <= 0 || box.height <= 0 || scale <= 0 || minimum <= 0) throw new RangeError("Table dimensions, scale and font sizes must be finite and positive.");
6112
6306
  const inline = inlineTableData(value, options.presentation);
6113
6307
  const grid = tableGrid(inline, options.path ?? "table");
@@ -6115,7 +6309,7 @@ function layoutTable(value, box, options = {}) {
6115
6309
  const numeric = new Set(grid.rows.flat().filter((cell) => !cell.header && typeof cell.value === "number" && Number.isFinite(cell.value)));
6116
6310
  const numericColumns = new Set(Array.from({ length: grid.columnCount }, (_, column) => column).filter((column) => {
6117
6311
  const body = grid.rows.flat().filter((cell) => !cell.header && cell.column === column && cell.value !== null && cell.value !== void 0);
6118
- return body.length > 0 && body.every((cell) => cell.colSpan === 1 && numeric.has(cell));
6312
+ return body.some((cell) => numeric.has(cell)) && body.every((cell) => cell.colSpan === 1 && (numeric.has(cell) || Array.isArray(cell.value)));
6119
6313
  }));
6120
6314
  const formats = resolveTableData(inline).formats;
6121
6315
  for (const row of grid.rows) for (const cell of row) {
@@ -6127,52 +6321,65 @@ function layoutTable(value, box, options = {}) {
6127
6321
  const cells = grid.rows.flat().map((cell) => {
6128
6322
  const padding = { top: 8, right: 10, bottom: 4, left: 10, ...cell.style.padding };
6129
6323
  const width = cellWidth * cell.colSpan - (padding.left + padding.right) * scale;
6130
- const style = { fontFamily: options.fontFamily ?? "sans-serif", fontWeight: cell.header ? 700 : 400, italic: false, path: cell.valuePath };
6131
- const numericDefault = numeric.has(cell) || cell.header && cell.colSpan === 1 && numericColumns.has(cell.column);
6324
+ const style = { fontFamily: options.fontFamily ?? "sans-serif", fontWeight: cell.header || cell.style.bold ? 700 : 400, italic: false, path: cell.valuePath };
6325
+ const numericDefault = numeric.has(cell) || (cell.header || Array.isArray(cell.value)) && cell.colSpan === 1 && numericColumns.has(cell.column);
6132
6326
  const alignment = cell.style.align ?? options.alignment ?? (numericDefault ? options.direction === "rtl" ? "left" : "right" : "left");
6133
6327
  return { ...cell, padding, width, textStyle: style, alignment };
6134
6328
  });
6135
6329
  const cellMinimum = (cell) => Array.isArray(cell.value) ? richMinimum(cell.value, requested, minimum) : minimum;
6136
- const fitCell = (cell, height, min) => {
6137
- const textBox = { width: Math.max(scale, cell.width), height: Math.max(scale, height) };
6138
- return Array.isArray(cell.value) ? fitRichText(cell.value, textBox, requested, min, { style: cell.textStyle, ...options.codeFontFamily ? { codeFontFamily: options.codeFontFamily } : {}, textMeasurement: options.textMeasurement, uniformLineHeight: true, direction: options.direction }) : fitText(flatten(cell.value), textBox, requested, min, textWidthMeasurer(resolveTextStyle(cell.textStyle, options.textMeasurement), options.textMeasurement), options.direction);
6330
+ let trial = requested;
6331
+ const cellSize = (cell) => {
6332
+ const floor = cellMinimum(cell);
6333
+ return gridFontSize(Math.max(trial, floor), floor);
6334
+ };
6335
+ const fitCell = (cell, height) => {
6336
+ const textBox = { width: Math.max(scale, cell.width), height: Math.max(scale, height) }, size = cellSize(cell);
6337
+ return Array.isArray(cell.value) ? richTextLayouter(cell.value, textBox, requested, { style: cell.textStyle, ...options.codeFontFamily ? { codeFontFamily: options.codeFontFamily } : {}, textMeasurement: options.textMeasurement, uniformLineHeight: true, direction: options.direction }, minimum)(size) : fitText(flatten(cell.value), textBox, size, size, textWidthMeasurer(resolveTextStyle(cell.textStyle, options.textMeasurement), options.textMeasurement), options.direction);
6139
6338
  };
6140
6339
  const textHeight = (fit) => "height" in fit ? fit.height : fit.lines.length * fit.lineHeight;
6141
- const required = (cell, natural2) => {
6142
- const floor = cellMinimum(cell), size = gridFontSize(natural2 ? Math.max(requested, floor) : floor, floor);
6340
+ const required = (cell, natural) => {
6341
+ const floor = cellMinimum(cell), size = gridFontSize(natural ? Math.max(trial, floor) : floor, floor);
6143
6342
  const textBox = { width: Math.max(scale, cell.width), height: scale };
6144
6343
  const fit = Array.isArray(cell.value) ? richTextLayouter(cell.value, textBox, requested, { style: cell.textStyle, ...options.codeFontFamily ? { codeFontFamily: options.codeFontFamily } : {}, textMeasurement: options.textMeasurement, uniformLineHeight: true }, minimum)(size) : fitText(flatten(cell.value), textBox, size, size, textWidthMeasurer(resolveTextStyle(cell.textStyle, options.textMeasurement), options.textMeasurement));
6145
6344
  return textHeight(fit) + (cell.padding.top + cell.padding.bottom) * scale;
6146
6345
  };
6147
- const natural = Array(grid.rowCount).fill(0), needed = Array(grid.rowCount).fill(0);
6148
- for (const cell of cells) if (cell.rowSpan === 1) {
6149
- natural[cell.row] = Math.max(natural[cell.row], required(cell, true));
6150
- needed[cell.row] = Math.max(needed[cell.row], required(cell, false));
6151
- }
6152
- for (const cell of cells) if (cell.rowSpan > 1) for (const [heights2, preferredSize] of [[natural, true], [needed, false]]) {
6153
- const current = heights2.slice(cell.row, cell.row + cell.rowSpan).reduce((a, b) => a + b, 0);
6154
- const extra = Math.max(0, required(cell, preferredSize) - current) / cell.rowSpan;
6155
- for (let r = cell.row; r < cell.row + cell.rowSpan; r++) heights2[r] += extra;
6156
- }
6157
- const preferred = natural.map((height) => Math.max(54 * scale, height));
6158
- for (let r = 0; r < needed.length; r++) needed[r] = Math.min(preferred[r], needed[r]);
6159
- const sum = (values) => values.reduce((a, b) => a + b, 0);
6160
- const preferredTotal = sum(preferred), naturalTotal = sum(natural), neededTotal = sum(needed);
6161
- const heights = preferred.map((height, r) => preferredTotal <= box.height ? height : naturalTotal <= box.height ? natural[r] + (height - natural[r]) * (box.height - naturalTotal) / Math.max(Number.EPSILON, preferredTotal - naturalTotal) : neededTotal <= box.height ? needed[r] + (natural[r] - needed[r]) * (box.height - neededTotal) / Math.max(Number.EPSILON, naturalTotal - neededTotal) : needed[r] * box.height / neededTotal);
6162
- const ys = [box.y];
6163
- for (const height of heights) ys.push(ys.at(-1) + height);
6164
- let overflow = false;
6165
- const rows3 = heights.map((height, r) => ({ box: { x: box.x, y: ys[r], width: box.width, height }, cells: [] }));
6166
- for (const cell of cells) {
6167
- const cellBox = { x: box.x + (options.direction === "rtl" ? columnCount - cell.column - cell.colSpan : cell.column) * cellWidth, y: ys[cell.row], width: cell.colSpan * cellWidth, height: ys[cell.row + cell.rowSpan] - ys[cell.row] };
6168
- const available = cellBox.height - (cell.padding.top + cell.padding.bottom) * scale;
6169
- const fit = fitCell(cell, available, minimum), height = textHeight(fit);
6170
- const offset = cell.style.verticalAlign === "bottom" ? Math.max(0, available - height) : cell.style.verticalAlign === "middle" ? Math.max(0, (available - height) / 2) : 0;
6171
- const textBox = { x: cellBox.x + cell.padding.left * scale, y: cellBox.y + cell.padding.top * scale + offset, width: Math.max(scale, cell.width), height: Math.max(scale, available - offset) };
6172
- overflow ||= cell.width <= 0 || available <= 0 || fit.overflow;
6173
- rows3[cell.row].cells.push({ value: cell.value, input: cell.input, path: cell.valuePath, sourcePath: cell.path, style: cell.style, alignment: cell.alignment, row: cell.row, column: cell.column, rowSpan: cell.rowSpan, colSpan: cell.colSpan, header: cell.header, rich: Array.isArray(cell.value), box: cellBox, textBox, textStyle: resolveTextStyle(cell.textStyle, options.textMeasurement), fit, ...fit.directions ? { direction: fit.directions[0] } : {} });
6174
- }
6175
- return { rows: rows3, columnCount, height: sum(heights), overflow };
6346
+ const place = () => {
6347
+ const natural = Array(grid.rowCount).fill(0), needed = Array(grid.rowCount).fill(0);
6348
+ for (const cell of cells) if (cell.rowSpan === 1) {
6349
+ natural[cell.row] = Math.max(natural[cell.row], required(cell, true));
6350
+ needed[cell.row] = Math.max(needed[cell.row], required(cell, false));
6351
+ }
6352
+ for (const cell of cells) if (cell.rowSpan > 1) for (const [heights2, preferredSize] of [[natural, true], [needed, false]]) {
6353
+ const current = heights2.slice(cell.row, cell.row + cell.rowSpan).reduce((a, b) => a + b, 0);
6354
+ const extra = Math.max(0, required(cell, preferredSize) - current) / cell.rowSpan;
6355
+ for (let r = cell.row; r < cell.row + cell.rowSpan; r++) heights2[r] += extra;
6356
+ }
6357
+ const preferred = natural.map((height) => Math.max(54 * scale, height));
6358
+ for (let r = 0; r < needed.length; r++) needed[r] = Math.min(preferred[r], needed[r]);
6359
+ const sum = (values) => values.reduce((a, b) => a + b, 0);
6360
+ const preferredTotal = sum(preferred), naturalTotal = sum(natural), neededTotal = sum(needed);
6361
+ const heights = preferred.map((height, r) => preferredTotal <= box.height ? height : naturalTotal <= box.height ? natural[r] + (height - natural[r]) * (box.height - naturalTotal) / Math.max(Number.EPSILON, preferredTotal - naturalTotal) : neededTotal <= box.height ? needed[r] + (natural[r] - needed[r]) * (box.height - neededTotal) / Math.max(Number.EPSILON, naturalTotal - neededTotal) : needed[r] * box.height / neededTotal);
6362
+ const ys = [box.y];
6363
+ for (const height of heights) ys.push(ys.at(-1) + height);
6364
+ let overflow = false;
6365
+ const rows3 = heights.map((height, r) => ({ box: { x: box.x, y: ys[r], width: box.width, height }, cells: [] }));
6366
+ for (const cell of cells) {
6367
+ const cellBox = { x: box.x + (options.direction === "rtl" ? columnCount - cell.column - cell.colSpan : cell.column) * cellWidth, y: ys[cell.row], width: cell.colSpan * cellWidth, height: ys[cell.row + cell.rowSpan] - ys[cell.row] };
6368
+ const available = cellBox.height - (cell.padding.top + cell.padding.bottom) * scale;
6369
+ const fit = fitCell(cell, available), height = textHeight(fit);
6370
+ const offset = cell.style.verticalAlign === "bottom" ? Math.max(0, available - height) : cell.style.verticalAlign === "middle" ? Math.max(0, (available - height) / 2) : 0;
6371
+ const textBox = { x: cellBox.x + cell.padding.left * scale, y: cellBox.y + cell.padding.top * scale + offset, width: Math.max(scale, cell.width), height: Math.max(scale, available - offset) };
6372
+ overflow ||= cell.width <= 0 || available <= 0 || fit.overflow;
6373
+ rows3[cell.row].cells.push({ value: cell.value, input: cell.input, path: cell.valuePath, sourcePath: cell.path, style: cell.style, alignment: cell.alignment, row: cell.row, column: cell.column, rowSpan: cell.rowSpan, colSpan: cell.colSpan, header: cell.header, rich: Array.isArray(cell.value), box: cellBox, textBox, textStyle: resolveTextStyle(cell.textStyle, options.textMeasurement), fit, ...fit.directions ? { direction: fit.directions[0] } : {} });
6374
+ }
6375
+ return { rows: rows3, columnCount, height: sum(heights), overflow };
6376
+ };
6377
+ for (let step = 0; ; step++) {
6378
+ const raw = requested - step * scale, last = step === 64 || raw <= minimum;
6379
+ trial = last ? snapFontSizeUp(minimum) : gridFontSize(raw, minimum);
6380
+ const layout = place();
6381
+ if (last || !layout.overflow) return layout;
6382
+ }
6176
6383
  }
6177
6384
  function tableOverflows(value, box, scale, settings, options, path) {
6178
6385
  return box.width <= 0 || box.height <= 0 || layoutTable(value, box, { scale, minFontSize: settings.minFontSize, fontFamily: options.fontFamilies?.body, ...options.fontFamilies?.code ? { codeFontFamily: options.fontFamilies.code } : {}, textMeasurement: options.textMeasurement, path, presentation: options.presentation }).overflow;
@@ -6200,15 +6407,15 @@ function regionParts(key) {
6200
6407
  }
6201
6408
  return void 0;
6202
6409
  }
6203
- function regionBox(parts, area, requestedGap) {
6204
- const gap = Math.min(requestedGap, area.width / 6, area.height / 6);
6205
- const cw = (area.width - gap * 2) / 3, ch = (area.height - gap * 2) / 3;
6410
+ function regionBox(parts, area2, requestedGap) {
6411
+ const gap = Math.min(requestedGap, area2.width / 6, area2.height / 6);
6412
+ const cw = (area2.width - gap * 2) / 3, ch = (area2.height - gap * 2) / 3;
6206
6413
  const [r, c] = parts;
6207
- return { x: area.x + Math.min(...c) * (cw + gap), y: area.y + Math.min(...r) * (ch + gap), width: (Math.max(...c) - Math.min(...c) + 1) * (cw + gap) - gap, height: (Math.max(...r) - Math.min(...r) + 1) * (ch + gap) - gap };
6414
+ return { x: area2.x + Math.min(...c) * (cw + gap), y: area2.y + Math.min(...r) * (ch + gap), width: (Math.max(...c) - Math.min(...c) + 1) * (cw + gap) - gap, height: (Math.max(...r) - Math.min(...r) + 1) * (ch + gap) - gap };
6208
6415
  }
6209
- function gridGeometry(count, area, cols, gap, weights, vertical) {
6416
+ function gridGeometry(count, area2, cols, gap, weights, vertical) {
6210
6417
  const rowCount = Math.ceil(count / cols);
6211
- gap = Math.min(gap, area.width / (cols * 2), area.height / (rowCount * 2));
6418
+ gap = Math.min(gap, area2.width / (cols * 2), area2.height / (rowCount * 2));
6212
6419
  const tracks = (length, total, weighted) => {
6213
6420
  const values = Array.from({ length }, (_, i) => weighted ? weights[i] ?? 1 : 1);
6214
6421
  const sum = values.reduce((a, b) => a + b, 0);
@@ -6220,12 +6427,12 @@ function gridGeometry(count, area, cols, gap, weights, vertical) {
6220
6427
  return track;
6221
6428
  });
6222
6429
  };
6223
- const xs = tracks(cols, area.width, !vertical), ys = tracks(rowCount, area.height, vertical);
6224
- const boxes = Array.from({ length: count }, (_, i) => ({ x: area.x + xs[i % cols].offset, y: area.y + ys[Math.floor(i / cols)].offset, width: xs[i % cols].size, height: ys[Math.floor(i / cols)].size }));
6430
+ const xs = tracks(cols, area2.width, !vertical), ys = tracks(rowCount, area2.height, vertical);
6431
+ const boxes = Array.from({ length: count }, (_, i) => ({ x: area2.x + xs[i % cols].offset, y: area2.y + ys[Math.floor(i / cols)].offset, width: xs[i % cols].size, height: ys[Math.floor(i / cols)].size }));
6225
6432
  return { boxes, columns: xs, rows: ys, gap };
6226
6433
  }
6227
- function gridBoxes(count, area, cols, gap, weights, vertical) {
6228
- return gridGeometry(count, area, cols, gap, weights, vertical).boxes;
6434
+ function gridBoxes(count, area2, cols, gap, weights, vertical) {
6435
+ return gridGeometry(count, area2, cols, gap, weights, vertical).boxes;
6229
6436
  }
6230
6437
  function assertComposition(value) {
6231
6438
  if (value.mode !== void 0 && !["auto", "grid", "row", "column"].includes(value.mode)) throw new RangeError("Unknown composition mode.");
@@ -6252,13 +6459,13 @@ function composeLayoutAreas(layout, options = {}) {
6252
6459
  ...mirror !== void 0 ? { design: { mirror } } : {}
6253
6460
  };
6254
6461
  const result = composeSlide(slide, { ...compose, layout: kept, [LAYOUT_MASTER]: true });
6255
- const areas = template.areas.map((area) => {
6256
- if (area.heading) {
6257
- const found = result.headingAreas?.find((entry) => entry.name === area.name);
6258
- return { name: area.name, heading: true, box: found ? { ...found.box } : { x: 0, y: 0, width: 0, height: 0 }, ...found?.parts ? { parts: found.parts } : {} };
6462
+ const areas = template.areas.map((area2) => {
6463
+ if (area2.heading) {
6464
+ const found = result.headingAreas?.find((entry) => entry.name === area2.name);
6465
+ return { name: area2.name, heading: true, box: found ? { ...found.box } : { x: 0, y: 0, width: 0, height: 0 }, ...found?.parts ? { parts: found.parts } : {} };
6259
6466
  }
6260
- const region = result.regions.find((entry) => entry.name === area.name);
6261
- return { name: area.name, heading: false, box: { ...!bleed && region.areaBox ? region.areaBox : region.box } };
6467
+ const region = result.regions.find((entry) => entry.name === area2.name);
6468
+ return { name: area2.name, heading: false, box: { ...!bleed && region.areaBox ? region.areaBox : region.box } };
6262
6469
  });
6263
6470
  return { contentBox: { ...result.contentBox }, areas };
6264
6471
  }
@@ -6280,7 +6487,7 @@ function composeSlide(input, options = {}) {
6280
6487
  const rasterPadding = (options.textRasterPadding ?? 1) * scale;
6281
6488
  if (!Number.isFinite(rasterPadding) || rasterPadding < 0) throw new RangeError("Text raster padding must be finite and nonnegative.");
6282
6489
  let coverGroup = false;
6283
- const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && hints.sources.contentAlignment !== "slide" ? hints.titleAlignment : hints.contentAlignment) ?? (field === "metric" ? "center" : "left");
6490
+ const alignmentFor = (field) => (field === "title" || (field === "tag" || coverGroup && field === "subtitle") && hints.sources.contentAlignment !== "slide" ? hints.titleAlignment : hints.contentAlignment) ?? (field === "metric" ? "center" : "left");
6284
6491
  const styleFor = (field, path2) => resolveTextStyle({
6285
6492
  fontFamily: (field === "title" ? options.fontFamilies?.heading : field === "code" ? options.fontFamilies?.code : field === "tag" ? options.fontFamilies?.accent ?? options.fontFamilies?.body : options.fontFamilies?.body) ?? (field === "code" ? "monospace" : "sans-serif"),
6286
6493
  fontWeight: field === "title" ? 700 : 400,
@@ -6305,9 +6512,9 @@ function composeSlide(input, options = {}) {
6305
6512
  const raw = start - trial * scale, last = trial === 64 || raw <= floor, fontSize = last ? floor : gridFontSize(raw, floor);
6306
6513
  const fit = richLayout ? richLayout(fontSize) : fitText(text2, box, fontSize, fontSize, measure, textDirection);
6307
6514
  const richLines = "richLines" in fit ? fit.richLines : void 0;
6308
- const lines = richLines ? richLines.map((line) => {
6515
+ const lines = richLines ? richLines.map((line2) => {
6309
6516
  let outline = null;
6310
- for (const fragment of line.fragments) {
6517
+ for (const fragment of line2.fragments) {
6311
6518
  if (fragment.kind === "tab") continue;
6312
6519
  const bounds = measureTextOutline(fragment.text, fragment.fontSize, fragment.style, options.textMeasurement);
6313
6520
  if (!bounds) continue;
@@ -6321,10 +6528,10 @@ function composeSlide(input, options = {}) {
6321
6528
  outline.height = bottom - outline.y;
6322
6529
  }
6323
6530
  }
6324
- return { width: line.width, y: line.y, baseline: line.baseline, height: line.height, outline };
6325
- }) : fit.sourceLines.map((line, index) => {
6531
+ return { width: line2.width, y: line2.y, baseline: line2.baseline, height: line2.height, outline };
6532
+ }) : fit.sourceLines.map((line2, index) => {
6326
6533
  let outline = null;
6327
- for (const segment2 of line.segments) {
6534
+ for (const segment2 of line2.segments) {
6328
6535
  if (segment2.kind === "tab") continue;
6329
6536
  const bounds = measureTextOutline(text2.slice(segment2.start, segment2.end), fontSize, style, options.textMeasurement);
6330
6537
  if (!bounds) continue;
@@ -6338,7 +6545,7 @@ function composeSlide(input, options = {}) {
6338
6545
  outline.height = bottom - outline.y;
6339
6546
  }
6340
6547
  }
6341
- return { width: line.width, y: index * fit.lineHeight, baseline: fontSize + index * fit.lineHeight, height: fit.lineHeight, outline };
6548
+ return { width: line2.width, y: index * fit.lineHeight, baseline: fontSize + index * fit.lineHeight, height: fit.lineHeight, outline };
6342
6549
  });
6343
6550
  const placement = placeTextLines(lines, box, alignment, rasterPadding, fit.directions), result = { ...fit, placement, overflow: fit.overflow || placement.overflow };
6344
6551
  if (!result.overflow || last) return result;
@@ -6355,7 +6562,7 @@ function composeSlide(input, options = {}) {
6355
6562
  const regions = Object.keys(slide).filter((key) => regionParts(key)).sort();
6356
6563
  const assets = record8(options.presentation).assets;
6357
6564
  const imageContext = (placement) => ({ fit: hints.imageFit, scale, mirrorSide, diagnostics: imageDiagnostics, assets, ...placement ? { placement } : {} });
6358
- const area = { left: 0, top: 0, right: width, bottom: height };
6565
+ const area2 = { left: 0, top: 0, right: width, bottom: height };
6359
6566
  const topImages = regions.length ? [] : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => !Array.isArray(record8(block).blocks) && record8(block).image !== void 0 ? [{ host: record8(block), hostPath: `${path}.blocks.${index}`, block: index }] : []) : slide.image !== void 0 ? [{ host: { type: "image", image: slide.image }, hostPath: path }] : [];
6360
6567
  const placed = [], usedEdges = /* @__PURE__ */ new Set();
6361
6568
  topImages.forEach((node) => {
@@ -6366,13 +6573,13 @@ function composeSlide(input, options = {}) {
6366
6573
  if (usedEdges.has(edge)) return;
6367
6574
  usedEdges.add(edge);
6368
6575
  const size = finite(value.size, 0.1, 0.9) ?? PLACEMENT_BAND;
6369
- const across = edge === "left" || edge === "right" ? area.right - area.left : area.bottom - area.top;
6576
+ const across = edge === "left" || edge === "right" ? area2.right - area2.left : area2.bottom - area2.top;
6370
6577
  const band = Math.max(0, Math.min(across, round2((edge === "left" || edge === "right" ? width : height) * size)));
6371
- const region = edge === "left" ? { x: area.left, y: area.top, width: band, height: area.bottom - area.top } : edge === "right" ? { x: round2(area.right - band), y: area.top, width: band, height: area.bottom - area.top } : edge === "top" ? { x: area.left, y: area.top, width: area.right - area.left, height: band } : { x: area.left, y: round2(area.bottom - band), width: area.right - area.left, height: band };
6372
- if (edge === "left") area.left += band;
6373
- else if (edge === "right") area.right = region.x;
6374
- else if (edge === "top") area.top += band;
6375
- else area.bottom = region.y;
6578
+ const region = edge === "left" ? { x: area2.left, y: area2.top, width: band, height: area2.bottom - area2.top } : edge === "right" ? { x: round2(area2.right - band), y: area2.top, width: band, height: area2.bottom - area2.top } : edge === "top" ? { x: area2.left, y: area2.top, width: area2.right - area2.left, height: band } : { x: area2.left, y: round2(area2.bottom - band), width: area2.right - area2.left, height: band };
6579
+ if (edge === "left") area2.left += band;
6580
+ else if (edge === "right") area2.right = region.x;
6581
+ else if (edge === "top") area2.top += band;
6582
+ else area2.bottom = region.y;
6376
6583
  const placement = { edge, size, inset: value.inset === true, path: `${node.hostPath}.placement` };
6377
6584
  placed.push({ ...node, placement, region });
6378
6585
  });
@@ -6382,11 +6589,11 @@ function composeSlide(input, options = {}) {
6382
6589
  const measuredFurniture = layoutFurniture(slide, options);
6383
6590
  const furniture = measuredFurniture.configured || measuredFurniture.diagnostics.length ? measuredFurniture : void 0;
6384
6591
  if (furniture) diagnostics.push(...furniture.diagnostics);
6385
- let bodyBottom = Math.min(area.bottom - padding, furniture && furniture.footerTop < height ? furniture.footerTop - gap * 0.5 : height - padding);
6386
- const headingTop = Math.max(area.top + padding, furniture?.headerBottom ? furniture.headerBottom + gap * 0.5 : padding);
6592
+ let bodyBottom = Math.min(area2.bottom - padding, furniture && furniture.footerTop < height ? furniture.footerTop - gap * 0.5 : height - padding);
6593
+ const headingTop = Math.max(area2.top + padding, furniture?.headerBottom ? furniture.headerBottom + gap * 0.5 : padding);
6387
6594
  let footnotes;
6388
6595
  if (citations) {
6389
- footnotes = layoutFootnotes(citations, { ...annotationOptions, x: area.left + padding, width: area.right - area.left - padding * 2, bottom: bodyBottom, maxHeight: Math.max(minSize * 1.22 + 2 * scale, (bodyBottom - headingTop) * FOOTNOTE_MAX_RATIO), path });
6596
+ footnotes = layoutFootnotes(citations, { ...annotationOptions, x: area2.left + padding, width: area2.right - area2.left - padding * 2, bottom: bodyBottom, maxHeight: Math.max(minSize * 1.22 + 2 * scale, (bodyBottom - headingTop) * FOOTNOTE_MAX_RATIO), path });
6390
6597
  diagnostics.push(...footnotes.diagnostics);
6391
6598
  bodyBottom = footnotes.box.y - gap * 0.5;
6392
6599
  }
@@ -6403,8 +6610,8 @@ function composeSlide(input, options = {}) {
6403
6610
  const resolved = resolveLogo(options.presentation, slide, { shape: "full", onDark: options.darkBackground, slideIndex: options.slideIndex });
6404
6611
  if (resolved) {
6405
6612
  const logoHeight = 56 * scale;
6406
- const logoWidth = Math.max(scale, Math.min(4 * logoHeight, area.right - area.left - 2 * padding));
6407
- const box = { x: round2(rtl ? area.right - padding - logoWidth : area.left + padding), y: round2(headingTop), width: round2(logoWidth), height: round2(logoHeight) };
6613
+ const logoWidth = Math.max(scale, Math.min(4 * logoHeight, area2.right - area2.left - 2 * padding));
6614
+ const box = { x: round2(rtl ? area2.right - padding - logoWidth : area2.left + padding), y: round2(headingTop), width: round2(logoWidth), height: round2(logoHeight) };
6408
6615
  logo = { box, shape: resolved.shape, path: resolved.path, source: resolved.source, variant: resolved.variant, reference: resolved.reference, anchor: rtl ? "right" : "left" };
6409
6616
  }
6410
6617
  }
@@ -6414,7 +6621,7 @@ function composeSlide(input, options = {}) {
6414
6621
  if (!slide[field] || Array.isArray(slide[field]) && !annotationText(slide[field])) continue;
6415
6622
  const requested = (field === "title" ? 54 : field === "tag" ? 16 : 25) * scale;
6416
6623
  const maxHeight = Math.max(height * (field === "title" ? 0.26 : field === "subtitle" ? 0.12 : 0.045), minSize * 1.22 + 2 * rasterPadding);
6417
- const box = { x: area.left + padding, y, width: area.right - area.left - padding * 2, height: maxHeight };
6624
+ const box = { x: area2.left + padding, y, width: area2.right - area2.left - padding * 2, height: maxHeight };
6418
6625
  const text2 = fitPlacedText(field, slide[field], annotationText(slide[field]), box, requested, minSize, `${path}.${field}`);
6419
6626
  box.height = Math.min(maxHeight, Math.max("richLines" in text2 ? text2.height : text2.lines.length * text2.lineHeight, text2.placement?.height ?? 0));
6420
6627
  if (furniture && box.y + box.height > bodyBottom + 0.01) diagnostics.push({ code: "text-overflow", path: `${path}.${field}`, message: "Repeated furniture leaves too little room for this heading. Change the header/footer or slide design." });
@@ -6458,7 +6665,7 @@ function composeSlide(input, options = {}) {
6458
6665
  ...caption ? { caption } : {}
6459
6666
  });
6460
6667
  }
6461
- const contentBox = { x: area.left + padding, y, width: area.right - area.left - padding * 2, height: Math.max(scale, bodyBottom - y) };
6668
+ const contentBox = { x: area2.left + padding, y, width: area2.right - area2.left - padding * 2, height: Math.max(scale, bodyBottom - y) };
6462
6669
  const collect = (host, basePath, depth = 0, ancestors = []) => {
6463
6670
  if (Array.isArray(host.blocks)) {
6464
6671
  if (depth >= MAX_COMPOSITION_DEPTH || ancestors.includes(host)) throw new RangeError(`Content groups must be acyclic and nest at most ${MAX_COMPOSITION_DEPTH} levels.`);
@@ -6579,29 +6786,29 @@ function composeSlide(input, options = {}) {
6579
6786
  return score;
6580
6787
  };
6581
6788
  const modeFor = (settings) => settings.mode ?? "auto";
6582
- const defaultColumns = (count, area2, settings) => {
6789
+ const defaultColumns = (count, area3, settings) => {
6583
6790
  const mode = modeFor(settings);
6584
- return mode === "column" ? 1 : mode === "row" ? Math.max(1, count) : settings.columns ?? Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count * area2.width / area2.height / 1.6))));
6791
+ return mode === "column" ? 1 : mode === "row" ? Math.max(1, count) : settings.columns ?? Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count * area3.width / area3.height / 1.6))));
6585
6792
  };
6586
6793
  const scoreNode = (node, box, settings, penalties) => {
6587
6794
  if (!node.children) return leafScore(node, box, settings, penalties);
6588
- const own3 = inheritedSettings(settings, node.composition), area2 = inset(box, own3), slotCount = Math.max(node.children.length, 0);
6589
- const cols = defaultColumns(slotCount, area2, own3);
6590
- const boxes = gridBoxes(slotCount, area2, cols, (own3.gap ?? 1 / 30) * Math.min(box.width, box.height), own3.weights ?? [], modeFor(own3) === "column");
6795
+ const own3 = inheritedSettings(settings, node.composition), area3 = inset(box, own3), slotCount = Math.max(node.children.length, 0);
6796
+ const cols = defaultColumns(slotCount, area3, own3);
6797
+ const boxes = gridBoxes(slotCount, area3, cols, (own3.gap ?? 1 / 30) * Math.min(box.width, box.height), own3.weights ?? [], modeFor(own3) === "column");
6591
6798
  return node.children.reduce((score, child, index) => score + scoreNode(child, boxes[index], own3, penalties), 0);
6592
6799
  };
6593
- const arrange = (nodes, area2, settings, reserved2 = 0, gapOverride, containerPath = path, recorded = true) => {
6800
+ const arrange = (nodes, area3, settings, reserved2 = 0, gapOverride, containerPath = path, recorded = true) => {
6594
6801
  const count = Math.max(nodes.length, reserved2);
6595
6802
  if (!count) return;
6596
- const mode = modeFor(settings), localGap = (settings.gap ?? 1 / 30) * Math.min(area2.width, area2.height);
6803
+ const mode = modeFor(settings), localGap = (settings.gap ?? 1 / 30) * Math.min(area3.width, area3.height);
6597
6804
  const actualGap = gapOverride ?? (settings === rootSettings ? gap : localGap);
6598
- let cols = defaultColumns(count, area2, settings);
6805
+ let cols = defaultColumns(count, area3, settings);
6599
6806
  const hasRegions = nodes.some((node) => node.region);
6600
6807
  const candidates = decisions && recorded ? [] : void 0;
6601
6808
  if (mode === "auto" && !hasRegions) {
6602
6809
  let best = Infinity;
6603
6810
  for (let candidate = 1; candidate <= Math.min(count, settings.columns ?? 6); candidate++) {
6604
- const boxes2 = gridBoxes(count, area2, candidate, actualGap, settings.weights ?? [], false);
6811
+ const boxes2 = gridBoxes(count, area3, candidate, actualGap, settings.weights ?? [], false);
6605
6812
  const emptySlots = (Math.ceil(count / candidate) * candidate - count) * 2;
6606
6813
  const penalties = candidates ? { cellProportions: 0, fontReduction: 0, textOverflow: 0, tableOverflow: 0, smallCells: 0, emptySlots } : void 0;
6607
6814
  const score = nodes.reduce((sum, node, i) => sum + scoreNode(node, boxes2[i], settings, penalties), 0) + emptySlots;
@@ -6619,16 +6826,21 @@ function composeSlide(input, options = {}) {
6619
6826
  ...hasRegions ? {} : { selectedColumns: cols },
6620
6827
  candidates: candidates ?? []
6621
6828
  });
6622
- const grid = gridGeometry(count, area2, cols, actualGap, settings.weights ?? [], mode === "column");
6623
- const mirrored = (box) => rtl ? { ...box, x: area2.x + area2.width - (box.x - area2.x) - box.width } : box;
6829
+ const grid = gridGeometry(count, area3, cols, actualGap, settings.weights ?? [], mode === "column");
6830
+ const mirrored = (box) => rtl ? { ...box, x: area3.x + area3.width - (box.x - area3.x) - box.width } : box;
6624
6831
  const boxes = grid.boxes.map(mirrored);
6625
- if (recorded && !hasRegions) flows.push({ path: containerPath, box: { ...area2 }, composition: { ...settings }, columns: rtl ? grid.columns.map((track) => ({ offset: area2.width - track.offset - track.size, size: track.size })) : grid.columns, rows: grid.rows, gap: grid.gap, itemCount: nodes.length, slotCount: count });
6832
+ if (recorded && !hasRegions) flows.push({ path: containerPath, box: { ...area3 }, composition: { ...settings }, columns: rtl ? grid.columns.map((track) => ({ offset: area3.width - track.offset - track.size, size: track.size })) : grid.columns, rows: grid.rows, gap: grid.gap, itemCount: nodes.length, slotCount: count });
6626
6833
  nodes.forEach((node, index) => {
6627
- const box = node.region ? mirrored(regionBox(node.region, area2, actualGap)) : boxes[index];
6834
+ const box = node.region ? mirrored(regionBox(node.region, area3, actualGap)) : boxes[index];
6628
6835
  placeNode(node, box, settings);
6629
6836
  });
6630
6837
  };
6631
6838
  let regionTag, templateCell = false;
6839
+ const composeChart = (node, box, settings) => {
6840
+ const resolved = resolveChartData(node.value, options.presentation, { path: node.path });
6841
+ const valueAxis = chartValueAxis(node.value, resolved, { width: box.width, height: box.height, scale, minFontSize: settings.minFontSize });
6842
+ return valueAxis ? { valueAxis } : void 0;
6843
+ };
6632
6844
  const placeNode = (node, box, settings, cards = hasCards, listColumns, metricReferenceHeight) => {
6633
6845
  if (node.children) {
6634
6846
  const own3 = inheritedSettings(settings, node.composition), inner = inset(box, own3);
@@ -6653,6 +6865,7 @@ function composeSlide(input, options = {}) {
6653
6865
  const columns2 = textValue !== void 0 && !internal ? listColumnsFor(node, box, settings, listColumns) : void 0;
6654
6866
  const text2 = internal ? body?.fit : columns2 ? columns2.length > 1 ? mergeListColumns(columns2) : columns2[0].text : textValue !== void 0 ? fitContent(node.field, node.value, textValue, box, contentNominalSize(node.field) * scale, snapFontSizeUp((settings.minFontSize ?? 16) * scale), node.path, node.payload.numbering) : void 0;
6655
6867
  const image = node.field === "image" ? composeImage(node.host ?? {}, node.hostPath ?? node.path, box, box, imageContext()) : void 0;
6868
+ const chart = node.field === "chart" ? composeChart(node, box, settings) : void 0;
6656
6869
  items.push({
6657
6870
  path: node.path,
6658
6871
  field: node.field,
@@ -6661,6 +6874,7 @@ function composeSlide(input, options = {}) {
6661
6874
  payload: node.payload,
6662
6875
  box: internal ? acceptedBox(box) : box,
6663
6876
  ...image ? { image } : {},
6877
+ ...chart ? { chart } : {},
6664
6878
  ...frameBox ? { frameBox } : {},
6665
6879
  text: text2,
6666
6880
  textStyle: body?.style ?? styleFor(node.field, node.path),
@@ -6877,9 +7091,9 @@ function composeSlide(input, options = {}) {
6877
7091
  const physical = (cell) => flip ? { ...cell, x: frame.x + frame.width - (cell.x - frame.x) - cell.width } : cell;
6878
7092
  const bleed = (cell) => {
6879
7093
  const near = (a, b) => Math.abs(a - b) < 0.01;
6880
- const left = near(cell.x, frame.x) ? area.left : cell.x, top = near(cell.y, frame.y) ? area.top : cell.y;
6881
- const right = near(cell.x + cell.width, frame.x + frame.width) ? area.right : cell.x + cell.width;
6882
- const bottom = near(cell.y + cell.height, frame.y + frame.height) ? area.bottom : cell.y + cell.height;
7094
+ const left = near(cell.x, frame.x) ? area2.left : cell.x, top = near(cell.y, frame.y) ? area2.top : cell.y;
7095
+ const right = near(cell.x + cell.width, frame.x + frame.width) ? area2.right : cell.x + cell.width;
7096
+ const bottom = near(cell.y + cell.height, frame.y + frame.height) ? area2.bottom : cell.y + cell.height;
6883
7097
  return { x: left, y: top, width: right - left, height: bottom - top };
6884
7098
  };
6885
7099
  const placeHeadings = (name, fields2) => {
@@ -6915,9 +7129,9 @@ function composeSlide(input, options = {}) {
6915
7129
  const slideComposition = record8(slide.composition);
6916
7130
  const overridden = ["mode", "columns", "weights"].some((key) => slideComposition[key] !== void 0) ? plan2.regions.find((region) => region.role === "primary" && region.flow !== "none")?.name : void 0;
6917
7131
  const settings = { ...composition };
6918
- const placeRegion = (region, units, area2) => {
7132
+ const placeRegion = (region, units, area3) => {
6919
7133
  regionTag = region.name === BELOW ? void 0 : region.name;
6920
- const cell = { ...area2, width: Math.max(scale, area2.width), height: Math.max(scale, area2.height) };
7134
+ const cell = { ...area3, width: Math.max(scale, area3.width), height: Math.max(scale, area3.height) };
6921
7135
  templateCell = true;
6922
7136
  try {
6923
7137
  const cards = hasCards && !region.bleed;
@@ -7065,4 +7279,4 @@ function resolveCanvasDimensions(input) {
7065
7279
  return { width, height };
7066
7280
  }
7067
7281
 
7068
- export { AUTO_LAYOUT, CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CATALOG_GROUP_PATTERN, CATALOG_REFERENCE_PATTERN, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, FURNITURE_GAP, FURNITURE_IMAGE_SHARE, HEADING_AREAS, LANGUAGES, LAYOUT_BODY_KINDS, LOGO_SHAPES, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_REGION_BLOCKS, MAX_ROMAN_VALUE, MAX_TEMPLATE_TRACKS, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCatalogsOptionError, OPFCompositionError, OPFLayoutTemplateError, OPFUnresolvedReferenceError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, PROMOTED_REGION_KEY, REGION_ANCHORS, REGION_FLOWS, REGION_KINDS, REGION_ROLES, RESERVED_REGION_NAMES, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, THEME_DESIGN_KEYS, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, allocateRows, annotationText, bindRegions, blockKind, captionSettings, catalogGroupDeclared, catalogGroupSource, catalogKinds, catalogRecords, catalogReferenceSites, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, checkCatalogsOption, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeLayoutAreas, composeSlide, datasetDiagnostics, defaultSlideBackground, dottedPath, embeddedCopy, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatVariableNumber, fromExcelNumberFormat, gridFlowShape, hasContentVariables, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isLayoutTemplate, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutMetric, layoutQuote, layoutRegion, layoutStructure, layoutTable, layoutTemplate, layoutTemplateIssues, layoutTimeline, layoutWatermark, listBuiltinVariables, listColumnBreaks, listNumbers, listVariables, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, parseReference, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, placedImageBlocks, pointerPath, promotedRegionKeys, provenanceOf, readingRows, recordReferenceSites, referencesSlide, regionAccepts, regionRoleRank, registeredCatalog, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveReference, resolveScriptFonts, resolveSlideDirection, resolveSlideVariables, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, rootNodes, scriptFontRole, sliceNumberedItems, slideCitations, slideTokenSpans, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unresolvedReference, unusedDatasets, usesSlideBuiltin, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };
7282
+ export { AUTO_LAYOUT, CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CATALOG_GROUP_PATTERN, CATALOG_REFERENCE_PATTERN, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_SERIES_TIER_SIZE, CHART_SERIES_TINT_MIX, CHART_TEXT_PX, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, FURNITURE_GAP, FURNITURE_IMAGE_SHARE, FURNITURE_ZONE_GUTTER, HEADING_AREAS, LANGUAGES, LAYOUT_BODY_KINDS, LOGO_SHAPES, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_REGION_BLOCKS, MAX_ROMAN_VALUE, MAX_TEMPLATE_TRACKS, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCatalogsOptionError, OPFCompositionError, OPFLayoutTemplateError, OPFUnresolvedReferenceError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, PROMOTED_REGION_KEY, REGION_ANCHORS, REGION_FLOWS, REGION_KINDS, REGION_ROLES, RESERVED_REGION_NAMES, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, THEME_DESIGN_KEYS, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, allocateRows, annotationText, bindRegions, blockKind, captionSettings, catalogGroupDeclared, catalogGroupSource, catalogKinds, catalogRecords, catalogReferenceSites, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, chartSeriesPalette, chartTextSize, chartValueAxis, checkCatalogsOption, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeLayoutAreas, composeSlide, datasetDiagnostics, defaultSlideBackground, dottedPath, embeddedCopy, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatVariableNumber, fromExcelNumberFormat, gridFlowShape, hasContentVariables, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isLayoutTemplate, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutMetric, layoutQuote, layoutRegion, layoutStructure, layoutTable, layoutTemplate, layoutTemplateIssues, layoutTimeline, layoutWatermark, listBuiltinVariables, listColumnBreaks, listNumbers, listVariables, maxIntervalsFor, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, niceScale, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, parseReference, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, placedImageBlocks, pointerPath, promotedRegionKeys, provenanceOf, readingRows, recordReferenceSites, referencesSlide, regionAccepts, regionRoleRank, registeredCatalog, resolveCanvasDimensions, resolveChartColors, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveReference, resolveScriptFonts, resolveSlideDirection, resolveSlideVariables, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, rootNodes, scriptFontRole, sliceNumberedItems, slideCitations, slideTokenSpans, snapFontSizeDown, snapFontSizeUp, stackCategoryValues, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unresolvedReference, unusedDatasets, usesSlideBuiltin, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };