@silurus/ooxml 0.70.2 → 0.71.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.
@@ -46,14 +46,23 @@ export declare interface BorderEdge {
46
46
  export declare interface Cell {
47
47
  col: number;
48
48
  row: number;
49
- colRef: string;
50
49
  value: CellValue;
51
- styleIndex: number;
50
+ /** Style index into the styles table. Omitted on the wire when `0` (the
51
+ * common unstyled case), so read it as `styleIndex ?? 0`. */
52
+ styleIndex?: number;
52
53
  /** Raw `<f>` formula text (ECMA-376 §18.3.1.40), when present. The renderer
53
54
  * uses this to recompute volatile functions (TODAY, NOW) at display time
54
55
  * so the cached `<v>` — frozen when the file was last saved — doesn't
55
56
  * show a stale date. */
56
57
  formula?: string;
58
+ /** Whether this cell displays its phonetic hint (furigana). The parser
59
+ * resolves it as `cell/@ph ?? row/@ph ?? false` — the per-cell `<c ph>`
60
+ * (ECMA-376 §18.3.1.4) wins when present (an explicit `ph="0"` overrides an
61
+ * enabled row), otherwise the row-level `<row ph>` (§18.3.1.73) is inherited,
62
+ * otherwise the schema default (false). Omitted on the wire when false, so
63
+ * read as `showPhonetic ?? false`. A cell whose String Item carries `<rPh>`
64
+ * runs still shows NO furigana unless the resolved value is true. */
65
+ showPhonetic?: boolean;
57
66
  }
58
67
 
59
68
  export declare interface CellAddress {
@@ -102,6 +111,15 @@ export declare type CellValue = {
102
111
  type: 'text';
103
112
  text: string;
104
113
  runs?: Run[];
114
+ /** ECMA-376 §18.4.6 phonetic runs (furigana) carried over from the
115
+ * resolved String Item. Present for inline strings, and populated by
116
+ * {@link resolveSharedStrings} for shared-string cells. Absent when the
117
+ * string has no furigana. */
118
+ phoneticRuns?: PhoneticRun[];
119
+ /** ECMA-376 §18.4.3 phonetic display properties (font index / char set /
120
+ * alignment) for the furigana above. Absent when the `<si>` had no
121
+ * `<phoneticPr>`. */
122
+ phoneticPr?: PhoneticProperties;
105
123
  } | {
106
124
  type: 'number';
107
125
  number: number;
@@ -111,6 +129,14 @@ export declare type CellValue = {
111
129
  } | {
112
130
  type: 'error';
113
131
  error: string;
132
+ }
133
+ /** Shared-string reference into `ParsedWorkbook.sharedStrings` (ECMA-376
134
+ * §18.4.8). Resolved to `{ type: 'text', ... }` by the workbook before the
135
+ * renderer (or any other consumer) sees it, so downstream code never
136
+ * encounters this variant. */
137
+ | {
138
+ type: 'shared';
139
+ si: number;
114
140
  };
115
141
 
116
142
  export declare interface CellXf {
@@ -223,6 +249,30 @@ export declare interface ChartDataLabelOverride {
223
249
  fontSizeHpt?: number;
224
250
  /** `<a:defRPr b="1">` inside the per-idx rich text. */
225
251
  fontBold?: boolean;
252
+ /** Per-point callout box (`<c:dLbl><c:spPr>`, ECMA-376 §21.2.2.47/§21.2.2.197):
253
+ * overrides the series-default box for this one slice. */
254
+ labelBox?: ChartLabelBox;
255
+ /**
256
+ * Per-point label-content flags (`<c:dLbl>` §21.2.2.47 carries the same
257
+ * show-flag group as the series `<c:dLbls>` §21.2.2.49: §21.2.2.189
258
+ * `<c:showVal>`, §21.2.2.177 `<c:showCatName>`, §21.2.2.180 `<c:showSerName>`,
259
+ * §21.2.2.187 `<c:showPercent>`). When present they OVERRIDE the series-level
260
+ * defaults for that one point (e.g. sample-14 slide-7's pie sets
261
+ * `showCatName=0 showPercent=1` per slice while the series default is
262
+ * `showCatName=1`, so each label is percent only). undefined = inherit the
263
+ * series default for that flag.
264
+ */
265
+ showVal?: boolean;
266
+ showCatName?: boolean;
267
+ showSerName?: boolean;
268
+ showPercent?: boolean;
269
+ /**
270
+ * `<c:dLbl><c:delete val="1"/>` (ECMA-376 §21.2.2.43) — the point's label is
271
+ * removed. Distinguishes a genuine delete from a `<c:dLbl>` that only carries
272
+ * style / flag overrides with no `<c:tx>` (both otherwise present as
273
+ * `text === ''`). true = skip the label; undefined/absent = not deleted.
274
+ */
275
+ deleted?: boolean;
226
276
  }
227
277
 
228
278
  export declare interface ChartDataPointOverride {
@@ -233,6 +283,18 @@ export declare interface ChartDataPointOverride {
233
283
  markerSize?: number;
234
284
  markerFill?: string;
235
285
  markerLine?: string;
286
+ /**
287
+ * `<c:dPt><c:explosion val>` (ECMA-376 §21.2.2.61) — the amount this
288
+ * pie/doughnut slice is moved out from the center. The schema type is
289
+ * `CT_UnsignedInt` (unbounded `xsd:unsignedInt`); the spec text only says
290
+ * "the amount the data point shall be moved from the center of the pie"
291
+ * and does not itself define units or a 0–100 range. We treat it as a
292
+ * de-facto percentage of the outer radius (0–100 typical), matching
293
+ * Office's UI (the Point Explosion slider caps at 100%) rather than a
294
+ * spec-mandated bound. undefined/absent = 0 (no explosion, flush with the
295
+ * ring). Only consulted by the pie/doughnut renderer.
296
+ */
297
+ explosion?: number;
236
298
  }
237
299
 
238
300
  export declare interface ChartErrBars {
@@ -250,6 +312,74 @@ export declare interface ChartErrBars {
250
312
  dash?: string;
251
313
  }
252
314
 
315
+ /**
316
+ * One box-and-whisker series (chartEx `boxWhisker`, MS 2014 chartex ext). Each
317
+ * `<cx:series>` references its own raw sample points via `<cx:dataId>`; the
318
+ * parser groups them by category and threads the `<cx:layoutPr>` flags. The
319
+ * renderer derives the statistics.
320
+ */
321
+ declare interface ChartexBoxSeries {
322
+ /** Series display name (`<cx:tx><cx:v>`). */
323
+ name: string;
324
+ /** Fill (hex, no '#') — theme accent cycled by series index. null = fall
325
+ * back to the renderer palette. */
326
+ color?: string | null;
327
+ /** Raw sample values grouped by category (outer = category index parallel to
328
+ * {@link ChartexBoxWhisker.categories}, inner = the points in that group). */
329
+ valuesByCategory: number[][];
330
+ /** `<cx:visibility meanMarker>` — draw the mean `×`. */
331
+ meanMarker: boolean;
332
+ /** `<cx:visibility meanLine>` — draw a mean connector line across categories. */
333
+ meanLine: boolean;
334
+ /** `<cx:visibility outliers>` — draw outlier points. */
335
+ showOutliers: boolean;
336
+ /** `<cx:visibility nonoutliers>` — draw the interior (non-outlier) sample
337
+ * points as jittered dots on top of the box. Flag parsed; interior-dot
338
+ * rendering is pending a fixture that enables it (every sample-24 series
339
+ * ships `nonoutliers="0"`, so there is nothing to verify against yet). */
340
+ showNonoutliers: boolean;
341
+ /** `<cx:statistics quartileMethod>` — "exclusive" (Excel default) | "inclusive". */
342
+ quartileMethod: string;
343
+ }
344
+
345
+ /** A chartEx box-and-whisker chart: unique categories + one series per column. */
346
+ declare interface ChartexBoxWhisker {
347
+ /** Unique category labels in first-seen order. */
348
+ categories: string[];
349
+ /** One entry per `<cx:series>`. */
350
+ series: ChartexBoxSeries[];
351
+ }
352
+
353
+ /** A chartEx sunburst: the flat rows the renderer folds into a ring tree. */
354
+ declare interface ChartexSunburst {
355
+ rows: ChartexSunburstRow[];
356
+ }
357
+
358
+ /**
359
+ * One row of a chartEx `sunburst`: the branch→…→leaf label chain (empty
360
+ * trailing segments trimmed) and its size value.
361
+ */
362
+ declare interface ChartexSunburstRow {
363
+ /** Label chain root→leaf. */
364
+ path: string[];
365
+ /** `<cx:numDim type="size">` value attaching to the deepest node in `path`. */
366
+ size: number;
367
+ }
368
+
369
+ /** Callout-box style for a pie/doughnut data label — the white (or themed)
370
+ * rounded rectangle with a thin border Word draws around a `bestFit` label
371
+ * placed outside its slice. From the label's `<c:spPr>` (§21.2.2.197). All
372
+ * fields optional: absent → transparent / unbordered. Mirror of Rust
373
+ * `ChartLabelBox`. */
374
+ declare interface ChartLabelBox {
375
+ /** `<a:solidFill>` resolved hex (no `#`). Box background. */
376
+ fill?: string;
377
+ /** `<a:ln><a:solidFill>` resolved hex (no `#`). Border stroke. */
378
+ borderColor?: string;
379
+ /** `<a:ln w>` border width in EMU (12700 EMU = 1 pt). */
380
+ borderWidthEmu?: number;
381
+ }
382
+
253
383
  /**
254
384
  * `<c:manualLayout>` block. Fractions are of the chart-space rect.
255
385
  * `xMode`/`yMode`: "edge" = absolute fraction from top-left, "factor" =
@@ -380,6 +510,35 @@ export declare interface ChartModel {
380
510
  valAxisTitleFontBold?: boolean | null;
381
511
  /** `<c:valAx><c:title>` run-prop color (hex without '#'). null = default. */
382
512
  valAxisTitleFontColor?: string | null;
513
+ /** `<c:catAx><c:txPr>…<a:latin typeface>` tick-label font. */
514
+ catAxisFontFace?: string | null;
515
+ /** `<c:valAx><c:txPr>…<a:latin typeface>` tick-label font. */
516
+ valAxisFontFace?: string | null;
517
+ /** `<c:catAx><c:title>…<a:latin typeface>` axis-title font. */
518
+ catAxisTitleFontFace?: string | null;
519
+ /** `<c:valAx><c:title>…<a:latin typeface>` axis-title font. */
520
+ valAxisTitleFontFace?: string | null;
521
+ /** `<c:dLbls><c:txPr>…<a:latin typeface>` data-label font. */
522
+ dataLabelFontFace?: string | null;
523
+ /** `<c:legend><c:txPr>…<a:latin typeface>` legend font. */
524
+ legendFontFace?: string | null;
525
+ /** `<c:legend><c:txPr>…<a:solidFill>` legend text color (hex without '#'). */
526
+ legendFontColor?: string | null;
527
+ /** `<c:legend><c:txPr>` legend font size (OOXML hundredths of a point). */
528
+ legendFontSizeHpt?: number | null;
529
+ /** `<c:legend><c:txPr>…defRPr@b` legend bold flag. */
530
+ legendFontBold?: boolean | null;
531
+ /**
532
+ * Theme font-scheme faces (`<a:fontScheme>`, ECMA-376 §20.1.4.2). Latin
533
+ * heading (majorFont) and body (minorFont) typefaces, used as the fallback
534
+ * for any chart text element whose own `<c:txPr>` supplies no `<a:latin>`.
535
+ * null when the theme is not threaded to the chart (then the renderer's
536
+ * built-in sans-serif remains, byte-stable). Axis titles / chart title use
537
+ * the major (heading) face; tick labels / data labels / legend use the
538
+ * minor (body) face — matching Office's default chart text styling.
539
+ */
540
+ themeMajorFontLatin?: string | null;
541
+ themeMinorFontLatin?: string | null;
383
542
  /** Explicit chart border color (hex without '#') from
384
543
  * `<c:chartSpace><c:spPr><a:ln><a:solidFill><a:srgbClr>`. Only set when the
385
544
  * XML explicitly declares a paintable line; null otherwise (no default
@@ -454,6 +613,163 @@ export declare interface ChartModel {
454
613
  * value axis (the common case). See {@link SecondaryValueAxis}.
455
614
  */
456
615
  secondaryValAxis?: SecondaryValueAxis | null;
616
+ /**
617
+ * `<c:date1904>` (ECMA-376 §21.2.2.38). When true the chart's serial
618
+ * date-times resolve against the 1904 date system (base 1904-01-01) instead
619
+ * of the default 1900 system. Threaded to the date formatters for date-axis
620
+ * category labels and value-axis tick labels. Omitted/false ⇒ 1900 system.
621
+ * Note: per §21.2.2.38 the element's `val` defaults to true when present but
622
+ * the attribute is omitted, so `<c:date1904/>` alone means date1904=true.
623
+ */
624
+ date1904?: boolean;
625
+ /**
626
+ * `<c:doughnutChart><c:holeSize val>` (ECMA-376 §21.2.2.60,
627
+ * `ST_HoleSizePercent` §21.2.3.55) — the doughnut hole diameter as a
628
+ * percentage 1–90 of the outer diameter. Ignored for pie (which has no
629
+ * hole). null/undefined = use the renderer's doughnut default when the
630
+ * element is absent. Note the ECMA `CT_HoleSize` schema default is 10%, but
631
+ * a real doughnut file always writes an explicit `<c:holeSize>` (Excel /
632
+ * PowerPoint emit 50–75%); the renderer falls back to 50% only for the
633
+ * pathological absent case.
634
+ */
635
+ holeSize?: number | null;
636
+ /**
637
+ * `<c:pieChart | doughnutChart><c:firstSliceAng val>` (ECMA-376 §21.2.2.52,
638
+ * `ST_FirstSliceAng` §21.2.3.15) — the angle in degrees (0–360, clockwise
639
+ * from the 12 o'clock position) at which the first slice begins.
640
+ * null/undefined = 0 (start at 12 o'clock), which matches the renderer's
641
+ * historical fixed −90° (canvas up) start.
642
+ */
643
+ firstSliceAngle?: number | null;
644
+ /**
645
+ * `<c:chartSpace><c:chart><c:dispBlanksAs val>` (ECMA-376 §21.2.2.42,
646
+ * `ST_DispBlanksAs` §21.2.3.10) — how blank (null) cells are plotted on
647
+ * line/area charts:
648
+ * - "gap" → leave a gap (break the line). The renderer's historical
649
+ * behavior and the model default when the element is absent.
650
+ * - "zero" → plot the blank as the value 0 (the point drops to the axis).
651
+ * - "span" → skip the blank but connect its neighbours with a straight
652
+ * line (bridge the gap).
653
+ * Note the XSD `@val` default is "zero" (applies when `<c:dispBlanksAs/>` is
654
+ * present but the attribute is omitted); when the ELEMENT is absent entirely
655
+ * Office falls back to "gap", which is what we model as the default. Only
656
+ * consulted for the line and area families. null/undefined = "gap".
657
+ */
658
+ dispBlanksAs?: string | null;
659
+ /**
660
+ * `<c:valAx><c:majorGridlines>` presence (ECMA-376 §21.2.2.100). `false` when
661
+ * the value axis exists but omits the element (Office suppresses value
662
+ * gridlines). null/undefined ⇒ the renderer's historical always-on value
663
+ * gridlines (byte-stable). `true` is redundant with the default but honored.
664
+ */
665
+ valAxisMajorGridlines?: boolean | null;
666
+ /**
667
+ * `<c:catAx><c:majorGridlines>` presence (§21.2.2.100). `true` turns on
668
+ * category-axis gridlines (Office omits them by default). null/undefined/false
669
+ * ⇒ no category gridlines (the historical default, byte-stable).
670
+ */
671
+ catAxisMajorGridlines?: boolean | null;
672
+ /**
673
+ * `<c:valAx><c:majorGridlines><c:spPr><a:ln><a:solidFill>` resolved gridline
674
+ * color (hex without `#`) — ECMA-376 §21.2.2.100. When set, the value-axis
675
+ * major gridlines are stroked in this color instead of the renderer's faint
676
+ * `#e0e0e0` default (e.g. sample-1 slide 5's `accent3` gridlines). null/absent
677
+ * ⇒ the historical default (byte-stable).
678
+ */
679
+ valAxisGridlineColor?: string | null;
680
+ /**
681
+ * `<c:valAx><c:majorGridlines><c:spPr><a:ln w>` gridline width in EMU. When
682
+ * set, the value-axis gridline stroke width is derived from this (floored so a
683
+ * hairline stays visible). null/absent ⇒ the renderer's 0.5 px default.
684
+ */
685
+ valAxisGridlineWidthEmu?: number | null;
686
+ /**
687
+ * `<c:catAx><c:majorGridlines><c:spPr><a:ln><a:solidFill>` resolved gridline
688
+ * color (hex without `#`). Only meaningful when {@link catAxisMajorGridlines}
689
+ * is on. null/absent ⇒ the faint default.
690
+ */
691
+ catAxisGridlineColor?: string | null;
692
+ /** `<c:catAx><c:majorGridlines><c:spPr><a:ln w>` gridline width in EMU. */
693
+ catAxisGridlineWidthEmu?: number | null;
694
+ /** `<c:valAx><c:minorGridlines>` presence (§21.2.2.109). Only drawn when a
695
+ * minor step is resolvable (see {@link valAxisMinorUnit}). */
696
+ valAxisMinorGridlines?: boolean | null;
697
+ /**
698
+ * `<c:valAx><c:majorUnit val>` (§21.2.2.103) — explicit distance between major
699
+ * gridlines/ticks, overriding the Excel-style auto "nice" step. null/undefined
700
+ * ⇒ auto step (byte-stable).
701
+ */
702
+ valAxisMajorUnit?: number | null;
703
+ /** `<c:valAx><c:minorUnit val>` (§21.2.2.112) — explicit minor step. Drives
704
+ * minor gridlines/ticks when present. null ⇒ no minor divisions. */
705
+ valAxisMinorUnit?: number | null;
706
+ /**
707
+ * `<c:valAx><c:scaling><c:logBase val>` (§21.2.2.98, `ST_LogBase` §21.2.3.25)
708
+ * — logarithmic value-axis base (>= 2). When set, values map to pixels in log
709
+ * space and gridlines fall on powers of the base. null/undefined ⇒ linear
710
+ * (byte-stable).
711
+ */
712
+ valAxisLogBase?: number | null;
713
+ /**
714
+ * `<c:valAx><c:scaling><c:orientation val>` (§21.2.2.130, `ST_Orientation`
715
+ * §21.2.3.30) — "minMax" (normal) | "maxMin" (reversed, so the value axis runs
716
+ * top→bottom max→min). null/undefined/"minMax" ⇒ normal (byte-stable).
717
+ */
718
+ valAxisOrientation?: 'minMax' | 'maxMin' | string | null;
719
+ /** `<c:catAx><c:scaling><c:orientation val>` — "maxMin" reverses the category
720
+ * axis left↔right. null/"minMax" ⇒ normal. */
721
+ catAxisOrientation?: 'minMax' | 'maxMin' | string | null;
722
+ /**
723
+ * `<c:catAx><c:tickLblPos val>` (§21.2.2.207, `ST_TickLblPos` §21.2.3.47) —
724
+ * "nextTo" (default) | "low" | "high" | "none". "none" hides the category tick
725
+ * labels. null/undefined ⇒ nextTo (byte-stable).
726
+ */
727
+ catAxisTickLabelPos?: string | null;
728
+ /** `<c:valAx><c:tickLblPos val>` (§21.2.2.207). "none" hides value tick labels. */
729
+ valAxisTickLabelPos?: string | null;
730
+ /**
731
+ * `<c:catAx><c:txPr><a:bodyPr rot>` (DrawingML `ST_Angle`, 60000ths of a
732
+ * degree) — category tick-label rotation. e.g. -2700000 = -45°. null/undefined
733
+ * /0 ⇒ horizontal labels (byte-stable).
734
+ */
735
+ catAxisLabelRotation?: number | null;
736
+ /**
737
+ * `<c:stockChart><c:hiLowLines>` presence (ECMA-376 §21.2.2.60). When true
738
+ * the stock renderer draws a vertical line spanning each category's low↔high
739
+ * value. Only set for `chartType === "stock"`; null/undefined on every other
740
+ * chart type (byte-stable).
741
+ */
742
+ stockHiLowLines?: boolean | null;
743
+ /**
744
+ * `<c:hiLowLines><c:spPr><a:ln><a:solidFill>` resolved color (hex, no `#`).
745
+ * null = the renderer's default gray hi-lo line.
746
+ */
747
+ stockHiLowLineColor?: string | null;
748
+ /**
749
+ * `<c:stockChart><c:upDownBars>` presence (ECMA-376 §21.2.2.227). Parsed so a
750
+ * stock file carrying open-close up/down bars is recognized; the renderer does
751
+ * NOT yet draw them (tracked follow-up). null/undefined when absent.
752
+ */
753
+ stockUpDownBars?: boolean | null;
754
+ /**
755
+ * Structured box-and-whisker data (`chartType === 'boxWhisker'`). Present
756
+ * ONLY for boxWhisker charts; null/absent otherwise so the flat
757
+ * `categories`/`series` model the other chartEx renderers consume is
758
+ * untouched. The renderer computes quartiles / mean / whiskers / outliers.
759
+ */
760
+ chartexBox?: ChartexBoxWhisker | null;
761
+ /**
762
+ * Structured sunburst hierarchy (`chartType === 'sunburst'`). Present ONLY
763
+ * for sunburst charts; null/absent otherwise.
764
+ */
765
+ chartexSunburst?: ChartexSunburst | null;
766
+ /**
767
+ * Theme accent palette (`accent1..6`, hex without '#') for chartEx charts
768
+ * that color by branch/series index (boxWhisker series, sunburst branches).
769
+ * null/absent when the resolver supplies no default palette (pptx); the
770
+ * renderer then falls back to its own `CHART_PALETTE`.
771
+ */
772
+ chartexAccents?: string[] | null;
457
773
  }
458
774
 
459
775
  export declare interface ChartSeries {
@@ -563,6 +879,31 @@ export declare interface ChartSeries {
563
879
  * series.
564
880
  */
565
881
  bubbleSizes?: (number | null)[] | null;
882
+ /**
883
+ * `<c:ser><c:smooth val>` (ECMA-376 §21.2.2.194) — line/area series flag
884
+ * requesting a smoothed (spline) curve through the points instead of straight
885
+ * segments. Only consulted for the line and area families (scatter carries its
886
+ * smoothing in `ChartModel.scatterStyle`). null/undefined/false = straight
887
+ * polyline (the default; byte-stable for series that never set it).
888
+ */
889
+ smooth?: boolean | null;
890
+ /**
891
+ * `<c:ser><c:trendline>` per-series trendlines (ECMA-376 §21.2.2.211,
892
+ * `CT_Trendline`). A series can carry several (e.g. a linear fit + a moving
893
+ * average). null/undefined/empty = no trendline (the default; byte-stable for
894
+ * series that never declare one).
895
+ */
896
+ trendLines?: ChartTrendline[] | null;
897
+ /**
898
+ * `<c:ser><c:spPr><a:ln><a:noFill/>` (ECMA-376 §21.2.2.198 CT_ShapeProperties
899
+ * → DrawingML §20.1.2.2.24 CT_LineProperties). true when the series connecting
900
+ * line is explicitly turned OFF. For a scatter/line series this OVERRIDES the
901
+ * chart-group `<c:scatterStyle>` (§21.2.2.42) / line default — Excel and
902
+ * PowerPoint draw markers only (no connecting line) even when the group style
903
+ * is `lineMarker`. null/undefined = no explicit line-off, so the group default
904
+ * governs (byte-stable for series that carry a paintable line).
905
+ */
906
+ lineHidden?: boolean | null;
566
907
  }
567
908
 
568
909
  export declare interface ChartSeriesDataLabels {
@@ -577,6 +918,51 @@ export declare interface ChartSeriesDataLabels {
577
918
  fontBold?: boolean;
578
919
  /** Series-level font size for data labels (OOXML hundredths of a point). */
579
920
  fontSizeHpt?: number;
921
+ /** Series-default callout box (`<c:dLbls><c:spPr>`, ECMA-376 §21.2.2.49/
922
+ * §21.2.2.197). When present the pie/doughnut renderer draws Word's boxed
923
+ * callout layout (box + optional leader line) instead of plain text. */
924
+ labelBox?: ChartLabelBox;
925
+ /** `<c:dLbls><c:showLeaderLines val>` (§21.2.2.183) — draw leader lines from
926
+ * a pulled-away label back to its slice. Default false. */
927
+ showLeaderLines?: boolean;
928
+ /** `<c:leaderLines><c:spPr><a:ln><a:solidFill>` (§21.2.2.92) resolved hex
929
+ * (no `#`). undefined → renderer uses a neutral grey. */
930
+ leaderLineColor?: string;
931
+ /** `<c:leaderLines><c:spPr><a:ln w>` leader-line width in EMU. */
932
+ leaderLineWidthEmu?: number;
933
+ }
934
+
935
+ /**
936
+ * `<c:ser><c:trendline>` (ECMA-376 §21.2.2.211). A regression/smoothing curve
937
+ * fitted to the series' data points.
938
+ */
939
+ declare interface ChartTrendline {
940
+ /**
941
+ * `<c:trendlineType val>` (§21.2.2.213, `ST_TrendlineType` §21.2.3.50):
942
+ * "linear" | "exp" | "log" | "power" | "poly" | "movingAvg". The renderer
943
+ * currently draws "linear" (least squares) and "movingAvg"; other types parse
944
+ * but are not yet plotted (tracked as a follow-up).
945
+ */
946
+ trendlineType: string;
947
+ /** `<c:order val>` — polynomial order (`poly`, default 2). */
948
+ order?: number | null;
949
+ /** `<c:period val>` — moving-average window (`movingAvg`, default 2). */
950
+ period?: number | null;
951
+ /** `<c:forward val>` — units to extend the line past the last point. */
952
+ forward?: number | null;
953
+ /** `<c:backward val>` — units to extend the line before the first point. */
954
+ backward?: number | null;
955
+ /** `<c:intercept val>` — forced y-intercept (linear/exp). null = free fit. */
956
+ intercept?: number | null;
957
+ /** `<c:dispRSqr val="1">` — show the R² value (label; not yet rendered). */
958
+ dispRSqr?: boolean | null;
959
+ /** `<c:dispEq val="1">` — show the fit equation (label; not yet rendered). */
960
+ dispEq?: boolean | null;
961
+ /** `<c:spPr><a:ln><a:solidFill>` trendline color (hex without '#'). null =
962
+ * inherit the series color. */
963
+ lineColor?: string | null;
964
+ /** `<c:spPr><a:ln w>` trendline width in EMU. */
965
+ lineWidthEmu?: number | null;
580
966
  }
581
967
 
582
968
  /**
@@ -584,7 +970,7 @@ export declare interface ChartSeriesDataLabels {
584
970
  * grouping (`Pct` = percent-stacked) so renderers do not need to inspect
585
971
  * separate `barDir`/`grouping` fields.
586
972
  */
587
- declare type ChartType = 'line' | 'stackedLine' | 'stackedLinePct' | 'clusteredBar' | 'clusteredBarH' | 'stackedBar' | 'stackedBarH' | 'stackedBarPct' | 'stackedBarHPct' | 'area' | 'stackedArea' | 'stackedAreaPct' | 'pie' | 'doughnut' | 'scatter' | 'bubble' | 'radar' | 'waterfall' | string;
973
+ declare type ChartType = 'line' | 'stackedLine' | 'stackedLinePct' | 'clusteredBar' | 'clusteredBarH' | 'stackedBar' | 'stackedBarH' | 'stackedBarPct' | 'stackedBarHPct' | 'area' | 'stackedArea' | 'stackedAreaPct' | 'pie' | 'doughnut' | 'scatter' | 'bubble' | 'radar' | 'waterfall' | 'stock' | 'boxWhisker' | 'sunburst' | string;
588
974
 
589
975
  export declare interface ConditionalFormat {
590
976
  sqref: CellRange_2[];
@@ -624,6 +1010,16 @@ export declare interface DefinedName {
624
1010
  formula: string;
625
1011
  }
626
1012
 
1013
+ /** ECMA-376 §20.1.8.23 `<a:duotone>` image effect, resolved to its two endpoint
1014
+ * colours (mirrors the shared Rust `ooxml_common::blip::Duotone`). `clr1` is the
1015
+ * dark endpoint (luminance 0), `clr2` the light endpoint (luminance 1); both are
1016
+ * 6-char uppercase hex WITHOUT a leading `#`, with per-colour transforms already
1017
+ * applied by the parser. */
1018
+ export declare interface Duotone {
1019
+ clr1: string;
1020
+ clr2: string;
1021
+ }
1022
+
627
1023
  export declare interface Dxf {
628
1024
  font: CellFont | null;
629
1025
  fill: CellFill | null;
@@ -638,6 +1034,47 @@ export declare interface Dxf {
638
1034
  /** @deprecated Use `ChartErrBars` from @silurus/ooxml-core. */
639
1035
  export declare type ErrBars = ChartErrBars;
640
1036
 
1037
+ /**
1038
+ * IX2 public find-result shape, shared by all three viewers.
1039
+ *
1040
+ * `findText` returns an ordered list of {@link FindMatch}. Every match carries
1041
+ * its ordinal position (`matchIndex`, 0-based, document order — the same index
1042
+ * `findNext` / `findPrev` cycle through), the matched `text`, and a
1043
+ * format-specific `location`. The location is where the three formats
1044
+ * legitimately differ — a docx match lives on a page, a pptx match on a slide,
1045
+ * an xlsx match in a sheet cell — so `FindMatch` is generic over it rather than
1046
+ * forcing an artificial common shape. Each viewer instantiates it with its own
1047
+ * location type:
1048
+ *
1049
+ * - `DocxViewer.findText` → `FindMatch<DocxMatchLocation>` ({ page })
1050
+ * - `PptxViewer.findText` → `FindMatch<PptxMatchLocation>` ({ slide })
1051
+ * - `XlsxViewer.findText` → `FindMatch<XlsxMatchLocation>` ({ sheet, ref, … })
1052
+ *
1053
+ * The generic default is `unknown` so `FindMatch` can be referenced without a
1054
+ * type argument (e.g. in generic UI code) while each viewer's return type stays
1055
+ * precise.
1056
+ */
1057
+ export declare interface FindMatch<Loc = unknown> {
1058
+ /** 0-based ordinal among all matches, in document order. This is the index
1059
+ * `findNext`/`findPrev` make active, so a caller can correlate the array it
1060
+ * got from `findText` with the active-match reported by navigation. */
1061
+ matchIndex: number;
1062
+ /** The text that matched (the query as it appears in the document — its
1063
+ * original case, not the folded form used for case-insensitive matching). */
1064
+ text: string;
1065
+ /** Where the match is, in the format's own coordinates. */
1066
+ location: Loc;
1067
+ }
1068
+
1069
+ /** Options for {@link findMatches}. */
1070
+ export declare interface FindMatchesOptions {
1071
+ /**
1072
+ * Match case exactly. Default `false` (case-insensitive, like a browser's
1073
+ * find-in-page). IX2 default — an integrator can pass `true`.
1074
+ */
1075
+ caseSensitive?: boolean;
1076
+ }
1077
+
641
1078
  export declare interface GradientFillSpec {
642
1079
  /** "linear" (default) or "path". */
643
1080
  gradientType: string;
@@ -660,9 +1097,60 @@ export declare type HiddenSheetMode = 'show' | 'skip' | 'dim';
660
1097
  export declare interface Hyperlink {
661
1098
  col: number;
662
1099
  row: number;
1100
+ /** External target (ECMA-376 §18.3.1.47 `r:id`, resolved via worksheet rels).
1101
+ * `null` for a purely internal hyperlink. */
663
1102
  url: string | null;
1103
+ /** Internal target (§18.3.1.47 `location`): a defined name or a cell reference
1104
+ * such as `Sheet1!A1`. Present when the hyperlink navigates within the
1105
+ * workbook rather than to an external URL. */
1106
+ location?: string | null;
1107
+ /** Optional display text (§18.3.1.47 `display`). Not used for rendering. */
1108
+ display?: string | null;
664
1109
  }
665
1110
 
1111
+ /**
1112
+ * Shared hyperlink model + URL sanitisation for docx / pptx / xlsx (IX1).
1113
+ *
1114
+ * All three formats carry the same two ECMA-376 concepts:
1115
+ * - an **external** hyperlink — an absolute URL resolved from a relationship
1116
+ * part target (`document.xml.rels` for docx §17.16.22, the slide rels for
1117
+ * pptx §21.1.2.3.5, the worksheet rels for xlsx §18.3.1.47), with
1118
+ * `TargetMode="External"`.
1119
+ * - an **internal** hyperlink — a jump within the document itself:
1120
+ * docx `w:anchor` -> a `<w:bookmarkStart w:name>` (§17.16.23), pptx
1121
+ * `action="ppaction://hlinksldjump"` -> a slide, xlsx `location` -> a defined
1122
+ * name or a `Sheet!A1` cell reference.
1123
+ *
1124
+ * The parsers (Rust, one per format) do the format-specific rels lookup and hand
1125
+ * each run / shape / cell a {@link HyperlinkTarget}. Everything downstream — the
1126
+ * text-layer overlay, the viewer default click behaviour, and any integrator
1127
+ * callback — is format-agnostic and consumes this one shape. Keeping the type +
1128
+ * the pure `sanitizeHyperlinkUrl` predicate here (not duplicated per package)
1129
+ * follows the cross-package unification principle: a scheme-allowlist bug fixed
1130
+ * once is fixed everywhere.
1131
+ */
1132
+ /**
1133
+ * A resolved hyperlink attached to a run, shape, or cell.
1134
+ *
1135
+ * - `external` — `url` is the raw target as authored in the file. It is NOT
1136
+ * guaranteed safe; run it through {@link sanitizeHyperlinkUrl} before
1137
+ * navigating. It is kept verbatim here so an integrator can apply its own
1138
+ * policy (e.g. allow `file:` on a trusted intranet viewer).
1139
+ * - `internal` — `ref` is the in-document destination, verbatim from the file:
1140
+ * docx: the bookmark name (`w:anchor`).
1141
+ * pptx: the internal action (e.g. `ppaction://hlinksldjump`), with the
1142
+ * resolved 0-based `slideIndex` when the rels target names a slide.
1143
+ * xlsx: the `location` string (a defined name or `Sheet1!A1`).
1144
+ */
1145
+ export declare type HyperlinkTarget = {
1146
+ kind: 'external';
1147
+ url: string;
1148
+ } | {
1149
+ kind: 'internal';
1150
+ ref: string;
1151
+ slideIndex?: number;
1152
+ };
1153
+
666
1154
  /**
667
1155
  * Image anchored to a rectangle of cells (EMU offsets within the anchor cells).
668
1156
  * 914400 EMU = 1 inch, 9525 EMU = 1 px @ 96 DPI.
@@ -711,6 +1199,15 @@ export declare interface ImageAnchor {
711
1199
  r: number;
712
1200
  b: number;
713
1201
  };
1202
+ /** ECMA-376 §20.1.8.6 `<a:alphaModFix@amt>` — the blip's overall opacity as a
1203
+ * fraction (0..1). Absent ⇒ opaque. The renderer sets `ctx.globalAlpha` so the
1204
+ * picture composites over the cells beneath it (e.g. a pink translucent photo
1205
+ * over a matching cell fill). */
1206
+ alpha?: number;
1207
+ /** ECMA-376 §20.1.8.23 `<a:duotone>` recolour effect. Absent (the common case)
1208
+ * ⇒ no effect. When present, the renderer remaps the image along the
1209
+ * `clr1`→`clr2` luminance ramp before drawing. */
1210
+ duotone?: Duotone;
714
1211
  }
715
1212
 
716
1213
  export declare interface LegendManualLayout {
@@ -760,6 +1257,35 @@ declare interface LoadOptions_2 {
760
1257
  * via `@font-face` in your application CSS.
761
1258
  */
762
1259
  useGoogleFonts?: boolean;
1260
+ /**
1261
+ * Password for an encrypted OOXML file ([MS-OFFCRYPTO] Agile Encryption).
1262
+ *
1263
+ * Password-protected Office documents are CFB (OLE2) containers, not ZIPs.
1264
+ * When this is set and the input is Agile-encrypted, `load()` decrypts it on
1265
+ * the main thread (via WebCrypto) and parses the recovered plaintext ZIP.
1266
+ *
1267
+ * Errors (thrown as {@link import('../errors/ooxml-error').OoxmlError}):
1268
+ * - no `password` on an encrypted file → code `'encrypted'`
1269
+ * - wrong `password` → code `'invalid-password'`
1270
+ * - a non-Agile scheme (Standard / Extensible / legacy) → code
1271
+ * `'unsupported-encryption'`
1272
+ *
1273
+ * Note: Agile Encryption uses a high password-hash spin count (commonly
1274
+ * 100,000), so decryption of a protected file adds roughly a second of
1275
+ * WebCrypto work before parsing begins.
1276
+ *
1277
+ * Security notes:
1278
+ * - This value is held as an ordinary JS `string` in memory for the
1279
+ * duration of key derivation. The library does not zero it, and does
1280
+ * not wrap it in a `SecureString`-equivalent — it becomes eligible for
1281
+ * garbage collection like any other string once nothing references it,
1282
+ * but no explicit wipe is performed. It is never logged or included in
1283
+ * thrown errors.
1284
+ * - Decryption recovers the plaintext but does not verify the file's HMAC
1285
+ * data-integrity tag ([MS-OFFCRYPTO] §2.3.4.14), so ciphertext tampering
1286
+ * is not detected — see "Security & Privacy" in the README.
1287
+ */
1288
+ password?: string;
763
1289
  /**
764
1290
  * Override the URL the parser worker fetches the WebAssembly module from.
765
1291
  *
@@ -829,6 +1355,31 @@ declare interface MathBar {
829
1355
  base: MathNode[];
830
1356
  }
831
1357
 
1358
+ /** Border-box object (`m:borderBox`, §22.1.2.11): a border/strikes around the
1359
+ * base. Absent flags ⇒ a full rectangular box. */
1360
+ declare interface MathBorderBox {
1361
+ kind: 'borderBox';
1362
+ /** §22.1.2 hide* — when true the corresponding edge is NOT drawn. */
1363
+ hideTop?: boolean;
1364
+ hideBot?: boolean;
1365
+ hideLeft?: boolean;
1366
+ hideRight?: boolean;
1367
+ /** §22.1.2 strike* — strikeBLTR = bottom-left→top-right, strikeTLBR =
1368
+ * top-left→bottom-right diagonal. */
1369
+ strikeH?: boolean;
1370
+ strikeV?: boolean;
1371
+ strikeBltr?: boolean;
1372
+ strikeTlbr?: boolean;
1373
+ base: MathNode[];
1374
+ }
1375
+
1376
+ /** Box object (`m:box`, §22.1.2.13): a logical grouping (operator emulator /
1377
+ * line-break control). Draws NO border — a transparent group around `base`. */
1378
+ declare interface MathBox {
1379
+ kind: 'box';
1380
+ base: MathNode[];
1381
+ }
1382
+
832
1383
  declare interface MathDelimiter {
833
1384
  kind: 'delimiter';
834
1385
  /** opening char (default '('). */
@@ -886,7 +1437,23 @@ declare interface MathNary {
886
1437
  body: MathNode[];
887
1438
  }
888
1439
 
889
- declare type MathNode = MathRun | MathFraction | MathScript | MathNary | MathDelimiter | MathRadical | MathLimit | MathArray | MathGroupChr | MathBar | MathAccent | MathFunc | MathGroup;
1440
+ declare type MathNode = MathRun | MathFraction | MathScript | MathNary | MathDelimiter | MathRadical | MathLimit | MathArray | MathGroupChr | MathBar | MathAccent | MathFunc | MathGroup | MathPhant | MathSPre | MathBox | MathBorderBox;
1441
+
1442
+ /** Phantom object (`m:phant`, §22.1.2.81): contributes the spacing of `base`
1443
+ * while optionally hiding it and/or zeroing individual dimensions. */
1444
+ declare interface MathPhant {
1445
+ kind: 'phant';
1446
+ /** §22.1.2.96 `m:show` — `false` hides the base (invisible but occupies space,
1447
+ * i.e. `<mphantom>`); `true` (default) shows it and the phant only tweaks
1448
+ * spacing. */
1449
+ show: boolean;
1450
+ /** §22.1.2 zeroWid / zeroAsc / zeroDesc — suppress width / ascent / descent so
1451
+ * the base takes no space along that axis. Omitted ⇒ false. */
1452
+ zeroWid?: boolean;
1453
+ zeroAsc?: boolean;
1454
+ zeroDesc?: boolean;
1455
+ base: MathNode[];
1456
+ }
890
1457
 
891
1458
  declare interface MathRadical {
892
1459
  kind: 'radical';
@@ -929,6 +1496,15 @@ declare interface MathScript {
929
1496
  sub?: MathNode[];
930
1497
  }
931
1498
 
1499
+ /** Pre-sub-superscript object (`m:sPre`, §22.1.2.99): sub + sup to the LEFT of
1500
+ * the base (e.g. ²₁A). */
1501
+ declare interface MathSPre {
1502
+ kind: 'sPre';
1503
+ sub: MathNode[];
1504
+ sup: MathNode[];
1505
+ base: MathNode[];
1506
+ }
1507
+
932
1508
  declare type MathStyle = 'roman' | 'italic' | 'bold' | 'boldItalic';
933
1509
 
934
1510
  declare interface MathSvg {
@@ -952,6 +1528,73 @@ export declare interface NumFmt {
952
1528
  formatCode: string;
953
1529
  }
954
1530
 
1531
+ /**
1532
+ * Typed error thrown by the docx / pptx / xlsx `load()` factories for failures
1533
+ * that carry a stable, programmatic {@link OoxmlErrorCode} (e.g. a
1534
+ * password-protected or legacy-binary file detected from its container magic).
1535
+ *
1536
+ * Note on workers: `instanceof OoxmlError` does not survive a structured-clone
1537
+ * across the worker boundary. Detection that needs a typed error is therefore
1538
+ * done on the main thread (before the worker is involved) so a genuine
1539
+ * `OoxmlError` instance is thrown to the caller. Errors that must cross the
1540
+ * worker boundary should carry the `code` string and be reconstructed on the
1541
+ * main side.
1542
+ */
1543
+ export declare class OoxmlError extends Error {
1544
+ readonly code: OoxmlErrorCode;
1545
+ constructor(code: OoxmlErrorCode, message: string);
1546
+ }
1547
+
1548
+ /**
1549
+ * Machine-readable code for a typed load-time failure.
1550
+ *
1551
+ * The container-level failures the `load()` factories detect on the main thread
1552
+ * before handing bytes to the parser worker (see `sniffCfb` / `decryptOoxml`).
1553
+ * This is the seed of the broader typed-error surface tracked as PD4 (OoxmlError
1554
+ * typed errors). Add codes here rather than throwing bare `Error(string)`, so
1555
+ * callers can `switch` on `err.code` instead of matching message text.
1556
+ *
1557
+ * - `'encrypted'` — password-protected, but no `password` was
1558
+ * supplied (pass `LoadOptions.password` to decrypt).
1559
+ * - `'invalid-password'` — a `password` was supplied but did not match.
1560
+ * - `'unsupported-encryption'`— encrypted with a scheme other than Agile
1561
+ * (Standard / Extensible / a legacy binary encryptor), which this library
1562
+ * cannot decrypt (PD8 implements Agile only).
1563
+ * - `'legacy-binary-format'` — a raw .doc / .xls / .ppt (not OOXML).
1564
+ * - `'not-ooxml'` — a CFB of an unrecognised kind, or otherwise
1565
+ * not an OOXML ZIP.
1566
+ */
1567
+ export declare type OoxmlErrorCode = 'encrypted' | 'invalid-password' | 'unsupported-encryption' | 'legacy-binary-format' | 'not-ooxml';
1568
+
1569
+ /**
1570
+ * The default action a viewer takes for an **external** hyperlink click when
1571
+ * the integrator supplies no `onHyperlinkClick` handler: sanitise the URL and,
1572
+ * if allowed, open it in a new tab with `noopener,noreferrer` so the opened page
1573
+ * gets no `window.opener` handle back into this document. A blocked scheme is a
1574
+ * silent no-op (returns `false`) — the click does nothing rather than navigate
1575
+ * somewhere dangerous.
1576
+ *
1577
+ * Internal targets are intentionally NOT handled here: the in-document jump
1578
+ * (page / slide / cell) is format-specific and lives in each viewer.
1579
+ *
1580
+ * Split out (not inlined in three viewers) so the "open in new tab, drop opener,
1581
+ * refuse unsafe schemes" policy is defined once. `win` is injected for tests;
1582
+ * defaults to the ambient `window`.
1583
+ *
1584
+ * @returns `true` if navigation was initiated, `false` if the URL was blocked.
1585
+ */
1586
+ export declare function openExternalHyperlink(url: string, allowed?: readonly string[], win?: Pick<Window, 'open'> | undefined): boolean;
1587
+
1588
+ /** `<sheetPr><outlinePr>` flags (ECMA-376 §18.3.1.61). Both default to `true`. */
1589
+ export declare interface OutlinePr {
1590
+ /** `true` (default) ⇒ a group's summary row sits *below* its detail rows;
1591
+ * `false` ⇒ above. */
1592
+ summaryBelow: boolean;
1593
+ /** `true` (default) ⇒ a group's summary column sits to the *right* of its
1594
+ * detail columns; `false` ⇒ to the left. */
1595
+ summaryRight: boolean;
1596
+ }
1597
+
955
1598
  export declare interface ParsedWorkbook {
956
1599
  workbook: Workbook;
957
1600
  styles: Styles;
@@ -999,6 +1642,38 @@ export declare interface PathInfo {
999
1642
  commands: PathCmd[];
1000
1643
  }
1001
1644
 
1645
+ /** ECMA-376 §18.18.56 ST_PhoneticAlignment — how the furigana is aligned over
1646
+ * the base text. Absent on {@link PhoneticProperties} defaults to `'left'`. */
1647
+ export declare type PhoneticAlignment = 'left' | 'center' | 'distributed' | 'noControl';
1648
+
1649
+ /** ECMA-376 §18.4.3 `<phoneticPr>` — phonetic display properties. */
1650
+ export declare interface PhoneticProperties {
1651
+ /** Zero-based index into `Styles.fonts` (§18.18.32 ST_FontId). Out of bounds
1652
+ * falls back to font 0 (§18.4.3). Drives the furigana font size / family. */
1653
+ fontId: number;
1654
+ /** §18.18.57 — absent means `'fullwidthKatakana'` (schema default). */
1655
+ type?: PhoneticType;
1656
+ /** §18.18.56 — absent means `'left'` (schema default). */
1657
+ alignment?: PhoneticAlignment;
1658
+ }
1659
+
1660
+ /** ECMA-376 §18.4.6 `<rPh sb=".." eb="..">` — one furigana run. `sb`/`eb` are
1661
+ * zero-based character offsets into the base text; the hint `text` is shown
1662
+ * over base characters `[sb, eb)`. */
1663
+ export declare interface PhoneticRun {
1664
+ /** Zero-based start character offset into the base text (inclusive). */
1665
+ sb: number;
1666
+ /** Zero-based end character offset into the base text (exclusive). */
1667
+ eb: number;
1668
+ /** The phonetic hint text (e.g. the katakana reading). */
1669
+ text: string;
1670
+ }
1671
+
1672
+ /** ECMA-376 §18.18.57 ST_PhoneticType — the East-Asian character set the
1673
+ * furigana is displayed in. Absent on {@link PhoneticProperties} defaults to
1674
+ * `'fullwidthKatakana'` per the CT_PhoneticPr schema. */
1675
+ export declare type PhoneticType = 'fullwidthKatakana' | 'halfwidthKatakana' | 'Hiragana' | 'noConversion';
1676
+
1002
1677
  export declare interface RenderViewportOptions {
1003
1678
  width?: number;
1004
1679
  height?: number;
@@ -1052,10 +1727,34 @@ export declare type ResolvedList = {
1052
1727
  formula: string;
1053
1728
  };
1054
1729
 
1730
+ /**
1731
+ * Resolve every `{ type: 'shared', si }` cell in `ws` to a concrete
1732
+ * `{ type: 'text', text, runs? }` by looking `si` up in the workbook
1733
+ * `sharedStrings` table (ECMA-376 §18.4.8). Mutates cells in place and returns
1734
+ * `ws` for chaining. Out-of-range / missing `si` resolves to empty text —
1735
+ * matching the parser's historical fallback. Idempotent: a `Worksheet` with no
1736
+ * `shared` cells is returned unchanged.
1737
+ *
1738
+ * This keeps the dedup win on the wire (each shared string ships ONCE in the
1739
+ * workbook) while every downstream consumer — renderer, formula engine, number
1740
+ * formatter, markdown — still sees fully-resolved cell text.
1741
+ */
1742
+ export declare function resolveSharedStrings(ws: Worksheet, sharedStrings: SharedString[]): Worksheet;
1743
+
1055
1744
  export declare interface Row {
1056
1745
  index: number;
1057
1746
  height: number | null;
1058
1747
  cells: Cell[];
1748
+ /** Outline (grouping) depth 0-7 (ECMA-376 §18.3.1.73 `<row outlineLevel>`).
1749
+ * Omitted on the wire when `0` (ungrouped); read as `outlineLevel ?? 0`. */
1750
+ outlineLevel?: number;
1751
+ /** `<row collapsed>` (§18.3.1.73): `true` on a summary row whose
1752
+ * one-level-deeper detail rows are collapsed. Omitted when false. */
1753
+ collapsed?: boolean;
1754
+ /** `<row hidden>` (§18.3.1.73): `true` when the row is hidden — most often
1755
+ * because a collapsed outline hides its detail rows. Distinct from
1756
+ * `height === 0`. Omitted on the wire when false. */
1757
+ hidden?: boolean;
1059
1758
  }
1060
1759
 
1061
1760
  export declare interface Run {
@@ -1113,6 +1812,13 @@ declare interface SecondaryValueAxis {
1113
1812
  lineHidden: boolean;
1114
1813
  /** `<c:majorTickMark>` — "cross" (default) | "out" | "in" | "none". */
1115
1814
  majorTickMark: string;
1815
+ /**
1816
+ * `<c:valAx><c:majorUnit val>` (§21.2.2.103) — explicit distance between
1817
+ * major ticks/gridlines on THIS secondary axis, overriding the Excel-style
1818
+ * auto "nice" step. null/undefined ⇒ auto step (byte-stable). Symmetric with
1819
+ * {@link ChartModel.valAxisMajorUnit} on the primary axis.
1820
+ */
1821
+ majorUnit?: number | null;
1116
1822
  /** `<c:title>` run-prop font size (hpt). */
1117
1823
  titleFontSizeHpt?: number | null;
1118
1824
  /** `<c:title>` run-prop bold flag. */
@@ -1186,6 +1892,12 @@ export declare type ShapeGeom = {
1186
1892
  r: number;
1187
1893
  b: number;
1188
1894
  };
1895
+ /** ECMA-376 §20.1.8.6 `<a:alphaModFix@amt>` opacity fraction (0..1) on the
1896
+ * leaf pic. Absent ⇒ opaque. Applied via `globalAlpha`. */
1897
+ alpha?: number;
1898
+ /** ECMA-376 §20.1.8.23 `<a:duotone>` recolour on the leaf pic. Absent ⇒
1899
+ * no effect. */
1900
+ duotone?: Duotone;
1189
1901
  };
1190
1902
 
1191
1903
  export declare interface ShapeInfo {
@@ -1300,6 +2012,12 @@ export declare type ShapeTextRun = {
1300
2012
  export declare interface SharedString {
1301
2013
  text: string;
1302
2014
  runs?: Run[];
2015
+ /** ECMA-376 §18.4.6 phonetic runs (furigana). Absent when the `<si>` has no
2016
+ * `<rPh>`. */
2017
+ phoneticRuns?: PhoneticRun[];
2018
+ /** ECMA-376 §18.4.3 phonetic display properties. Absent when the `<si>` has
2019
+ * no `<phoneticPr>`. */
2020
+ phoneticPr?: PhoneticProperties;
1303
2021
  }
1304
2022
 
1305
2023
  export declare interface SheetMeta {
@@ -1460,11 +2178,51 @@ export declare interface ViewportRange {
1460
2178
 
1461
2179
  /** Serializable subset of RenderViewportOptions: drop the callback, the image
1462
2180
  * cache, and the `fetchImage` loader (all non-cloneable; the worker owns its
1463
- * own cache and supplies its own in-worker fetchImage). */
1464
- export declare type WireRenderViewportOptions = Omit<RenderViewportOptions, 'onTextRun' | 'loadedImages' | 'fetchImage'>;
2181
+ * own cache and supplies its own in-worker fetchImage). Extended with the
2182
+ * optional {@link WireSizeOverrides} so view-only size mutations reach the
2183
+ * worker's local sheet copy; absent (the common case) when nothing has been
2184
+ * resized or collapsed, keeping the wire payload unchanged. */
2185
+ export declare type WireRenderViewportOptions = Omit<RenderViewportOptions, 'onTextRun' | 'loadedImages' | 'fetchImage'> & {
2186
+ sizeOverrides?: WireSizeOverrides;
2187
+ };
2188
+
2189
+ /**
2190
+ * View-only per-band size overrides for one sheet, carried with every worker
2191
+ * `renderViewport` request. The render worker draws from its own worker-local
2192
+ * parsed-sheet cache, so main-thread Worksheet mutations (outline
2193
+ * collapse/expand via the size-0 hidden encoding, drag-to-resize #567) never
2194
+ * reach it on their own — without this channel the gutter/overlays update but
2195
+ * the grid bitmap stays stale.
2196
+ *
2197
+ * Semantics: keys are 1-based band indices; a number is the band's current
2198
+ * `rowHeights` / `colWidths` model value, `null` means "no entry — fall back
2199
+ * to the sheet default". The main thread accumulates every band the user has
2200
+ * touched this session (entries are updated in place, never removed), so
2201
+ * re-applying the full map is idempotent and converges the worker's cached
2202
+ * sheet to the main model even across worker-side re-parses.
2203
+ */
2204
+ export declare interface WireSizeOverrides {
2205
+ rows?: Record<number, number | null>;
2206
+ cols?: Record<number, number | null>;
2207
+ }
1465
2208
 
1466
2209
  export declare interface Workbook {
1467
2210
  sheets: SheetMeta[];
2211
+ /** Workbook date system (`<workbookPr date1904>`, ECMA-376 §18.2.28).
2212
+ * `true` selects the 1904 date system (Mac-authored workbooks); serial
2213
+ * dates are resolved against the 1904 epoch (§18.17.4.1). Omitted from the
2214
+ * parser JSON when false (default 1900 date system). */
2215
+ date1904?: boolean;
2216
+ /** #773 partial degradation: a WORKBOOK-LEVEL degradation that leaves every
2217
+ * sheet openable. Set when a shared workbook part was PRESENT but corrupt —
2218
+ * most commonly `xl/sharedStrings.xml` (§18.4.9): a broken shared-string table
2219
+ * silently blanks every string cell across ALL sheets, so unlike a per-sheet
2220
+ * break it can't be attributed to one placeholder sheet. Tagged with the
2221
+ * offending part (e.g. `"xl/sharedStrings.xml: <detail>"`) so the loss is
2222
+ * surfaced instead of silent, while every sheet still renders its non-string
2223
+ * content. Absent (`undefined`) when every shared part read cleanly. Also set
2224
+ * (`"(zip container): <detail>"`) for a whole-container degradation (#774). */
2225
+ parseError?: string;
1468
2226
  }
1469
2227
 
1470
2228
  export declare interface Worksheet {
@@ -1472,6 +2230,17 @@ export declare interface Worksheet {
1472
2230
  rows: Row[];
1473
2231
  colWidths: Record<number, number>;
1474
2232
  rowHeights: Record<number, number>;
2233
+ /** Per-column outline (grouping) depth 0-7 (ECMA-376 §18.3.1.13
2234
+ * `<col outlineLevel>`), keyed by 1-based column index. Present only for
2235
+ * grouped columns; absent (⇒ level 0) on outline-free sheets. */
2236
+ colOutlineLevels?: Record<number, number>;
2237
+ /** Per-column `<col collapsed>` (§18.3.1.13): `true` on a summary column whose
2238
+ * one-level-deeper detail columns are collapsed. Only `true` entries. */
2239
+ colCollapsed?: Record<number, boolean>;
2240
+ /** Per-column `<col hidden>` (§18.3.1.13): `true` when the column is hidden
2241
+ * (e.g. a collapsed outline hides its detail columns). Distinct from
2242
+ * `colWidths[c] === 0`. Only `true` entries. */
2243
+ colHidden?: Record<number, boolean>;
1475
2244
  defaultColWidth: number;
1476
2245
  defaultRowHeight: number;
1477
2246
  mergeCells: MergeCell[];
@@ -1494,6 +2263,11 @@ export declare interface Worksheet {
1494
2263
  * grid so column A sits on the right (ECMA-376 §18.3.1.87
1495
2264
  * `<sheetView rightToLeft>`). Defaults to false. */
1496
2265
  rightToLeft?: boolean;
2266
+ /** Outline display flags from `<sheetPr><outlinePr>` (ECMA-376 §18.3.1.61).
2267
+ * Absent when the sheet declares no `<outlinePr>`; consumers apply the
2268
+ * schema defaults (`summaryBelow` / `summaryRight` both `true`). Decides
2269
+ * which side of a group the summary row/column (and its +/- toggle) sits. */
2270
+ outlinePr?: OutlinePr;
1497
2271
  /** Sheet tab color (ECMA-376 §18.3.1.79). */
1498
2272
  tabColor?: string | null;
1499
2273
  /** AutoFilter header range (ECMA-376 §18.3.1.2). */
@@ -1537,6 +2311,17 @@ export declare interface Worksheet {
1537
2311
  defaultFontFamily?: string;
1538
2312
  /** Point size of the workbook's Normal-style font (`<fonts>[N].sz.val`). */
1539
2313
  defaultFontSize?: number;
2314
+ /** Workbook date system (`<workbookPr date1904>`, ECMA-376 §18.2.28),
2315
+ * denormalized onto every worksheet by the parser so the cell formatter can
2316
+ * resolve serial dates (§18.17.4.1) without a workbook back-reference.
2317
+ * `true` = 1904 date system. Omitted (⇒ false) for the default 1900 system. */
2318
+ date1904?: boolean;
2319
+ /** RB7 partial degradation: set when THIS sheet's part could not be
2320
+ * read/parsed. The workbook still opens with the OTHER sheets intact; this one
2321
+ * is an empty placeholder (`rows` empty) whose `parseError` names the offending
2322
+ * part (e.g. `"xl/worksheets/sheet3.xml: <detail>"`). Absent (`undefined`) for
2323
+ * every healthy sheet. The renderer paints a visible error overlay. */
2324
+ parseError?: string;
1540
2325
  }
1541
2326
 
1542
2327
  /**
@@ -1563,6 +2348,20 @@ export declare interface XlsxComment {
1563
2348
  text: string;
1564
2349
  }
1565
2350
 
2351
+ /** Where an xlsx match lives: the sheet, its name, and the cell (A1 + row/col). */
2352
+ export declare interface XlsxMatchLocation {
2353
+ /** 0-based sheet index. */
2354
+ sheet: number;
2355
+ /** The sheet's display name. */
2356
+ sheetName: string;
2357
+ /** A1 cell reference, e.g. `"B7"`. */
2358
+ ref: string;
2359
+ /** 1-based row. */
2360
+ row: number;
2361
+ /** 1-based column. */
2362
+ col: number;
2363
+ }
2364
+
1566
2365
  /** Emitted once per cell that has text, with the cell's canvas-pixel bounds. */
1567
2366
  export declare interface XlsxTextRunInfo {
1568
2367
  text: string;
@@ -1578,13 +2377,53 @@ export declare interface XlsxTextRunInfo {
1578
2377
  col: number;
1579
2378
  }
1580
2379
 
1581
- export declare class XlsxViewer {
2380
+ export declare class XlsxViewer implements ZoomableViewer {
1582
2381
  private wb;
1583
2382
  /** The single subtree root the constructor appended to the caller's
1584
2383
  * container. destroy() removes it to return the container to its original
1585
2384
  * (empty) state. */
1586
2385
  private wrapper;
1587
2386
  private canvas;
2387
+ /** Region holding the outline gutters (top/left) and the inset {@link canvasArea}.
2388
+ * When the active sheet has no outlining the gutters collapse to 0 px and this
2389
+ * is a transparent pass-through, so an outline-free sheet lays out identically. */
2390
+ private gridRegion;
2391
+ /** Left gutter canvas: row group brackets + toggles (XL4). */
2392
+ private rowGutter;
2393
+ /** Top gutter canvas: column group brackets + toggles (XL4). */
2394
+ private colGutter;
2395
+ /** Top-left corner canvas: numbered level buttons (XL4). */
2396
+ private cornerGutter;
2397
+ /** Cached extents (unscaled CSS px) of the current sheet's gutters; both 0 for
2398
+ * an outline-free sheet. `w` insets {@link canvasArea} from the left, `h` from
2399
+ * the top. */
2400
+ private gutter;
2401
+ /** Per-axis outline layout (group brackets + toggles) for the current sheet,
2402
+ * recomputed on sheet switch and after each collapse/expand. `null` axis ⇒ no
2403
+ * outlining on that axis. */
2404
+ private rowOutline;
2405
+ private colOutline;
2406
+ private rowOutlineBands;
2407
+ private colOutlineBands;
2408
+ /** Original row heights / column widths stashed the first time a band is
2409
+ * collapsed, so expanding restores a custom size rather than the default.
2410
+ * Keyed by band index; per current worksheet (cleared on sheet switch). */
2411
+ private stashedRowHeights;
2412
+ private stashedColWidths;
2413
+ /**
2414
+ * Per-sheet cumulative record of every view-only size mutation (outline
2415
+ * collapse/expand, drag-to-resize #567), keyed by sheet index. Value = the
2416
+ * band's current model size, or `null` when the model has no entry (default
2417
+ * size). Serialized as {@link WireSizeOverrides} with every worker
2418
+ * `renderViewport` so the worker's local sheet cache converges to the
2419
+ * main-thread model — without it the worker keeps drawing the file's
2420
+ * original sizes and the grid bitmap goes stale under the (up-to-date)
2421
+ * gutter and overlays. Entries are updated in place and never removed
2422
+ * (idempotent re-application); the whole store resets when a new workbook
2423
+ * loads. Main mode never reads it (the main renderer draws from the mutated
2424
+ * model directly).
2425
+ */
2426
+ private sizeOverrideStore;
1588
2427
  private canvasArea;
1589
2428
  private scrollHost;
1590
2429
  private spacer;
@@ -1608,6 +2447,26 @@ export declare class XlsxViewer {
1608
2447
  * holds one context type for its lifetime, so this is obtained once and the
1609
2448
  * main-mode 2d render path is never used on the same canvas. */
1610
2449
  private _bitmapCtx;
2450
+ /** Set by {@link destroy} (first line). Guards {@link _reportRenderError} so a
2451
+ * render rejection that lands AFTER teardown is swallowed rather than surfaced
2452
+ * to an `onError` / `console.error` on a dead viewer — parity with the scroll
2453
+ * viewers' `_destroyed` flag. */
2454
+ private _destroyed;
2455
+ /**
2456
+ * Concurrent-load latch (generation token). Every {@link load} increments this
2457
+ * and captures the value; after its workbook finishes loading it re-checks the
2458
+ * live value and BAILS (destroying its own just-loaded workbook) if a newer
2459
+ * `load()` has since started. Without it, two overlapping `load(A)`/`load(B)`
2460
+ * calls race the WASM parse / worker init, and whichever RESOLVES last wins the
2461
+ * swap — even the stale `load(A)` resolving after `load(B)`; the loser's freshly
2462
+ * created workbook (never installed, or installed then overwritten) then leaks
2463
+ * its worker + pinned WASM allocation. The latch composes with SC20: the check
2464
+ * runs AFTER the new workbook loads but BEFORE the field assignment and
2465
+ * `previous?.destroy()`, so a superseded load never touches `this.wb` nor frees
2466
+ * the current (newer) workbook. {@link destroy} also bumps it so a load in
2467
+ * flight at teardown is treated as superseded and its workbook cleaned up.
2468
+ */
2469
+ private _loadGen;
1611
2470
  private resizeObserver;
1612
2471
  /**
1613
2472
  * Pending `requestAnimationFrame` handle for a coalesced re-render, or `null`
@@ -1646,13 +2505,27 @@ export declare class XlsxViewer {
1646
2505
  * strand the view at the sheet's far end once the host gains its real size.
1647
2506
  */
1648
2507
  private effectiveH;
2508
+ /** Gesture-only pointer anchor for the NEXT `setScale`, in canvasArea-viewport
2509
+ * px (`{ x, y }` from the wheel event, relative to the grid's top-left). Set by
2510
+ * the Ctrl/⌘+wheel handler right before it calls `setScale` so the zoom pivots
2511
+ * on the cursor ("zoom toward the pointer") in BOTH axes, past the fixed
2512
+ * header + frozen-pane lead-in; consumed and cleared by `setScale`. `null` for
2513
+ * every non-gesture source (the public `setScale`, the +/- steppers, the zoom
2514
+ * slider, `fitWidth`/`fitPage`), which keep the historical START-anchored
2515
+ * (top-left) preservation so their behaviour is unchanged. */
2516
+ private _pendingZoomAnchor;
1649
2517
  private anchorCell;
1650
2518
  private activeCell;
1651
2519
  private selectionMode;
1652
2520
  private isSelecting;
1653
2521
  private selectionOverlay;
2522
+ /** IX2 — find-highlight overlay (matched-cell boxes). */
2523
+ private findOverlay;
2524
+ /** IX2 — find state (matches + active cursor). */
2525
+ private _find;
1654
2526
  private keydownHandler;
1655
2527
  private pendingTap;
2528
+ private pendingClick;
1656
2529
  private resizeDrag;
1657
2530
  /** DOM overlay element that shows the hovered cell's comment. Lives in
1658
2531
  * canvasArea above the scrollHost; `pointer-events:none` so it never blocks
@@ -1660,6 +2533,11 @@ export declare class XlsxViewer {
1660
2533
  private commentPopup;
1661
2534
  /** `"row:col"` → comment for the current sheet, rebuilt on every showSheet. */
1662
2535
  private commentMap;
2536
+ /** IX1 — `"row:col"` → hyperlink for the current sheet, rebuilt on every
2537
+ * showSheet. Keys mirror the renderer's `hyperlinkMap` (1-based row/col, the
2538
+ * first cell of a hyperlink `ref` range per the parser), so a `getCellAt`
2539
+ * {row,col} looks up directly. */
2540
+ private hyperlinkMap;
1663
2541
  /** `"row:col"` of the cell whose popup is currently shown (or pending), so a
1664
2542
  * pointermove within the same cell doesn't restart the show timer. */
1665
2543
  private commentPopupKey;
@@ -1681,17 +2559,80 @@ export declare class XlsxViewer {
1681
2559
  * click; installed only while the panel is open. */
1682
2560
  private validationOutsideHandler;
1683
2561
  constructor(container: HTMLElement, opts?: XlsxViewerOptions);
2562
+ /** Every non-empty cell of a sheet with its rendered display text (IX2 find
2563
+ * source). Reads the parsed worksheet model directly — no render — so search
2564
+ * covers the whole sheet, not just the on-screen viewport. */
2565
+ private _collectSheetCells;
1684
2566
  /**
1685
2567
  * Load an XLSX from URL or ArrayBuffer and render the first sheet.
1686
2568
  *
1687
- * Error contract (shared by all three viewers): on failure, if an `onError`
1688
- * callback was provided it is invoked and `load` resolves normally; if not,
1689
- * the error is rethrown so it is never silently swallowed.
2569
+ * Error contract (shared by all three viewers):
2570
+ * - Parse/load failure (the underlying `XlsxWorkbook.load()` call itself
2571
+ * rejects): if an `onError` callback was provided it is invoked and `load`
2572
+ * resolves normally; if not, the error is rethrown so it is never silently
2573
+ * swallowed.
2574
+ * - Render failure (the first sheet fails to draw AFTER a successful
2575
+ * parse/load): routed to the shared `_reportRenderError` contract (`onError`
2576
+ * if provided, else `console.error` — never silent) and `load` still
2577
+ * RESOLVES, matching every subsequent navigation call.
1690
2578
  */
1691
2579
  load(source: string | ArrayBuffer): Promise<void>;
1692
2580
  /** The loaded workbook, or throws if {@link load} has not completed. */
1693
2581
  private get workbook();
1694
2582
  showSheet(index: number): Promise<void>;
2583
+ /** Recompute the per-axis outline layout for `ws` and cache the band lists.
2584
+ * Both axes are `null` (gutters collapse to 0) when the sheet has no
2585
+ * outlining, so an outline-free sheet is untouched. */
2586
+ private buildOutline;
2587
+ /** Size and place the three gutter canvases (corner / col / row) from the
2588
+ * current outline, and inset {@link canvasArea} by the gutter extents. When
2589
+ * neither axis is grouped both extents are 0 and canvasArea covers the whole
2590
+ * region — pixel-identical to a viewer built before XL4. */
2591
+ private layoutGutters;
2592
+ /** Paint all visible gutter strips for the current scroll offset. Called at the
2593
+ * end of every grid render so the brackets track scroll / zoom exactly. */
2594
+ private renderGutters;
2595
+ /** Draw one axis's group brackets and +/- toggles into its gutter canvas,
2596
+ * aligned to the on-screen band positions via {@link getCellRect}. */
2597
+ private paintAxisGutter;
2598
+ /** Draw a small square +/- toggle centered at (cx, cy) in gutter-canvas CSS px. */
2599
+ private drawToggleBox;
2600
+ /** Draw one numbered level button centered at (cx, cy) in gutter-canvas CSS
2601
+ * px. Shared by the row bank (in the row gutter's top strip) and the column
2602
+ * bank (in the column gutter's left strip). */
2603
+ private drawLevelButton;
2604
+ /** Paint the corner (intersection of the two gutters) as plain background.
2605
+ * The numbered level banks live in each axis gutter's own header strip
2606
+ * (see paintAxisGutter), so the corner carries no interactive content. */
2607
+ private paintCornerGutter;
2608
+ /** Handle a click in a row/col gutter: hit-test the +/- toggles and toggle the
2609
+ * matching group's collapse state. */
2610
+ private onGutterPointerDown;
2611
+ /** Flip a single group's collapse state in the in-memory model, then rebuild
2612
+ * the outline + repaint. View-only: the file is never written. */
2613
+ private applyGroupToggle;
2614
+ /** Collapse/expand the whole sheet to `level` on one axis. */
2615
+ private applyLevelButton;
2616
+ /** Set a row/column hidden by mapping to the size-0 encoding the axis/renderer
2617
+ * already understand, stashing the original size so expand can restore it. */
2618
+ private setBandHidden;
2619
+ /** Record band `index`'s CURRENT model size (or `null` = no entry) in the
2620
+ * per-sheet override store. Called after every view-only size mutation —
2621
+ * outline hide/show above and drag-to-resize (#567) — so worker renders
2622
+ * converge to the main model. */
2623
+ private recordSizeOverride;
2624
+ /** The current sheet's override store serialized for the wire, or undefined
2625
+ * when nothing has been mutated (keeps the request payload unchanged). */
2626
+ private wireSizeOverrides;
2627
+ /** Update the `collapsed` flag on a band's model entry so the outline rebuild
2628
+ * reflects the new state. */
2629
+ private setBandCollapsed;
2630
+ /** Shared tail of a gutter interaction: invalidate the axis cache, rebuild the
2631
+ * outline (collapsed flags changed), refresh dependent geometry, re-render. */
2632
+ private afterOutlineMutation;
2633
+ /** Rebuild only the layout + band lists (not the stashes) after a collapse
2634
+ * state change, so the +/- glyphs and bracket set stay in sync. */
2635
+ private buildOutlineLayoutOnly;
1695
2636
  /** True when the current sheet's grid is laid out right-to-left. */
1696
2637
  private get isRtl();
1697
2638
  /** Maximum horizontal scroll offset the native scroll host allows (≥ 0). */
@@ -1816,6 +2757,46 @@ export declare class XlsxViewer {
1816
2757
  * whole range) to mirror Excel, which attaches the button to the active
1817
2758
  * cell of the selection. */
1818
2759
  private maybeDrawValidationDropdown;
2760
+ /**
2761
+ * Redraw the find-highlight overlay: one translucent box per matched cell on
2762
+ * the current sheet, the active match in a stronger colour. Uses the SAME
2763
+ * `getCellRect` + `screenX` + header/frozen clamp the selection overlay uses,
2764
+ * so a box lands exactly on the drawn cell at any scroll offset / zoom / RTL.
2765
+ * Rebuilt on every render and scroll (cheap DOM geometry, no canvas paint).
2766
+ */
2767
+ private updateFindOverlay;
2768
+ /**
2769
+ * IX2 — find every occurrence of `query` across every sheet and highlight the
2770
+ * matched cells. Returns every match in document order (sheet ascending, then
2771
+ * row-major within a sheet), each tagged with its
2772
+ * `{ sheet, sheetName, ref, row, col }`. A cell is the search unit: search
2773
+ * runs over each cell's *rendered* display text (number formats, dates, rich
2774
+ * text flattened), so a query matches what the grid shows. Case-insensitive by
2775
+ * default; pass `{ caseSensitive: true }` for an exact match. An empty query
2776
+ * clears the find.
2777
+ */
2778
+ findText(query: string, opts?: FindMatchesOptions): Promise<FindMatch<XlsxMatchLocation>[]>;
2779
+ /**
2780
+ * IX2 — move to the next match (wrap-around), switching sheets and scrolling
2781
+ * the matched cell into view as needed, and highlight it as the active match.
2782
+ * Returns the now-active match, or `null` when there are none. Call
2783
+ * {@link findText} first.
2784
+ */
2785
+ findNext(): Promise<FindMatch<XlsxMatchLocation> | null>;
2786
+ /** IX2 — move to the previous match (wrap-around). */
2787
+ findPrev(): Promise<FindMatch<XlsxMatchLocation> | null>;
2788
+ /** IX2 — clear all highlights and reset the find state. */
2789
+ clearFind(): void;
2790
+ private _activateMatch;
2791
+ /**
2792
+ * Scroll the grid so cell (row, col) is comfortably in view. Computes the
2793
+ * cell's absolute logical offset from the axis metrics (the same the renderer
2794
+ * uses) and nudges `scrollHost.scrollTop` / start-anchored horizontal scroll
2795
+ * only when the cell is outside the scrollable viewport — an in-view cell is
2796
+ * left where it is (Excel's find behaviour). Frozen cells are always visible,
2797
+ * so they need no scroll.
2798
+ */
2799
+ private _scrollCellIntoView;
1819
2800
  /** Toggle the dropdown panel for the active cell's list validation. Called
1820
2801
  * from pointerdown when the arrow rect is hit. Re-clicking the same arrow
1821
2802
  * closes it. */
@@ -1843,6 +2824,31 @@ export declare class XlsxViewer {
1843
2824
  * collision (Excel allows at most one note per cell, so this is moot in
1844
2825
  * practice). */
1845
2826
  private buildCommentMap;
2827
+ /** IX1 — index the current sheet's hyperlinks by `"row:col"` (1-based, first
2828
+ * cell of the `ref` range) so a clicked/hovered cell resolves in O(1). Keys
2829
+ * match the renderer's `hyperlinkMap` exactly (`${hl.row}:${hl.col}`). */
2830
+ private buildHyperlinkMap;
2831
+ /** IX1 — the hyperlink at a cell, or null. `getCellAt` returns 1-based
2832
+ * {row,col}, matching the parser/renderer keying. */
2833
+ private hyperlinkAtCell;
2834
+ /**
2835
+ * IX1 — dispatch a click on a hyperlinked cell. Builds a
2836
+ * {@link HyperlinkTarget} from the parsed hyperlink (external `url` wins over
2837
+ * internal `location`, matching Excel: a `<hyperlink>` carrying both navigates
2838
+ * to the external target) and routes it to the caller's `onHyperlinkClick`
2839
+ * (which fully owns behaviour) or the built-in default. Returns true when a
2840
+ * hyperlink was found and dispatched.
2841
+ */
2842
+ private dispatchHyperlink;
2843
+ /**
2844
+ * IX1 default handler for an internal `location` target (§18.3.1.47): a defined
2845
+ * name or a cell ref like `Sheet1!A1`. Best-effort: if the part before `!`
2846
+ * names a sheet in the workbook, switch to it. There is no scroll-to-cell
2847
+ * primitive on this viewer, so the cell part is not yet honoured (switching the
2848
+ * sheet already lands the user on the right surface). A bare defined name that
2849
+ * does not resolve to a sheet is a documented no-op.
2850
+ */
2851
+ private navigateInternalHyperlink;
1846
2852
  /** Show the popup for the comment on `cell` after the hover dwell, anchored to
1847
2853
  * the cell's current on-screen rect. No-op when the cell carries no comment.
1848
2854
  * Re-hovering the same cell does not restart the timer. */
@@ -1877,10 +2883,46 @@ export declare class XlsxViewer {
1877
2883
  private zoomPosToScale;
1878
2884
  /** Inverse of {@link zoomPosToScale}: scale factor → slider position [0,100]. */
1879
2885
  private zoomScaleToPos;
1880
- /** Set the cell/header scale and re-lay-out the current sheet. Clamped to the
1881
- * zoom bounds; keeps the slider thumb, percentage label and the row-header-
1882
- * aligned tab-nav width in sync. */
2886
+ /**
2887
+ * IX9 {@link ZoomableViewer} — set the cell/header scale (`1` = 100%; the
2888
+ * viewer's `cellScale`) and re-lay-out the current sheet. Clamped to the zoom
2889
+ * bounds and snapped to whole percent; keeps the slider thumb, percentage label
2890
+ * and the row-header-aligned tab-nav width in sync, and fires `onScaleChange`
2891
+ * when the resolved scale actually changes.
2892
+ */
1883
2893
  setScale(scale: number): void;
2894
+ /** IX9 {@link ZoomableViewer} — the current zoom factor (`1` = 100%). This is
2895
+ * the viewer's `cellScale`; `1` before anything is set. */
2896
+ getScale(): number;
2897
+ /** IX9 {@link ZoomableViewer} — step up to the next rung of the shared zoom
2898
+ * ladder (clamped to `zoomMax` by {@link setScale}). */
2899
+ zoomIn(): void;
2900
+ /** IX9 {@link ZoomableViewer} — step down to the next lower ladder rung. */
2901
+ zoomOut(): void;
2902
+ /**
2903
+ * IX9 {@link ZoomableViewer} — fit the used data range's WIDTH to the canvas
2904
+ * area. The "content" is the natural (100%) width of the row header plus the
2905
+ * used columns; the container is `canvasArea.clientWidth`. A no-op (defers) when
2906
+ * nothing is loaded or the container is unlaid-out. Routes through
2907
+ * {@link setScale}, so the result is clamped/snapped and fires `onScaleChange`.
2908
+ */
2909
+ fitWidth(): void;
2910
+ /**
2911
+ * IX9 {@link ZoomableViewer} — fit the used data range's WIDTH AND HEIGHT inside
2912
+ * the canvas area (header + used columns/rows), so the whole used range is
2913
+ * visible without scrolling. Takes the tighter of the width- and height-fit
2914
+ * factors. Defers when unloaded / unlaid-out; routes through {@link setScale}.
2915
+ */
2916
+ fitPage(): void;
2917
+ /** Shared fit implementation for {@link fitWidth} / {@link fitPage}: derive the
2918
+ * natural (cs=1) content extent of the used data range, ask core's pure
2919
+ * {@link fitScale} for the factor, and apply it via {@link setScale}. */
2920
+ private _fit;
2921
+ /** Natural (unscaled, cs=1) CSS-px extent of a worksheet's used data range:
2922
+ * the row/column header plus every used column width / row height. Mirrors
2923
+ * {@link updateSpacerSize} at cs=1 (same used-range detection) so the fit
2924
+ * targets exactly the region the spacer/scroll extent covers. */
2925
+ private _naturalContentExtent;
1884
2926
  private updateSpacerSize;
1885
2927
  /**
1886
2928
  * Coalesce a re-render into the next animation frame. Called from the
@@ -1896,6 +2938,11 @@ export declare class XlsxViewer {
1896
2938
  */
1897
2939
  private scheduleRender;
1898
2940
  private renderCurrentSheet;
2941
+ /** Route a render failure to `onError`, or `console.error` when none is given
2942
+ * (never fully silent), and never after teardown. Mirrors the scroll viewers'
2943
+ * `_reportRenderError`. */
2944
+ private _reportRenderError;
2945
+ private _renderCurrentSheet;
1899
2946
  private computeHeaderHighlight;
1900
2947
  get sheetNames(): string[];
1901
2948
  /** The underlying <canvas> element the grid is drawn on. */
@@ -1932,9 +2979,20 @@ export declare interface XlsxViewerOptions extends LoadOptions_2 {
1932
2979
  * own zoom control). */
1933
2980
  showZoomSlider?: boolean;
1934
2981
  /** Lower/upper bounds for the zoom slider as scale factors. Default 0.1–4
1935
- * (10%–400%, matching Excel's zoom range). */
2982
+ * (10%–400%, matching Excel's zoom range). Also the clamp range for the IX9
2983
+ * {@link ZoomableViewer} zoom contract ({@link XlsxViewer.setScale} etc.). */
1936
2984
  zoomMin?: number;
1937
2985
  zoomMax?: number;
2986
+ /**
2987
+ * IX9 — fires whenever the zoom factor actually changes (`1` = 100%), whatever
2988
+ * the source: {@link XlsxViewer.setScale}, {@link XlsxViewer.zoomIn} /
2989
+ * {@link XlsxViewer.zoomOut}, {@link XlsxViewer.fitWidth} /
2990
+ * {@link XlsxViewer.fitPage}, the built-in zoom slider, the +/- buttons, or a
2991
+ * Ctrl/⌘+wheel gesture. Named `onScaleChange` to match the docx/pptx viewers so
2992
+ * all five share one notification shape. Not fired when a call resolves to the
2993
+ * same (clamped/snapped) scale.
2994
+ */
2995
+ onScaleChange?: (scale: number) => void;
1938
2996
  onReady?: (sheetNames: string[]) => void;
1939
2997
  /**
1940
2998
  * Called when the active sheet changes, with the new sheet's zero-based
@@ -1948,6 +3006,16 @@ export declare interface XlsxViewerOptions extends LoadOptions_2 {
1948
3006
  onError?: (err: Error) => void;
1949
3007
  /** Called when the selected cell range changes. null means no selection. */
1950
3008
  onSelectionChange?: (selection: CellRange | null) => void;
3009
+ /**
3010
+ * IX1 (design decision — NOT user-confirmed, integrator may veto). Fires when a
3011
+ * cell carrying a hyperlink (ECMA-376 §18.3.1.47) is clicked. Default when
3012
+ * omitted: external → {@link openExternalHyperlink} (new tab, sanitised,
3013
+ * noopener); internal (`location`) → navigate to the referenced sheet/cell
3014
+ * when resolvable. When supplied, this callback fully owns the behaviour and
3015
+ * receives the raw {@link HyperlinkTarget} verbatim (URL sanitisation is the
3016
+ * default handler's job, so a blocked scheme still reaches a custom callback).
3017
+ */
3018
+ onHyperlinkClick?: (target: HyperlinkTarget) => void;
1951
3019
  /**
1952
3020
  * Color of the cell-selection highlight. A single CSS color drives both the
1953
3021
  * selection rectangle's border (drawn in this color) and its fill (the same
@@ -2006,6 +3074,12 @@ export declare class XlsxWorkbook {
2006
3074
  * `renderViewport` call reuses it — equations in shapes render when present,
2007
3075
  * and are skipped (engine tree-shaken) when omitted. */
2008
3076
  private math;
3077
+ /** Google-Fonts `FontFace` objects this workbook preloaded into `document.fonts`
3078
+ * (main mode only — in worker mode the worker owns them and terminates with its
3079
+ * own FontFaceSet). Released in {@link destroy} so they do not leak into the
3080
+ * shared FontFaceSet for the lifetime of the SPA (deduped + refcounted in core,
3081
+ * so a web font shared with another open workbook survives until both go). */
3082
+ private googleFontFaces;
2009
3083
  private _mode;
2010
3084
  private constructor();
2011
3085
  /** Parse an XLSX from a URL or ArrayBuffer. */
@@ -2045,6 +3119,22 @@ export declare class XlsxWorkbook {
2045
3119
  * route-through-worker decision).
2046
3120
  */
2047
3121
  getImage(imagePath: string, mimeType: string): Promise<Blob>;
3122
+ /**
3123
+ * Project the workbook to GitHub-flavoured markdown: each sheet becomes a
3124
+ * `## SheetName` section followed by a pipe table of its populated bounding
3125
+ * box (fully-empty middle rows trimmed, ULP noise masked). Styling, charts,
3126
+ * and drawings are discarded — the projection is meant for AI ingestion and
3127
+ * full-text search, not layout.
3128
+ *
3129
+ * Runs entirely in the worker off the archive opened at {@link load} (no
3130
+ * re-copy of the file, no re-parse of the model on the main thread), so it
3131
+ * works in BOTH `mode: 'main'` and `mode: 'worker'`.
3132
+ *
3133
+ * @example
3134
+ * const wb = await XlsxWorkbook.load(buffer);
3135
+ * const md = await wb.toMarkdown();
3136
+ */
3137
+ toMarkdown(): Promise<string>;
2048
3138
  /**
2049
3139
  * Resolve a `list`-type data-validation `formula1` (ECMA-376 §18.3.1.32) into
2050
3140
  * the set of allowed values to display, evaluated relative to `sheetIndex`
@@ -2061,6 +3151,16 @@ export declare class XlsxWorkbook {
2061
3151
  * Read-only: this only reads cell values for display; it never writes.
2062
3152
  */
2063
3153
  resolveValidationList(sheetIndex: number, formula1: string | undefined): Promise<ResolvedList>;
3154
+ /**
3155
+ * IX2 — the display string a cell shows on the grid, i.e. exactly what
3156
+ * {@link renderViewport} would draw (number formats, dates, booleans, rich
3157
+ * text flattened). Used by {@link XlsxViewer.findText} to search the *rendered*
3158
+ * text rather than the raw stored value, so a search matches what the user
3159
+ * sees. Threads the workbook styles + the sheet's date system through the
3160
+ * shared {@link formatCellValue} (the same call the renderer and
3161
+ * validation-list expansion use). Returns `''` before the workbook is loaded.
3162
+ */
3163
+ cellText(ws: Worksheet, cell: Cell): string;
2064
3164
  renderViewport(target: HTMLCanvasElement | OffscreenCanvas, sheetIndex: number, viewport: ViewportRange, opts?: RenderViewportOptions): Promise<void>;
2065
3165
  /**
2066
3166
  * Render a sheet viewport and return it as an ImageBitmap (both modes; in
@@ -2080,4 +3180,90 @@ export declare class XlsxWorkbook {
2080
3180
  destroy(): void;
2081
3181
  }
2082
3182
 
3183
+ /**
3184
+ * IX9 — the shared zoom API contract for every viewer (DocxViewer, PptxViewer,
3185
+ * DocxScrollViewer, PptxScrollViewer, XlsxViewer).
3186
+ *
3187
+ * This module owns ONLY the pure, DOM-free pieces of the contract: the type
3188
+ * ({@link ZoomableViewer}), the discrete zoom-step ladder ({@link nextZoomStep} /
3189
+ * {@link prevZoomStep}), the fit-to-content scale math ({@link fitScale}), and the
3190
+ * range clamp ({@link clampScale}). Each viewer implements the interface with its
3191
+ * own scale field and re-render path; this keeps ONE definition of "what a zoom
3192
+ * factor means" and "what the +/- steps are" across all five, so a host can drive
3193
+ * any viewer through the same six calls without special-casing the format.
3194
+ *
3195
+ * SCALE SEMANTICS (the contract): a scale of `1` means 100% — the content at its
3196
+ * natural size (a docx page at `widthPt × PT_TO_PX`, a pptx slide at
3197
+ * `slideWidth / EMU_PER_PX`, an xlsx grid at `cellScale` 1). `getScale()` and
3198
+ * `setScale(n)` speak this user-facing factor for EVERY viewer.
3199
+ *
3200
+ * KNOWN FAMILY DIFFERENCE — the INITIAL scale right after load (deliberate,
3201
+ * documented rather than papered over): the single-canvas viewers (DocxViewer /
3202
+ * PptxViewer) and XlsxViewer start at `1` (or the effective factor implied by an
3203
+ * explicit `width` option); the continuous-scroll viewers (DocxScrollViewer /
3204
+ * PptxScrollViewer) AUTO-FIT to the container on first layout, so their
3205
+ * `getScale()` right after load reports the fit-to-width BASE factor (≠ 1 unless
3206
+ * the container happens to match the natural width). The unit is identical — only
3207
+ * the starting point differs, because fit-to-width is the natural resting state
3208
+ * of a continuous document viewer.
3209
+ *
3210
+ * PRE-LOAD `setScale` (family-unified, IX9 F1): a `setScale` called before the
3211
+ * content is loaded / before the layout is established is LATCHED — never
3212
+ * silently dropped — and applied once the viewer establishes its scale (the
3213
+ * single-canvas viewers honour it on the first render; the scroll viewers apply
3214
+ * it right after the base fit establishes, firing `onScaleChange` at application
3215
+ * time). `getScale()` reports the latched factor while it is pending.
3216
+ *
3217
+ * API SHAPE (idiomatic default — the integrator MAY veto; see the IX9 PR): a
3218
+ * six-method surface plus one change notification (`onScaleChange`). Deliberately
3219
+ * NO new UI here — the contract is API only (design decision IX9 §4). Touch-pinch
3220
+ * (IX8) is out of scope.
3221
+ */
3222
+ /**
3223
+ * The zoom contract every viewer satisfies. All scales are the user-facing factor
3224
+ * where `1` = 100% (see the module note). `fitWidth`/`fitPage` are async because a
3225
+ * fit re-renders at the new scale; the getters/steppers resolve synchronously.
3226
+ */
3227
+ declare interface ZoomableViewer {
3228
+ /** The current zoom factor (`1` = 100%). Never throws — returns the default
3229
+ * (`1`) before anything is loaded, or the latched pending factor when a
3230
+ * pre-load `setScale` is waiting to be applied (see the module note). */
3231
+ getScale(): number;
3232
+ /** Set the absolute zoom factor (`1` = 100%), clamped to the viewer's
3233
+ * `[zoomMin, zoomMax]`. Re-renders at the new scale and fires `onScaleChange`
3234
+ * when the clamped value actually changes. Called BEFORE the content is
3235
+ * loaded / the layout is established, the (clamped) factor is LATCHED and
3236
+ * applied once the viewer establishes its scale — family-unified semantics
3237
+ * (IX9 F1): never silently dropped by any viewer. */
3238
+ setScale(scale: number): void | Promise<void>;
3239
+ /** Step up to the next larger rung of the shared zoom ladder (25 %→400 %),
3240
+ * clamped to `zoomMax`. Equivalent to `setScale(nextZoomStep(getScale()))`. */
3241
+ zoomIn(): void | Promise<void>;
3242
+ /** Step down to the next smaller ladder rung, clamped to `zoomMin`. */
3243
+ zoomOut(): void | Promise<void>;
3244
+ /** Fit the content's WIDTH to the container (the common "fit width" / "fit
3245
+ * page width" verb). Sets the scale so one page/slide/sheet-column-run spans
3246
+ * the available width, then re-renders. Resolves once the fit render settles.
3247
+ *
3248
+ * PERSISTENCE is viewer-implementation-dependent (deliberate, by family): the
3249
+ * single-canvas viewers (DocxViewer / PptxViewer) and XlsxViewer apply the fit
3250
+ * ONE-SHOT — they observe no container resizes, so a later resize does NOT
3251
+ * re-fit (call `fitWidth()` again after a layout change). The continuous-
3252
+ * scroll viewers (DocxScrollViewer / PptxScrollViewer) re-fit their width-fit
3253
+ * base on every container resize, so a `fitWidth()` there effectively
3254
+ * PERSISTS across resizes (the resize re-fit preserves the width-fit state). */
3255
+ fitWidth(): void | Promise<void>;
3256
+ /** Fit the WHOLE content (width AND height) inside the container, so an entire
3257
+ * page/slide is visible without scrolling. Sets the scale to the smaller of the
3258
+ * width- and height-fit factors, then re-renders.
3259
+ *
3260
+ * PERSISTENCE is viewer-implementation-dependent, and — unlike `fitWidth` —
3261
+ * a page fit does NOT persist across container resizes on ANY viewer: the
3262
+ * single-canvas viewers and XlsxViewer observe no resizes at all (one-shot),
3263
+ * and the continuous-scroll viewers' resize handler re-applies the WIDTH fit
3264
+ * (preserving the zoom multiplier), not the page fit. Re-invoke `fitPage()`
3265
+ * after a layout change to re-fit. */
3266
+ fitPage(): void | Promise<void>;
3267
+ }
3268
+
2083
3269
  export { }