import "fast-xml-parser"; import "jszip"; import { ChatTransport, LanguageModel, ToolSet, UIMessage } from "ai"; import "pptx-viewer-mcp"; import { Options } from "html2canvas-pro"; //#region ../core/dist/text-operations-mlsQMjIC.d.ts //#region src/core/types/actions.d.ts /** * Action types: hyperlinks, slide jumps, macros, and action buttons. * * @module pptx-types/actions */ /** * A parsed shape-level action from `a:hlinkClick` or `a:hlinkHover`. * * @example * ```ts * const link: PptxAction = { * url: "https://example.com", * tooltip: "Visit Example", * highlightClick: true, * }; * * const slideJump: PptxAction = { * action: "ppaction://hlinksldjump", * targetSlideIndex: 3, * }; * // => satisfies PptxAction * ``` */ interface PptxAction { /** Relationship ID referencing the action target. */ rId?: string; /** OOXML action string (e.g. `ppaction://hlinksldjump`). */ action?: string; /** Tooltip text shown on hover. */ tooltip?: string; /** Whether the shape should highlight on click. */ highlightClick?: boolean; /** Resolved URL or file path from the slide relationship map. */ url?: string; /** Zero-based index into the slides array for internal slide jumps. */ targetSlideIndex?: number; /** Relationship ID of an optional click sound (`a:snd/@r:embed`). */ soundRId?: string; /** Resolved media target path for the optional click sound. */ soundPath?: string; } /** * High-level action type for the action settings UI. * Maps to OOXML `ppaction://` verbs + external URLs. * * @example * ```ts * const type: ElementActionType = "slide"; * // => "slide" — one of: "none" | "url" | "slide" | "firstSlide" | "lastSlide" | "prevSlide" | "nextSlide" | "endShow" * ``` */ type ElementActionType = 'none' | 'url' | 'slide' | 'firstSlide' | 'lastSlide' | 'prevSlide' | 'nextSlide' | 'endShow'; /** * User-facing action configuration stored on an element. * This is a convenience wrapper around the lower-level `PptxAction` that maps * to/from OOXML hyperlink/action attributes. * * @example * ```ts * const action: ElementAction = { * trigger: "click", * type: "url", * url: "https://example.com", * }; * * const jumpToSlide: ElementAction = { * trigger: "click", * type: "slide", * slideIndex: 5, * }; * // => satisfies ElementAction * ``` */ interface ElementAction { /** When the action fires. */ trigger: 'click' | 'hover'; /** What kind of action to perform. */ type: ElementActionType; /** External URL (for 'url' type). */ url?: string; /** Zero-based slide index (for 'slide' type). */ slideIndex?: number; } //#endregion //#region src/core/types/common.d.ts /** * Shared value types used across the entire PPTX editor type system. * * Contains primitive enums, small interfaces, and the XML object alias * that almost every other type file imports. * * @module pptx-types/common */ /** * Underline style tokens from OOXML `a:rPr/@u`. * * These map directly to the OpenXML `ST_TextUnderlineType` simple type. * * @example * ```ts * const style: UnderlineStyle = "wavy"; * // => "wavy" — one of: sng | dbl | heavy | dotted | dash | wavy | none | ... * ``` */ type UnderlineStyle = 'sng' | 'dbl' | 'heavy' | 'dotted' | 'dottedHeavy' | 'dash' | 'dashHeavy' | 'dashLong' | 'dashLongHeavy' | 'dotDash' | 'dotDashHeavy' | 'dotDotDash' | 'dotDotDashHeavy' | 'wavy' | 'wavyHeavy' | 'wavyDbl' | 'none'; /** * Connector connection point reference — links a connector endpoint to a * specific shape on the slide. * * When both `shapeId` and `connectionSiteIndex` are set, the connector * end snaps to that shapes’s connection site and “follows” the shape when * it is moved. * * @example * ```ts * const start: ConnectorConnectionPoint = { * shapeId: "shape_1", * connectionSiteIndex: 2, * }; * // => { shapeId: "shape_1", connectionSiteIndex: 2 } satisfies ConnectorConnectionPoint * ``` */ interface ConnectorConnectionPoint { /** ID of the shape this connector endpoint is attached to. */ shapeId?: string; /** Connection site index on the target shape (0-based). */ connectionSiteIndex?: number; } /** * Arrow head types for connector start/end. * * Maps to `a:headEnd/@type` and `a:tailEnd/@type` in OOXML. * * @example * ```ts * const arrow: ConnectorArrowType = "triangle"; * // => "triangle" — one of: none | triangle | stealth | diamond | oval | arrow * ``` */ type ConnectorArrowType = 'none' | 'triangle' | 'stealth' | 'diamond' | 'oval' | 'arrow'; /** * Stroke dash pattern types for lines and shape outlines. * * Maps to `a:ln/a:prstDash/@val` in OOXML. Use `"custom"` for * user-defined dash/space arrays. * * @example * ```ts * const dash: StrokeDashType = "dashDot"; * // => "dashDot" — one of: solid | dot | dash | lgDash | dashDot | custom | ... * ``` */ type StrokeDashType = 'solid' | 'dot' | 'dash' | 'lgDash' | 'dashDot' | 'lgDashDot' | 'lgDashDotDot' | 'sysDot' | 'sysDash' | 'sysDashDot' | 'sysDashDotDot' | 'custom'; /** * Shadow effect properties for a single shadow layer. * * Represents parsed values from an `` node. Multiple instances * can be stored in {@link ShapeStyle.shadows} for compound shadow effects. * * @example * ```ts * const shadow: ShadowEffect = { * color: "#000000", * opacity: 0.4, * blur: 6, * angle: 315, * distance: 4, * }; * // => { color: "#000000", opacity: 0.4, blur: 6, angle: 315, distance: 4 } satisfies ShadowEffect * ``` */ interface ShadowEffect { /** Shadow color as hex string. */ color: string; /** Shadow opacity (0-1). */ opacity: number; /** Blur radius in pixels. */ blur: number; /** Shadow angle in degrees (0-360). */ angle: number; /** Shadow distance in pixels. */ distance: number; /** Whether shadow rotates with shape. */ rotateWithShape?: boolean; } /** * Strongly-typed parsed XML node from fast-xml-parser. * * The parser is configured with `attributeNamePrefix: '@_'`, * `parseAttributeValue: false`, and `parseTagValue: false`, so attribute and * text values are always strings at runtime. This type encodes that: * * - **Attributes** — keys matching `` `@_${string}` `` return * `string | undefined` directly. * - **Text content** — `#text` returns `string | undefined`. * - **Child elements** — any other string key returns * `XmlObject | XmlObject[] | string | undefined`. The union reflects that * fast-xml-parser may emit an object (single child), an array (repeated * children), or a bare string (text-only element collapsed by the parser). * * For traversal, prefer the helpers in {@link ./../utils/xml-access} — * `xmlChild` / `xmlChildren` / `xmlAttr` / `xmlText` / `xmlPath` — which * narrow the union and normalize the single-vs-array duality. Direct * indexing works for attributes (typed as string) but chained child access * (`obj['p:spPr']?.['a:xfrm']`) requires the helpers or a narrowing cast * because TypeScript cannot index into the `XmlObject[] | string` part of * the union. */ interface XmlObject { /** Attributes (`@_`-prefixed keys) are always strings at runtime. */ [attr: `@_${string}`]: string | undefined; /** Element text content surfaces under `#text` when present. */ '#text'?: string; /** * Child elements keyed by their (namespaced) tag name. fast-xml-parser * emits a single object for unique elements, an array for repeated ones, * and a bare string for elements collapsed to their text content. Use * the helpers in `utils/xml-access` to narrow this union. */ [child: string]: XmlObject | XmlObject[] | string | undefined; } /** * Lock attributes from an element's non-visual properties node. * * When a flag is `true` the corresponding user interaction is disabled * in the editor (e.g. `noRotation` prevents free rotation of the shape). * * One bag covers every family, but the families are NOT interchangeable in * the file: `a:spLocks` (`CT_ShapeLocking`), `a:picLocks`, `a:cxnSpLocks`, * `a:grpSpLocks` (`CT_GroupLocking`) and `a:graphicFrameLocks` * (`CT_GraphicalObjectFrameLocking`) each declare their own attribute subset. * `runtime/shape-lock-containers` holds that table and is what decides which * of these fields may be written for a given element. * * @example * ```ts * const locks: PptxShapeLocks = { noMove: true, noResize: true }; * // => { noMove: true, noResize: true } satisfies PptxShapeLocks * ``` */ interface PptxShapeLocks { noGrouping?: boolean; noRotation?: boolean; noMove?: boolean; noResize?: boolean; noTextEdit?: boolean; noSelect?: boolean; noChangeAspect?: boolean; noEditPoints?: boolean; noAdjustHandles?: boolean; noChangeArrowheads?: boolean; noChangeShapeType?: boolean; /** * `a:graphicFrameLocks/@noDrilldown`: forbids selecting the individual * parts inside a graphic frame (a chart series, a SmartArt node). Declared * ONLY by `CT_GraphicalObjectFrameLocking`, so it is written for tables, * charts, SmartArt, OLE objects and graphic-frame media, and never onto * `a:spLocks` / `a:picLocks` / `a:cxnSpLocks` / `a:grpSpLocks`. */ noDrilldown?: boolean; /** * Text-box flag from `p:cNvSpPr/@txBox`. Not a lock in the strict sense, * but it lives on the same non-visual-properties node as `a:spLocks`, so * it is captured here to round-trip through the model. When `true` the * shape is a plain text box (no fill/line by default). */ txBox?: boolean; } /** * A drawing guide parsed from OOXML extension lists. * * Slide-level and presentation-level guides are shown as thin coloured * lines that help users align elements. * * @example * ```ts * const guide: PptxDrawingGuide = { * id: "g1", * orientation: "horz", * positionEmu: 457200, * color: "#FF0000", * }; * // => { id: "g1", orientation: "horz", positionEmu: 457200, color: "#FF0000" } satisfies PptxDrawingGuide * ``` */ interface PptxDrawingGuide { /** Unique identifier (from `@_id` attribute or generated). */ id: string; /** Orientation: horizontal or vertical. */ orientation: 'horz' | 'vert'; /** Position in EMU (converted from pos attribute). */ positionEmu: number; /** Optional guide colour as hex string (e.g. "#FF0000"). */ color?: string; } //#endregion //#region src/core/types/geometry.d.ts /** * Geometry types: adjustment handles, custom geometry points, segments, * paths, and custom path properties. * * @module pptx-types/geometry */ /** * Defines an adjustment handle position for a shape geometry. * * Adjustment handles allow users to interactively reshape preset shapes * (e.g. rounding a rectangle corner or adjusting arrow head width). * * @example * ```ts * const handle: GeometryAdjustmentHandle = { * guideName: "adj", * xFraction: 0.25, * minValue: 0, * maxValue: 50000, * }; * // => satisfies GeometryAdjustmentHandle * ``` */ interface GeometryAdjustmentHandle { /** Name of the adjustment guide this handle controls (e.g. "adj", "adj1"). */ guideName: string; /** X position as a fraction of shape width (0..1), or undefined if the handle only moves vertically. */ xFraction?: number; /** Y position as a fraction of shape height (0..1), or undefined if the handle only moves horizontally. */ yFraction?: number; /** Minimum allowed value for the adjustment guide. */ minValue?: number; /** Maximum allowed value for the adjustment guide. */ maxValue?: number; } /** * A single point in a custom geometry path. * * @example * ```ts * const pt: CustomGeometryPoint = { x: 100, y: 200 }; * // => satisfies CustomGeometryPoint * ``` */ interface CustomGeometryPoint { x: number; y: number; } /** * A segment within a custom geometry path. * * Discriminated union over `type` — can be a moveTo, lineTo, * cubic Bézier, quadratic Bézier, or close command. * * @example * ```ts * const segments: CustomGeometrySegment[] = [ * { type: "moveTo", pt: { x: 0, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 100 } }, * { type: "close" }, * ]; * // => satisfies CustomGeometrySegment[] * ``` */ type CustomGeometrySegment = { type: 'moveTo'; pt: CustomGeometryPoint; } | { type: 'lineTo'; pt: CustomGeometryPoint; } | { type: 'cubicBezTo'; pts: [CustomGeometryPoint, CustomGeometryPoint, CustomGeometryPoint]; } | { type: 'quadBezTo'; pts: [CustomGeometryPoint, CustomGeometryPoint]; } | { type: 'arcTo'; /** Horizontal radius of the ellipse. */ wR: number; /** Vertical radius of the ellipse. */ hR: number; /** Start angle in 60000ths of a degree. */ stAng: number; /** Sweep angle in 60000ths of a degree. */ swAng: number; } | { type: 'close'; }; /** * A single sub-path in a custom geometry definition (maps to one `a:path`). * * @example * ```ts * const path: CustomGeometryPath = { * width: 100, * height: 100, * segments: [ * { type: "moveTo", pt: { x: 0, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 100 } }, * ], * }; * // => satisfies CustomGeometryPath * ``` */ interface CustomGeometryPath { /** Coordinate-space width for this sub-path. */ width: number; /** Coordinate-space height for this sub-path. */ height: number; /** Ordered list of drawing segments. */ segments: CustomGeometrySegment[]; /** Path fill mode (`a:path/@fill`): norm, lighten, lightenLess, darken, darkenLess, none. */ fillMode?: 'norm' | 'lighten' | 'lightenLess' | 'darken' | 'darkenLess' | 'none'; /** Whether the path is stroked (`a:path/@stroke`). */ stroke?: boolean; /** 3D extrusion compatibility (`a:path/@extrusionOk`). */ extrusionOk?: boolean; } /** * Auxiliary raw XML preserved from `a:custGeom` for round-trip serialization. * These are stored opaquely so adjustment guides, handles, connection sites, * and the text rectangle are not lost when a custGeom is edited and saved. */ interface CustomGeometryRawData { /** Raw `a:avLst` XML content (adjustment value list). */ avLstXml?: unknown; /** Raw `a:gdLst` XML content (guide list). */ gdLstXml?: unknown; /** Raw `a:ahLst` XML content (adjustment handles). */ ahLstXml?: unknown; /** Raw `a:cxnLst` XML content (connection sites). */ cxnLstXml?: unknown; /** Raw `a:rect` XML content (text rectangle). */ rectXml?: unknown; } /** * XY-style adjustment handle (`a:ahXY`) on a custom geometry. * * Allows interactive editing of one or two guide values constrained to a * rectangular range. Coordinates are formula references (e.g. `"adj1"`, * `"w/2"`, `"0"`) preserved verbatim so they can re-emit unchanged. * * @example * ```ts * const handle: AdjustHandleXY = { * gdRefX: "adj1", * minX: "0", * maxX: "w", * posX: "adj1", * posY: "h/2", * }; * // => satisfies AdjustHandleXY * ``` */ interface AdjustHandleXY { /** Guide reference for the X axis (`@_gdRefX`). */ gdRefX?: string; /** Guide reference for the Y axis (`@_gdRefY`). */ gdRefY?: string; /** Minimum X value, as a formula reference (`@_minX`). */ minX?: string; /** Maximum X value (`@_maxX`). */ maxX?: string; /** Minimum Y value (`@_minY`). */ minY?: string; /** Maximum Y value (`@_maxY`). */ maxY?: string; /** Handle position X (formula or literal) from `a:pos/@_x`. */ posX?: string; /** Handle position Y from `a:pos/@_y`. */ posY?: string; } /** * Polar-style adjustment handle (`a:ahPolar`) on a custom geometry. * * Drives a guide via radial distance and angle rather than XY coordinates. * * @example * ```ts * const handle: AdjustHandlePolar = { * gdRefR: "adj1", * gdRefAng: "adj2", * posX: "wd2", * posY: "hd2", * }; * // => satisfies AdjustHandlePolar * ``` */ interface AdjustHandlePolar { /** Guide reference for the radial distance (`@_gdRefR`). */ gdRefR?: string; /** Guide reference for the angle (`@_gdRefAng`). */ gdRefAng?: string; /** Minimum radial value (`@_minR`). */ minR?: string; /** Maximum radial value (`@_maxR`). */ maxR?: string; /** Minimum angle (`@_minAng`). */ minAng?: string; /** Maximum angle (`@_maxAng`). */ maxAng?: string; /** Handle position X from `a:pos/@_x`. */ posX?: string; /** Handle position Y from `a:pos/@_y`. */ posY?: string; } /** * Connection site (`a:cxn`) on a custom geometry. * * Defines a point on a custom shape that connectors may snap to. * * @example * ```ts * const cxn: ConnectionSite = { ang: "0", posX: "0", posY: "hd2" }; * // => satisfies ConnectionSite * ``` */ interface ConnectionSite { /** Approach angle (`@_ang`) — formula or literal degree-1/60000 value. */ ang?: string; /** Site position X from `a:pos/@_x`. */ posX?: string; /** Site position Y from `a:pos/@_y`. */ posY?: string; } /** * Typed text rectangle (`a:rect`) on a custom geometry. * * Each edge is the formula or literal string preserved from the source XML * (`"l"`, `"t"`, `"r"`, `"b"`, or any guide name / formula). */ interface CustomGeometryTextRect { /** Left edge formula reference (`@_l`). */ l?: string; /** Top edge (`@_t`). */ t?: string; /** Right edge (`@_r`). */ r?: string; /** Bottom edge (`@_b`). */ b?: string; } /** * Custom (non-preset) geometry path — only on shapes and pictures. * * Contains SVG path data and/or structured custom geometry paths * parsed from `a:custGeom/a:pathLst`. * * @example * ```ts * const custom: PptxCustomPathProperties = { * pathData: "M 0 0 L 100 0 L 100 100 Z", * pathWidth: 100, * pathHeight: 100, * }; * // => satisfies PptxCustomPathProperties * ``` */ interface PptxCustomPathProperties { /** SVG path data for custom shapes. */ pathData?: string; /** Coordinate-space width for the custom path. */ pathWidth?: number; /** Coordinate-space height for the custom path. */ pathHeight?: number; /** Structured custom geometry paths for editing (maps to a:custGeom/a:pathLst). */ customGeometryPaths?: CustomGeometryPath[]; /** Raw adjustment/guide/handle/connection/text-rectangle XML preserved for serialization. */ customGeometryRawData?: CustomGeometryRawData; /** * Typed XY adjustment handles parsed from `a:custGeom/a:ahLst/a:ahXY`. * SDK-built shapes can populate this and the writer will emit `` entries * even when no raw XML was preserved. */ customGeometryAdjustHandlesXY?: AdjustHandleXY[]; /** * Typed polar adjustment handles parsed from `a:custGeom/a:ahLst/a:ahPolar`. */ customGeometryAdjustHandlesPolar?: AdjustHandlePolar[]; /** * Typed connection sites parsed from `a:custGeom/a:cxnLst/a:cxn`. */ customGeometryConnectionSites?: ConnectionSite[]; /** * Typed text rectangle parsed from `a:custGeom/a:rect`. When present this is * preferred over {@link customGeometryRawData}'s `rectXml` on save. */ customGeometryTextRect?: CustomGeometryTextRect; } //#endregion //#region src/core/types/effect-dag.d.ts type EffectDagBlendMode = 'darken' | 'lighten' | 'mult' | 'over' | 'screen'; type EffectDagContainerType = 'sib' | 'tree'; type EffectDagNode = EffectDagContainer | EffectDagBlend | EffectDagXfrm | EffectDagRelOff | EffectDagBlur | EffectDagAlphaOutset | EffectDagPresetShadow | EffectDagRawLeaf; interface EffectDagContainer { kind: 'cont'; type: EffectDagContainerType; name?: string; children: EffectDagNode[]; } interface EffectDagBlend { kind: 'blend'; mode: EffectDagBlendMode; container: EffectDagContainer; } interface EffectDagXfrm { kind: 'xfrmEffect'; sx?: number; sy?: number; kx?: number; ky?: number; tx?: number; ty?: number; } interface EffectDagRelOff { kind: 'relOff'; tx?: number; ty?: number; } /** Typed CT_BlurEffect with its original payload retained for lossless edits. */ interface EffectDagBlur { kind: 'blur'; radiusEmu?: number; grow?: boolean; xml: XmlObject; } /** Typed CT_AlphaOutsetEffect with original XML retained for lossless edits. */ interface EffectDagAlphaOutset { kind: 'alphaOutset'; radiusEmu?: number; xml: XmlObject; } /** Typed CT_PresetShadowEffect with colour and extension XML retained verbatim. */ interface EffectDagPresetShadow { kind: 'prstShdw'; preset?: `shdw${number}`; distanceEmu?: number; direction?: number; xml: XmlObject; } interface EffectDagRawLeaf { kind: 'raw'; tag: string; xml: Record; } //#endregion //#region src/core/types/three-d.d.ts /** * 3-D effect properties, text warp (WordArt) presets, and scene/shape bevel * definitions parsed from OOXML `a:sp3d`, `a:scene3d`, and `a:bodyPr/a:prstTxWarp`. * * @module pptx-types/three-d */ /** * Bevel preset type tokens from OOXML `a:bevelT/@prst` / `a:bevelB/@prst`. * * @example * ```ts * const bevel: BevelPresetType = "circle"; * // => "circle" — one of: "circle" | "relaxedInset" | "cross" | "coolSlant" | "angle" | … * ``` */ type BevelPresetType = 'circle' | 'relaxedInset' | 'cross' | 'coolSlant' | 'angle' | 'softRound' | 'convex' | 'slope' | 'divot' | 'riblet' | 'hardEdge' | 'artDeco' | 'none'; /** * Material preset type tokens from OOXML `a:sp3d/@prstMaterial`. * * @example * ```ts * const mat: MaterialPresetType = "plastic"; * // => "plastic" — one of: "matte" | "warmMatte" | "plastic" | "metal" | "dkEdge" | … * ``` */ type MaterialPresetType = 'matte' | 'warmMatte' | 'plastic' | 'metal' | 'dkEdge' | 'softEdge' | 'flat' | 'softmetal' | 'clear' | 'powder' | 'translucentPowder' | 'legacyMatte' | 'legacyPlastic' | 'legacyMetal' | 'legacyWireframe'; /** * 3D text body extrusion/bevel from `a:bodyPr/a:sp3d`. * * @example * ```ts * const text3d: Text3DStyle = { * extrusionHeight: 57150, * presetMaterial: "plastic", * bevelTopType: "circle", * bevelTopWidth: 25400, * bevelTopHeight: 25400, * }; * // => satisfies Text3DStyle * ``` */ interface Text3DStyle { /** Extrusion height (depth) in EMU. */ extrusionHeight?: number; /** Extrusion colour as hex string. */ extrusionColor?: string; /** Preset material, e.g. "matte", "plastic", "metal". */ presetMaterial?: MaterialPresetType; /** Top bevel preset type. */ bevelTopType?: BevelPresetType; /** Top bevel width in EMU. */ bevelTopWidth?: number; /** Top bevel height in EMU. */ bevelTopHeight?: number; /** Bottom bevel preset type. */ bevelBottomType?: BevelPresetType; /** Bottom bevel width in EMU. */ bevelBottomWidth?: number; /** Bottom bevel height in EMU. */ bevelBottomHeight?: number; } /** * 3D scene/camera properties from `a:scene3d`. * * @example * ```ts * const scene: Pptx3DScene = { * cameraPreset: "perspectiveFront", * lightRigType: "threePt", * lightRigDirection: "t", * }; * // => satisfies Pptx3DScene * ``` */ interface Pptx3DScene { /** Camera preset type, e.g. "orthographicFront", "perspectiveFront". */ cameraPreset?: string; /** Camera field of view in 1/60000 degrees (`a:camera/@fov`). */ cameraFieldOfView?: number; /** Camera zoom as an OOXML percentage fraction (`a:camera/@zoom`, 1 = 100%). */ cameraZoom?: number; /** Camera rotation around X axis in 1/60000 degrees. */ cameraRotX?: number; /** Camera rotation around Y axis in 1/60000 degrees. */ cameraRotY?: number; /** Camera rotation around Z axis in 1/60000 degrees. */ cameraRotZ?: number; /** Light rig type, e.g. "threePt", "balanced", "harsh". */ lightRigType?: string; /** Light rig direction, e.g. "t", "b", "l", "r", "tl". */ lightRigDirection?: string; /** Light-rig rotation latitude in 1/60000 degrees. */ lightRigRotX?: number; /** Light-rig rotation longitude in 1/60000 degrees. */ lightRigRotY?: number; /** Light-rig rotation revolution in 1/60000 degrees. */ lightRigRotZ?: number; /** Whether a 3D backdrop plane is present (`a:backdrop`). */ hasBackdrop?: boolean; /** Backdrop plane anchor X in EMU. */ backdropAnchorX?: number; /** Backdrop plane anchor Y in EMU. */ backdropAnchorY?: number; /** Backdrop plane anchor Z in EMU. */ backdropAnchorZ?: number; /** Backdrop normal vector X component. */ backdropNormalX?: number; /** Backdrop normal vector Y component. */ backdropNormalY?: number; /** Backdrop normal vector Z component. */ backdropNormalZ?: number; /** Backdrop up vector X component. */ backdropUpX?: number; /** Backdrop up vector Y component. */ backdropUpY?: number; /** Backdrop up vector Z component. */ backdropUpZ?: number; } /** * 3D shape extrusion/bevel from `a:sp3d`. * * @example * ```ts * const shape3d: Pptx3DShape = { * extrusionHeight: 76200, * extrusionColor: "#4F81BD", * presetMaterial: "metal", * bevelTopType: "circle", * bevelTopWidth: 12700, * bevelTopHeight: 12700, * }; * // => satisfies Pptx3DShape * ``` */ interface Pptx3DShape { /** * Position of the shape along the Z axis, in EMU (`a:sp3d/@z`, default 0). * Independent of {@link extrusionHeight}: it moves the whole shape forward * or back in 3D space rather than adding depth to it, most commonly used to * stack several shapes at different depths under one `a:scene3d` camera. */ positionZ?: number; /** Extrusion height in EMU. */ extrusionHeight?: number; /** Extrusion colour. */ extrusionColor?: string; /** Contour width in EMU. */ contourWidth?: number; /** Contour colour. */ contourColor?: string; /** Preset material, e.g. "matte", "warmMatte", "metal". */ presetMaterial?: string; /** Top bevel type, e.g. "circle", "relaxedInset". */ bevelTopType?: string; /** Top bevel width in EMU. */ bevelTopWidth?: number; /** Top bevel height in EMU. */ bevelTopHeight?: number; /** Bottom bevel type, e.g. "circle", "relaxedInset". */ bevelBottomType?: string; /** Bottom bevel width in EMU. */ bevelBottomWidth?: number; /** Bottom bevel height in EMU. */ bevelBottomHeight?: number; } /** * Known OOXML preset text warp types (WordArt transforms). * * Falls back to `string` for unknown presets not yet catalogued. * * @example * ```ts * const warp: PptxTextWarpPreset = "textArchUp"; * // => "textArchUp" — one of: "textNoShape" | "textPlain" | "textStop" | "textArchUp" | … * ``` */ type PptxTextWarpPreset = 'textNoShape' | 'textPlain' | 'textStop' | 'textTriangle' | 'textTriangleInverted' | 'textChevron' | 'textChevronInverted' | 'textRingInside' | 'textRingOutside' | 'textArchUp' | 'textArchDown' | 'textCircle' | 'textButton' | 'textArchUpPour' | 'textArchDownPour' | 'textCirclePour' | 'textButtonPour' | 'textCurveUp' | 'textCurveDown' | 'textCanUp' | 'textCanDown' | 'textWave1' | 'textWave2' | 'textWave4' | 'textDoubleWave1' | 'textInflate' | 'textDeflate' | 'textInflateBottom' | 'textDeflateBottom' | 'textInflateTop' | 'textDeflateTop' | 'textFadeRight' | 'textFadeLeft' | 'textFadeUp' | 'textFadeDown' | 'textSlantUp' | 'textSlantDown' | 'textCascadeUp' | 'textCascadeDown' | 'textDeflateInflate' | 'textDeflateInflateDeflate' | string; //#endregion //#region src/core/types/shape-style.d.ts interface PptxCustomDashSegment { /** Dash length as a non-negative percentage in thousandths of one percent. */ dash: number; /** Space length as a non-negative percentage in thousandths of one percent. */ space: number; } /** * Comprehensive visual style for a shape, connector, or image element. * * All fields are optional. When absent, the element inherits from theme * or layout defaults. The interface models both simple styling (solid fill + * basic stroke) and advanced effects (multiple shadow layers, gradient * fills, 3-D extrusion). * * @example * ```ts * // Simple blue filled shape with a thin black outline: * const simple: ShapeStyle = { * fillColor: "#0055AA", * fillMode: "solid", * strokeColor: "#000000", * strokeWidth: 1, * }; * * // Gradient fill with a soft shadow: * const fancy: ShapeStyle = { * fillMode: "gradient", * fillGradientType: "linear", * fillGradientAngle: 135, * fillGradientStops: [ * { color: "#FF6B6B", position: 0 }, * { color: "#556270", position: 1 }, * ], * shadowColor: "#000000", * shadowBlur: 10, * shadowOffsetX: 4, * shadowOffsetY: 4, * shadowOpacity: 0.3, * }; * // => both satisfy the ShapeStyle interface * ``` */ interface ShapeStyle { fillColor?: string; /** * Raw XML colour-choice node preserved from `a:solidFill` for round-trip * serialisation. Captures `a:schemeClr` / `a:sysClr` / `a:prstClr` / * `a:srgbClr` plus colour transforms (`lumMod`, `lumOff`, `tint`, * `shade`, `satMod`, `alpha`, …). On save we re-emit verbatim when the * resolved {@link fillColor} still matches this node, otherwise we fall * back to canonical ``. */ fillColorXml?: XmlObject; fillGradient?: string; /** Original `gradFill` XML retained for unknown-child and extension round-tripping. */ fillGradientXml?: XmlObject; fillMode?: 'solid' | 'gradient' | 'pattern' | 'none' | 'image' | 'theme' | 'group'; /** * ``: the shape paints with the SLIDE BACKGROUND's fill * rather than its own or its theme style's. * * PowerPoint's designer emits full-bleed rectangles this way, and they also * carry an `a:fillRef` pointing at `accent1`. Ignoring the attribute painted * those panels in the accent colour, so a black-and-white title slide came out * black-and-blue. The load pipeline copies the resolved slide background onto * the fill fields; the flag stays for round-trip and for renderers that want * to re-resolve against a changed background. */ useBackgroundFill?: boolean; fillPatternPreset?: string; fillPatternBackgroundColor?: string; /** Original `pattFill` XML retained for unknown-child round-tripping. */ fillPatternXml?: XmlObject; /** Raw XML node for pattern fill foreground colour (preserves color transforms). */ fillPatternFgClrXml?: XmlObject; /** Raw XML node for pattern fill background colour (preserves color transforms). */ fillPatternBgClrXml?: XmlObject; /** Data-URI or URL for image fill (when fillMode === "image"). */ fillImageUrl?: string; /** How the image is sized within the shape: stretch to fill, or tile/repeat. */ fillImageMode?: 'stretch' | 'tile'; fillGradientStops?: Array<{ color: string; position: number; opacity?: number; /** Raw XML colour node preserved for round-trip (e.g. a:schemeClr with transforms). */ originalColorXml?: XmlObject; }>; fillGradientAngle?: number; fillGradientType?: 'linear' | 'radial'; /** Path gradient sub-type from `a:path/@path` (e.g. "circle", "rect", "shape"). */ fillGradientPathType?: 'circle' | 'rect' | 'shape'; /** Focal point for path (radial) gradients, derived from `a:fillToRect`. * Values are 0..1 fractions relative to shape bounds. */ fillGradientFocalPoint?: { x: number; y: number; }; /** Raw fillToRect LTRB values (0..1 fractions) from `a:fillToRect`. * Defines the inner rectangle where the gradient reaches its final stop. * l/t are insets from left/top edges; r/b are insets from right/bottom edges. */ fillGradientFillToRect?: { l: number; t: number; r: number; b: number; }; /** Raw tileRect LTRB values (0..1 fractions, may be negative) from * `a:gradFill/a:tileRect`. Defines the rectangle the gradient tile occupies * before any flip/tiling is applied. */ fillGradientTileRect?: { l: number; t: number; r: number; b: number; }; /** Gradient tile flip mode (`a:gradFill/@flip`). * `none` = no tiling flip (default), `x|y|xy` = mirror in the named axis. */ fillGradientFlip?: 'none' | 'x' | 'y' | 'xy'; /** Whether the gradient rotates with the shape (`a:gradFill/@rotWithShape`). * Defaults to true per the schema; preserved for round-trip when the source * authored the attribute explicitly. */ fillGradientRotWithShape?: boolean; /** Whether the linear gradient is scaled to the shape (`a:lin/@scaled`). * Defaults to true per the schema; preserved for round-trip. */ fillGradientScaled?: boolean; fillOpacity?: number; strokeColor?: string; /** * Raw XML colour-choice node preserved from `a:ln/a:solidFill` for * round-trip serialisation. See {@link fillColorXml} for the rationale. */ strokeColorXml?: XmlObject; /** * Kind of fill painted on the outline (`a:ln` child). Distinguishes a solid * outline from a gradient/pattern/none outline so save can emit the correct * single line fill instead of collapsing every outline to `a:solidFill` * (which, alongside a preserved `a:gradFill`/`a:pattFill`, produces an * invalid dual-fill ``). */ strokeFillMode?: 'solid' | 'gradient' | 'pattern' | 'none'; /** Raw `a:ln/a:gradFill` XML preserved for round-trip when the outline is * gradient-filled. Re-emitted verbatim as the line's single fill on save. */ strokeGradientXml?: XmlObject; /** Raw `a:ln/a:pattFill` XML preserved for round-trip when the outline is * pattern-filled. Re-emitted verbatim as the line's single fill on save. */ strokePatternXml?: XmlObject; /** * Structured stops of a gradient outline (`a:ln/a:gradFill/a:gsLst`), in the * same shape as {@link fillGradientStops}. * * The raw XML above round-trips a gradient outline on save, but a renderer * cannot paint from it: it needs resolved colours and positions. Without * these, every binding fell back to {@link strokeColor} - a single averaged * colour - so a two-tone outline painted flat and a fade-to-transparent * outline painted fully opaque. */ strokeGradientStops?: ShapeStyle['fillGradientStops']; /** Gradient outline angle in OOXML degrees (`a:lin/@ang`), 0 = left to right. */ strokeGradientAngle?: number; /** Gradient outline kind: `linear` (`a:lin`) or `radial` (`a:path`). */ strokeGradientType?: ShapeStyle['fillGradientType']; /** Path-gradient shape for a radial outline (`a:path/@path`). */ strokeGradientPathType?: ShapeStyle['fillGradientPathType']; /** Preset name of a pattern outline (`a:ln/a:pattFill/@prst`). */ strokePatternPreset?: string; /** Background colour of a pattern outline (`a:ln/a:pattFill/a:bgClr`). */ strokePatternBackgroundColor?: string; strokeWidth?: number; strokeOpacity?: number; strokeDash?: StrokeDashType; /** Line join style (`a:ln/@join`): round, bevel, or miter. */ lineJoin?: 'round' | 'bevel' | 'miter'; /** Miter limit (`a:miter/@lim`) in EMU-percent units (default 800000 = 8.0). Only meaningful when lineJoin is 'miter'. */ miterLimit?: number; /** Line cap style (`a:ln/@cap`): flat, rnd, or sq. */ lineCap?: 'flat' | 'rnd' | 'sq'; /** Compound line type (`a:ln/@cmpd`). */ compoundLine?: 'sng' | 'dbl' | 'thickThin' | 'thinThick' | 'tri'; /** Pen line alignment (`a:ln/@algn`): `ctr` (centre, default) or `in` (inside). */ lineAlignment?: 'ctr' | 'in'; shadowColor?: string; /** Preserved source `a:effectLst`, including unknown effects and extensions. */ effectListXml?: XmlObject; /** Original outer-shadow node used for lossless surgical updates. */ outerShadowXml?: XmlObject; /** Resolved source shadow colour used to detect colour edits. */ outerShadowOriginalColor?: string; /** Source shadow opacity used to detect alpha edits. */ outerShadowOriginalOpacity?: number; shadowBlur?: number; shadowOffsetX?: number; shadowOffsetY?: number; shadowOpacity?: number; /** Preset shadow name from `a:prstShdw/@prst` (e.g. "shdw1"..."shdw20"). */ presetShadowName?: string; /** Shadow angle in degrees (0-360). Parsed from `@_dir` (60000ths of a degree). */ shadowAngle?: number; /** Shadow distance in pixels. Parsed from `@_dist` (EMUs). */ shadowDistance?: number; /** Whether shadow rotates with shape. Parsed from `@_rotWithShape`. */ shadowRotateWithShape?: boolean; /** Outer-shadow horizontal scaling (`a:outerShdw/@sx`) in 1000ths of a percent (default 100000 = 100%). */ shadowScaleX?: number; /** Outer-shadow vertical scaling (`a:outerShdw/@sy`). */ shadowScaleY?: number; /** Outer-shadow horizontal skew (`a:outerShdw/@kx`) in 60000ths of a degree. */ shadowSkewX?: number; /** Outer-shadow vertical skew (`a:outerShdw/@ky`). */ shadowSkewY?: number; /** Outer-shadow alignment (`a:outerShdw/@algn`). */ shadowAlignment?: 'tl' | 't' | 'tr' | 'l' | 'ctr' | 'r' | 'bl' | 'b' | 'br'; /** Inner-shadow rotateWithShape (`a:innerShdw/@rotWithShape`). */ innerShadowRotateWithShape?: boolean; /** Reflection fade direction (`a:reflection/@fadeDir`) in 60000ths of a degree. */ reflectionFadeDirection?: number; /** Reflection horizontal scaling (`a:reflection/@sx`). */ reflectionScaleX?: number; /** Reflection vertical scaling (`a:reflection/@sy`). */ reflectionScaleY?: number; /** Reflection horizontal skew (`a:reflection/@kx`). */ reflectionSkewX?: number; /** Reflection vertical skew (`a:reflection/@ky`). */ reflectionSkewY?: number; /** Reflection alignment (`a:reflection/@algn`). */ reflectionAlignment?: 'tl' | 't' | 'tr' | 'l' | 'ctr' | 'r' | 'bl' | 'b' | 'br'; /** Reflection rotateWithShape (`a:reflection/@rotWithShape`). */ reflectionRotateWithShape?: boolean; /** Reflection start position (`a:reflection/@stPos`) as 0-1 fraction. */ reflectionStartPosition?: number; /** Multiple shadow layers (for advanced effects). */ shadows?: ShadowEffect[]; glowColor?: string; /** Original glow node used for lossless surgical updates. */ glowXml?: XmlObject; /** Resolved source glow colour used to detect colour edits. */ glowOriginalColor?: string; /** Source glow opacity used to detect alpha edits. */ glowOriginalOpacity?: number; glowRadius?: number; glowOpacity?: number; softEdgeRadius?: number; /** Inner shadow colour (`a:innerShdw`). */ innerShadowColor?: string; /** Original inner-shadow node used for lossless surgical updates. */ innerShadowXml?: XmlObject; /** Resolved source inner-shadow colour used to detect colour edits. */ innerShadowOriginalColor?: string; /** Source inner-shadow opacity used to detect alpha edits. */ innerShadowOriginalOpacity?: number; /** Inner shadow opacity (0-1). */ innerShadowOpacity?: number; /** Inner shadow blur radius in px. */ innerShadowBlur?: number; /** Inner shadow horizontal offset in px. */ innerShadowOffsetX?: number; /** Inner shadow vertical offset in px. */ innerShadowOffsetY?: number; /** Original soft-edge node, including vendor attributes and extensions. */ softEdgeXml?: XmlObject; /** Reflection effect — distance from shape bottom in px. */ reflectionBlurRadius?: number; /** Original reflection node, including vendor attributes and extensions. */ reflectionXml?: XmlObject; /** Reflection start opacity (0-1). */ reflectionStartOpacity?: number; /** Reflection end opacity (0-1). */ reflectionEndOpacity?: number; /** Reflection end position (0-1 fraction of shape height). */ reflectionEndPosition?: number; /** Reflection direction in degrees. */ reflectionDirection?: number; /** Reflection rotation in degrees (`a:reflection/@rot` in 60000ths). */ reflectionRotation?: number; /** Reflection distance in px. */ reflectionDistance?: number; /** Standalone blur effect radius in px (`a:effectLst > a:blur`). */ blurRadius?: number; /** Whether the blur effect grows the bounds of the shape (`a:blur/@grow`). */ blurGrow?: boolean; connectorStartArrow?: ConnectorArrowType; /** Start arrow width size ('sm' | 'med' | 'lg'). */ connectorStartArrowWidth?: 'sm' | 'med' | 'lg'; /** Start arrow length size ('sm' | 'med' | 'lg'). */ connectorStartArrowLength?: 'sm' | 'med' | 'lg'; connectorEndArrow?: ConnectorArrowType; /** End arrow width size ('sm' | 'med' | 'lg'). */ connectorEndArrowWidth?: 'sm' | 'med' | 'lg'; /** End arrow length size ('sm' | 'med' | 'lg'). */ connectorEndArrowLength?: 'sm' | 'med' | 'lg'; /** Connection point for the start of a connector. */ connectorStartConnection?: ConnectorConnectionPoint; /** Connection point for the end of a connector. */ connectorEndConnection?: ConnectorConnectionPoint; /** Custom dash pattern, measured relative to line width in thousandths of one percent. */ customDashSegments?: PptxCustomDashSegment[]; /** Original `a:ds` payloads retained by index for lossless edits. */ customDashSegmentXml?: XmlObject[]; /** Original `a:custDash` payload retained for lossless edits. */ customDashXml?: XmlObject; /** 3D scene/camera settings from `a:scene3d`. */ scene3d?: Pptx3DScene; /** 3D shape extrusion/bevel from `a:sp3d`. */ shape3d?: Pptx3DShape; /** Line-level shadow colour from `a:ln/a:effectLst/a:outerShdw`. */ lineShadowColor?: string; /** Line-level shadow opacity (0-1). */ lineShadowOpacity?: number; /** Line-level shadow blur radius in px. */ lineShadowBlur?: number; /** Line-level shadow horizontal offset in px. */ lineShadowOffsetX?: number; /** Line-level shadow vertical offset in px. */ lineShadowOffsetY?: number; /** Line-level glow colour from `a:ln/a:effectLst/a:glow`. */ lineGlowColor?: string; /** Line-level glow radius in px. */ lineGlowRadius?: number; /** Line-level glow opacity (0-1). */ lineGlowOpacity?: number; /** Raw `a:effectDag` XML node preserved for round-trip serialisation. */ effectDagXml?: XmlObject; /** * Typed effect graph parsed from {@link ShapeStyle.effectDagXml}. The four * structural container nodes (`a:cont`, `a:blend`, `a:xfrmEffect`, * `a:relOff`) are fully typed; any other leaf effect (e.g. `a:outerShdw`, * `a:glow`, `a:alphaInv`) is captured as * {@link import('./effect-dag').EffectDagRawLeaf} so we never have to * recurse into the full effect taxonomy. */ effectDagTree?: EffectDagContainer; /** Grayscale flag from effectDag `a:grayscl`. */ dagGrayscale?: boolean; /** Bi-level threshold (0-100) from effectDag `a:biLevel`. */ dagBiLevel?: number; /** Brightness adjustment (-100 to 100) from effectDag `a:lum/@bright`. */ dagLumBrightness?: number; /** Contrast adjustment (-100 to 100) from effectDag `a:lum/@contrast`. */ dagLumContrast?: number; /** Hue rotation in degrees (0-360) from effectDag `a:hsl/@hue`. */ dagHslHue?: number; /** Saturation adjustment from effectDag `a:hsl/@sat`. */ dagHslSaturation?: number; /** Luminance adjustment from effectDag `a:hsl/@lum`. */ dagHslLuminance?: number; /** Alpha modulation fixed (0-100) from effectDag `a:alphaModFix`. */ dagAlphaModFix?: number; /** Tint hue in degrees from effectDag `a:tint/@hue`. */ dagTintHue?: number; /** Tint amount (0-100) from effectDag `a:tint/@amt`. */ dagTintAmount?: number; /** Duotone colour pair from effectDag `a:duotone`. */ dagDuotone?: { color1: string; color2: string; }; /** Fill overlay blend mode from effectDag `a:fillOverlay/@blend`. */ dagFillOverlayBlend?: 'over' | 'mult' | 'screen' | 'darken' | 'lighten'; /** * Fill overlay tint colour (hex `#RRGGBB`) from effectDag `a:fillOverlay`'s * `a:solidFill`/`a:gradFill`. Painted as a blended overlay layer over the * element; the blend mode comes from {@link dagFillOverlayBlend}. */ dagFillOverlayColor?: string; /** Fill overlay tint opacity (0-1), from the overlay fill colour's alpha. */ dagFillOverlayOpacity?: number; /** `` — 1-based index into the theme's lnStyleLst. */ lnRefIdx?: number; /** Raw XML colour child of `` (e.g. `` with transforms). */ lnRefColorXml?: XmlObject; /** `` — 1-based index into fillStyleLst (1-3) or bgFillStyleLst (1001-1003). */ fillRefIdx?: number; /** Raw XML colour child of ``. */ fillRefColorXml?: XmlObject; /** `` — 1-based index into the theme's effectStyleLst. */ effectRefIdx?: number; /** Raw XML colour child of ``. */ effectRefColorXml?: XmlObject; /** `` — typically `major`, `minor`, or `none`. */ fontRefIdx?: string; /** Raw XML colour child of ``. */ fontRefColorXml?: XmlObject; /** * The fill `` resolved to, recorded ONLY when the shape's own * `spPr` authored no fill at all, so the reference is what paints it. * * Its absence therefore means "the fill is the shape's own", and its * presence plus an unchanged flat fill means "still purely inherited": see * `authored-shape-style.ts`, the shape-scope twin of `TextStyle`'s * `inheritedRunStyle`. */ inheritedFillStyle?: ShapeStyle; /** * The outline `` resolved to, recorded before `spPr/a:ln` was * layered on top. A property that still equals this baseline was never * authored on the shape and must not be written back as if it were. */ inheritedLineStyle?: ShapeStyle; /** * The shadow/glow/reflection/soft-edge/3D properties `` * resolved from the theme's `effectStyleLst`, recorded ONLY for the * properties the shape had not already authored itself. A shape whose * effects still match this baseline was never given its own effects and * must not have them written back as a literal `spPr/a:effectLst`; see * `authored-shape-style.ts`'s `effectIsPurelyStyleMatrix`. */ inheritedEffectStyle?: ShapeStyle; } //#endregion //#region src/core/types/text.d.ts /** * Rich text style properties for a text run or paragraph. * * Combines character-level formatting (font, bold, colour …), * paragraph-level controls (alignment, spacing, indentation), and * body-level properties (autofit, insets, text direction). All * fields are optional — unset properties inherit from layout/master * placeholders or theme defaults. * * @remarks * Font sizes are stored in **points**. Spatial measurements (insets, * margins) are in **pixels** (pre-converted from EMU during parsing). * * @example * ```ts * const heading: TextStyle = { * fontFamily: "Montserrat", * fontSize: 36, * bold: true, * color: "#1A1A2E", * align: "center", * lineSpacing: 1.15, * }; * * const body: TextStyle = { * fontFamily: "Open Sans", * fontSize: 14, * color: "#444444", * align: "left", * paragraphSpacingAfter: 8, * }; * // => both satisfy the TextStyle interface * ``` */ interface TextStyle { /** Original `a:rPr` XML retained by projections that share the shape-text model. */ runPropertiesXml?: XmlObject; /** * The properties this run's OWN `a:rPr` authored, and nothing else. * * A run style is assembled as * `{...inheritedRunStyle, ...authoredRunStyle}`, so the flat style is a * fully RESOLVED view: it cannot say whether `fontSize: 60` came from the * run, from the shape's `a:lstStyle`, from the layout placeholder, from the * master `p:txStyles` or from the theme. Omission is meaningful in OOXML * (§21.1.2.3), so a writer that re-emits the resolved view converts every * inherited value into an authored one and the deck stops being * theme-driven after one save. * * This is the run-scope twin of {@link TextSegment.paragraphProperties}, * which is parsed strictly from the paragraph's own `a:pPr` for the same * reason. Present only for runs that came from a parsed deck; absent for * SDK-built text, where the flat style IS the only description and must be * written out in full. */ authoredRunStyle?: TextStyle; /** * The resolved inheritance baseline {@link authoredRunStyle} was layered * on top of (shape `a:lstStyle` -> placeholder -> layout -> master * `p:txStyles` -> theme -> `p:defaultTextStyle`). * * Kept alongside the authored half because the two answer different * questions. The authored half says "the source pinned this"; the baseline * says "this value is what inheritance already produces", which is how an * EDIT is told apart from an inherited value: an editor mutates the flat * style without knowing about either field, so a property that now differs * from the baseline was either authored or edited and must be written, * while one that still matches can be left to inherit. * * Holds a reference to the per-paragraph baseline object rather than a * copy, so carrying it costs one pointer per run. */ inheritedRunStyle?: TextStyle; /** * Snapshot of the ELEMENT-scope paragraph geometry (alignment, margins, * indent, line and paragraph spacing, tab stops, rtl, line-break flags) as * the load pipeline resolved it. * * Present only on an `element.textStyle` that came from a parsed deck, and * populated only with the geometry keys. It exists so the save path can * answer one question it otherwise cannot: has the user CHANGED the body's * alignment or indent, or is the value simply what the shape's * `a:lstStyle`, its layout placeholder and the master already produce? * Element-level text panels (`textAdvancedPatch`, `alignPatch` and friends * in `pptx-viewer-shared`) write `element.textStyle` and never touch * `segment.paragraphProperties`, so a diff against this snapshot is the * only way to tell an edit from an inheritance artefact. * * @see element-paragraph-geometry.ts */ resolvedParagraphGeometry?: TextStyle; fontFamily?: string; fontSize?: number; /** When true, some form of autofit is in effect; see {@link autoFitMode} for which. */ autoFit?: boolean; /** Explicit autofit mode from OOXML body properties. * - 'shrink': `a:spAutoFit` - resize the SHAPE to fit the text (never the font) * - 'normal': `a:normAutofit` - shrink the TEXT to fit the shape (via `fontScale`/`lnSpcReduction`) * - 'none': `a:noAutofit` - explicitly no auto-fit (text overflows) * - undefined: no autofit element present (inherit from layout/master) */ autoFitMode?: 'shrink' | 'normal' | 'none'; /** Font scale percentage for normAutofit (e.g. 0.9 = 90%). Only meaningful when autoFit is true. */ autoFitFontScale?: number; /** Line spacing reduction for normAutofit (e.g. 0.2 = reduce by 20%). Only meaningful when autoFit is true. */ autoFitLineSpacingReduction?: number; bold?: boolean; italic?: boolean; underline?: boolean; /** Specific underline style (e.g. "sng", "dbl", "wavy"). Falls back to "sng" when `underline` is true. */ underlineStyle?: UnderlineStyle; /** Underline colour as hex string (`a:uFill` / `a:uLn`). When absent, inherits text colour. */ underlineColor?: string; /** * When true, the source authored `` to explicitly suppress * underline (rather than omitting the attribute entirely). Preserved so the * writer can re-emit the explicit `none` token instead of dropping it. */ underlineExplicitNone?: boolean; /** * Underline line properties parsed from `` — width, dash * preset, and end caps. Captured as a typed object so the writer can * round-trip the line styling that previously was dropped (only the * solidFill colour was carried before). */ underlineLine?: { /** Line width in EMU (raw OOXML) for `a:uLn/@w`. */ widthEmu?: number; /** Compound line type (`a:uLn/@cmpd`). */ compound?: string; /** Cap style (`a:uLn/@cap`). */ cap?: string; /** Pen alignment (`a:uLn/@algn`). */ algn?: string; /** Preset dash value (`a:uLn/a:prstDash/@val`). */ prstDash?: string; /** Raw `a:uLn/a:headEnd` XML preserved verbatim. */ headEndXml?: XmlObject; /** Raw `a:uLn/a:tailEnd` XML preserved verbatim. */ tailEndXml?: XmlObject; }; /** When `` is present — underline line follows the text run line. */ underlineLineFollowsText?: boolean; /** When `` is present — underline fill follows the text run fill. */ underlineFillFollowsText?: boolean; strikethrough?: boolean; /** Specific strike type: single or double from `a:rPr/@strike`. */ strikeType?: 'sngStrike' | 'dblStrike'; /** Text outline width in px (`a:rPr > a:ln/@w` in EMU). */ textOutlineWidth?: number; /** Text outline colour as hex string (`a:rPr > a:ln > a:solidFill`). */ textOutlineColor?: string; /** When true, the text body has no fill (`a:rPr > a:noFill`), producing hollow/outline-only text. */ textFillNone?: boolean; /** Superscript/subscript baseline shift as percentage (`a:rPr/@baseline`). Positive = super, negative = sub. */ baseline?: number; /** Character spacing in hundredths of a point (`a:rPr/@spc`). */ characterSpacing?: number; /** Kerning threshold in hundredths of a point (`a:rPr/@kern`). 0 = none. */ kerning?: number; /** Text highlight colour as hex string (`a:highlight`). */ highlightColor?: string; /** * Raw colour-choice XML preserved from `a:highlight` so a themed highlight * (`a:schemeClr` / `a:sysClr` / `a:prstClr`) re-emits with its original * identity rather than being flattened to `` on save. On save we * re-emit verbatim when the resolved {@link highlightColor} still matches. */ highlightColorXml?: XmlObject; /** Text-level gradient fill CSS string (from `a:rPr > a:gradFill`). */ textFillGradient?: string; /** Structured gradient stops for text fill round-trip serialization. */ textFillGradientStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient angle in degrees for text fill round-trip. */ textFillGradientAngle?: number; /** Gradient type for text fill round-trip ('linear' | 'radial'). */ textFillGradientType?: 'linear' | 'radial'; /** Text-level pattern fill preset (from `a:rPr > a:pattFill`). */ textFillPattern?: string; /** Text-level pattern foreground colour. */ textFillPatternForeground?: string; /** Text-level pattern background colour. */ textFillPatternBackground?: string; hyperlink?: string; /** Relationship ID for the hyperlink (`a:hlinkClick/@r:id`) — preserved for round-trip serialization. */ hyperlinkRId?: string; /** Hyperlink tooltip text (`a:hlinkClick/@tooltip`). */ hyperlinkTooltip?: string; /** Hyperlink action type (`a:hlinkClick/@action`). */ hyperlinkAction?: string; /** Whether the hyperlink target is an internal slide jump (targetSlideIndex style). */ hyperlinkTargetSlideIndex?: number; color?: string; /** * Raw XML colour-choice node preserved from `a:rPr/a:solidFill` for * round-trip serialisation. Captures `a:schemeClr` / `a:sysClr` / * `a:prstClr` / `a:srgbClr` plus colour transforms. On save we re-emit * verbatim when the resolved {@link color} still matches this node. */ colorXml?: XmlObject; align?: 'left' | 'center' | 'right' | 'justify' | 'justLow' | 'dist' | 'thaiDist'; vAlign?: 'top' | 'middle' | 'bottom'; /** Right-to-left paragraph/run direction (`a:pPr/@rtl`, `a:rPr/@rtl`). */ rtl?: boolean; /** Body text direction (`a:bodyPr/@vert`). * * Values map to OOXML `a:bodyPr/@vert` attribute values: * - `"horizontal"` — default horizontal text (`horz`) * - `"vertical"` — standard vertical text, right-to-left columns (`vert`) * - `"vertical270"` — text rotated 270 degrees (`vert270`) * - `"eaVert"` — East Asian vertical text with CJK glyphs upright (`eaVert`) * - `"wordArtVert"` — WordArt vertical, each character upright stacked (`wordArtVert`) * - `"wordArtVertRtl"` — WordArt vertical, right-to-left direction (`wordArtVertRtl`) * - `"mongolianVert"` — Mongolian vertical text, left-to-right columns (`mongolianVert`) */ textDirection?: 'horizontal' | 'vertical' | 'vertical270' | 'eaVert' | 'wordArtVert' | 'wordArtVertRtl' | 'mongolianVert'; /** Body column count (`a:bodyPr/@numCol`). */ columnCount?: number; /** Column spacing in px (`a:bodyPr/@spcCol` in EMU). */ columnSpacing?: number; /** Horizontal overflow mode from `a:bodyPr/@hOverflow`. */ hOverflow?: 'overflow' | 'clip'; /** Vertical overflow mode from `a:bodyPr/@vertOverflow`. */ vertOverflow?: 'overflow' | 'clip' | 'ellipsis'; /** Body text left inset in px (`a:bodyPr/@lIns` in EMU). */ bodyInsetLeft?: number; /** Body text top inset in px (`a:bodyPr/@tIns` in EMU). */ bodyInsetTop?: number; /** Body text right inset in px (`a:bodyPr/@rIns` in EMU). */ bodyInsetRight?: number; /** Body text bottom inset in px (`a:bodyPr/@bIns` in EMU). */ bodyInsetBottom?: number; /** Paragraph spacing before in px. */ paragraphSpacingBefore?: number; /** Paragraph spacing after in px. */ paragraphSpacingAfter?: number; /** Line spacing multiplier (e.g. 1.2 = 120%). Used when mode is proportional (spcPct). */ lineSpacing?: number; /** Exact line spacing in points (from `a:lnSpc > a:spcPts`). Takes priority over `lineSpacing` when set. */ lineSpacingExactPt?: number; /** Paragraph left margin in px (`a:pPr/@marL` in EMU). */ paragraphMarginLeft?: number; /** Paragraph right margin in px (`a:pPr/@marR` in EMU). */ paragraphMarginRight?: number; /** Paragraph first-line indent in px (`a:pPr/@indent` in EMU). */ paragraphIndent?: number; /** Tab stop positions and alignments (`a:pPr/a:tabLst/a:tab`). */ tabStops?: Array<{ position: number; align: 'l' | 'ctr' | 'r' | 'dec'; leader?: 'none' | 'dot' | 'hyphen' | 'underscore'; }>; /** Body text wrapping mode from `a:bodyPr/@wrap`. */ textWrap?: 'square' | 'none'; /** Preset text warp type from `a:bodyPr/a:prstTxWarp`. */ textWarpPreset?: PptxTextWarpPreset; /** Primary adjustment value for text warp (from `a:prstTxWarp/a:avLst/a:gd` with name "adj"). * Stored as raw OOXML 1/60000th units (e.g. 50000 = default for many presets). */ textWarpAdj?: number; /** Secondary adjustment value for text warp (from `a:prstTxWarp/a:avLst/a:gd` with name "adj2"). * Stored as raw OOXML 1/60000th units. */ textWarpAdj2?: number; /** Text capitalization style from `a:rPr/@cap`. */ textCaps?: 'all' | 'small' | 'none'; /** * When true, the source authored `` explicitly. This * differs from {@link textCaps} = `"none"` only because the writer must * preserve the explicit token rather than collapse it to omission. */ textCapsExplicitNone?: boolean; /** Symbol font family from `a:sym`. */ symbolFont?: string; /** East Asian font family from `a:ea`. */ eastAsiaFont?: string; /** Complex Script font family from `a:cs`. */ complexScriptFont?: string; /** * Theme-font token (`+mj-lt` / `+mn-lt` / ...) authored on `a:latin`, when * present. {@link fontFamily} holds the resolved concrete face for * rendering; this preserves the token linkage so the writer re-emits the * token rather than the flattened face (see #84). */ latinFontThemeToken?: string; /** Theme-font token authored on `a:ea` (e.g. `+mn-ea`), when present. */ eastAsiaFontThemeToken?: string; /** Theme-font token authored on `a:cs` (e.g. `+mn-cs`), when present. */ complexScriptFontThemeToken?: string; /** * Automatic per-script fallback face resolved from the theme's * `` overrides for a run whose text is dominantly * CJK / Arabic / Hebrew / Thai (see #83). A rendering hint only: it is not * serialised back on save, so it never disturbs the round-trip typefaces. */ scriptFallbackFont?: string; /** Text language from `a:rPr/@lang`. */ language?: string; /** Hyperlink mouse-over target from `a:hlinkMouseOver`. */ hyperlinkMouseOver?: string; /** * Raw `a:snd` (embedded WAV audio) child of `a:hlinkClick`, preserved * verbatim (carries `@r:embed` + `@name`). Round-tripped on save so the * click sound survives instead of being dropped. */ hyperlinkSoundXml?: XmlObject; /** Raw `a:snd` child of `a:hlinkMouseOver`, preserved verbatim for round-trip. */ hyperlinkMouseOverSoundXml?: XmlObject; /** Hyperlink invalidUrl attribute (`a:hlinkClick/@invalidUrl`). */ hyperlinkInvalidUrl?: string; /** Hyperlink target frame (`a:hlinkClick/@tgtFrame`). */ hyperlinkTargetFrame?: string; /** Whether hyperlink history is tracked (`a:hlinkClick/@history`). */ hyperlinkHistory?: boolean; /** Whether hyperlink uses highlight-click effect (`a:hlinkClick/@highlightClick`). */ hyperlinkHighlightClick?: boolean; /** Whether hyperlink ends a sound (`a:hlinkClick/@endSnd`). */ hyperlinkEndSound?: boolean; /** Kumimoji (ideographic text combining) flag for vertical CJK text (`a:rPr/@kumimoji`). */ kumimoji?: boolean; /** Normalize height flag (`a:rPr/@normalizeH`). */ normalizeHeight?: boolean; /** No proofing flag (`a:rPr/@noProof`). */ noProof?: boolean; /** Dirty flag indicating run has been edited (`a:rPr/@dirty`). */ dirty?: boolean; /** Error flag indicating spelling error (`a:rPr/@err`). */ spellingError?: boolean; /** Smart tag clean flag (`a:rPr/@smtClean`). */ smartTagClean?: boolean; /** Bookmark link target (`a:rPr/@bmk`). */ bookmark?: string; /** Alternative language for the run (`a:rPr/@altLang`). Populated for runs * authored in mixed-script documents (e.g. Asian/Latin combined). */ altLanguage?: string; /** SmartTag (Office grammar tag) GUID id (`a:rPr/@smtId`). Round-tripped * verbatim — the engine doesn't interpret it. */ smartTagId?: number; /** Latin font PANOSE classification string from `a:rPr > a:latin/@panose`. */ latinFontPanose?: string; /** Latin font pitch + family flag from `a:rPr > a:latin/@pitchFamily`. */ latinFontPitchFamily?: number; /** Latin font character set id from `a:rPr > a:latin/@charset`. */ latinFontCharset?: number; /** East-Asian font PANOSE from `a:rPr > a:ea/@panose`. */ eastAsiaFontPanose?: string; /** East-Asian font pitch + family flag from `a:rPr > a:ea/@pitchFamily`. */ eastAsiaFontPitchFamily?: number; /** East-Asian font character set id from `a:rPr > a:ea/@charset`. */ eastAsiaFontCharset?: number; /** Complex-script font PANOSE from `a:rPr > a:cs/@panose`. */ complexScriptFontPanose?: string; /** Complex-script font pitch + family flag from `a:rPr > a:cs/@pitchFamily`. */ complexScriptFontPitchFamily?: number; /** Complex-script font character set id from `a:rPr > a:cs/@charset`. */ complexScriptFontCharset?: number; /** Symbol-font PANOSE from `a:rPr > a:sym/@panose`. */ symbolFontPanose?: string; /** Symbol-font pitch + family flag from `a:rPr > a:sym/@pitchFamily`. */ symbolFontPitchFamily?: number; /** Symbol-font character set id from `a:rPr > a:sym/@charset`. */ symbolFontCharset?: number; /** Paragraph list type for toggling bullet / numbered lists via the toolbar. * - `'bullet'` — character bullet (default "•") * - `'numbered'` — auto-numbered list (arabicPeriod) * - `'none'` — explicitly no list */ listType?: 'bullet' | 'numbered' | 'none'; /** Default tab size in px (`a:pPr/@defTabSz` in EMU). */ defaultTabSize?: number; /** East Asian line break flag (`a:pPr/@eaLnBrk`). */ eaLineBreak?: boolean; /** Latin line break flag (`a:pPr/@latinLnBrk`). */ latinLineBreak?: boolean; /** Font alignment (`a:pPr/@fontAlgn`): 'auto' | 'base' | 'ctr' | 't' | 'b'. */ fontAlignment?: string; /** Hanging punctuation flag (`a:pPr/@hangingPunct`). */ hangingPunctuation?: boolean; /** Whether to space first and last paragraph from body edges (`a:bodyPr/@spcFirstLastPara`). */ spaceFirstLastParagraph?: boolean; /** Right-to-left column flow (`a:bodyPr/@rtlCol`). */ rtlColumns?: boolean; /** Whether text originates from WordArt (`a:bodyPr/@fromWordArt`). */ fromWordArt?: boolean; /** Whether text anchoring is centered (`a:bodyPr/@anchorCtr`). */ anchorCenter?: boolean; /** Force anti-aliasing (`a:bodyPr/@forceAA`). */ forceAntiAlias?: boolean; /** Upright text in 3D views (`a:bodyPr/@upright`). */ upright?: boolean; /** Compatible line spacing flag (`a:bodyPr/@compatLnSpc`). */ compatibleLineSpacing?: boolean; /** * Text body rotation in **degrees** (`a:bodyPr/@rot`). * * OOXML stores the value as 60000ths of a degree. Positive values rotate * the body clockwise. When undefined, the attribute is omitted on save * (PowerPoint treats absent `rot` as inherit/none). */ textBodyRotation?: number; /** Text shadow colour as hex string (`a:outerShdw`). */ textShadowColor?: string; /** Text shadow blur radius in px. */ textShadowBlur?: number; /** Text shadow horizontal offset in px. */ textShadowOffsetX?: number; /** Text shadow vertical offset in px. */ textShadowOffsetY?: number; /** Text shadow opacity (0-1). */ textShadowOpacity?: number; /** Text inner shadow colour (`a:innerShdw`). */ textInnerShadowColor?: string; /** Text inner shadow opacity (0-1). */ textInnerShadowOpacity?: number; /** Text inner shadow blur radius in px. */ textInnerShadowBlur?: number; /** Text inner shadow horizontal offset in px. */ textInnerShadowOffsetX?: number; /** Text inner shadow vertical offset in px. */ textInnerShadowOffsetY?: number; /** Preset shadow type from `a:prstShdw/@prst` (e.g. "shdw1"..."shdw20"). */ textPresetShadowName?: string; /** Preset shadow colour as hex string. */ textPresetShadowColor?: string; /** Preset shadow opacity (0-1). */ textPresetShadowOpacity?: number; /** Preset shadow distance in px. */ textPresetShadowDistance?: number; /** Preset shadow direction in degrees. */ textPresetShadowDirection?: number; /** Text blur effect radius in px (`a:blur`). */ textBlurRadius?: number; /** * Raw `a:effectDag` XML node from `a:rPr`, preserved verbatim for * round-trip serialisation. Mirrors the shape-level * {@link import('./shape-style').ShapeStyle.effectDagXml} field. */ textEffectDagXml?: XmlObject; /** * Typed effect graph parsed from `textEffectDagXml`. The four structural * container nodes (`a:cont`, `a:blend`, `a:xfrmEffect`, `a:relOff`) are * fully typed; any other leaf effect is captured as * {@link import('./effect-dag').EffectDagRawLeaf} so we never have to * recurse into the full effect taxonomy. */ textEffectDagTree?: EffectDagContainer; /** Text alpha modulation fixed (0-100) from `a:alphaModFix`. */ textAlphaModFix?: number; /** Text alpha modulation from `a:alphaMod` (0-100 percentage). */ textAlphaMod?: number; /** Text hue shift in degrees from `a:hsl/@hue`. */ textHslHue?: number; /** Text saturation adjustment from `a:hsl/@sat`. */ textHslSaturation?: number; /** Text luminance adjustment from `a:hsl/@lum`. */ textHslLuminance?: number; /** Text colour change from colour as hex string (`a:clrChange`). */ textClrChangeFrom?: string; /** Text colour change to colour as hex string. */ textClrChangeTo?: string; /** Text duotone colour pair (`a:duotone`). */ textDuotone?: { color1: string; color2: string; }; /** Text glow colour as hex string (`a:glow`). */ textGlowColor?: string; /** Text glow radius in px. */ textGlowRadius?: number; /** Text glow opacity (0-1). */ textGlowOpacity?: number; /** Text reflection enabled flag. */ textReflection?: boolean; /** Text reflection blur radius in px. */ textReflectionBlur?: number; /** Text reflection start opacity (0-1). */ textReflectionStartOpacity?: number; /** Text reflection end opacity (0-1). */ textReflectionEndOpacity?: number; /** Text reflection offset distance in px. */ textReflectionOffset?: number; /** 3D extrusion/bevel settings on the text body. */ text3d?: Text3DStyle; /** 3D scene (camera + light rig) settings on the text body (`a:bodyPr/a:scene3d`). */ textBodyScene3d?: Pptx3DScene; /** Raw `a:scene3d` subtree used to preserve extensions and unmodelled children. */ textBodyScene3dXml?: XmlObject; /** * `a:bodyPr/a:flatTx` - an explicit "render this text flat" marker. `sp3d` * and `flatTx` are a mutually exclusive OOXML choice (`EG_Text3D`), so a * shape/run that overrides an inherited 3D text body with `` * carries no `text3d` of its own; without this explicit flag a later * inheritance merge has no signal to stop `text3d`/`textBodyScene3d` from * an ancestor (layout/master) leaking back in, the way `noFill` stops an * inherited fill. A renderer must short-circuit 3D-text application * whenever this is `true`, regardless of what `text3d`/`textBodyScene3d` * otherwise hold. */ flatText?: boolean; /** * Raw `` subtree captured from ``. Preserved verbatim so * authored extensions (e.g. content placeholders, custom application data) * survive a round-trip even though the engine doesn't interpret them. */ bodyPropertiesExtLstXml?: XmlObject; /** * Raw `` subtree captured from ``. Only meaningful on the * paragraph-level style (paragraphs propagate this via the first segment). */ paragraphPropertiesExtLstXml?: XmlObject; /** * Raw `` subtree captured from ``. Persisted verbatim on * save when present — covers run-level extensions the typed model doesn't * model (e.g. `a14:hiddenFill` and similar). */ runPropertiesExtLstXml?: XmlObject; /** * Raw `` XML node captured from ``. The schema permits * `defRPr` directly inside `pPr` so that paragraph defaults can specify the * end-paragraph run formatting; previously this was dropped on save. We * persist the parsed XML object so it round-trips verbatim. * * Only meaningful on the *first* segment of each paragraph (matches the * convention used for {@link bulletInfo} / {@link endParaRunProperties}). */ paragraphDefaultRunPropertiesXml?: XmlObject; } /** * Structured bullet metadata attached to the first {@link TextSegment} * of each paragraph. * * Describes how the paragraph bullet should render: character bullets * (`char`), auto-numbered lists (`autoNumType`), or picture bullets * (`imageRelId` / `imageDataUrl`). Set `none: true` when `a:buNone` * explicitly suppresses the bullet. * * @example * ```ts * // Simple character bullet: * const bullet: BulletInfo = { char: "•", color: "#333333" }; * * // Auto-numbered list starting at 1: * const numbered: BulletInfo = { * autoNumType: "arabicPeriod", * autoNumStartAt: 1, * }; * // => { char: "•", color: "#333333" } and { autoNumType: "arabicPeriod", autoNumStartAt: 1 } * ``` */ interface BulletInfo { /** Bullet character (e.g. "•", "-", "»") from `a:buChar`. */ char?: string; /** Auto-numbering type (e.g. "arabicPeriod", "romanUcPeriod") from `a:buAutoNum`. */ autoNumType?: string; /** Auto-numbering start value. */ autoNumStartAt?: number; /** * Auto-numbering ORDINAL OFFSET: the zero-based distance of this paragraph * within its own numbered list, such that * `autoNumStartAt + paragraphIndex` is the ordinal to render. Despite the * name it is NOT the paragraph's position in the text body; the two agree * only for a list that starts at the first paragraph and is never * interrupted. * * It has to be the offset rather than the raw position because every * consumer that re-derives a marker from `BulletInfo` alone (the renderer's * `resolveParagraphBullet`, the Markdown converter's `resolveListMarker`) * computes `autoNumStartAt + paragraphIndex`. The load path resolves the * real sequence itself, restarting the count after any paragraph that * interrupts the list, and publishes the offset here so those consumers * land on the same number. With the raw position they did not, and BOTH * markers were painted ("3.1. Item"), because the paragraph builder drops * the parsed marker segment only when the two strings agree. * * Runtime-only: derived at parse time and never serialized. OOXML has no * counterpart (`a:buAutoNum` carries only `@type` and `@startAt`), so the * writer neither reads nor emits it. */ paragraphIndex?: number; /** Bullet font family from `a:buFont`. */ fontFamily?: string; /** Bullet size as percentage of text font size from `a:buSzPct`. */ sizePercent?: number; /** Bullet size in points from `a:buSzPts`. */ sizePts?: number; /** Bullet color as hex string from `a:buClr`. */ color?: string; /** * Raw colour-choice XML captured from `` so that themed bullets * (`a:schemeClr`, `a:sysClr`, `a:prstClr`) round-trip with their original * identity rather than being flattened to `` on save. */ colorXml?: XmlObject; /** True when `a:buNone` explicitly suppresses bullets. */ none?: boolean; /** Picture bullet: relationship ID from `a:buBlip` → `a:blip[@r:embed]`. */ imageRelId?: string; /** Picture bullet: data URL of the embedded image. */ imageDataUrl?: string; /** * Raw `` XML captured at parse time. Carries the full blipFill * subtree (`a:tile`, `a:stretch`, `a:srcRect`, `a:blip > a:extLst`) so the * writer can emit the complete original definition rather than the bare * `a:blip[@r:embed]` mapping. When set, the writer prefers it over * {@link imageRelId} for emission. */ imageBlipFillXml?: XmlObject; /** When true, `` was specified — inherit the bullet font from * the run text, not from a buFont declaration. */ fontInherit?: boolean; /** When true, `` was specified — inherit the bullet colour from * the run text. */ colorInherit?: boolean; /** When true, `` was specified — inherit the bullet size from * the run text font size. */ sizeInherit?: boolean; } /** * A single text run within a paragraph. * * A text body is decomposed into an array of `TextSegment` objects, * each with its own style. Paragraph breaks are represented as * segments with `isParagraphBreak: true`. * * @example * ```ts * const segments: TextSegment[] = [ * { text: "Bold intro ", style: { bold: true, fontSize: 16 } }, * { text: "and normal text.", style: { fontSize: 16 } }, * { text: "", style: {}, isParagraphBreak: true }, * { text: "Second paragraph.", style: { fontSize: 14 } }, * ]; * // => 4 segments: 2 styled runs, 1 paragraph break, 1 normal run * ``` */ interface TextSegment { text: string; style: TextStyle; /** When this segment originated from an `a:fld` element, stores the field type (e.g. "slidenum", "datetime"). */ fieldType?: string; /** When this segment originated from an `a:fld` element, stores the field GUID. */ fieldGuid?: string; /** * Original attribute name used to author the field GUID — `'uuid'` for the * `a:fld/@uuid` form authored by some legacy producers, `'id'` for the * canonical `a:fld/@id` form. Preserved so the writer round-trips whichever * spelling the source used (PowerPoint accepts both). Defaults to `'id'` * on save when undefined. */ fieldGuidAttr?: 'uuid' | 'id'; /** * Raw per-field paragraph properties (`a:fld > a:pPr`). The schema permits * `pPr` inside an `a:fld` so the field can carry its own paragraph-level * formatting; preserved verbatim on save when present. */ fieldParagraphPropertiesXml?: XmlObject; /** Raw OMML XML node for equation segments (from `a14:m` / `m:oMathPara`). */ equationXml?: Record; /** * Optional equation number for numbered equations (e.g. "(1)", "(2.3)"). * When present, the equation is rendered centered with the number right-aligned. */ equationNumber?: string; /** Whether this segment represents a paragraph break rather than renderable text. */ isParagraphBreak?: boolean; /** * Whether this segment represents a soft line break (`a:br`) rather than * a paragraph terminator. Soft line breaks remain inside the same paragraph * but force a line wrap and may carry their own run properties. * * The renderer should treat the segment text as `"\n"` when present. */ isLineBreak?: true; /** * Raw `a:rPr` XML for an `a:br` (soft line break) segment, captured verbatim * during parse so the writer can re-emit attributes/colours/fonts that the * typed model doesn't represent. Only meaningful when {@link isLineBreak} * is `true`. */ breakRunProperties?: Record; /** Structured bullet info for the first segment of a paragraph. */ bulletInfo?: BulletInfo; /** * Outline level for the paragraph this segment starts (`a:p/@lvl`). * * Only meaningful on the first segment of a paragraph (matching the * convention used for {@link bulletInfo}). Stored as the raw OOXML * value (0 = top level, 1-8 = nested) and serialised back when non-zero. */ paragraphLevel?: number; /** * Raw `a:endParaRPr` XML node for the paragraph this segment starts. * * Captured verbatim on parse so attributes and child colours/fonts that * the typed model doesn't represent survive a round-trip. Only meaningful * on the first segment of a paragraph. */ endParaRunProperties?: Record; /** * Per-paragraph properties (alignment, spacing, margins, indent, tab stops, * rtl) authored on this paragraph's own `a:pPr` (#69). Only meaningful on * the first segment of a paragraph. When present, the writer emits these * per paragraph instead of collapsing one shape-level pPr onto every * paragraph. Only the paragraph-geometry keys of {@link TextStyle} are * populated; unrelated fields fall back to the shape-level style. */ paragraphProperties?: TextStyle; /** * Phonetic annotation text from `a:ruby > a:rt` (e.g. furigana, pinyin). * When present, the renderer should wrap the base text with an HTML `` tag. */ rubyText?: string; /** * Ruby text alignment from `a:rubyPr > @val` attribute. * Values: "ctr" (center), "l" (left), "r" (right), "dist" (distribute), "distCat", "distLetter". * @default "ctr" */ rubyAlignment?: string; /** * Ruby text font size as a percentage of the base text font size * from `a:rubyPr/@hps` (half-point size) or inferred from rt run font size. * Stored in **points** for consistency with `TextStyle.fontSize`. */ rubyFontSize?: number; /** * Style for the ruby (phonetic) text run, parsed from `a:rt > a:r > a:rPr`. * Used by the renderer to apply font family, colour, etc. to the `` element. */ rubyStyle?: TextStyle; } //#endregion //#region src/core/types/element-base.d.ts /** * Properties shared by **every** element on a slide. * * Position and size are in pixels (converted from EMU at parse time). * Optional properties apply to subsets of elements or may be absent in * the original OOXML. * * @example * ```ts * const base: PptxElementBase = { * id: "el_001", * x: 100, y: 50, * width: 400, height: 200, * rotation: 15, * opacity: 0.9, * }; * // => satisfies PptxElementBase * ``` */ interface PptxElementBase { id: string; /** * The shape's native OOXML id from `p:cNvPr/@id` (an unsigned integer, as a * string), captured on load. Distinct from {@link id}, which is a synthetic * positional identity (`${slidePath}-shape-${index}`) the loader assigns for * selection / undo / template tracking. Animations target shapes by this * native id (`p:spTgt/@spid`), so it is the stable key used to reconcile an * animation to the element it animates across a save/reload round trip. * Absent on SDK-created elements until one is minted at save time. */ shapeId?: string; /** Element name from `cNvPr/@name`. Used for morph transition matching via the `!!` naming convention. */ name?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@type` (lower-cased) when the shape is a placeholder: * `title`, `ctrtitle`, `body`, `subtitle`, `ftr`, `dt`, `sldnum`, ... * * Captured on load so consumers can tell a footer placeholder from a text box * without re-walking `rawXml`. Absent on non-placeholder shapes and on * SDK-created elements. */ placeholderType?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@sz` (lower-cased): `"full"`, `"half"`, or * `"quarter"`. Captured on load for round-trip completeness. Per * ECMA-376 §19.3.1.36 (CT_Placeholder) this size hint is only meaningful * when NO `a:xfrm` exists anywhere in the placeholder's inheritance * chain (slide -> layout -> master); every real-world corpus placeholder * that carries `@sz` already has an explicit `a:xfrm` at the master * level, so no renderer currently derives a size from this field. */ placeholderSz?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@orient` (only `"vert"` is meaningful per * `ST_Direction`). Captured on load for round-trip completeness. In * practice every placeholder observed with `orient="vert"` also carries * an explicit `a:bodyPr/@vert`, which already drives vertical-text * rendering, so this field is not currently read by any renderer. */ placeholderOrient?: 'vert'; x: number; y: number; width: number; height: number; rotation?: number; /** Skew along the X axis in degrees (parsed from `@_skewX` in 1/60000ths of a degree). */ skewX?: number; /** Skew along the Y axis in degrees (parsed from `@_skewY` in 1/60000ths of a degree). */ skewY?: number; flipHorizontal?: boolean; flipVertical?: boolean; /** Whether this element is hidden (used by the Elements Panel visibility toggle). */ hidden?: boolean; /** Element-level opacity (0-1). */ opacity?: number; rawXml?: XmlObject; /** Shape-level click action (from `a:hlinkClick` on `p:cNvPr`). */ actionClick?: PptxAction; /** Shape-level hover action (from `a:hlinkHover` on `p:cNvPr`). */ actionHover?: PptxAction; /** Shape lock attributes parsed from `p:cNvSpPr/a:spLocks`. */ locks?: PptxShapeLocks; /** * Opaque `` children captured from the shape's `` whose * URI is not recognised by a typed extractor (hidden fill/line, image * effects, …). Preserved verbatim and re-emitted on save so unknown * vendor extensions survive a round-trip. * * Mirrors the existing `effectDagXml` / `endParaRunProperties` raw-XML * preservation pattern. */ extLstXml?: XmlObject[]; } /** * Text content mixin — present on text boxes and shapes. * * Shapes can contain text overlaid on the shape geometry, so both * `TextPptxElement` and `ShapePptxElement` extend this interface. * * @example * ```ts * const props: PptxTextProperties = { * text: "Hello World", * textStyle: { fontSize: 24, bold: true, color: "#333333" }, * }; * // => satisfies PptxTextProperties * ``` */ interface PptxTextProperties { text?: string; textStyle?: TextStyle; /** Rich text segments with individual styling. */ textSegments?: TextSegment[]; /** Per-paragraph indentation (marginLeft, indent) for multi-level bullet support. */ paragraphIndents?: Array<{ marginLeft?: number; indent?: number; }>; /** Placeholder prompt text inherited from layout/master (e.g. "Click to add title"). Shown as a greyed-out hint when the shape has no user-entered text. */ promptText?: string; /** * The string {@link text} was INHERITED from, when this is a header / footer / * date / slide-number placeholder whose own body the file leaves empty. * * PowerPoint keeps the footer string on the slide master and writes each * slide's copy of the `ftr` placeholder empty, so the empty body means * "render the master's footer here". Rendering needs the resolved string, but * SAVING it into the slide would pin that slide to today's master text and * silently detach it from the Header & Footer dialog. The save writer * therefore leaves the authored empty body alone while `text` still equals * this value, and writes a genuine per-slide override once it does not. */ inheritedPlaceholderText?: string; /** Linked text box chain ID from `a:bodyPr > a:linkedTxbx/@id` or `a:txbx > a:linkedTxbx/@id`. Text overflows from one linked frame to the next. */ linkedTxbxId?: number; /** Sequence number within a linked text box chain (0-based). */ linkedTxbxSeq?: number; } /** * Shape styling & geometry mixin — present on shapes, connectors, and images. * * @example * ```ts * const props: PptxShapeProperties = { * shapeType: "roundRect", * shapeStyle: { fillColor: "#0055AA", strokeWidth: 2 }, * shapeAdjustments: { adj: 16667 }, * }; * // => satisfies PptxShapeProperties * ``` */ interface PptxShapeProperties { shapeStyle?: ShapeStyle; /** Preset geometry name, e.g. "rect", "ellipse", "roundRect". */ shapeType?: string; /** Geometry adjustment values, e.g. `{ adj: 16667 }`. */ shapeAdjustments?: Record; /** Adjustment handles for interactive shape modification (yellow diamond handles). */ adjustmentHandles?: GeometryAdjustmentHandle[]; } /** * Text styling for a single indent level (0–8) inside a placeholder’s * `a:lstStyle`. * * Used during placeholder inheritance to fill in defaults for font, * bullet, and spacing properties the slide element does not override. * * @example * ```ts * const level0: PlaceholderTextLevelStyle = { * fontSize: 32, * bold: true, * bulletChar: "•", * }; * // => satisfies PlaceholderTextLevelStyle * ``` */ interface PlaceholderTextLevelStyle { fontFamily?: string; fontSize?: number; bold?: boolean; italic?: boolean; color?: string; /** * The `a:defRPr/a:solidFill` node this level's {@link color} came from. * * Master and layout text styles are parsed and cached before any slide is, * so a scheme alias such as `tx1` was resolved through the map that was * active then. A slide carrying `p:clrMapOvr` routes the same alias * somewhere else, so the alias has to be resolved again against the slide * that is inheriting it; {@link color} is only the reading taken at parse * time. Absent when the level declares no colour, or declares a literal one. */ colorChoiceXml?: XmlObject; bulletChar?: string; bulletAutoNumType?: string; bulletFontFamily?: string; bulletSizePercent?: number; /** Bullet colour from `a:buClr` as hex string. */ bulletColor?: string; /** Bullet size in points from `a:buSzPts`. */ bulletSizePts?: number; /** True when `a:buNone` is present at this level. */ bulletNone?: boolean; marginLeft?: number; indent?: number; alignment?: string; lineSpacing?: number; lineSpacingExactPt?: number; spaceBefore?: number; spaceAfter?: number; } //#endregion //#region src/core/types/chart-axis.d.ts /** Tick-mark placement from ChartML `ST_TickMark`. */ type PptxChartTickMark = 'cross' | 'in' | 'none' | 'out'; /** Typed axis tick and category/date label controls. */ interface PptxChartAxisLabelFormatting { /** Primary and secondary tick-mark placement. */ majorTickMark?: PptxChartTickMark; minorTickMark?: PptxChartTickMark; /** Tick-label position from ChartML `ST_TickLblPos`. */ tickLblPos?: 'high' | 'low' | 'nextTo' | 'none'; /** Automatic category/date axis behavior (`c:auto`). */ auto?: boolean; /** Category-axis label alignment (`c:lblAlgn`). */ labelAlignment?: 'ctr' | 'l' | 'r'; /** Category/date label distance, from 0 through 1000 percent. */ labelOffset?: number; /** Number of category/date labels between rendered labels. */ tickLabelSkip?: number; /** Number of category/date tick positions between major tick marks. */ tickMarkSkip?: number; /** Suppress multi-level category labels (`c:noMultiLvlLbl`). */ noMultiLevelLabels?: boolean; } //#endregion //#region src/core/types/chart-pivot-format.d.ts interface PptxChartPivotFormat { index: number; shapePropertiesXml?: XmlObject | null; markerXml?: XmlObject | null; dataLabelXml?: XmlObject | null; extensionListXml?: XmlObject | null; rawXml?: XmlObject; } /** Editable classic ChartML `c:pivotFmts` collection. */ interface PptxChartPivotFormats { formats: PptxChartPivotFormat[]; rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-pivot-source.d.ts /** Editable classic ChartML `c:pivotSource` metadata. */ interface PptxChartPivotSource { /** Pivot table reference stored as `c:name` text. */ name: string; /** Required unsigned format identifier stored in `c:fmtId/@val`. */ formatId: number; /** Internal source subtree used to preserve extensions and foreign markup. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-print-settings.d.ts /** Headers and footers used when a classic ChartML chart is printed. */ interface PptxChartPrintHeaderFooter { oddHeader?: string; oddFooter?: string; evenHeader?: string; evenFooter?: string; firstHeader?: string; firstFooter?: string; alignWithMargins?: boolean; differentOddEven?: boolean; differentFirst?: boolean; /** Original subtree retained for foreign attributes and extension children. */ rawXml?: unknown; } /** Required page margins from ChartML `c:pageMargins`, measured in inches. */ interface PptxChartPageMargins { left: number; right: number; top: number; bottom: number; header: number; footer: number; /** Original leaf retained for foreign attributes. */ rawXml?: unknown; } /** Printer page configuration from ChartML `c:pageSetup`. */ interface PptxChartPageSetup { paperSize?: number; firstPageNumber?: number; orientation?: 'default' | 'portrait' | 'landscape'; blackAndWhite?: boolean; draft?: boolean; useFirstPageNumber?: boolean; horizontalDpi?: number; verticalDpi?: number; copies?: number; /** Original leaf retained for foreign attributes. */ rawXml?: unknown; } /** Editable `c:printSettings` content from a classic ChartML chart space. */ interface PptxChartPrintSettings { headerFooter?: PptxChartPrintHeaderFooter | null; pageMargins?: PptxChartPageMargins | null; pageSetup?: PptxChartPageSetup | null; /** `null` removes the legacy header/footer drawing relationship element. */ legacyDrawingHeaderFooterRelationshipId?: string | null; /** Original subtree retained for unknown and extension content. */ rawXml?: unknown; } //#endregion //#region src/core/types/chart-protection.d.ts /** Editable classic ChartML `c:protection` settings. */ interface PptxChartProtection { /** Prevent editing the chart object. */ chartObject?: boolean | null; /** Prevent editing the chart data. */ data?: boolean | null; /** Prevent editing chart formatting. */ formatting?: boolean | null; /** Prevent selecting chart elements. */ selection?: boolean | null; /** Prevent chart user-interface operations. */ userInterface?: boolean | null; /** Internal source subtree used to preserve foreign markup during edits. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-user-shapes.d.ts /** * Types for chart drawing-overlay shapes (`c:userShapes`). * * A chart's `c:userShapes` element carries an `r:id` that references a * separate drawing part (`ppt/drawings/drawingN.xml`) whose root is a * `c:userShapes` element populated with `cdr:relSizeAnchor` / * `cdr:absSizeAnchor` wrappers around `sp` / `pic` / `cxnSp` shapes drawn on * top of the chart plot. These interfaces describe the parsed, renderable * overlay model. The raw reference is preserved separately on * {@link PptxChartData.userShapesXml} for verbatim round-trip save; this model * is render-only. * * @module pptx-types/chart-user-shapes */ /** A single paragraph of overlay-shape text with light formatting. */ interface PptxChartUserShapeParagraph { /** Joined run text of the paragraph. */ text: string; /** Font size in points (`a:rPr/@sz` divided by 100), when present. */ fontSize?: number; /** Whether the first run is bold (`a:rPr/@b`). */ bold?: boolean; /** Whether the first run is italic (`a:rPr/@i`). */ italic?: boolean; /** Resolved run colour hex (e.g. `"#FF0000"`), when present. */ color?: string; /** Paragraph alignment (`a:pPr/@algn`): left / centre / right. */ align?: 'l' | 'ctr' | 'r'; } /** * A parsed chart-overlay shape positioned by a drawing anchor. * * Position is expressed as chart-relative fractions in {@link from}. For a * `relSizeAnchor` the opposite corner is {@link to} (also fractional); for an * `absSizeAnchor` the extent is {@link ext} in EMU. */ interface PptxChartUserShape { /** Shape kind: text/preset shape, connector, or picture. */ kind: 'sp' | 'cxnSp' | 'pic'; /** Anchor kind that positioned the shape. */ anchor: 'rel' | 'abs'; /** Top-left corner as chart-relative fractions (0-1). */ from: { x: number; y: number; }; /** Bottom-right corner as chart-relative fractions (0-1); relSizeAnchor only. */ to?: { x: number; y: number; }; /** Extent in EMU (cx, cy); absSizeAnchor only. */ ext?: { cx: number; cy: number; }; /** Preset geometry name (`a:prstGeom/@prst`), defaulting to `"rect"`. */ prst?: string; /** Resolved solid-fill hex colour, when present. */ fill?: string; /** Resolved line/stroke hex colour, when present. */ stroke?: string; /** Line width in points (`a:ln/@w` divided by 12700), when present. */ strokeWidth?: number; /** Text paragraphs of the shape's `txBody`, when present. */ paragraphs?: PptxChartUserShapeParagraph[]; } //#endregion //#region src/core/types/chart.d.ts /** * Supported chart type discriminators. * * @example * ```ts * const type: PptxChartType = "bar"; * // => "bar" — one of: "bar" | "line" | "pie" | "doughnut" | "area" | "scatter" | … * ``` */ type PptxChartType = 'bar' | 'line' | 'pie' | 'ofPie' | 'doughnut' | 'area' | 'scatter' | 'bubble' | 'radar' | 'stock' | 'bar3D' | 'line3D' | 'pie3D' | 'area3D' | 'surface' | 'histogram' | 'waterfall' | 'funnel' | 'treemap' | 'sunburst' | 'boxWhisker' | 'regionMap' | 'combo' | 'unknown'; /** * Bar series direction (OOXML `ST_BarDir`): `"col"` is a vertical column * chart, `"bar"` a horizontal bar chart. * * @example * ```ts * const dir: PptxChartBarDirection = "col"; * // => "col" - one of: "col" | "bar" * ``` */ type PptxChartBarDirection = 'col' | 'bar'; /** * Supported trendline regression types. * * @example * ```ts * const type: PptxChartTrendlineType = "linear"; * // => "linear" — one of: "linear" | "exponential" | "logarithmic" | "polynomial" | "power" | "movingAvg" * ``` */ type PptxChartTrendlineType = 'linear' | 'exponential' | 'logarithmic' | 'polynomial' | 'power' | 'movingAvg'; /** * Configuration for a chart trendline (regression line). * * @example * ```ts * const trendline: PptxChartTrendline = { * trendlineType: "linear", * displayEq: true, * displayRSq: true, * color: "#FF0000", * }; * // => satisfies PptxChartTrendline * ``` */ interface PptxChartTrendline { trendlineType: PptxChartTrendlineType; name?: string; order?: number; period?: number; forward?: number; backward?: number; intercept?: number; displayRSq?: boolean; displayEq?: boolean; color?: string; label?: PptxChartTrendlineLabel | null; } /** Typed, commonly edited properties of `c:trendlineLbl`. */ interface PptxChartTrendlineLabel { layout?: PptxChartManualLayout; numberFormatCode?: string; sourceLinked?: boolean; } /** Error-bar direction axis. */ type PptxChartErrBarDir = 'x' | 'y'; /** Error-bar display type (both sides, negative only, or positive only). */ type PptxChartErrBarType = 'both' | 'minus' | 'plus'; /** * How the error-bar value is calculated. * * @example * ```ts * const valType: PptxChartErrValType = "percentage"; * // => "percentage" — one of: "cust" | "fixedVal" | "percentage" | "stdDev" | "stdErr" * ``` */ type PptxChartErrValType = 'cust' | 'fixedVal' | 'percentage' | 'stdDev' | 'stdErr'; /** * Error bars for a chart series. * * @example * ```ts * const bars: PptxChartErrBars = { * direction: "y", * barType: "both", * valType: "percentage", * val: 5, * }; * // => satisfies PptxChartErrBars * ``` */ interface PptxChartErrBars { direction: PptxChartErrBarDir; barType: PptxChartErrBarType; valType: PptxChartErrValType; val?: number; customPlus?: number[]; customMinus?: number[]; noEndCap?: boolean; color?: string; } /** * Visibility flags for the chart data table (axes + legend keys). * * @example * ```ts * const dt: PptxChartDataTable = { * showHorzBorder: true, * showVertBorder: true, * showOutline: true, * showKeys: true, * }; * // => satisfies PptxChartDataTable * ``` */ interface PptxChartDataTable { showHorzBorder?: boolean; showVertBorder?: boolean; showOutline?: boolean; showKeys?: boolean; /** Table border/fill formatting (`c:dTable/c:spPr`). */ spPr?: PptxChartShapeProps; /** * Cell text defaults (`c:dTable/c:txPr/a:p/a:pPr/a:defRPr`). Reuses the same * shape as a legend entry's text override since both are a flat paragraph * default-run-property style (size/bold/italic/font/colour). */ txPr?: PptxChartLegendTextStyle; } /** * Line appearance for chart helper lines (drop lines, hi-low lines). * * @example * ```ts * const style: PptxChartLineStyle = { * color: "#AAAAAA", * width: 1, * dashStyle: "dash", * }; * // => satisfies PptxChartLineStyle * ``` */ interface PptxChartLineStyle { color?: string; width?: number; dashStyle?: string; } /** Marker symbol types for line/scatter chart data points. */ type PptxChartMarkerSymbol = 'circle' | 'dash' | 'diamond' | 'dot' | 'none' | 'picture' | 'plus' | 'square' | 'star' | 'triangle' | 'x' | 'auto'; /** * `ST_ScatterStyle` (ECMA-376 §21.2.3.40): how a scatter chart joins its points. * `line`/`lineMarker` connect them with straight segments, `smooth`/ * `smoothMarker` with a bezier, `marker`/`none` not at all. */ type PptxChartScatterStyle = 'none' | 'line' | 'lineMarker' | 'marker' | 'smooth' | 'smoothMarker'; /** Shape properties extracted from c:spPr for chart formatting. */ interface PptxChartShapeProps { fillColor?: string; strokeColor?: string; strokeWidth?: number; /** Line dash style (a:prstDash/@val), e.g. 'solid', 'dash', 'dot', 'lgDash'. */ strokeDashStyle?: string; } /** Up/down bar formatting on line and stock charts (`c:upDownBars`). */ interface PptxChartUpDownBars { /** Gap between bars as a percentage, constrained to 0 through 500. */ gapWidth?: number; upBars?: PptxChartShapeProps; downBars?: PptxChartShapeProps; } /** Marker appearance on a chart series or data point. */ interface PptxChartMarker { symbol: PptxChartMarkerSymbol; /** Marker size in points, constrained by ST_MarkerSize to 2 through 72. */ size?: number; spPr?: PptxChartShapeProps; } /** Per-data-point formatting override (c:dPt). */ interface PptxChartDataPoint { idx: number; spPr?: PptxChartShapeProps; explosion?: number; invertIfNegative?: boolean; marker?: PptxChartMarker; /** Render a bubble-chart point with a 3-D appearance. */ bubble3D?: boolean; } /** Schema values accepted by `c:dLblPos`. */ type PptxChartDataLabelPosition = 'bestFit' | 'b' | 'ctr' | 'inBase' | 'inEnd' | 'l' | 'outEnd' | 'r' | 't'; /** Individual data label override (c:dLbl). */ interface PptxChartDataLabel { idx: number; /** Suppress this data point's automatically generated label. */ deleted?: boolean; showVal?: boolean; showCatName?: boolean; showSerName?: boolean; showPercent?: boolean; showLegendKey?: boolean; showBubbleSize?: boolean; position?: PptxChartDataLabelPosition; text?: string; separator?: string; showLeaderLines?: boolean; } /** Axis number format. */ interface PptxChartAxisNumFmt { formatCode: string; sourceLinked?: boolean; } /** Typed contents of a value-axis display-unit label (`c:dispUnitsLbl`). */ interface PptxChartDisplayUnitsLabel { /** Literal label text. Omit to preserve the source text subtree. */ text?: string; /** Manual label placement. `null` removes only the manual layout. */ layout?: PptxChartManualLayout | null; /** Label shape formatting. `null` removes `c:spPr`. */ spPr?: PptxChartShapeProps | null; } /** Axis formatting for category, value, or date axes. */ interface PptxChartAxisFormatting extends PptxChartAxisLabelFormatting { axisType: 'catAx' | 'valAx' | 'dateAx' | 'serAx'; /** Axis position: "b" (bottom), "l" (left), "r" (right), "t" (top). */ axPos?: 'b' | 'l' | 'r' | 't'; /** Unique axis identifier (c:axId/@val) used to link series to axes. */ axisId?: number; /** Cross-axis identifier — the axis this axis crosses. */ crossAxisId?: number; /** Automatic crossing mode (`c:crosses`). Mutually exclusive with `crossesAt`. */ crosses?: 'autoZero' | 'min' | 'max'; /** Explicit crossing value (`c:crossesAt`). Units depend on the axis type. */ crossesAt?: number; /** Whether a value axis crosses between or at category tick marks. */ crossBetween?: 'between' | 'midCat'; numFmt?: PptxChartAxisNumFmt; titleText?: string; spPr?: PptxChartShapeProps; fontFamily?: string; fontSize?: number; fontBold?: boolean; fontColor?: string; /** Whether major gridlines are present (`c:majorGridlines`). */ majorGridlines?: boolean; /** Whether minor gridlines are present (`c:minorGridlines`). */ minorGridlines?: boolean; majorGridlinesSpPr?: PptxChartShapeProps; minorGridlinesSpPr?: PptxChartShapeProps; /** Minimum axis value override (c:min/@val). */ min?: number; /** Maximum axis value override (c:max/@val). */ max?: number; /** Axis value direction (`c:scaling/c:orientation/@val`). */ orientation?: 'minMax' | 'maxMin'; /** Whether the axis is deleted/hidden (c:delete/@val). */ deleted?: boolean; /** * Display units for value axis (c:dispUnits/c:builtInUnit/@val). * When set to 'custom', the actual divisor is in {@link displayUnitsValue}. */ displayUnits?: 'hundreds' | 'thousands' | 'tenThousands' | 'hundredThousands' | 'millions' | 'tenMillions' | 'hundredMillions' | 'billions' | 'trillions' | 'custom'; /** Custom display unit divisor value (c:dispUnits/c:custUnit/@val). Only used when displayUnits is 'custom'. */ displayUnitsValue?: number; /** * Display-unit label contents (`c:dispUnits/c:dispUnitsLbl`). A string is * retained as a compatibility shorthand for `{ text: string }`; `null` * explicitly removes the label. */ displayUnitsLabel?: string | PptxChartDisplayUnitsLabel | null; /** Whether logarithmic scaling is enabled (presence of c:scaling/c:logBase). */ logScale?: boolean; /** Logarithmic base value (c:scaling/c:logBase/@val), typically 10 or e. */ logBase?: number; /** Major-unit interval between primary tick marks (c:majorUnit/@val). */ majorUnit?: number; /** Minor-unit interval between secondary tick marks (c:minorUnit/@val). */ minorUnit?: number; /** Calendar unit used to interpret date-axis serial values. */ baseTimeUnit?: 'days' | 'months' | 'years'; majorTimeUnit?: 'days' | 'months' | 'years'; minorTimeUnit?: 'days' | 'months' | 'years'; } /** 3D wall or floor element formatting. */ interface PptxChart3DSurface { thickness?: number; spPr?: PptxChartShapeProps; } /** * One colour band for a surface chart (`c:bandFmts/c:bandFmt`, * ECMA-376 §21.2.2.19 / CT_BandFmt). `index` is the band's position * (`c:idx/@val`) among the value axis's major-unit bands, in authored order. */ interface PptxChartBandFmt { index: number; spPr?: PptxChartShapeProps; } /** Office 2016 ChartEx box-and-whisker series layout options. */ interface PptxChartBoxWhiskerOptions { quartileMethod?: 'inclusive' | 'exclusive'; showMeanLine?: boolean; showMeanMarker?: boolean; /** Show non-outlier (inner) data points. */ showInnerPoints?: boolean; showOutlierPoints?: boolean; } /** Office 2016 ChartEx histogram and Pareto series layout options. */ interface PptxChartHistogramOptions { /** Maps to `clusteredColumn` for histogram columns or `paretoLine`. */ layout?: 'histogram' | 'pareto'; /** Exactly one of binSize and binCount is emitted by the ChartEx writer. */ binSize?: number; binCount?: number; intervalClosed?: 'l' | 'r'; underflow?: number | 'auto'; overflow?: number | 'auto'; } /** Office 2016 ChartEx waterfall series layout options. */ interface PptxChartWaterfallOptions { /** Zero-based data point indexes rendered as absolute subtotal or total bars. */ subtotalIndices?: number[]; /** Whether connector lines are visible between adjacent bars. */ connectorLines?: boolean; } /** Office 2016 ChartEx geographic series dimensions and layout options. */ interface PptxChartRegionMapOptions { /** Optional provider entity identifiers aligned with categories and values. */ entityIds?: string[]; /** Original `cx:pt/@idx` values for category points. */ categorySourceIndices?: number[]; /** Original `cx:pt/@idx` values for colour-value points. */ valueSourceIndices?: number[]; /** Original `cx:pt/@idx` values for entity-ID points. */ entityIdSourceIndices?: number[]; regionLabelLayout?: 'none' | 'bestFitOnly' | 'showAll'; projectionType?: 'mercator' | 'miller' | 'robinson' | 'albers'; viewedRegionType?: 'dataOnly' | 'postalCode' | 'county' | 'state' | 'countryRegion' | 'countryRegionList' | 'world'; cultureLanguage?: string; /** ISO-3166-1 alpha-2 region code. */ cultureRegion?: string; attribution?: string; /** Opaque authored provider cache under `cx:geography/cx:geoCache`. */ geographyCache?: XmlObject; } /** Layout for parent category labels in a hierarchical ChartEx treemap. */ type PptxChartParentLabelLayout = 'none' | 'banner' | 'overlapping'; /** Per-series layout options for an Office 2016+ ChartEx treemap. */ interface PptxChartTreemapOptions { parentLabelLayout?: PptxChartParentLabelLayout; } /** * A single data series within a chart. * * @example * ```ts * const series: PptxChartSeries = { * name: "Revenue", * values: [100, 120, 140], * color: "#4F81BD", * trendlines: [{ trendlineType: "linear" }], * }; * // => satisfies PptxChartSeries * ``` */ interface PptxChartSeries { name: string; values: number[]; /** * Per-series x values from `c:ser/c:xVal` (scatter and bubble series only). * * Every `CT_ScatterSer` / `CT_BubbleSer` carries its OWN `c:xVal`, so two * series in one scatter chart routinely plot against different x ranges (the * normal case for measurement data). Reading the x values off the first * series and reusing them everywhere plotted every series against series 1's * x axis. Absent for category-axis chart kinds, where * {@link PptxChartData.categories} is the x axis. */ xValues?: number[]; /** * Per-series bubble sizes from `c:ser/c:bubbleSize` (bubble series only), * aligned index-for-index with {@link values}. * * `CT_BubbleSer` carries x, y AND size, so a one-series bubble chart is fully * specified. Absent when the source omits `c:bubbleSize`. */ bubbleSizes?: number[]; /** * Series-level data-label content flags from `c:ser/c:dLbls`. * * PowerPoint writes the flags a user picks in "Format Data Labels" onto the * SERIES, and leaves the chart-type-level `c:dLbls` all-zero, so reading only * the chart-level group reports "show nothing" for a chart that visibly shows * percentages. These override {@link PptxChartStyle.dataLabels}. */ dataLabelOptions?: PptxChartDataLabelOptions; /** * Whether the series line is explicitly suppressed * (`c:ser/c:spPr/a:ln/a:noFill`). Line-drawn kinds (line, scatter, radar) * use this to decide whether to draw a connecting line at all; a marker-only * scatter is authored as `scatterStyle="lineMarker"` PLUS this flag, never by * changing the scatter style. */ lineNoFill?: boolean; /** * Blank-value mask aligned index-for-index with {@link values}: `true` marks * a category whose numeric cache point (`c:numCache/c:pt`) was absent or * empty, i.e. a genuine blank rather than a real `0`. Present only when the * source series actually contains blanks; when set, blank slots in * {@link values} carry `0` as a placeholder. Renderers honour * `c:dispBlanksAs` (gap / zero / span) using this mask. */ blanks?: boolean[]; /** * ECMA-376 number-format code for this series' values, resolved from * `c:ser/c:dLbls/c:numFmt/@formatCode` and falling back to the value cache's * own `c:numCache/c:formatCode` (which is what `@sourceLinked="1"` means). * Data labels render through it: a percentage series caches fractions, so * without the code `0.52` reaches the label where PowerPoint shows `52%`. */ numberFormat?: string; color?: string; trendlines?: PptxChartTrendline[]; errBars?: PptxChartErrBars[]; dataPoints?: PptxChartDataPoint[]; marker?: PptxChartMarker; dataLabels?: PptxChartDataLabel[]; explosion?: number; /** * Series-level `c:invertIfNegative`: when true, bar/column data points with a * negative value are drawn with an inverted (lightened) fill. A per-point * `c:dPt/c:invertIfNegative` overrides this for that point. Absent when the * source XML omits the flag. */ invertIfNegative?: boolean; /** * Whether this line/scatter series is drawn with bezier smoothing * (`c:ser/c:smooth/@val`). Absent when the source XML omits `c:smooth`. */ smooth?: boolean; /** Axis ID this series is plotted against (links to PptxChartAxisFormatting.axisId). */ axisId?: number; /** * Per-series chart type, used for combo charts where individual series are * plotted with different chart types (e.g. a bar series and a line series in * the same chart). Maps to the OOXML chart-type container that holds the * series (`c:barChart`, `c:lineChart`, etc.). Omitted for single-type charts, * where the chart-level {@link PptxChartData.chartType} applies to every * series. */ seriesChartType?: PptxChartType; boxWhiskerOptions?: PptxChartBoxWhiskerOptions; histogramOptions?: PptxChartHistogramOptions; waterfallOptions?: PptxChartWaterfallOptions; regionMapOptions?: PptxChartRegionMapOptions; treemapOptions?: PptxChartTreemapOptions; } /** * Chart-level data-label options (`c:dLbls` directly under a chart-type * container, applying to every series). Mirrors the OOXML `c:show*` flags * and `c:dLblPos`. */ interface PptxChartDataLabelOptions { /** Show the numeric value (`c:showVal`). */ showValue?: boolean; /** Show the category name (`c:showCatName`). */ showCategory?: boolean; /** Show the series name (`c:showSerName`). */ showSeriesName?: boolean; /** Show the percentage (`c:showPercent`, pie/doughnut). */ showPercent?: boolean; /** Show the legend key swatch (`c:showLegendKey`). */ showLegendKey?: boolean; /** Show bubble size (`c:showBubbleSize`). */ showBubbleSize?: boolean; /** Text placed between combined label components (`c:separator`). */ separator?: string; /** Show leader lines where supported (`c:showLeaderLines`). */ showLeaderLines?: boolean; /** * Label position (`c:dLblPos`). Valid values depend on the chart type * (`ctr`, `inEnd`, `inBase`, `outEnd`, `bestFit`, `l`, `r`, `t`, `b`). * Omit to let PowerPoint use the type default. */ position?: PptxChartDataLabelPosition; } /** Typed text defaults for a single chart legend entry. */ interface PptxChartLegendTextStyle { fontFamily?: string; fontSize?: number; bold?: boolean; italic?: boolean; color?: string; } /** Per-series legend entry override (`c:legendEntry`). */ interface PptxChartLegendEntry { index: number; deleted?: boolean; textStyle?: PptxChartLegendTextStyle; } /** * Style / formatting metadata for a chart. * * @example * ```ts * const style: PptxChartStyle = { * styleId: 2, * hasLegend: true, * legendPosition: "b", * hasDataLabels: true, * }; * // => satisfies PptxChartStyle * ``` */ interface PptxChartStyle { /** Chart style index from `c:style/@val`. */ styleId?: number; /** Whether the chart has a visible legend. */ hasLegend?: boolean; /** Legend position (t, b, l, r, tr). */ legendPosition?: string; /** Per-series visibility and text-style overrides. */ legendEntries?: PptxChartLegendEntry[]; /** Whether the chart has a title. */ hasTitle?: boolean; /** Whether gridlines are visible. */ hasGridlines?: boolean; /** * Chart-area fill from `c:chartSpace/c:spPr`: a resolved colour, or the * literal `'none'` when the source declares ``. Absent when the * chart says nothing, in which case the renderer picks its own default. * PowerPoint decks routinely set `a:noFill` so the chart floats on the slide * background; painting a panel behind it boxes the chart in. */ chartAreaFill?: string; /** Plot-area fill from `c:plotArea/c:spPr`. See {@link chartAreaFill}. */ plotAreaFill?: string; /** Whether data labels are shown. */ hasDataLabels?: boolean; /** Chart-level data-label content/position options (when `hasDataLabels`). */ dataLabels?: PptxChartDataLabelOptions; } /** * External data source reference for a chart (c:externalData). * * Charts can reference an external Excel workbook via a relationship ID * that points to an external file (TargetMode="External"). The * `autoUpdate` flag indicates whether the chart should refresh its * cached data from the external source on open. * * @example * ```ts * const ext: PptxExternalData = { * relId: "rId2", * targetPath: "file:///C:/Data/budget.xlsx", * autoUpdate: true, * }; * // => satisfies PptxExternalData * ``` */ interface PptxExternalData { /** Relationship ID referencing the external data source in the chart .rels. */ relId: string; /** Resolved external file path or URL from the relationship target. */ targetPath?: string; /** Whether to auto-update data from the external source on open. */ autoUpdate?: boolean; /** Raw binary data of the embedded xlsx workbook (from ppt/embeddings/). */ embeddedWorkbookData?: Uint8Array; } /** * Options specific to the OOXML "Pie of Pie" / "Bar of Pie" chart * (`c:ofPieChart`, ECMA-376 §21.2.2.126 / CT_OfPieChart). * * The primary discriminator is {@link ofPieType}: `"pie"` produces a * pie-of-pie chart whose secondary plot is itself a pie, while `"bar"` * produces a bar-of-pie chart whose secondary plot is a horizontal bar. * * - {@link splitType} chooses the split rule. * - {@link splitPos} is the threshold value used by `pos`/`val`/`percent`. * - {@link secondPieSize} controls the secondary plot's size (5–200%). * - {@link serLines} toggles the leader lines connecting the plots. * - {@link gapWidth} is the gap between the plots in percent (0–500). */ interface PptxChartOfPieOptions { ofPieType: 'pie' | 'bar'; splitType?: 'auto' | 'cust' | 'percent' | 'pos' | 'val'; splitPos?: number; custSplit?: number[]; secondPieSize?: number; serLines?: boolean; gapWidth?: number; } /** Classic `c:bubbleChart` options from CT_BubbleChart. */ interface PptxBubbleChartOptions { bubble3D?: boolean; /** Bubble diameter scale in percent, constrained to 0 through 300. */ bubbleScale?: number; showNegativeBubbles?: boolean; sizeRepresents?: 'area' | 'w'; } /** * 3D viewing parameters for a chart (`c:view3D`, ECMA-376 §21.2.2.228 / * CT_View3D). * * All fields are optional and round-trip verbatim. * * - {@link rotX} — X-axis rotation in degrees (-90…90). * - {@link rotY} — Y-axis rotation in degrees (0…360). * - {@link depthPercent} — chart depth as a percentage of base width. * - {@link rAngAx} — `true` if axes meet at right angles. * - {@link perspective} — perspective angle in degrees (0…240). * - {@link hPercent} — height as a percentage of chart width. */ interface PptxChartView3D { rotX?: number; rotY?: number; depthPercent?: number; rAngAx?: boolean; perspective?: number; hPercent?: number; } /** * Chart "chrome" flags from `c:chart` that round-trip cleanly even when * rendering ignores them. * * - {@link autoTitleDeleted} — `c:autoTitleDeleted/@val`. Suppresses the * auto-generated title for single-series charts. * - {@link dispBlanksAs} — `c:dispBlanksAs/@val`. How blank cells * render: `"gap"`, `"zero"`, or `"span"`. * - {@link showDLblsOverMax} — `c:showDLblsOverMax/@val`. Keeps data * labels visible for points exceeding the value-axis maximum. * * `c:plotVisOnly` lives on {@link PptxChartData.plotVisibleOnly} and is * intentionally not duplicated here. */ interface PptxChartChrome { autoTitleDeleted?: boolean; dispBlanksAs?: 'gap' | 'zero' | 'span'; showDLblsOverMax?: boolean; } /** Manual chart placement from `c:layout/c:manualLayout` (CT_ManualLayout). */ interface PptxChartManualLayout { layoutTarget?: 'inner' | 'outer'; xMode?: 'edge' | 'factor'; yMode?: 'edge' | 'factor'; widthMode?: 'edge' | 'factor'; heightMode?: 'edge' | 'factor'; x?: number; y?: number; width?: number; height?: number; /** * Raw `c:extLst` (CT_ExtensionList) of the `c:manualLayout`, captured * verbatim so it round-trips through the typed model. Without this, a dirty * write of an edited layout would drop the extension list (the manual node * is rebuilt from the typed fields). Emitted as the trailing child, matching * the CT_ManualLayout schema order. */ ext?: XmlObject; } /** * Typed manual layouts for chart regions that accept `c:layout`. * A `null` region removes its manual layout without removing extensions. */ interface PptxChartLayouts { title?: PptxChartManualLayout | null; plotArea?: PptxChartManualLayout | null; legend?: PptxChartManualLayout | null; } /** Parsed data extracted from an embedded xlsx workbook. */ interface PptxEmbeddedWorkbookData { /** Category labels from the first column/row. */ categories: string[]; /** Data series extracted from worksheet cells. */ series: Array<{ name: string; values: number[]; }>; /** Whether the workbook uses the 1904 date system. */ date1904?: boolean; } /** Raw numeric category cache used by a classic ChartML date axis. */ interface PptxChartDateCategories { values: number[]; /** False/default uses Excel's 1900 date system; true uses the 1904 system. */ date1904?: boolean; /** Number format copied from the numeric category cache. */ formatCode?: string; } /** * Complete parsed chart data for a {@link ChartPptxElement}. * * @example * ```ts * const chart: PptxChartData = { * title: "Q4 Sales", * chartType: "bar", * categories: ["Jan", "Feb", "Mar"], * series: [ * { name: "Revenue", values: [100, 120, 140] }, * ], * grouping: "clustered", * style: { hasLegend: true, legendPosition: "b" }, * }; * // => satisfies PptxChartData * ``` */ interface PptxChartData { title?: string; chartType: PptxChartType; categories: string[]; /** * Hierarchical category levels in source XML order, for both ChartEx * hierarchy charts (`cx:multiLvlStrRef`) and classic multi-level category * axes (`c:cat/c:multiLvlStrRef`, e.g. a PowerPoint Quarter > Month * grouping). Level 0 contains the leaf labels and remains mirrored by * {@link categories} for consumers that only understand a flat category * axis. Parent (grouping) levels are forward-filled: a blank cache slot * continues the previous group's label, matching how the source stores a * merged category header sparsely. */ categoryLevels?: string[][]; dateCategories?: PptxChartDateCategories; series: PptxChartSeries[]; /** Chart style/formatting metadata. */ style?: PptxChartStyle; /** Grouping mode for bar/area/line charts: 'clustered' | 'stacked' | 'percentStacked' */ grouping?: 'clustered' | 'stacked' | 'percentStacked'; /** * Whether the first (or only) series varies its point colours * (`c:varyColors/@val`). Pie/doughnut default this on; single-series * bar/column honour it by giving each point a distinct palette colour. * Absent when the source XML omits `c:varyColors`. */ varyColors?: boolean; /** * Pie/doughnut start angle in degrees clockwise from 12 o'clock * (`c:firstSliceAng/@val`, 0 through 360). Absent uses the default 0. */ firstSliceAngle?: number; /** * Doughnut hole diameter as a percentage of the outer diameter * (`c:holeSize/@val`, 10 through 90). Absent uses the renderer default. */ doughnutHoleSize?: number; /** * Bar series direction (`c:barDir/@val`): `"col"` draws vertical columns, * `"bar"` draws horizontal bars. Absent means `"col"` (PowerPoint's own * default), so only horizontal bar charts need to carry the field. */ barDirection?: PptxChartBarDirection; /** * Scatter presentation mode (`c:scatterChart/c:scatterStyle/@val`). * * `lineMarker` (PowerPoint's own default for every scatter it writes) and * `smoothMarker` draw a connecting line; `marker` and `none` do not. Whether * the MARKERS appear is decided separately by `c:marker/c:symbol`, and * whether the LINE appears is further gated by * {@link PptxChartSeries.lineNoFill} - PowerPoint expresses "markers only" as * `lineMarker` plus an `a:ln/a:noFill`, not as `marker`. */ scatterStyle?: PptxChartScatterStyle; /** * Bar/column gap between category clusters as a percentage of bar width * (`c:gapWidth/@val`, 0 through 500). Absent uses the renderer default. */ barGapWidth?: number; /** * Clustered bar/column overlap between series within a category as a * percentage (`c:overlap/@val`, -100 through 100). Absent uses 0. */ barOverlap?: number; /** Internal: path to the chart XML part in the PPTX archive (for round-trip save). */ chartPartPath?: string; /** Internal: relationship ID linking the graphic frame to the chart part. */ chartRelationshipId?: string; /** `null` explicitly removes an existing ChartML data table. */ dataTable?: PptxChartDataTable | null; dropLines?: PptxChartLineStyle; hiLowLines?: PptxChartLineStyle; /** `null` explicitly removes an existing up/down-bars container. */ upDownBars?: PptxChartUpDownBars | null; axes?: PptxChartAxisFormatting[]; floor?: PptxChart3DSurface; sideWall?: PptxChart3DSurface; backWall?: PptxChart3DSurface; /** Per-band surface-chart colour overrides (`c:surfaceChart/c:bandFmts`). */ bandFmts?: PptxChartBandFmt[]; /** External data source reference (c:externalData) linking to an external workbook. */ externalData?: PptxExternalData; /** * Parsed data from the embedded xlsx workbook (from ppt/embeddings/). * * When a chart references an embedded Excel workbook via `c:externalData`, * the xlsx is parsed to extract categories and series. This data serves as * a fallback when the chart XML's cached series data is empty or incomplete. */ embeddedWorkbookData?: PptxEmbeddedWorkbookData; /** * Pivot table data source reference (c:pivotSource). * * When present, the chart's data originates from a PivotTable. * The chart still renders using its cached series data; this field * is metadata about the data origin, preserved for round-trip fidelity. */ pivotSource?: PptxChartPivotSource | null; /** * Whether only visible cells are plotted (c:plotVisOnly). * When `true` (the default), hidden cells are excluded from the chart. * When `false`, hidden data IS plotted. */ plotVisibleOnly?: boolean; /** * Color palette extracted from the chart's Office 2013+ color style part * (`chartColorStyle*.xml`). When present, this palette takes priority over * the `c:style/@val`-derived palette in `getChartStylePalette`. * * Each entry is a resolved hex colour string (e.g. `"#4472C4"`). */ colorPalette?: string[]; /** * Color cycling method from the chart color style part's `meth` attribute. * * - `"cycle"` — repeat the palette colours in order (default) * - `"withinLinear"` — gradient within each series * - `"acrossLinear"` — gradient across series */ colorMethod?: 'cycle' | 'withinLinear' | 'acrossLinear'; /** Internal source color-style part path used for lossless dirty saves. */ colorStylePartPath?: string; /** Internal parsed palette snapshot used to detect actual edits. */ colorStyleOriginalPalette?: string[]; /** Internal parsed method snapshot used to detect actual edits. */ colorStyleOriginalMethod?: 'cycle' | 'withinLinear' | 'acrossLinear'; /** * Pie-of-pie / Bar-of-pie options (`c:ofPieChart`, CT_OfPieChart). * * Present only when {@link chartType} is `"ofPie"`. Carries the split * configuration, secondary plot size, and serLines flag so that an * `ofPieChart` element can be re-emitted on save with full fidelity. */ ofPieOptions?: PptxChartOfPieOptions; /** Classic bubble-chart display options (`c:bubbleChart`). */ bubbleOptions?: PptxBubbleChartOptions; /** * 3D viewing parameters (`c:view3D`, CT_View3D). * * Parsed from and emitted to `c:chart/c:view3D`. Absent when the * chart XML has no `c:view3D` element. */ view3D?: PptxChartView3D; /** * Top-level chart chrome flags (`c:autoTitleDeleted`, * `c:dispBlanksAs`, `c:showDLblsOverMax`). * * Each flag is omitted from the emitted XML when absent on the * source data, so absence does not produce empty `` placeholders. */ chartChrome?: PptxChartChrome; /** `c:chartSpace/c:printSettings`; `null` removes the container on save. */ printSettings?: PptxChartPrintSettings | null; /** `c:chartSpace/c:protection`; `null` removes the container on save. */ protection?: PptxChartProtection | null; /** Editable manual placement for the title, plot area, and legend. */ layouts?: PptxChartLayouts; /** * Raw `c:userShapes` XML subtree (a drawing tree) preserved verbatim. * * `c:userShapes` references a separate drawing part containing * shapes drawn over the chart. The reference is preserved as-is so * that round-trip save re-emits the original element without * attempting to parse the nested drawing tree. */ userShapesXml?: unknown; /** * Parsed, renderable drawing-overlay shapes resolved from the separate * drawing part referenced by `c:userShapes/@r:id` * (`ppt/drawings/drawingN.xml`). Each entry carries chart-relative anchor * geometry plus light shape/text formatting so the viewer can render an * overlay on top of the chart plot. Render-only: {@link userShapesXml} * remains the source of truth for round-trip save. */ userShapes?: PptxChartUserShape[]; /** * Raw `c:pivotFmts` XML subtree preserved verbatim. * * `c:pivotFmts` carries a list of `c:pivotFmt` formatting overrides * for charts whose data originates from a PivotTable. Preserved * verbatim for round-trip fidelity. */ /** Typed pivot-chart format persistence; `null` removes `c:pivotFmts`. */ pivotFormats?: PptxChartPivotFormats | null; /** * Color-map override (`c:clrMapOvr`) carrying 12 attributes that * remap theme colour roles for this chart only. Preserved as a flat * `attribute → value` map for round-trip fidelity. */ clrMapOvr?: Record; } //#endregion //#region src/core/types/image.d.ts /** * Image recolour/adjustment properties parsed from blip extensions. * * These effects are stored in the OpenXML `` extension list * and applied non-destructively to the original image data. * * @example * ```ts * const fx: PptxImageEffects = { * brightness: 20, * contrast: -10, * grayscale: true, * }; * // => { brightness: 20, contrast: -10, grayscale: true } satisfies PptxImageEffects * ``` */ /** * One `a14:foregroundMark` / `a14:backgroundMark` polyline hint recorded while * the user painted over the picture in PowerPoint's "Remove Background" mode. * Coordinates are 0..1 fractions of the image. */ interface PptxBackgroundRemovalMark { x1: number; y1: number; x2: number; y2: number; } /** * PowerPoint "Remove Background" state (`a14:backgroundRemoval`). * * The four edges are the RETAINED rectangle as 0..1 fractions of the image * (OOXML stores them as per-100000 relative units), and the mark lists are the * segmentation hints the user painted. * * **This is edit-time metadata, not a render instruction.** PowerPoint bakes the * removal into the bitmap referenced by the main `a:blip/@r:embed` and keeps the * pristine original in `a14:imgLayer/@r:embed`. Verified against PowerPoint COM: * a slide exported with and without this element is byte-identical. A renderer * that clips to the retained rectangle would clip an image whose background has * already been removed. * * @example * ```ts * const removal: PptxBackgroundRemoval = { top: 0.12, bottom: 0.88, left: 0.07, right: 0.93 }; * // => retains the middle of the image; the marks list stays empty * ``` */ interface PptxBackgroundRemoval { /** Top edge of the retained rectangle (0..1 fraction of the image height). */ top: number; /** Bottom edge of the retained rectangle (0..1 fraction of the image height). */ bottom: number; /** Left edge of the retained rectangle (0..1 fraction of the image width). */ left: number; /** Right edge of the retained rectangle (0..1 fraction of the image width). */ right: number; /** Strokes marking regions the user forced to be foreground. */ foregroundMarks?: PptxBackgroundRemovalMark[]; /** Strokes marking regions the user forced to be background. */ backgroundMarks?: PptxBackgroundRemovalMark[]; /** Original effect XML, retained for lossless re-emission. */ rawXml?: XmlObject; } interface PptxImageEffects { /** Brightness adjustment (-100 to 100). */ brightness?: number; /** Contrast adjustment (-100 to 100). */ contrast?: number; /** Duotone colour pair. */ duotone?: { color1: string; color2: string; /** Original effect XML, retained while the resolved colours are unchanged. */ rawXml?: XmlObject; }; /** Grayscale flag. */ grayscale?: boolean; /** Saturation adjustment (-100 to 100). */ saturation?: number; /** Color wash overlay. */ colorWash?: { color: string; opacity: number; }; /** Artistic effect name (blur, pencilGrayscale, paintStrokes, etc.). */ artisticEffect?: string; /** Artistic effect radius/amount, normalised to 0..100. */ artisticRadius?: number; /** * Every numeric attribute of the source `a14:artistic*` element, raw and * un-normalised (`trans`, `pencilSize`, `crackSpacing`, …). The attribute set * differs per effect, so this is the lossless companion to the single * {@link PptxImageEffects.artisticRadius} number. */ artisticParams?: Record; /** * Name of the artistic effect ALREADY baked into the image data, which a * renderer must not apply a second time. Set from the `a14` blip extension, * which PowerPoint writes alongside a pre-rendered bitmap (see * {@link PptxBackgroundRemoval}), and normally equal to * {@link PptxImageEffects.artisticEffect}. * * It records the NAME rather than a boolean so that picking a different * effect in this library's inspector (which patches `artisticEffect` alone) * still renders: the two names then differ. */ artisticPrerenderedEffect?: string; /** * PowerPoint "Remove Background" state (`a14:backgroundRemoval`). Edit-time * metadata: the removal is already baked into the image data. */ backgroundRemoval?: PptxBackgroundRemoval; /** * `a14:imgLayer/@r:embed` — relationship id of the PRISTINE original image * the baked effects were derived from (PowerPoint stores it as an HD Photo * `.wdp` part, which browsers cannot decode). */ originalImageRelId?: string; /** Alpha modulation fixed: non-negative percentage (100 means unchanged opacity). */ alphaModFix?: number; /** Original alpha modulation fixed node, including foreign attributes. */ alphaModFixRawXml?: XmlObject; /** Bi-level threshold — converts to 1-bit black/white (0-100). */ biLevel?: number; /** Colour change — swap one colour range for another (used for transparency keying). */ clrChange?: { clrFrom: string; clrTo: string; /** Whether the target colour is fully transparent (alpha = 0). */ clrToTransparent?: boolean; /** Original effect XML, including colour transforms and extensions. */ rawXml?: XmlObject; }; /** Original grayscale node, including extension or foreign attributes. */ grayscaleRawXml?: XmlObject; /** Original bi-level node, including extension or foreign attributes. */ biLevelRawXml?: XmlObject; /** * Alpha inverse effect (`a:alphaInv`). Inverts the alpha channel; an optional * colour child shifts the inversion baseline. */ alphaInv?: { /** Optional baseline colour (hex). */ color?: string; /** Original effect XML, including colour transforms and foreign attributes. */ rawXml?: XmlObject; }; /** Alpha ceiling (`a:alphaCeiling`) — clamps any non-zero alpha to fully opaque. Boolean flag. */ alphaCeiling?: boolean; /** Original alpha ceiling node, including foreign attributes. */ alphaCeilingRawXml?: XmlObject; /** Alpha floor (`a:alphaFloor`) — clamps any non-fully-opaque alpha to fully transparent. Boolean flag. */ alphaFloor?: boolean; /** Original alpha floor node, including foreign attributes. */ alphaFloorRawXml?: XmlObject; /** * Alpha modulate (`a:alphaMod`). The schema requires a single `cont` (effect * container) child; we preserve the inner XML opaquely for round-trip. */ alphaMod?: { /** Raw opaque XML for the `a:cont` child to preserve on save. */ contRawXml?: Record; /** Original effect XML, including foreign attributes. */ rawXml?: XmlObject; /** * Multiplicative alpha percentage (0..100+), read from a nested * `` inside `contRawXml` when present - the common * real-world shape of `a:alphaMod` (` * `). Derived/read-only: a renderer multiplies the source alpha * by this, distinct from the top-level {@link alphaModFix} sibling * effect. Not written back independently on save; `contRawXml` is what * round-trips. */ amt?: number; }; /** Alpha replace (`a:alphaRepl`) — replaces alpha with the given fixed-percent value (0..100). */ alphaRepl?: number; /** Original alpha replace node, including foreign attributes. */ alphaReplRawXml?: XmlObject; /** Alpha bi-level (`a:alphaBiLevel`) — threshold (0..100) above which alpha becomes fully opaque. */ alphaBiLevel?: number; /** Original alpha bi-level node, including foreign attributes. */ alphaBiLevelRawXml?: XmlObject; /** * Colour replace (`a:clrRepl`) — replaces all colour information in an image * with the given solid colour. Stores the raw colour child to preserve scheme * colour references and modifiers. */ clrRepl?: { /** Resolved hex colour. */ color: string; /** Raw opaque colour XML for round-trip. */ rawXml?: Record; }; /** Luminance modulation (`a:lum`) — bright/contrast as fixed percentages (0..100). */ lum?: { bright?: number; contrast?: number; }; /** HSL modulation (`a:hsl`) — hue (0..360 degrees), saturation/luminance (-100..100). */ hsl?: { hue?: number; sat?: number; lum?: number; }; /** Image-effect tint (`a:tint` inside blip) — hue (0..360), amount (-100..100). */ tint?: { hue?: number; amt?: number; }; /** * Fill overlay (`a:fillOverlay`) — overlays a fill on top of the blip. * Stores blend mode and the raw inner fill XML for round-trip. */ fillOverlay?: { blend: 'over' | 'mult' | 'screen' | 'darken' | 'lighten'; /** Raw opaque fill XML preserved for round-trip. */ fillRawXml?: Record; /** * Resolved hex colour, when the overlay fill is a plain `a:solidFill` * (the common case for a picture-style colour overlay). `undefined` for * a gradient/pattern/picture overlay fill - see {@link resolvedGradient} / * {@link resolvedPattern} instead. `fillRawXml` still round-trips * losslessly regardless of which of the three resolved. */ resolvedColor?: string; /** Resolved opacity (0-1) of the `a:solidFill` overlay colour, when resolved. */ resolvedOpacity?: number; /** * Resolved gradient, when the overlay fill is `a:gradFill`. A renderer * composites this as an SVG paint server (`` / * ``) rather than a flat flood colour. */ resolvedGradient?: { type: 'linear' | 'radial'; /** Gradient angle in degrees (`a:lin/@ang`), for a linear gradient. */ angle?: number; stops: Array<{ color: string; position: number; opacity?: number; }>; }; /** * Resolved preset pattern, when the overlay fill is `a:pattFill`. A * renderer composites this as a tiled SVG paint server. */ resolvedPattern?: { preset: string; foreground?: string; background?: string; }; }; /** Blur (`a:blur`) — radius in EMU and grow flag. */ blur?: { rad?: number; grow?: boolean; }; } /** * Shape names used for crop-to-shape (CSS `clip-path` equivalent). * * @example * ```ts * const shape: PptxCropShape = "ellipse"; * // => "ellipse" — one of: none | ellipse | roundedRect | triangle | diamond | pentagon | hexagon | star * ``` */ type PptxCropShape = 'none' | 'ellipse' | 'roundedRect' | 'triangle' | 'diamond' | 'pentagon' | 'hexagon' | 'star'; /** * Image content mixin — present on image and picture elements. * * Contains the decoded image data (base64 data URL or archive path), * alt text, crop insets, tiling settings, and image effects. * * @example * ```ts * const props: PptxImageProperties = { * imagePath: "ppt/media/image1.png", * altText: "Company logo", * cropLeft: 0.05, * cropRight: 0.05, * }; * // => { imagePath: "ppt/media/image1.png", altText: "Company logo", cropLeft: 0.05, cropRight: 0.05 } * ``` */ interface PptxImageProperties { /** Base64 data-URL for the decoded image. */ imageData?: string; /** Path within the PPTX ZIP archive. */ imagePath?: string; /** Base64 data-URL for an SVG variant (from blip extension asvg:svgBlip). Preferred over raster when available. */ svgData?: string; /** Path to the SVG file within the PPTX ZIP archive. */ svgPath?: string; /** Alt text / description from `p:cNvPr/@descr`. */ altText?: string; /** Crop from left edge as 0..1 fraction (OOXML `a:srcRect/@l`). */ cropLeft?: number; /** Crop from top edge as 0..1 fraction (OOXML `a:srcRect/@t`). */ cropTop?: number; /** Crop from right edge as 0..1 fraction (OOXML `a:srcRect/@r`). */ cropRight?: number; /** Crop from bottom edge as 0..1 fraction (OOXML `a:srcRect/@b`). */ cropBottom?: number; /** * Stretch target inset from the left frame edge as a signed fraction * (OOXML `a:stretch/a:fillRect/@l`). Unlike `cropLeft` (which selects a * region of the SOURCE bitmap), a fill-rect selects the region of the * FRAME the whole image is stretched into; negative values push the * image beyond the frame edge, and the overflow is clipped. */ fillRectLeft?: number; /** Stretch target inset from the top frame edge (`a:fillRect/@t`). */ fillRectTop?: number; /** Stretch target inset from the right frame edge (`a:fillRect/@r`). */ fillRectRight?: number; /** Stretch target inset from the bottom frame edge (`a:fillRect/@b`). */ fillRectBottom?: number; /** Image tiling offset X in px. */ tileOffsetX?: number; /** Image tiling offset Y in px. */ tileOffsetY?: number; /** Image tiling scale X as percentage (100 = 100%). */ tileScaleX?: number; /** Image tiling scale Y as percentage (100 = 100%). */ tileScaleY?: number; /** Image tiling flip mode. */ tileFlip?: 'none' | 'x' | 'y' | 'xy'; /** Image tiling alignment. */ tileAlignment?: string; /** * Print-resolution hint in DPI (`a:blipFill/@dpi`). PowerPoint records this * when it downsamples an embedded image for a target print quality; it has * no on-screen rendering effect (a `0`/absent value means "use the source * image's native resolution"). Parsed for round-trip / API fidelity only. */ dpi?: number; /** Image recolour/artistic effect properties. */ imageEffects?: PptxImageEffects; /** Crop-to-shape — CSS clip-path shape name. */ cropShape?: PptxCropShape; } //#endregion //#region src/core/types/media.d.ts /** * Discriminator for embedded media element types. * * @example * ```ts * const kind: PptxMediaType = "video"; * // => "video" — one of: "video" | "audio" | "unknown" * ``` */ type PptxMediaType = 'video' | 'audio' | 'unknown'; type PptxMediaReferenceKind = 'audioCd' | 'wavAudioFile' | 'audioFile' | 'videoFile' | 'quickTimeFile'; interface PptxAudioCdPosition { track: number; time?: number; /** Original `st` or `end` node, retained for lossless edits. */ rawXml?: XmlObject; } /** * A named bookmark within a media clip timeline. * * @example * ```ts * const bm: MediaBookmark = { * id: "bm1", * time: 12.5, * label: "Intro ends", * }; * // => satisfies MediaBookmark * ``` */ interface MediaBookmark { id: string; /** Position in seconds from the start of the clip. */ time: number; /** User-visible label for this bookmark. */ label: string; } /** * Runtime-extracted metadata about a media clip (populated from HTMLMediaElement). * * @example * ```ts * const meta: MediaMetadata = { * duration: 120.5, * videoWidth: 1920, * videoHeight: 1080, * codecInfo: "video/mp4; codecs=\"avc1.640028\"", * }; * // => satisfies MediaMetadata * ``` */ interface MediaMetadata { /** Duration in seconds. */ duration?: number; /** Video width in pixels (video only). */ videoWidth?: number; /** Video height in pixels (video only). */ videoHeight?: number; /** MIME type / codec string reported by the browser. */ codecInfo?: string; } /** * A closed-caption / subtitle track associated with a media element. * * @example * ```ts * const track: MediaCaptionTrack = { * id: "t1", * label: "English", * language: "en", * kind: "subtitles", * isDefault: true, * }; * // => satisfies MediaCaptionTrack * ``` */ interface MediaCaptionTrack { /** Unique ID for this track. */ id: string; /** Human-readable label (e.g. "English", "Spanish"). */ label: string; /** BCP-47 language code (e.g. "en", "es"). */ language: string; /** Track kind: subtitles, captions, or descriptions. */ kind: 'subtitles' | 'captions' | 'descriptions'; /** Data URL or path to the VTT/SRT content within the PPTX archive. */ src?: string; /** Inline VTT content (for embedded captions). */ content?: string; /** Whether this track is the default/active one. */ isDefault?: boolean; } //#endregion //#region src/core/types/smart-art-constraint-rules.d.ts type PptxSmartArtConstraintRelationship = 'self' | 'ch' | 'des'; type PptxSmartArtConstraintOperator = 'none' | 'equ' | 'gte' | 'lte'; type PptxSmartArtConstraintPointType = 'all' | 'doc' | 'node' | 'norm' | 'nonNorm' | 'asst' | 'nonAsst' | 'parTrans' | 'pres' | 'sibTrans'; interface PptxSmartArtConstraintTarget { for?: PptxSmartArtConstraintRelationship; forName?: string; pointType?: PptxSmartArtConstraintPointType; } /** Editable DiagramML CT_Constraint. */ interface PptxSmartArtConstraint extends PptxSmartArtConstraintTarget { type: string; referenceType?: string; referenceFor?: PptxSmartArtConstraintRelationship; referenceForName?: string; referencePointType?: PptxSmartArtConstraintPointType; operator?: PptxSmartArtConstraintOperator; value?: number; factor?: number; /** Original constraint retained for foreign attributes and extension content. */ rawXml?: XmlObject; } /** Editable DiagramML CT_NumericRule. */ interface PptxSmartArtNumericRule extends PptxSmartArtConstraintTarget { type: string; value?: number; factor?: number; max?: number; /** Original rule retained for foreign attributes and extension content. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/smart-art-layout-definition.d.ts interface PptxSmartArtLocalizedText { value: string; language?: string; } interface PptxSmartArtLayoutCategory { type: string; priority: number; } interface PptxSmartArtAlgorithmParameter { type: string; value?: string; } /** Typed DiagramML CT_Algorithm data attached to a layout node. */ interface PptxSmartArtLayoutAlgorithm { type: string; revision?: number; parameters?: PptxSmartArtAlgorithmParameter[]; } interface PptxSmartArtIteratorAttributes { name?: string; reference?: string; axis?: string[]; pointTypes?: string[]; hideLastTransition?: boolean[]; start?: number[]; count?: number[]; step?: number[]; } interface PptxSmartArtForEach extends PptxSmartArtIteratorAttributes { rawXml?: XmlObject; } interface PptxSmartArtWhen extends PptxSmartArtIteratorAttributes { function: string; argument?: string; operator: string; value: string; rawXml?: XmlObject; } interface PptxSmartArtChoose { name?: string; when: PptxSmartArtWhen[]; otherwise?: { name?: string; rawXml?: XmlObject; } | null; rawXml?: XmlObject; } /** A single `dgm:adj/@val` adjustment, keyed by its `@idx` (1-based, like `a:gd`). */ interface PptxSmartArtShapeAdjustment { index: number; value: number; } /** * Typed DiagramML CT_Shape data (`dgm:shape`) attached to a layout node: the * per-node preset geometry override real (and third-party/custom) layout * definitions use so a layoutNode can be e.g. an ellipse or a chevron instead * of the arranger family's hardcoded default shape. */ interface PptxSmartArtLayoutNodeShape { /** `dgm:shape/@type`: a preset geometry name (`roundRect`, `ellipse`, `chevron`, `conn`, ...). */ presetGeometry?: string; /** `dgm:adjLst/dgm:adj` entries (adjustment index -> value, as authored). */ adjustments?: PptxSmartArtShapeAdjustment[]; /** `dgm:shape/@hideGeom`: the shape is present only to size text, never painted. */ hideGeometry?: boolean; } /** Identity and ordering metadata from DiagramML CT_LayoutNode. */ interface PptxSmartArtLayoutNode { name?: string; styleLabel?: string; childOrder?: 'b' | 't'; moveWith?: string; algorithm?: PptxSmartArtLayoutAlgorithm; forEach?: PptxSmartArtForEach[]; choose?: PptxSmartArtChoose[]; constraints?: PptxSmartArtConstraint[]; rules?: PptxSmartArtNumericRule[]; /** `dgm:shape`: this node's own preset geometry override, when present. */ shape?: PptxSmartArtLayoutNodeShape; children?: PptxSmartArtLayoutNode[]; } /** Metadata and root node from DiagramML CT_DiagramDefinition. */ interface PptxSmartArtLayoutDefinition { uniqueId?: string; minimumVersion?: string; defaultStyle?: string; titles?: PptxSmartArtLocalizedText[]; descriptions?: PptxSmartArtLocalizedText[]; categories?: PptxSmartArtLayoutCategory[]; rootNode: PptxSmartArtLayoutNode; /** Original definition retained for constraint evaluation and foreign rules. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/smart-art-node.d.ts /** * A single run of text inside a SmartArt node, capturing the run text and the * raw `a:rPr` run-properties object verbatim so per-run formatting (bold, * colour, size, etc.) survives a load -> edit -> save round-trip instead of * collapsing to a single unstyled run. * * @example * ```ts * const run: PptxSmartArtTextRun = { * text: "Bold", * rPr: { "@_b": "1", "@_lang": "en-US" }, * }; * // => satisfies PptxSmartArtTextRun * ``` */ interface PptxSmartArtTextRun { /** Run text content. */ text: string; /** * Raw parsed `a:rPr` run-properties object, preserved verbatim for * round-trip. Untyped XML, hence the loose record shape. */ rPr?: Record; /** Resolved standard shape-text style derived from {@link rPr}. */ style?: TextStyle; /** Raw run XML used to retain unmodelled extension children on save. */ rawXml?: Record; /** Original direct-child order, including unmodelled extension children. */ childOrder?: string[]; } /** An ordered item within a SmartArt text paragraph. */ type PptxSmartArtTextParagraphItem = { kind: 'run'; run: PptxSmartArtTextRun; } | { kind: 'break'; rPr?: Record; style?: TextStyle; rawXml?: Record; childOrder?: string[]; } | { kind: 'field'; id?: string; fieldType?: string; text: string; rPr?: Record; style?: TextStyle; pPr?: Record; rawXml?: Record; childOrder?: string[]; } | { kind: 'tab'; rawXml?: Record; childOrder?: string[]; } | { kind: 'raw'; name: string; value: unknown; }; /** A complete `a:p` paragraph in a SmartArt data-model text body. */ interface PptxSmartArtTextParagraph { /** Paragraph properties (`a:pPr`) preserved verbatim. */ pPr?: Record; /** Text children in source order. */ items: PptxSmartArtTextParagraphItem[]; /** End-paragraph run properties (`a:endParaRPr`) preserved verbatim. */ endParaRPr?: Record; /** Resolved style for the paragraph terminator. */ endParaStyle?: TextStyle; /** Raw paragraph XML used to retain unmodelled extension children on save. */ rawXml?: Record; } /** * Per-node visual override for a SmartArt node. * * Captures the individual fill / line / font colour and the bold / italic * emphasis a user has set on one specific node, independent of the diagram's * colour scheme and quick style. All colours are hex strings (e.g. "#FF0000"). * Every field is optional: only the overridden aspects are carried, so an * empty object means "no per-node override". * * The parser reads these from the data point's `spPr` solid fill / line colour * and the first run's `rPr` (b / i / solidFill) when present, and the save path * writes them back so the override survives a load -> edit -> save round-trip. * * @example * ```ts * const style: PptxSmartArtNodeStyle = { * fillColor: "#FF0000", * fontColor: "#FFFFFF", * bold: true, * }; * // => satisfies PptxSmartArtNodeStyle * ``` */ interface PptxSmartArtNodeStyle { /** Solid fill colour override (hex, e.g. "#4F81BD"). */ fillColor?: string; /** Outline / line colour override (hex). */ lineColor?: string; /** Text (font) colour override (hex). */ fontColor?: string; /** Bold emphasis override for the node's runs. */ bold?: boolean; /** Italic emphasis override for the node's runs. */ italic?: boolean; } /** * Manual layout override for a `type="pres"` presentation point, read from its * `dgm:prSet` attributes. PowerPoint writes these when the user drags, resizes, * rotates, or flips a SmartArt node by hand in its own diagram editor; without * them the node silently reverts to its algorithmic position whenever there is * no cached `dsp:` drawing part to fall back on. * * Every field is optional: only the attributes actually present on `prSet` are * populated. Angle and scale/factor units are already normalised to degrees and * plain ratios (a `custScaleX="150000"` becomes `scaleX: 1.5`), so a consumer * never has to know the raw `60000ths-of-a-degree` / `100000ths-of-a-percent` * XML encodings. * * @example * ```ts * const custom: SmartArtNodeCustomLayout = { angle: 15, scaleX: 1.2 }; * // => a node manually rotated 15 degrees and widened 20% in PowerPoint * ``` */ interface SmartArtNodeCustomLayout { /** `custAng`: additional rotation in degrees. */ angle?: number; /** `custScaleX`: horizontal scale ratio (1 = no change). */ scaleX?: number; /** `custScaleY`: vertical scale ratio (1 = no change). */ scaleY?: number; /** `custSzX`: horizontal size ratio, layered on top of {@link scaleX}. */ sizeX?: number; /** `custSzY`: vertical size ratio, layered on top of {@link scaleY}. */ sizeY?: number; /** `custFlipHor`: the node was manually mirrored horizontally. */ flipHorizontal?: boolean; /** `custFlipVert`: the node was manually mirrored vertically. */ flipVertical?: boolean; /** `custLinFactX`: manual position nudge along X, as a fraction of the container width. */ linearFactorX?: number; /** `custLinFactY`: manual position nudge along Y, as a fraction of the container height. */ linearFactorY?: number; /** * `custLinFactNeighborX`: spacing compensation applied to a NEIGHBOURING * node when this one is resized. Parsed for round-trip completeness; not * applied by the per-node final transform (it has no effect on this node's * own geometry; folding it into a neighbour's geometry would require * whole-layout awareness the final transform pass does not have). */ linearFactorNeighborX?: number; /** `custLinFactNeighborY`: see {@link linearFactorNeighborX} (Y axis). */ linearFactorNeighborY?: number; /** `custRadScaleRad`: manual radius scale ratio for a radial/cycle node. */ radialScaleRadius?: number; /** `custRadScaleInc`: manual angular-position nudge for a radial/cycle node. */ radialScaleIncrement?: number; /** `custT`: whether `prSet` declares a custom transform is present at all. */ hasCustomTransform?: boolean; } /** * A single node in the SmartArt data model. * * @example * ```ts * const node: PptxSmartArtNode = { * id: "1", * text: "CEO", * children: [ * { id: "2", text: "VP Marketing", parentId: "1" }, * { id: "3", text: "VP Engineering", parentId: "1" }, * ], * }; * // => satisfies PptxSmartArtNode * ``` */ interface PptxSmartArtNode { id: string; text: string; /** CT_Pt connection identifier, when the point references a connection. */ connectionId?: string | null; parentId?: string; children?: PptxSmartArtNode[]; /** Node type from `@_type` attribute (e.g. "doc", "node", "asst", "pres"). */ nodeType?: string; /** * The node's own quick-style role (`dgm:prSet/@presStyleLbl` from its * paired `type="pres"` presentation point, resolved via a `presOf` * connection back to this content point). Structural names like `node1`, * `asst2`, `bgShp`, `revTx`; distinct from {@link nodeType}, which is the * data-model `@_type` ("node"/"asst"/...). Used to pick this node's own * colour list from a colour transform's per-role palettes instead of the * generic cycled palette (see `applySmartArtRoleColors`). */ styleRole?: string; /** * Per-run text + run-properties for the node's first paragraph, captured at * parse time. When the joined run text still equals {@link text} (the node * was not edited, or was edited only in ways that preserve the run split), * the save path rebuilds the paragraph from these runs so per-run rich text * is not flattened. When {@link text} diverges, the runs are ignored. */ runs?: PptxSmartArtTextRun[]; /** * Complete typed paragraph model. Unlike {@link runs}, this retains every * paragraph and the ordered run, field, break, and tab children within it. */ paragraphs?: PptxSmartArtTextParagraph[]; /** * Optional per-node visual override (fill / line / font colour, bold / * italic). Read at parse time from the point's `spPr` / first-run `rPr`, set * by the editing op, honoured by the render path, and written back on save so * it round-trips. */ style?: PptxSmartArtNodeStyle; /** * Manual layout override read from the node's `dgm:prSet` `cust*` * attributes (drag/resize/rotate/flip performed in PowerPoint's own diagram * editor). Applied as a final transform after algorithmic layout by * {@link module:smartart-layout-interpreter-custom} so it survives even * when there is no cached `dsp:` drawing to fall back on. */ customLayout?: SmartArtNodeCustomLayout; } //#endregion //#region src/core/types/smart-art-style-definition.d.ts /** Editable metadata shared by DiagramML quick-style and color definitions. */ interface PptxSmartArtDefinitionText { value: string; language?: string; } interface PptxSmartArtDefinitionCategory { type: string; priority: number; } type PptxSmartArtColorApplicationMethod = 'span' | 'cycle' | 'repeat'; type PptxSmartArtHueDirection = 'cw' | 'ccw'; /** CT_Colors application metadata. Color-choice children remain preserved XML. */ interface PptxSmartArtColorListMetadata { method?: PptxSmartArtColorApplicationMethod; hueDirection?: PptxSmartArtHueDirection; } /** CT_StyleLabel metadata from a quick-style definition. */ interface PptxSmartArtQuickStyleLabel { name: string; } /** CT_CTStyleLabel metadata from a color-transform definition. */ interface PptxSmartArtColorStyleLabel { name: string; fill?: PptxSmartArtColorListMetadata; line?: PptxSmartArtColorListMetadata; effect?: PptxSmartArtColorListMetadata; textLine?: PptxSmartArtColorListMetadata; textFill?: PptxSmartArtColorListMetadata; textEffect?: PptxSmartArtColorListMetadata; } interface PptxSmartArtDefinitionMetadata { uniqueId?: string; minimumVersion?: string; titles?: PptxSmartArtDefinitionText[]; descriptions?: PptxSmartArtDefinitionText[]; categories?: PptxSmartArtDefinitionCategory[]; } /** Typed CT_ColorTransform metadata and the resolved legacy color palette. */ interface PptxSmartArtColorTransform extends PptxSmartArtDefinitionMetadata { /** Legacy resolved display name. */ name?: string; /** Ordered resolved fill colors for rendering. */ fillColors: string[]; /** Ordered resolved line colors for rendering. */ lineColors: string[]; /** Ordered resolved text-fill colors (primary styleLbl `txFillClrLst`). */ textFillColors?: string[]; /** Ordered resolved text-line colors (primary styleLbl `txLinClrLst`). */ textLineColors?: string[]; /** Ordered resolved effect colors (primary styleLbl `effectClrLst`). */ effectColors?: string[]; /** Ordered resolved text-effect colors (primary styleLbl `txEffectClrLst`). */ textEffectColors?: string[]; /** Fill-list span/cycle + hue-direction interpolation of the primary styleLbl. */ fillInterpolation?: PptxSmartArtColorListMetadata; /** Line-list span/cycle + hue-direction interpolation of the primary styleLbl. */ lineInterpolation?: PptxSmartArtColorListMetadata; /** Ordered CT_CTStyleLabel metadata. */ labels?: PptxSmartArtColorStyleLabel[]; /** * Every `styleLbl`'s own resolved fill/line colour list, keyed by name * (e.g. `node1`, `asst0`, `bgShp`, `revTx`). Unlike {@link fillColors} / * {@link lineColors} (which collapse to ONE "primary" node-role list), * this keeps every role so a node can be coloured from its OWN role's * palette (see `PptxSmartArtNode.styleRole` and `applySmartArtRoleColors`) * instead of a generic cycled colour. */ roleColors?: Record; } /** Typed CT_StyleDefinition metadata and legacy rendering hint. */ interface PptxSmartArtQuickStyle extends PptxSmartArtDefinitionMetadata { /** Legacy resolved display name. */ name?: string; /** Legacy effect-intensity rendering hint. */ effectIntensity?: string; /** Ordered CT_StyleLabel metadata. Complex style payload remains preserved XML. */ labels?: PptxSmartArtQuickStyleLabel[]; } //#endregion //#region src/core/types/smart-art.d.ts /** * Resolved SmartArt layout category. * * @example * ```ts * const cat: SmartArtLayoutType = "hierarchy"; * // => "hierarchy" — one of: "list" | "process" | "cycle" | "hierarchy" | "relationship" | … * ``` */ type SmartArtLayoutType = 'list' | 'process' | 'cycle' | 'hierarchy' | 'relationship' | 'matrix' | 'pyramid' | 'funnel' | 'gear' | 'target' | 'timeline' | 'venn' | 'chevron' | 'bending' | 'unknown'; /** * Named SmartArt layout presets for creation (subset of PowerPoint layouts). * * @example * ```ts * const layout: SmartArtLayout = "hierarchy"; * // => "hierarchy" — one of: "basicBlockList" | "alternatingHexagons" | "hierarchy" | … * ``` */ type SmartArtLayout = 'basicBlockList' | 'alternatingHexagons' | 'basicChevronProcess' | 'basicCycle' | 'basicPie' | 'basicRadial' | 'basicVenn' | 'continuousBlockProcess' | 'convergingRadial' | 'hierarchy' | 'horizontalBulletList' | 'linearVenn' | 'segmentedProcess' | 'stackedList' | 'tableList' | 'trapezoidList' | 'upwardArrow' | 'basicFunnel' | 'basicTarget' | 'interlockingGears' | 'basicTimeline' | 'basicMatrix' | 'basicPyramid' | 'invertedPyramid' | 'bendingProcess' | 'stepDownProcess' | 'alternatingFlow' | 'descendingProcess' | 'pictureAccentList' | 'verticalBlockList' | 'groupedList' | 'pyramidList' | 'horizontalPictureList' | 'accentProcess' | 'verticalChevronList'; /** * SmartArt colour scheme presets. * * @example * ```ts * const scheme: SmartArtColorScheme = "colorful1"; * // => "colorful1" — one of: "colorful1" | "colorful2" | "colorful3" | "monochromatic1" | "monochromatic2" * ``` */ type SmartArtColorScheme = 'colorful1' | 'colorful2' | 'colorful3' | 'monochromatic1' | 'monochromatic2'; /** * SmartArt visual style intensity. * * @example * ```ts * const style: SmartArtStyle = "moderate"; * // => "moderate" — one of: "flat" | "moderate" | "intense" * ``` */ type SmartArtStyle = 'flat' | 'moderate' | 'intense'; /** * A connection between two SmartArt data-model nodes. * * @example * ```ts * const conn: PptxSmartArtConnection = { * sourceId: "1", * destId: "2", * type: "parOf", * }; * // => satisfies PptxSmartArtConnection * ``` */ interface PptxSmartArtConnection { /** Stable CT_Cxn model identifier. Required when serialized. */ modelId?: string | null; /** Model ID of the source node. */ sourceId: string; /** Model ID of the destination node. */ destId: string; /** Connection type (e.g. "parOf", "presOf", "sibTrans"). */ type?: string; /** Source index for ordering sibling connections. */ srcOrd?: number; /** Destination index for ordering. */ destOrd?: number; /** Model ID of the parent transition point associated with this edge. */ parentTransitionId?: string | null; /** Model ID of the sibling transition point associated with this edge. */ siblingTransitionId?: string | null; /** Layout presentation identifier used by presentation connections. */ presentationId?: string | null; /** * Connector text, read from the linked `parTrans`/`sibTrans` transition * point's `dgm:t` (via {@link parentTransitionId} / {@link siblingTransitionId}). * PowerPoint's own diagram editor lets a user type text directly onto an * org-chart relationship connector; `undefined` when the transition point * carries no text. Written back to that point on save. */ label?: string; } /** * A pre-computed shape from `ppt/diagrams/drawing*.xml`. * * @example * ```ts * const shape: PptxSmartArtDrawingShape = { * id: "s1", * shapeType: "roundRect", * x: 100, y: 50, width: 200, height: 80, * fillColor: "#4F81BD", * text: "CEO", * }; * // => satisfies PptxSmartArtDrawingShape * ``` */ interface PptxSmartArtDrawingShape extends PptxCustomPathProperties { /** Shape ID within the drawing. */ id: string; /** Preset geometry type (e.g. "roundRect", "ellipse"). */ shapeType?: string; /** Position and size in EMU-based pixels. */ x: number; y: number; width: number; height: number; /** Rotation in degrees. */ rotation?: number; /** Skew along the X axis in degrees. */ skewX?: number; /** Skew along the Y axis in degrees. */ skewY?: number; /** * The cached shape declares `a:noFill`. Renderers must leave it unpainted * rather than substituting a palette colour, because these shapes usually sit * on top of a painted shape whose fill has to stay visible. */ fillNone?: boolean; /** Solid fill colour (hex). */ fillColor?: string; /** * Gradient fill stops when the cached shape uses `a:gradFill`. Positions are * 0..100 (percent). Renderers emit an SVG/CSS gradient instead of a flat box. */ fillGradientStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient geometry type (`linear` for `a:lin`, `radial` for `a:path`). */ fillGradientType?: 'linear' | 'radial'; /** Linear gradient angle in degrees (0..360). */ fillGradientAngle?: number; /** Pattern fill preset name from `a:pattFill/@prst` (e.g. "pct50", "cross"). */ fillPatternPreset?: string; /** Pattern fill foreground colour (hex) from `a:pattFill/a:fgClr`. */ fillPatternForegroundColor?: string; /** Pattern fill background colour (hex) from `a:pattFill/a:bgClr`. */ fillPatternBackgroundColor?: string; /** * Relationship id of a picture (blip) fill's embedded image, from * `a:blipFill/a:blip/@r:embed`. The image bytes are resolved separately; see * {@link fillImageUrl}. */ fillBlipEmbedId?: string; /** * Resolved data-URI/URL for a picture (blip) fill, when the embedded image * part could be resolved. Absent when only {@link fillBlipEmbedId} is known. */ fillImageUrl?: string; /** Whether the cached shape carries an outer-shadow effect (`a:effectLst`). */ hasShadow?: boolean; /** Resolved outer-shadow colour (hex), when present. */ shadowColor?: string; /** Stroke colour (hex). */ strokeColor?: string; /** Stroke width in points. */ strokeWidth?: number; /** Text content of the shape. */ text?: string; /** Standard rich-text segments projected from the associated SmartArt node. */ textSegments?: TextSegment[]; /** Font size in points. */ fontSize?: number; /** Font colour (hex). */ fontColor?: string; } /** * Background / outline extracted from `dgm:bg` and `dgm:whole`. * * @example * ```ts * const chrome: PptxSmartArtChrome = { * backgroundColor: "#F0F0F0", * outlineColor: "#333333", * outlineWidth: 1, * }; * // => satisfies PptxSmartArtChrome * ``` */ interface PptxSmartArtChrome { /** Background fill colour (hex). */ backgroundColor?: string; /** Outline stroke colour (hex). */ outlineColor?: string; /** Outline stroke width in points. */ outlineWidth?: number; } /** * Presentation layout variables from `dgm:prSet/dgm:presLayoutVars` (data model) * or `dgm:varLst` (layout definition defaults). * * These drive how the DiagramML layout interpreter arranges points: flow * direction, hierarchy branch style, org-chart mode, and child count limits. * The fallback layout engine can consult them for direction/org-chart hints. * * @example * ```ts * const vars: PptxSmartArtPresLayoutVars = { direction: "rev", orgChart: true }; * // => satisfies PptxSmartArtPresLayoutVars * ``` */ interface PptxSmartArtPresLayoutVars { /** Flow direction (`dgm:dir`): "norm" (default) or "rev" (reversed/RTL). */ direction?: 'norm' | 'rev'; /** Hierarchy branch style (`dgm:hierBranch`): std/init/l/r/hang. */ hierarchyBranch?: 'std' | 'init' | 'l' | 'r' | 'hang'; /** Org-chart mode enabled (`dgm:orgChart`). */ orgChart?: boolean; /** Maximum children per node (`dgm:chMax`, -1 = unbounded). */ childMax?: number; /** Preferred children per node (`dgm:chPref`, -1 = unbounded). */ childPreferred?: number; /** Whether bullets are enabled (`dgm:bulletEnabled`). */ bulletEnabled?: boolean; /** Animation-by-level setting (`dgm:animLvl`). */ animationLevel?: string; /** Animate-one setting (`dgm:animOne`). */ animateOne?: string; /** Allowed resize handles (`dgm:resizeHandles`). */ resizeHandles?: string; } /** * Complete parsed SmartArt data for a {@link SmartArtPptxElement}. * * @example * ```ts * const data: PptxSmartArtData = { * resolvedLayoutType: "hierarchy", * layout: "hierarchy", * colorScheme: "colorful1", * style: "moderate", * nodes: [ * { id: "1", text: "CEO", children: [ * { id: "2", text: "VP Marketing", parentId: "1" }, * ]}, * ], * }; * // => satisfies PptxSmartArtData * ``` */ interface PptxSmartArtData { layoutType?: string; resolvedLayoutType?: SmartArtLayoutType; /** Named layout preset (used when creating new SmartArt). */ layout?: SmartArtLayout; /** Colour scheme for the SmartArt graphic. */ colorScheme?: SmartArtColorScheme; /** Visual style intensity. */ style?: SmartArtStyle; nodes: PptxSmartArtNode[]; /** Connections between data-model nodes. */ connections?: PptxSmartArtConnection[]; /** Pre-computed shapes from `ppt/diagrams/drawing*.xml`. */ drawingShapes?: PptxSmartArtDrawingShape[]; /** Background and outline chrome from `dgm:bg` / `dgm:whole`. */ chrome?: PptxSmartArtChrome; /** Colour transform from `ppt/diagrams/colors*.xml`. */ colorTransform?: PptxSmartArtColorTransform; /** Quick style from `ppt/diagrams/quickStyles*.xml`. */ quickStyle?: PptxSmartArtQuickStyle; /** Editable metadata from the related DiagramML layout definition. */ layoutDefinition?: PptxSmartArtLayoutDefinition; /** * Presentation layout variables (direction, hierarchy branch, org-chart, * child limits, bullets) from `dgm:presLayoutVars` / layout `dgm:varLst`. * Consulted by the fallback layout engine for direction/org-chart hints. */ presLayoutVars?: PptxSmartArtPresLayoutVars; /** Relationship ID for the diagram data part (for round-trip save). */ dataRelId?: string; /** Relationship ID for the diagram layout part. */ layoutRelId?: string; /** Relationship ID for the drawing part. */ drawingRelId?: string; /** Relationship ID for the colours part. */ colorsRelId?: string; /** Relationship ID for the quick-styles part. */ styleRelId?: string; /** Internal save hint: the layout definition changed in the editor. */ layoutDirty?: boolean; /** Internal save hint: typed layout-definition metadata changed. */ layoutDefinitionDirty?: boolean; /** Internal save hint: quick-style definition metadata changed. */ quickStyleDirty?: boolean; /** Internal save hint: color-transform definition metadata changed. */ colorTransformDirty?: boolean; /** Internal save hint: cached drawing geometry or text changed in the editor. */ drawingDirty?: boolean; } //#endregion //#region src/core/types/table.d.ts /** * Table types: cell styling, cell data, rows, table data, and the parsed * table style map from `ppt/tableStyles.xml`. * * @module pptx-types/table */ /** * Per-cell visual style for a table cell. * * All fields are optional - unset values inherit from the table style. * * @example * ```ts * const header: PptxTableCellStyle = { * bold: true, * fontSize: 14, * color: "#FFFFFF", * backgroundColor: "#0055AA", * align: "center", * }; * // => satisfies PptxTableCellStyle * ``` */ interface PptxTableCellStyle { fontSize?: number; bold?: boolean; italic?: boolean; underline?: boolean; color?: string; /** * Font family from the first run's `a:rPr/a:latin@typeface` (falling back to * `a:ea` / `a:cs`). Per-run families live on {@link PptxTableCellTextRun}. */ fontFamily?: string; /** * Raw XML colour-choice node preserved from `a:tc/a:txBody/.../a:rPr/a:solidFill` * for round-trip serialisation. Currently unused by the cell-level writer * (cell text colour falls through `writeCellTextFormatting`), reserved for * future expansion alongside the run-properties round-trip path. */ colorXml?: XmlObject; backgroundColor?: string; /** * Raw XML colour-choice node preserved from cell `a:tcPr/a:solidFill` for * round-trip serialisation. Re-emitted verbatim when the resolved * {@link backgroundColor} still matches the original colour. */ backgroundColorXml?: XmlObject; borderColor?: string; /** Top border width in px. */ borderTopWidth?: number; /** Bottom border width in px. */ borderBottomWidth?: number; /** Left border width in px. */ borderLeftWidth?: number; /** Right border width in px. */ borderRightWidth?: number; /** Top border color as hex. */ borderTopColor?: string; /** Bottom border color as hex. */ borderBottomColor?: string; /** Left border color as hex. */ borderLeftColor?: string; /** Right border color as hex. */ borderRightColor?: string; align?: 'left' | 'center' | 'right' | 'justify'; vAlign?: 'top' | 'middle' | 'bottom'; /** Text direction from `a:tcPr/@vert` (spec values from CT_TextVerticalType). */ textDirection?: 'vert' | 'vert270' | 'eaVert' | 'wordArtVert' | 'wordArtVertRtl' | 'mongolianVert'; /** Cell left margin in px (from a:tcPr > a:tcMar > a:marL). */ marginLeft?: number; /** Cell right margin in px. */ marginRight?: number; /** Cell top margin in px. */ marginTop?: number; /** Cell bottom margin in px. */ marginBottom?: number; /** Diagonal border top-left to bottom-right color. */ borderDiagDownColor?: string; /** Diagonal border top-left to bottom-right width in px. */ borderDiagDownWidth?: number; /** Diagonal border bottom-left to top-right color. */ borderDiagUpColor?: string; /** Diagonal border bottom-left to top-right width in px. */ borderDiagUpWidth?: number; /** Table cell border dash style (legacy single value). */ borderDash?: string; /** Per-edge border dash styles. */ borderTopDash?: string; borderBottomDash?: string; borderLeftDash?: string; borderRightDash?: string; /** Cell text shadow colour. */ textShadowColor?: string; /** Cell text shadow blur radius in px. */ textShadowBlur?: number; /** Cell text shadow horizontal offset in px. */ textShadowOffsetX?: number; /** Cell text shadow vertical offset in px. */ textShadowOffsetY?: number; /** Cell text shadow opacity (0-1). */ textShadowOpacity?: number; /** Cell text glow colour. */ textGlowColor?: string; /** Cell text glow radius in px. */ textGlowRadius?: number; /** Cell text glow opacity (0-1). */ textGlowOpacity?: number; /** Cell fill mode: solid, gradient, pattern, image, or none. */ fillMode?: 'solid' | 'gradient' | 'pattern' | 'image' | 'none'; /** Gradient fill stops (colours with positions). */ gradientFillStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient angle in degrees. */ gradientFillAngle?: number; /** Gradient type: linear or radial. */ gradientFillType?: 'linear' | 'radial'; /** Path gradient sub-type. */ gradientFillPathType?: 'circle' | 'rect' | 'shape'; /** Focal point for radial gradients (0–1 fractions). */ gradientFillFocalPoint?: { x: number; y: number; }; /** Raw fillToRect LTRB values (0–1 fractions) for gradient sizing. */ gradientFillFillToRect?: { l: number; t: number; r: number; b: number; }; /** Pre-computed CSS gradient string for rendering. */ gradientFillCss?: string; /** Pattern fill preset name (e.g. "ltDnDiag"). */ patternFillPreset?: string; /** Pattern fill foreground colour. */ patternFillForeground?: string; /** Pattern fill background colour. */ patternFillBackground?: string; /** * Image fill (`a:tcPr/a:blipFill`, CT_TableCellProperties). Resolved * archive-relative path (or external `http(s):`/`data:` URL) for the * cell's background image, from `a:blipFill/a:blip/@r:embed` (or * `@r:link`). Present when `fillMode` is `'image'`. * * The parser resolves this synchronously (path only, no binary read); * a viewer's load pipeline resolves it further to a displayable * `data:`/`blob:` URL, written back to {@link backgroundImageFillData}. */ backgroundImageFillPath?: string; /** * Displayable image data (`data:` or `blob:` URL) for an image cell * fill, once resolved by the load pipeline. Renderers should prefer * this over {@link backgroundImageFillPath} when both are present. */ backgroundImageFillData?: string; /** * Cell 3D bevel + lighting from `a:tcPr/a:cell3D` (CT_Cell3D, * ECMA-376 §21.1.3.1). Rendered as a CSS bevel treatment. */ cell3D?: PptxTableCell3D; /** * `a:tcPr/@anchorCtr` - centre the text block in the direction * perpendicular to the text flow (horizontal centring for horizontal text). */ anchorCtr?: boolean; /** * `a:tcPr/@horzOverflow` (ST_TextHorzOverflowType): `clip` clips text at * the cell edge, `overflow` (the default) lets it spill. */ horzOverflow?: 'clip' | 'overflow'; } /** * Cell 3D bevel + lighting parsed from `a:tcPr/a:cell3D` (CT_Cell3D). * * Only the fields needed to render a plausible bevel treatment are captured; * verbatim round-trip of the full node is handled separately by the save path. * * @example * ```ts * const c3d: PptxTableCell3D = { * bevelWidth: 8, * bevelHeight: 8, * bevelPreset: 'circle', * material: 'plastic', * }; * // => satisfies PptxTableCell3D * ``` */ interface PptxTableCell3D { /** Bevel width in px (from `a:bevel@w`, EMU converted). */ bevelWidth?: number; /** Bevel height in px (from `a:bevel@h`, EMU converted). */ bevelHeight?: number; /** Bevel preset name (`a:bevel@prst`, e.g. `circle`, `relaxedInset`). */ bevelPreset?: string; /** Preset material (`a:cell3D@prstMaterial`, e.g. `plastic`, `metal`). */ material?: string; /** Light rig type (`a:lightRig@rig`, e.g. `threePt`, `soft`). */ lightRig?: string; /** Light rig direction (`a:lightRig@dir`, e.g. `tl`, `t`, `tr`). */ lightRigDirection?: string; } /** * One styled text run inside a table cell's `a:txBody`. * * `PptxTableCell.text` is a flat string and `PptxTableCell.style` describes * only the FIRST run, so a cell mixing formats ("Revenue **grew 42%** last * year") cannot be represented by those two alone. {@link PptxTableCell.runs} * carries the full sequence, with paragraph and line breaks as marker entries * so a renderer can walk it linearly. * * Structurally identical to `pptx-viewer-shared`'s `CellTextRun`, which every * binding's table renderer already consumes. * * @example * ```ts * const runs: PptxTableCellTextRun[] = [ * { text: "Revenue " }, * { text: "grew 42%", bold: true, color: "#C00000" }, * ]; * // => satisfies PptxTableCellTextRun[] * ``` */ interface PptxTableCellTextRun { /** Run text. Empty for the break markers below. */ text: string; /** This entry starts a new paragraph (`a:p` boundary) rather than carrying text. */ isParagraphBreak?: boolean; /** This entry is a soft line break (`a:br`) rather than carrying text. */ isLineBreak?: boolean; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; /** Resolved run colour as a CSS colour string. */ color?: string; /** Run font size in points (`a:rPr@sz` / 100). */ fontSize?: number; /** Run font family from `a:rPr/a:latin@typeface` (or `a:ea` / `a:cs`). */ fontFamily?: string; } /** * A single table cell with text content, optional style, and merge info. * * @example * ```ts * const cell: PptxTableCell = { * text: "$1.5M", * style: { bold: true, align: "right" }, * gridSpan: 1, * }; * // => satisfies PptxTableCell * ``` */ interface PptxTableCell { text: string; style?: PptxTableCellStyle; /** * Per-run formatting for the cell's text, when it has any beyond what * {@link style} can express. Present only for cells whose `a:txBody` * actually carries runs; renderers fall back to {@link text} when absent. * * Editing a cell's text invalidates these (the editor produces a plain * string), so an edit path must clear them alongside setting `text`. */ textRuns?: PptxTableCellTextRun[]; /** Column span (defaults to 1). */ gridSpan?: number; /** Row span (defaults to 1). */ rowSpan?: number; /** Whether this cell is merged vertically with the cell above. */ vMerge?: boolean; /** Whether this cell is horizontally merged with the cell to the left (gridSpan continuation). */ hMerge?: boolean; /** * Opaque round-trip storage for `a:tcPr` attributes that don't yet have * typed equivalents on {@link PptxTableCellStyle} (e.g. `horzOverflow`, * `anchorCtr`, `headers`, `hideSlicers`, `slicerCacheId`). Keys are the * raw XML attribute names without the `@_` prefix used by * fast-xml-parser. Re-emitted verbatim by the save writer when present. */ extraAttributes?: Record; } /** * A single table row with an optional height and an array of cells. * * @example * ```ts * const row: PptxTableRow = { * height: 40, * cells: [ * { text: "Name" }, * { text: "Score" }, * ], * }; * // => satisfies PptxTableRow * ``` */ interface PptxTableRow { /** Row height in px. */ height?: number; cells: PptxTableCell[]; } /** * Complete parsed table data for a {@link TablePptxElement}. * * Includes row/cell data, column widths, banding flags, and the applied * table style ID. * * @example * ```ts * const data: PptxTableData = { * rows: [ * { cells: [{ text: "Product" }, { text: "Revenue" }] }, * { cells: [{ text: "Widget A" }, { text: "$3.4M" }] }, * ], * columnWidths: [0.6, 0.4], * firstRowHeader: true, * bandedRows: true, * }; * // => satisfies PptxTableData * ``` */ interface PptxTableData { rows: PptxTableRow[]; /** Column widths as proportion of total (summing to 1). */ columnWidths: number[]; /** Whether the table has banded rows. */ bandedRows?: boolean; /** Whether the first row is a header. */ firstRowHeader?: boolean; /** Whether banded columns are enabled. */ bandedColumns?: boolean; /** Whether the last row is styled as a total row. */ lastRow?: boolean; /** Whether the first column is styled as a header column. */ firstCol?: boolean; /** Whether the last column is styled specially. */ lastCol?: boolean; /** Table style ID from `a:tblPr/a:tblStyle@val` or `a:tblPr@tblStyle`. */ tableStyleId?: string; /** Number of rows per banding group (default 1). */ bandRowCycle?: number; /** Number of columns per banding group (default 1). */ bandColCycle?: number; /** Right-to-left table layout from `a:tblPr/@rtl`. */ rtl?: boolean; } /** * A single fill reference within a table style section. * * @example * ```ts * const fill: ParsedTableStyleFill = { * schemeColor: "accent1", * tint: 40000, // 40% tint * }; * // => satisfies ParsedTableStyleFill * ``` */ interface ParsedTableStyleFill { /** * Theme colour key (e.g. `accent1`, `dk1`). Empty string when the fill is a * non-scheme fill (explicit sRGB, gradient, pattern, or none) that carries * no theme colour reference; the renderer then resolves {@link color}, * {@link gradient}, {@link pattern}, or {@link noFill} instead. */ schemeColor: string; /** Tint value (0-100 000). */ tint?: number; /** Shade value (0-100 000). */ shade?: number; /** Explicit sRGB hex colour (e.g. `#FF8800`) from `a:srgbClr`. */ color?: string; /** The fill was `a:noFill`: renders transparent and clears lower layers. */ noFill?: boolean; /** Gradient fill parsed from `a:gradFill`. */ gradient?: ParsedTableStyleGradient; /** Preset pattern fill parsed from `a:pattFill`. */ pattern?: ParsedTableStylePattern; /** Image texture fill parsed from `a:blipFill`. */ image?: ParsedTableStyleImage; } /** * An image texture fill parsed from a table style section's `a:blipFill`. * * `ppt/tableStyles.xml` is a presentation-level part parsed once (not * per-slide), so this mirrors the per-CELL `a:tcPr/a:blipFill` two-field lazy * pattern (`PptxTableCellStyle.backgroundImageFillPath` / * `backgroundImageFillData`): `path` starts out as a raw archive-relative * path (or an already-external `http(s):`/`data:` URL), and a load pipeline * patches it to a displayable URL in `data` once resolved. */ interface ParsedTableStyleImage { /** Archive-relative path, or an already-displayable external/data URL. */ path?: string; /** Displayable URL once a load pipeline has resolved `path`. */ data?: string; } /** A single colour stop within a {@link ParsedTableStyleGradient}. */ interface ParsedTableStyleGradientStop { /** Stop position as a percentage (0-100). */ position: number; /** Stop colour (scheme or explicit sRGB). */ fill: ParsedTableStyleFill; } /** A gradient fill parsed from a table style section's `a:gradFill`. */ interface ParsedTableStyleGradient { /** Ordered colour stops. */ stops: ParsedTableStyleGradientStop[]; /** Linear gradient angle in degrees (from `a:lin@ang`, 60000ths -> deg). */ angle?: number; /** Gradient family: linear (`a:lin`) or radial (`a:path`). */ type: 'linear' | 'radial'; } /** A preset pattern fill parsed from a table style section's `a:pattFill`. */ interface ParsedTableStylePattern { /** OOXML preset name (e.g. `ltDnDiag`) from `a:pattFill@prst`. */ preset: string; /** Foreground colour (`a:fgClr`). */ foreground?: ParsedTableStyleFill; /** Background colour (`a:bgClr`). */ background?: ParsedTableStyleFill; } /** * A single entry in the parsed table style map. * * Contains fill colours for whole-table, banded rows/columns, first/last * row, and first/last column sections. * * @example * ```ts * const entry: ParsedTableStyleEntry = { * styleId: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}", * styleName: "Medium Style 2 - Accent 1", * accentKey: "accent1", * wholeTblFill: { schemeColor: "accent1", tint: 20000 }, * band1HFill: { schemeColor: "accent1", tint: 40000 }, * firstRowFill: { schemeColor: "accent1" }, * }; * // => satisfies ParsedTableStyleEntry * ``` */ /** Text properties from a:tcTxStyle in a table style section. */ interface ParsedTableStyleText { /** Font bold. */ bold?: boolean; /** Font italic. */ italic?: boolean; /** Font underline (from `a:tcTxStyle@u`, any value other than `none`). */ underline?: boolean; /** Font colour as theme scheme key. */ fontSchemeColor?: string; /** Font colour tint (0-100 000). */ fontTint?: number; /** Font colour shade (0-100 000). */ fontShade?: number; /** Explicit sRGB hex font colour (e.g. `#FF0000`) from `a:srgbClr`. */ fontColor?: string; /** Typeface from `a:font@typeface` (latin font). */ fontFace?: string; /** Font-collection index from `a:fontRef@idx` (`minor`, `major`, `none`). */ fontRefIdx?: string; } /** * A single border side within a table style's `a:tcStyle/a:tcBdr`. * * Corresponds to one of `a:left`, `a:right`, `a:top`, `a:bottom`, * `a:insideH`, `a:insideV`, `a:tl2br`, `a:bl2tr` (each a * `CT_ThemeableLineStyle` wrapping an `a:ln`). * * @example * ```ts * const side: ParsedTableStyleBorder = { * width: 1, * dash: 'solid', * fill: { schemeColor: 'lt1' }, * }; * // => satisfies ParsedTableStyleBorder * ``` */ interface ParsedTableStyleBorder { /** Line width in px (converted from the `a:ln@w` EMU value). */ width?: number; /** OOXML `a:prstDash@val` (e.g. `solid`, `dash`, `sysDot`). */ dash?: string; /** Border colour as a theme scheme fill (from `a:ln/a:solidFill/a:schemeClr`). */ fill?: ParsedTableStyleFill; /** Explicit hex colour when the line used `a:srgbClr` (e.g. `#808080`). */ color?: string; /** The line was `a:noFill` - an explicit "no border" that clears lower layers. */ noFill?: boolean; } /** * The set of border sides parsed from a table style section's * `a:tcStyle/a:tcBdr` element. */ interface ParsedTableStyleBorders { left?: ParsedTableStyleBorder; right?: ParsedTableStyleBorder; top?: ParsedTableStyleBorder; bottom?: ParsedTableStyleBorder; /** Interior horizontal borders between rows in the region. */ insideH?: ParsedTableStyleBorder; /** Interior vertical borders between columns in the region. */ insideV?: ParsedTableStyleBorder; /** Top-left to bottom-right diagonal. */ tl2br?: ParsedTableStyleBorder; /** Bottom-left to top-right diagonal. */ bl2tr?: ParsedTableStyleBorder; } /** * Table background style (CT_TableBackgroundStyle, ECMA-376 §21.1.3.7). * * Corresponds to the `` child of ``. Currently * captures only the resolved scheme-fill colour (verbatim XML for fill * / effect references is preserved separately by the save path). */ interface ParsedTableBackground { /** Solid fill (resolved from `a:fill > a:solidFill > a:schemeClr`). */ fill?: ParsedTableStyleFill; /** Has an `a:effectLst` child that should be round-tripped. */ hasEffectLst?: boolean; } interface ParsedTableStyleEntry { styleId: string; styleName?: string; /** Dominant accent key derived from fills (e.g. `accent1`). */ accentKey?: string; /** Table-level background (``). */ tableBackground?: ParsedTableBackground; wholeTblFill?: ParsedTableStyleFill; band1HFill?: ParsedTableStyleFill; band2HFill?: ParsedTableStyleFill; band1VFill?: ParsedTableStyleFill; band2VFill?: ParsedTableStyleFill; firstRowFill?: ParsedTableStyleFill; lastRowFill?: ParsedTableStyleFill; firstColFill?: ParsedTableStyleFill; lastColFill?: ParsedTableStyleFill; /** Corner cell fills (``, ``, ``, ``). */ seCellFill?: ParsedTableStyleFill; swCellFill?: ParsedTableStyleFill; neCellFill?: ParsedTableStyleFill; nwCellFill?: ParsedTableStyleFill; /** * Per-role border styling from `a:tcStyle/a:tcBdr`. These supply the * gridlines/edges a styled table inherits from its table style when the * cells carry no explicit per-cell `a:lnX` overrides. */ wholeTblBorders?: ParsedTableStyleBorders; firstRowBorders?: ParsedTableStyleBorders; lastRowBorders?: ParsedTableStyleBorders; firstColBorders?: ParsedTableStyleBorders; lastColBorders?: ParsedTableStyleBorders; band1HBorders?: ParsedTableStyleBorders; band2HBorders?: ParsedTableStyleBorders; band1VBorders?: ParsedTableStyleBorders; band2VBorders?: ParsedTableStyleBorders; seCellBorders?: ParsedTableStyleBorders; swCellBorders?: ParsedTableStyleBorders; neCellBorders?: ParsedTableStyleBorders; nwCellBorders?: ParsedTableStyleBorders; /** Per-role text styling from a:tcTxStyle. */ wholeTblText?: ParsedTableStyleText; firstRowText?: ParsedTableStyleText; lastRowText?: ParsedTableStyleText; firstColText?: ParsedTableStyleText; lastColText?: ParsedTableStyleText; band1HText?: ParsedTableStyleText; band2HText?: ParsedTableStyleText; band1VText?: ParsedTableStyleText; band2VText?: ParsedTableStyleText; seCellText?: ParsedTableStyleText; swCellText?: ParsedTableStyleText; neCellText?: ParsedTableStyleText; nwCellText?: ParsedTableStyleText; } /** * Map of GUID → table style entry. * * Parsed from `ppt/tableStyles.xml` and indexed by the style GUID * referenced in `a:tblPr@tblStyle`. * * @example * ```ts * const styles: ParsedTableStyleMap = { * "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}": { * styleId: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}", * styleName: "Medium Style 2 - Accent 1", * accentKey: "accent1", * }, * }; * // => satisfies ParsedTableStyleMap * ``` */ type ParsedTableStyleMap = Record; //#endregion //#region src/core/types/elements.d.ts /** * A text box — a plain rectangle containing text, typically with no * visible fill or stroke. * * @example * ```ts * const title: TextPptxElement = { * type: "text", * id: "txt_1", x: 50, y: 30, width: 800, height: 60, * text: "Welcome", * textStyle: { fontSize: 36, bold: true }, * }; * // => satisfies TextPptxElement * ``` */ interface TextPptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties { type: 'text'; } /** * A shape — may contain text and custom geometry (preset or freeform). * * @example * ```ts * const rect: ShapePptxElement = { * type: "shape", * id: "shp_1", x: 100, y: 200, width: 300, height: 150, * shapeType: "roundRect", * shapeStyle: { fillColor: "#00AA55" }, * text: "OK", * }; * // => satisfies ShapePptxElement * ``` */ interface ShapePptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties, PptxCustomPathProperties { type: 'shape'; } /** * A connector (straight, bent, or curved line between shapes). * * Connector endpoints can snap to specific shapes via * `shapeStyle.connectorStartConnection` / `connectorEndConnection`. * * @example * ```ts * const line: ConnectorPptxElement = { * type: "connector", * id: "cxn_1", x: 100, y: 100, width: 200, height: 0, * shapeStyle: { * strokeColor: "#333", * connectorEndArrow: "triangle", * }, * }; * // => satisfies ConnectorPptxElement * ``` */ interface ConnectorPptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties { type: 'connector'; } /** * An image element from an OOXML `` node with `type: "image"`. * * @example * ```ts * const img: ImagePptxElement = { * type: "image", * id: "img_1", x: 0, y: 0, width: 960, height: 540, * imagePath: "ppt/media/image1.png", * altText: "Background scenery", * }; * // => satisfies ImagePptxElement * ``` */ interface ImagePptxElement extends PptxElementBase, PptxShapeProperties, PptxCustomPathProperties, PptxImageProperties { type: 'image'; } /** * A picture element from an OOXML `` node with `type: "picture"`. * * Functionally identical to {@link ImagePptxElement} but distinguished by * the `type` discriminant for semantic clarity. */ interface PicturePptxElement extends PptxElementBase, PptxShapeProperties, PptxCustomPathProperties, PptxImageProperties { type: 'picture'; } /** * A single unrecognised `//` extension on a * graphicFrame, captured verbatim so the round-trip can preserve future or * vendor-specific markup that the parser doesn't yet understand. * * The XML is preserved as a fast-xml-parser object tree (the same shape as * `rawXml` on other elements) so the save layer can re-emit it through the * existing builder without lossy string manipulation. */ interface PptxGraphicFrameExtension { /** The `@_uri` attribute identifying the extension (e.g. `{C3CD43...}`). */ uri: string; /** Parsed XML payload of the extension, suitable for re-serialization. */ xml: XmlObject; } /** * A table embedded via a ``. * * @example * ```ts * const tbl: TablePptxElement = { * type: "table", * id: "tbl_1", x: 50, y: 200, width: 860, height: 300, * tableData: { * rows: [ * { cells: [{ text: "Name" }, { text: "Score" }] }, * { cells: [{ text: "Alice" }, { text: "95" }] }, * ], * }, * }; * // => satisfies TablePptxElement * ``` */ interface TablePptxElement extends PptxElementBase { type: 'table'; /** Parsed table cell data for editing. */ tableData?: PptxTableData; /** * Unrecognised extensions captured from `a:graphicData/a:extLst` so they * round-trip losslessly. See {@link PptxGraphicFrameExtension}. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A chart embedded via a ``. * * Chart data is parsed from the related `chartN.xml` / `chartExN.xml` * parts inside the PPTX archive. */ interface ChartPptxElement extends PptxElementBase { type: 'chart'; chartData?: PptxChartData; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A SmartArt diagram embedded via a ``. * * SmartArt data is extracted from `dgm:dataModel` parts. The editor * supports real structural editing (adding, removing and reordering nodes, * editing node text, and switching layout presets) with a lossless * `ptLst` round-trip. When the file carries PowerPoint's own pre-computed * drawing part, that exact layout is used; otherwise an algorithmic layout * engine approximates it, so complex custom layouts may not match * PowerPoint pixel-for-pixel. */ interface SmartArtPptxElement extends PptxElementBase { type: 'smartArt'; smartArtData?: PptxSmartArtData; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * Recognised OLE object application types derived from `progId` / `clsId`. * * Used to show type-specific icons and previews in the editor. */ type OleObjectType = 'excel' | 'word' | 'pdf' | 'visio' | 'mathtype' | 'package' | 'unknown'; /** * An OLE (Object Linking and Embedding) object. * * OLE objects can be embedded Excel sheets, Word documents, PDFs, Visio * diagrams, MathType equations, or generic "packages". They carry a * preview image for display and optional binary data for extraction. * * @example * ```ts * const ole: OlePptxElement = { * type: "ole", * id: "ole_1", x: 100, y: 200, width: 400, height: 300, * oleObjectType: "excel", * oleProgId: "Excel.Sheet.12", * fileName: "budget.xlsx", * }; * // => satisfies OlePptxElement * ``` */ interface OlePptxElement extends PptxElementBase { type: 'ole'; oleTarget?: string; oleProgId?: string; oleName?: string; /** CLSID of the OLE object (from `@_classid`). */ oleClsId?: string; /** Detected application type (excel, word, pdf, etc.). */ oleObjectType?: OleObjectType; /** File extension for the embedded binary (e.g. "xlsx", "docx"). */ oleFileExtension?: string; /** Original file name when available. */ fileName?: string; /** Whether this is a linked (vs. embedded) object. */ isLinked?: boolean; /** External file path for linked OLE objects (TargetMode="External"). */ externalPath?: string; /** Data-URL or path for the OLE preview image. */ previewImage?: string; /** Decoded preview image as a data-URL. */ previewImageData?: string; /** Whether the OLE object is shown as an icon (`p:oleObj/@showAsIcon`). */ oleShowAsIcon?: boolean; /** Authored display width of the OLE object preview, in EMU (`@imgW`). */ oleImgW?: number; /** Authored display height of the OLE object preview, in EMU (`@imgH`). */ oleImgH?: number; /** * The recovered embedded payload as a data-URL (e.g. * `data:application/vnd...;base64,...`), suitable for download or * open-in-new-tab. For a generic "Package" OLE object this is the unwrapped * inner file; for a plain embedded file (e.g. `.xlsx`) it is that file * directly. Undefined when the embedding is missing or unreadable. * * Stored as a data-URL string to mirror how images store decoded bytes * ({@link ImagePptxElement.imageData}) and to stay serialization-safe. */ oleEmbeddedData?: string; /** Original file name of the embedded payload when recoverable. */ oleEmbeddedFileName?: string; /** MIME type of the embedded payload, derived from its extension/ProgID. */ oleEmbeddedMimeType?: string; /** Size of the embedded payload in bytes. */ oleEmbeddedByteSize?: number; /** * `p:link/@followColorScheme` (`ST_OleObjectFollowColorScheme`): whether a * LINKED OLE object's icon recolours to match the presentation theme. * Only meaningful when {@link isLinked} is `true`. ECMA-376 §19.3.1.28. */ oleFollowColorScheme?: 'none' | 'full' | 'textAndBackground'; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * An audio or video media element. * * Media elements reference files inside the PPTX archive * (`mediaPath`) and may include trim points, poster frames, and * playback settings for presentation mode. * * @example * ```ts * const video: MediaPptxElement = { * type: "media", * id: "vid_1", x: 50, y: 100, width: 640, height: 360, * mediaType: "video", * mediaPath: "ppt/media/media1.mp4", * autoPlay: true, * volume: 0.8, * }; * // => satisfies MediaPptxElement * ``` */ interface MediaPptxElement extends PptxElementBase { type: 'media'; mediaType?: PptxMediaType; mediaPath?: string; mediaData?: string; mediaMimeType?: string; mediaReferenceKind?: PptxMediaReferenceKind; mediaReferenceName?: string; /** Explicit DrawingML `audioFile/@contentType` value when present. */ mediaReferenceContentType?: string; audioCdStart?: PptxAudioCdPosition; audioCdEnd?: PptxAudioCdPosition; rawMediaReferenceXml?: XmlObject; /** Trim start in milliseconds (from p:cMediaNode p:cTn @st). */ trimStartMs?: number; /** Trim end in milliseconds (from p:cMediaNode p:cTn @end). */ trimEndMs?: number; /** Path to the poster/preview image inside the ZIP. */ posterFramePath?: string; /** Base64 data-URL for the poster frame image. */ posterFrameData?: string; /** Whether media should play full-screen during presentation. */ fullScreen?: boolean; /** Whether media should loop continuously. */ loop?: boolean; /** Fade-in duration in seconds. */ fadeInDuration?: number; /** Fade-out duration in seconds. */ fadeOutDuration?: number; /** Playback volume (0 to 1). */ volume?: number; /** Whether media auto-plays on slide entry. */ autoPlay?: boolean; /** Whether audio continues playing across slide transitions (presentation mode). */ playAcrossSlides?: boolean; /** Hide the element when media is not actively playing. */ hideWhenNotPlaying?: boolean; /** Named time bookmarks within the clip. */ bookmarks?: MediaBookmark[]; /** Playback speed multiplier (1 = normal, 2 = double, 0.5 = half). */ playbackSpeed?: number; /** Runtime-extracted metadata (duration, resolution, codec). */ metadata?: MediaMetadata; /** Closed caption / subtitle tracks. */ captionTracks?: MediaCaptionTrack[]; /** Whether the media source is missing/broken (file not found in archive). */ mediaMissing?: boolean; /** * Whether the media is linked (external `r:link`) rather than embedded * (`r:embed`). Defaults to embedded when undefined. */ isLinked?: boolean; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A group container that holds child elements. * * Children inherit the group’s transform, so moving/resizing the group * affects all children proportionally. * * @example * ```ts * const group: GroupPptxElement = { * type: "group", * id: "grp_1", x: 0, y: 0, width: 960, height: 540, * children: [textEl, shapeEl], * }; * // => satisfies GroupPptxElement * ``` */ interface GroupPptxElement extends PptxElementBase { type: 'group'; /** Child elements contained within this group. */ children: PptxElement[]; /** Fill style extracted from the group's `p:grpSpPr`, used for `a:grpFill` inheritance. */ groupFill?: ShapeStyle; } /** * A freehand ink / drawing stroke captured with a stylus or mouse. * * Ink strokes are stored as SVG path data strings. Each path may * have independent colour, width, and opacity. */ interface InkPptxElement extends PptxElementBase { type: 'ink'; /** SVG path data for ink strokes. */ inkPaths: string[]; /** Per-path stroke colours. */ inkColors?: string[]; /** Per-path stroke widths. */ inkWidths?: number[]; /** Per-path opacities (0-1). */ inkOpacities?: number[]; /** Drawing tool used: pen, highlighter, or eraser. */ inkTool?: 'pen' | 'highlighter' | 'eraser'; /** * Per-path arrays of per-point pressure values (0-1). * * Each entry corresponds to the path at the same index in `inkPaths`. * Each inner array contains one pressure value per sampled point along * the stroke. When present, the renderer uses these values to produce * variable-width strokes that reflect stylus/pen pressure. */ inkPointPressures?: number[][]; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A single ink stroke within a {@link ContentPartPptxElement}. */ interface ContentPartInkStroke { path: string; color: string; width: number; opacity: number; /** * Per-point pressure values (0-1) for this stroke. * * When present, the renderer uses these values to produce * variable-width strokes that reflect stylus/pen pressure. */ pressures?: number[]; } /** * A content-part element wrapped in `mc:AlternateContent`. * * Typically contains ink strokes from modern PowerPoint pen/highlighter. */ interface ContentPartPptxElement extends PptxElementBase { type: 'contentPart'; /** Ink strokes contained in this content part. */ inkStrokes?: ContentPartInkStroke[]; /** Package path of the related InkML part. */ inkPartPath?: string; /** Parsed InkML root retained for unknown-node preservation on dirty save. */ inkPartRawXml?: XmlObject; } /** * A Slide Zoom or Section Zoom element (PowerPoint Zoom Object). * * Zoom elements display a live thumbnail of the target slide and * navigate to it on click during presentation mode. * * @example * ```ts * const zoom: ZoomPptxElement = { * type: "zoom", * id: "zm_1", x: 300, y: 200, width: 200, height: 120, * zoomType: "slide", * targetSlideIndex: 5, * }; * // => satisfies ZoomPptxElement * ``` */ interface ZoomPptxElement extends PptxElementBase, PptxImageProperties { type: 'zoom'; /** Type of zoom: slide-level, section-level, or a multi-section summary. */ zoomType: 'slide' | 'section' | 'summary'; /** Zero-based index of the target slide. */ targetSlideIndex: number; /** Section ID for section zoom. */ targetSectionId?: string; /** Ordered section tiles in a Summary Zoom container. */ summaryTargets?: SummaryZoomTarget[]; /** Layout mode authored on the Summary Zoom container. */ summaryLayout?: 'grid' | 'fixed'; } /** A single section tile within a PowerPoint Summary Zoom container. */ interface SummaryZoomTarget extends PptxImageProperties { sectionId: string; targetSlideIndex: number; x: number; y: number; width: number; height: number; title?: string; description?: string; offsetFactorX?: number; offsetFactorY?: number; scaleFactorX?: number; scaleFactorY?: number; rawXml?: XmlObject; } /** * A 3D model object embedded via `p16:model3D` inside an * `mc:AlternateContent` block (PowerPoint 365+). * * The element carries the path to the `.glb`/`.gltf` binary inside * the ZIP and a poster/fallback image for rendering in viewers that * do not support interactive 3D. */ interface Model3DPptxElement extends PptxElementBase, PptxImageProperties { type: 'model3d'; /** Path to the 3D model file inside the ZIP. */ modelPath?: string; /** Base64 data URL of the 3D model binary. */ modelData?: string; /** MIME type of the model (e.g. "model/gltf-binary"). */ modelMimeType?: string; /** Poster/preview image shown when 3D rendering is unavailable. */ posterImage?: string; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** An element whose type is not recognised by the parser. */ interface UnknownPptxElement extends PptxElementBase { type: 'unknown'; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A single element on a PPTX slide. * * This is a **discriminated union**: narrow on `element.type` to access * variant-specific properties like `imageData` (image/picture), `pathData` * (shape), or `textSegments` (text/shape). */ type PptxElement = TextPptxElement | ShapePptxElement | ConnectorPptxElement | ImagePptxElement | PicturePptxElement | TablePptxElement | ChartPptxElement | SmartArtPptxElement | OlePptxElement | MediaPptxElement | GroupPptxElement | InkPptxElement | ContentPartPptxElement | ZoomPptxElement | Model3DPptxElement | UnknownPptxElement; //#endregion //#region src/core/types/masters.d.ts /** * A placeholder slot declared on a master or layout. * * The geometry fields are in CSS pixels (EMU / {@link EMU_PER_PX}) and are only * present when the shape carried an explicit `a:xfrm`. Placeholders that * inherit their frame from the master leave them undefined, so consumers that * draw placeholder outlines (the layout gallery) must skip those entries * rather than assume a zero-sized box at the origin. * * @example * ```ts * const frame: PptxPlaceholderFrame = { type: "body", idx: "1", x: 63, y: 130 }; * // => satisfies PptxPlaceholderFrame * ``` */ interface PptxPlaceholderFrame { /** `p:ph/@type`, lower-cased by the parser; defaults to `body` when omitted. */ type: string; /** `p:ph/@idx`, when present. */ idx?: string; /** Left offset in CSS pixels, when the shape declares `a:off`. */ x?: number; /** Top offset in CSS pixels, when the shape declares `a:off`. */ y?: number; /** Width in CSS pixels, when the shape declares `a:ext`. */ width?: number; /** Height in CSS pixels, when the shape declares `a:ext`. */ height?: number; } /** * Parsed notes master from `ppt/notesMasters/notesMaster1.xml`. * * @example * ```ts * const notes: PptxNotesMaster = { * path: "ppt/notesMasters/notesMaster1.xml", * backgroundColor: "#FFFFFF", * placeholders: [{ type: "body" }, { type: "sldImg" }], * }; * // => satisfies PptxNotesMaster * ``` */ interface PptxNotesMaster { /** File path within the PPTX archive. */ path: string; /** Background colour of the notes master. */ backgroundColor?: string; /** Background image data URL. */ backgroundImage?: string; /** Placeholder shapes found on the notes master. */ placeholders?: PptxPlaceholderFrame[]; /** Editable elements on the notes master (header, footer, date, page number, slide image, notes body). */ elements?: PptxElement[]; /** Header/footer flags from `` on the notes master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** Colour map from `` (12 alias attributes). Applied at save time. */ clrMap?: Record; /** * Notes text defaults from `` (`CT_TextListStyle`, ECMA-376 * §19.3.1.34): the same `a:defPPr` + `a:lvl1pPr`..`a:lvl9pPr` shape as * {@link PptxMasterTextStyles}'s per-category styles, keyed 0-8 with the * default at `-1`. Governs the notes body placeholder's font size, indent * levels, and bullet style wherever a notes slide does not override them. * Parsed read-only for the render cascade; the save side preserves the * original `` XML verbatim rather than re-serialising this. */ notesStyle?: PptxTextStyleLevels; } /** * Parsed handout master from `ppt/handoutMasters/handoutMaster1.xml`. * * @example * ```ts * const handout: PptxHandoutMaster = { * path: "ppt/handoutMasters/handoutMaster1.xml", * slidesPerPage: 6, * }; * // => satisfies PptxHandoutMaster * ``` */ interface PptxHandoutMaster { /** File path within the PPTX archive. */ path: string; /** Background colour of the handout master. */ backgroundColor?: string; /** Background image data URL. */ backgroundImage?: string; /** Placeholder shapes found on the handout master. */ placeholders?: PptxPlaceholderFrame[]; /** Editable elements on the handout master (header, footer, date, page number, slide placeholders). */ elements?: PptxElement[]; /** Number of slides per page for handout print layout (1, 2, 3, 4, 6, or 9). */ slidesPerPage?: number; /** Header/footer flags from `` on the handout master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** Colour map from `` (12 alias attributes). Applied at save time. */ clrMap?: Record; } /** * Active tab within the master view sidebar. * * @example * ```ts * const tab: MasterViewTab = "slides"; * // => "slides" — one of: "slides" | "notes" | "handout" * ``` */ type MasterViewTab = 'slides' | 'notes' | 'handout'; /** * Structured slide master data. * * @example * ```ts * const master: PptxSlideMaster = { * path: "ppt/slideMasters/slideMaster1.xml", * name: "Office Theme", * backgroundColor: "#FFFFFF", * themePath: "ppt/theme/theme1.xml", * }; * // => satisfies PptxSlideMaster * ``` */ interface PptxSlideMaster { /** File path within the PPTX archive. */ path: string; /** Human-readable name if available. */ name?: string; /** Background colour of the slide master. */ backgroundColor?: string; /** Background image data URL for the slide master. */ backgroundImage?: string; /** Theme file path this master references. */ themePath?: string; /** Layout paths associated with this master. */ layoutPaths?: string[]; /** Placeholder shapes on the master. */ placeholders?: PptxPlaceholderFrame[]; /** Parsed element shapes on the master slide (for master view rendering). */ elements?: PptxElement[]; /** Parsed slide layout objects associated with this master. */ layouts?: PptxSlideLayout[]; /** Text styles from `p:txStyles` — title, body, and other text defaults. */ txStyles?: PptxMasterTextStyles; /** Header/footer flags from `` on this master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** * Colour map from `` (12 alias attributes: bg1/tx1/bg2/tx2, * accent1-6, hlink, folHlink). Applied at save time when present. */ clrMap?: Record; /** * Whether the master is marked as preserved (prevent auto-deletion, * `@preserve`). Mirrors {@link PptxSlideLayout.preserve}: PowerPoint * silently drops an unused master unless this is set. * * ECMA-376 §19.3.1.38 (CT_SlideMaster). */ preserve?: boolean; } /** * Per-level paragraph properties for a text style category. * Each entry maps a 0-based level index to its style defaults. */ type PptxTextStyleLevels = Record; /** * Text styles parsed from `p:txStyles` on a slide master. * Provides cascading defaults for title, body, and other text. */ interface PptxMasterTextStyles { /** Title text style (`p:titleStyle`). */ titleStyle?: PptxTextStyleLevels; /** Body text style (`p:bodyStyle`). */ bodyStyle?: PptxTextStyleLevels; /** Other text style (`p:otherStyle`). */ otherStyle?: PptxTextStyleLevels; } /** * Per-part header/footer flags from `` (CT_HeaderFooter, ECMA-376 * §19.3.1.21). Defaults are "all true" — fields are only set on the typed * model when they were explicitly read, so callers can distinguish "unset" * (preserve original XML) from "false" (override). */ interface PptxHeaderFooterFlags { /** `@hdr` — show header placeholder. Spec default: `true`. */ hasHeader?: boolean; /** `@ftr` — show footer placeholder. Spec default: `true`. */ hasFooter?: boolean; /** `@dt` — show date/time placeholder. Spec default: `true`. */ hasDateTime?: boolean; /** `@sldNum` — show slide-number placeholder. Spec default: `true`. */ hasSlideNumber?: boolean; } /** * A slide layout associated with a slide master. * * @example * ```ts * const layout: PptxSlideLayout = { * path: "ppt/slideLayouts/slideLayout2.xml", * name: "Title and Content", * }; * // => satisfies PptxSlideLayout * ``` */ interface PptxSlideLayout { /** File path within the PPTX archive. */ path: string; /** Human-readable layout name. */ name?: string; /** Background colour of the layout. */ backgroundColor?: string; /** Background image data URL for the layout. */ backgroundImage?: string; /** Parsed element shapes on the layout. */ elements?: PptxElement[]; /** Placeholder shapes on the layout. */ placeholders?: PptxPlaceholderFrame[]; /** Matching name attribute for layout identification (`@matchingName`). */ matchingName?: string; /** Whether the layout is marked as preserved (prevent deletion, `@preserve`). */ preserve?: boolean; /** Whether master placeholder animations should play (`@showMasterPhAnim`). */ showMasterPhAnim?: boolean; /** Whether this layout is user-drawn (`@userDrawn`). */ userDrawn?: boolean; /** Colour map override from `p:clrMapOvr`. */ clrMapOverride?: Record; /** Header/footer flags from `` on this layout (P-H3). */ headerFooter?: PptxHeaderFooterFlags; } /** * Rendered content of a single layout, used to draw gallery thumbnails. * * Produced on demand rather than during load: materialising every layout's * artwork (and decoding its images) up front costs a noticeable amount of time * on decks with many masters, and most sessions never open the layout gallery * at all. * * @example * ```ts * const preview: PptxLayoutPreview = { * path: "ppt/slideLayouts/slideLayout2.xml", * width: 960, * height: 540, * elements: [], * placeholders: [{ type: "title" }], * }; * // => satisfies PptxLayoutPreview * ``` */ interface PptxLayoutPreview { /** ZIP path of the layout this preview belongs to. */ path: string; /** Slide width in CSS pixels, so a thumbnail can compute its own scale. */ width: number; /** Slide height in CSS pixels. */ height: number; /** Background resolved from the layout, falling back to its master's. */ backgroundColor?: string; /** Background image data URL, when the layout or master declares one. */ backgroundImage?: string; /** The layout's own artwork (pictures, shapes and static text). */ elements: PptxElement[]; /** Placeholder slots, drawn as outlined frames in the gallery. */ placeholders: PptxPlaceholderFrame[]; } /** * A theme part available in the presentation package. * * @example * ```ts * const opt: PptxThemeOption = { * path: "ppt/theme/theme1.xml", * name: "Office Theme", * }; * // => satisfies PptxThemeOption * ``` */ interface PptxThemeOption { /** File path within the PPTX archive (e.g. `ppt/theme/theme2.xml`). */ path: string; /** Human-readable theme name from `a:theme/@name`, when present. */ name?: string; } //#endregion //#region src/core/types/animation.d.ts /** * Built-in animation preset names used for entrance, exit, and emphasis effects. * * @example * ```ts * const preset: PptxAnimationPreset = "fadeIn"; * // => "fadeIn" — one of: none | fadeIn | flyIn | zoomIn | fadeOut | flyOut | zoomOut | spin | pulse | ... * ``` */ type PptxAnimationPreset = 'none' | 'appear' | 'fadeIn' | 'flyIn' | 'zoomIn' | 'bounceIn' | 'wipeIn' | 'splitIn' | 'dissolveIn' | 'wheelIn' | 'blindsIn' | 'boxIn' | 'floatIn' | 'riseUp' | 'swivel' | 'expandIn' | 'checkerboardIn' | 'flashIn' | 'peekIn' | 'randomBarsIn' | 'spinnerIn' | 'growTurnIn' | 'fadeOut' | 'flyOut' | 'zoomOut' | 'bounceOut' | 'wipeOut' | 'shrinkOut' | 'dissolveOut' | 'disappear' | 'spin' | 'pulse' | 'colorWave' | 'bounce' | 'flash' | 'growShrink' | 'teeter' | 'transparency' | 'boldFlash' | 'wave'; /** Animation timing curve. */ type PptxAnimationTimingCurve = 'ease' | 'ease-in' | 'ease-out' | 'linear'; /** Repeat mode for animations. */ type PptxAnimationRepeatMode = 'untilNextClick' | 'untilEndOfSlide'; /** Animation trigger type from OOXML `p:cTn`. */ type PptxAnimationTrigger = 'onClick' | 'onShapeClick' | 'onHover' | 'afterPrevious' | 'withPrevious' | 'afterDelay'; /** * Native animation kind. The historic shape-targeted preset animations are * implicitly the default kind (`undefined`). Media animations (`p:audio`, * `p:video`) emit dedicated entries so playback order on the slide timeline * is preserved alongside other animations. */ type PptxNativeAnimationKind = 'media'; /** A target selected by `p:tgtEl` in the PresentationML timing model. */ type PptxAnimationTarget = { type: 'shape'; shapeId: string; rawXml?: XmlObject; } | { type: 'slide'; rawXml?: XmlObject; } | { type: 'sound'; relationshipId: string; name?: string; rawXml?: XmlObject; } | { type: 'ink'; shapeId: string; rawXml?: XmlObject; } | { type: 'unknown'; rawXml: XmlObject; }; /** Nested build choice carried by `p:bldGraphic`. */ type PptxGraphicBuild = { mode: 'asOne'; rawXml?: XmlObject; } | { mode: 'sub'; kind: 'diagram'; build: string; reverse: boolean; rawXml?: XmlObject; } | { mode: 'sub'; kind: 'chart'; build: string; animateBackground: boolean; rawXml?: XmlObject; }; /** * A single `p:tmpl` timing template parsed from a TEXT `p:bldP/p:tmplLst` * (CT_TLTemplate, ECMA-376 §19.5.85; the list itself is CT_TLTemplateList, * §19.5.84). * * PowerPoint writes these as the timing PowerPoint would apply to a build * level that does not yet have an instantiated effect, so that promoting or * demoting an outline paragraph, or adding a new bullet at a level with no * prior animation, has a default to clone. They are not consulted at * playback: the animation actually shown for every paragraph level already * visible on the slide is the real, instantiated `p:tnLst` under * `p:timing/p:tnLst`, which the rest of this parser already models in full. * * The nested time-node tree under each template's own `p:tnLst` is kept as * a preserved `XmlObject` rather than deep-parsed into * {@link PptxNativeAnimation} records: it is schema-identical to the * top-level timing tree but scoped to a template that is never itself * executed, so structurally modelling it would stand up a second, unused * parallel animation model. Parsing stops at typed round-trip; see * `docs/guide/limitations.md`. */ interface PptxTimingTemplate { /** Build level this template targets, from `p:tmpl/@lvl` (ST_TLLevel, default 0). */ level: number; /** Preserved `p:tnLst` (CT_TimeNodeList) subtree, verbatim. */ timeNodeList: XmlObject; /** Preserved `p:tmpl` XML node (its attributes plus any unmodelled children). */ rawXml?: XmlObject; } /** * Parsed native animation record from `p:timing / p:tnLst`. * * Represents a single animation node in the OOXML timing tree, * including motion paths, scale transforms, and text build settings. * * @example * ```ts * const anim: PptxNativeAnimation = { * targetId: "shape_1", * presetClass: "entr", * presetId: 10, * trigger: "afterPrevious", * durationMs: 500, * }; * // => { targetId: "shape_1", presetClass: "entr", presetId: 10, trigger: "afterPrevious", durationMs: 500 } * ``` */ interface PptxNativeAnimation { /** Target element/shape ID. */ targetId?: string; /** Full timing target, including sound and ink target variants. */ target?: PptxAnimationTarget; /** Trigger type. */ trigger?: PptxAnimationTrigger; /** Shape ID that triggers this animation when clicked (interactive sequence). */ triggerShapeId?: string; /** Effect preset class (entr, exit, emph, path). */ presetClass?: 'entr' | 'exit' | 'emph' | 'path'; /** Effect preset sub-type identifier. */ presetId?: number; /** * Effect preset direction/variant code from `p:cTn/@presetSubtype` * (ECMA-376 CT_TLCommonTimeNodeData). For Fly In/Out this encodes the * edge/corner the object travels from as a bitmask (1=top, 2=right, * 4=bottom, 8=left; corners combine bits). Absent means the preset default. */ presetSubtype?: number; /** Duration in milliseconds. */ durationMs?: number; /** Delay in milliseconds. */ delayMs?: number; /** * Acceleration fraction in the range 0..1, parsed from `p:cTn/@accel` * (ST_PositiveFixedPercentage, stored as 1000ths of a percent). A non-zero * value means the effect eases in (starts slow). Absent means no easing-in. */ accel?: number; /** * Deceleration fraction in the range 0..1, parsed from `p:cTn/@decel`. * A non-zero value means the effect eases out (ends slow). Absent means no * easing-out. When both {@link accel} and {@link decel} are set, the effect * eases in and out. */ decel?: number; /** Trigger delay in milliseconds (for afterDelay). */ triggerDelayMs?: number; /** SVG path string for motion path animations (`p:animMotion/@path`). */ motionPath?: string; /** Motion origin: "layout" or "parent". */ motionOrigin?: string; /** * Whether the element auto-rotates to follow the motion path tangent. * Viewer-authoring-only hint: OOXML has no such flag (`p:animMotion/@rAng` * is a plain rotation angle that PowerPoint writes as "0" on every path), * so the parser never sets this. */ motionPathRotateAuto?: boolean; /** Path edit mode from `p:animMotion/@pathEditMode` (e.g. "relative", "fixed"). */ motionPathEditMode?: string; /** Comma-separated point-types string from `p:animMotion/@ptsTypes`. */ motionPtsTypes?: string; /** Rotation angle in degrees for `p:animRot/@by` (converted from 60000ths). */ rotationBy?: number; /** Starting rotation angle in degrees for `p:animRot/@from` (converted from 60000ths). */ rotationFrom?: number; /** Ending rotation angle in degrees for `p:animRot/@to` (converted from 60000ths). */ rotationTo?: number; /** X scale factor (percentage / 100) for `p:animScale/p:by/@x`. */ scaleByX?: number; /** Y scale factor (percentage / 100) for `p:animScale/p:by/@y`. */ scaleByY?: number; /** Starting X scale factor for `p:animScale/p:from/@x`. */ scaleFromX?: number; /** Starting Y scale factor for `p:animScale/p:from/@y`. */ scaleFromY?: number; /** Ending X scale factor for `p:animScale/p:to/@x`. */ scaleToX?: number; /** Ending Y scale factor for `p:animScale/p:to/@y`. */ scaleToY?: number; /** Whether `p:animScale/@zoomContents` was set ("1"/"true"). */ scaleZoomContents?: boolean; /** Parsed `p:tav` keyframes from `p:tavLst` (CT_TLAnimVariantList). */ keyframes?: PptxAnimationKeyframe[]; /** Repeat count (e.g. `2`, `Infinity` for indefinite). */ repeatCount?: number; /** Whether the animation plays in reverse after completion. */ autoReverse?: boolean; /** Text build type from `p:bldP/@build` in `p:bldLst`. */ buildType?: PptxTextBuildType; /** Build level for multi-level lists from `p:bldP/@bldLvl`. */ buildLevel?: number; /** Group ID linking a `p:bldP` entry to its timing animation node. */ groupId?: string; /** Sound relationship ID to play when animation triggers (`p:stSnd`). */ soundRId?: string; /** Resolved sound file path from relationship. */ soundPath?: string; /** Whether to stop any currently playing sound (`p:endSnd`). */ stopSound?: boolean; /** * End-state behaviour from `p:cTn/@fill` (ST_TLTimeNodeFillType, ECMA-376 * §19.5.27). `hold`/`freeze` mean the effect's final frame persists after * it finishes; `remove` (the default when absent) means the target reverts * to its pre-effect appearance. `transition` behaves like `hold` until the * next time node starts. Absent means the OOXML default (`remove`). */ fill?: 'remove' | 'freeze' | 'hold' | 'transition'; /** * Restart behaviour from `p:cTn/@restart` (ST_TLTimeNodeRestartType). * Absent means the OOXML default (`always`). */ restart?: 'always' | 'whenNotActive' | 'never'; /** * Repeat duration in milliseconds from `p:cTn/@repeatDur`. `Infinity` * represents the literal `"indefinite"` token. */ repeatDurMs?: number; /** * Playback speed multiplier from `p:cTn/@spd` (ST_Percentage, normalized * from OOXML's 1000ths-of-a-percent storage to a plain percentage, e.g. * `150` for 150% / double speed). Absent means normal (100%) speed. */ speedPct?: number; /** * Reverse the paragraph build order from `p:bldP/@rev` (TEXT build only). * Not to be confused with {@link PptxGraphicBuild}'s `reverse` field, which * carries the unrelated `p:bldDgm`/`@rev` DIAGRAM-build reverse flag. */ buildReverse?: boolean; /** * Auto-advance time in milliseconds from `p:bldP/@advAuto`. `Infinity` * represents the literal `"indefinite"` token. Absent means the build * step waits for a click. */ buildAdvAutoMs?: number; /** * Per-build-level timing templates from a TEXT `p:bldP/p:tmplLst` * (ECMA-376 §19.5.84 CT_TLTemplateList). Parsed for round-trip only; see * {@link PptxTimingTemplate} for why they are not consulted at playback. */ buildTemplates?: PptxTimingTemplate[]; /** * Whether the enclosing `p:seq` allows concurrent play with its siblings, * from `p:seq/@concurrent`. Parsed for round-trip; not yet honoured by * playback (see `docs/guide/limitations.md`). */ seqConcurrent?: boolean; /** Next-action behaviour from `p:seq/@nextAc` (ST_TLNextActionType). */ seqNextAction?: 'none' | 'seek'; /** Previous-action behaviour from `p:seq/@prevAc` (ST_TLPreviousActionType). */ seqPrevAction?: 'none' | 'skipTimeNode'; /** * Whether the enclosing click-level group (a direct `p:par` child of the * `mainSeq`) begins automatically when the slide appears, rather than waiting * for a click. * * PowerPoint gates a click step with a lone ``; * a group that also carries a time-node condition (`onBegin`/`onEnd` with a * `@tn`) or a finite delay starts on slide entry ("With/After Previous" as the * first effect on the slide). The flat animation list cannot express that on * its own, so the parse layer stamps it here. */ groupAutoStart?: boolean; /** * Index of the enclosing effect-wrapper `p:par` inside the click-level group. * * Effects that share a wrapper are OOXML siblings: they all start when that * wrapper starts, and each `p:cond/@delay` is measured from the wrapper's * start, NOT chained off the effect before it. Playback uses this to place * simultaneous effects at their true offsets instead of accumulating delays. */ parGroupIndex?: number; /** Structured start conditions parsed from `p:stCondLst`. */ startConditions?: AnimationCondition[]; /** Structured end conditions parsed from `p:endCondLst`. */ endConditions?: AnimationCondition[]; /** Preserved raw `p:endCondLst` XML node for lossless round-trip. */ rawEndCondLst?: XmlObject; /** Color animation data from `p:animClr`. */ colorAnimation?: PptxColorAnimation; /** Text-level target: character range or paragraph range from `p:txEl`. */ textTarget?: PptxTextAnimationTarget; /** Whether this animation is inside an exclusive container (`p:excl`). */ exclusive?: boolean; /** Command type from `p:cmd` (@_type: call/evt/verb). */ commandType?: string; /** Command string from `p:cmd` (@_cmd). */ commandString?: string; /** Iteration configuration from `p:iterate`. */ iterate?: PptxAnimationIterate; /** * Discriminator for non-preset animation kinds. When `undefined`, the * entry represents the default shape-effect animation. The `'media'` * kind represents a `p:audio` / `p:video` timing node, captured here so * playback order in the timeline is preserved alongside other animations. */ kind?: PptxNativeAnimationKind; /** * For `kind === 'media'`, identifies whether this is an audio or video * media node so writers know which OOXML element to re-emit. */ mediaType?: 'audio' | 'video'; /** * SmartArt build attribute (`p:bldDgm/@bld`) when this animation is * associated with a SmartArt diagram build. Common values include * `whole`, `one`, `lvlOne`, `lvlAtOnce`. */ smartArtBuild?: string; /** * Graphic-frame build attribute (`p:bldGraphic/@bld`) when this animation * is associated with a generic graphic frame build (charts, tables, etc. * that aren't OLE charts). */ graphicBuild?: string; /** * OLE-embedded chart build attribute (`p:bldOleChart/@bld`) when this * animation stages an OLE chart graphic frame. Values follow * ST_TLOleChartBuildType: `allAtOnce`, `series`, `category`, `seriesEl`, * `categoryEl`. Lets a staged-reveal renderer build the chart by series / * category / element to match PowerPoint, rather than as one whole element. */ oleChartBuild?: string; /** Schema-accurate `p:bldGraphic/p:bldAsOne|p:bldSub` representation. */ graphicBuildProperties?: PptxGraphicBuild; /** * Opaque map of `p:cTn` attributes that don't have a typed home on this * interface but must round-trip through parse → save. Keys are stored * verbatim including the `@_` prefix used by the underlying XML parser * (e.g. `@_evtFilter`, `@_display`, `@_masterRel`, `@_nodePh`, * `@_endSync`, `@_progress`). The `subTnLst` child element is also * preserved here under the literal key `p:subTnLst`. The `afterEffect` * attribute is surfaced separately as a typed boolean ({@link afterEffect}) * because it changes write semantics for subsequent timing nodes. */ cTnAttributes?: Record; /** * Whether the OOXML `p:cTn/@afterEffect` flag is set. Indicates this node * runs after the parent effect's main body has completed; affects how * subsequent peer nodes are sequenced when serialised back to OOXML. */ afterEffect?: boolean; /** * "After animation" end-state behaviour carried over from the matching * {@link PptxElementAnimation.afterAnimation} entry for this effect's * element. Not populated by the native-timing parser itself (there is no * single `p:cTn` attribute for it): `applyAfterAnimationFromEditorList` in * `pptx-viewer-shared` merges it in from the editor's per-element * animation list before playback, since that is the model the animation * panel writes `afterAnimation` into. */ afterAnimationAction?: PptxAfterAnimationAction; /** Dim-to color hex, present when {@link afterAnimationAction} is `dimToColor`. */ afterAnimationColor?: string; /** * Parsed `p:animEffect` filter descriptor. `presetId`/`presetClass` remain * the primary effect selector (see `resolveEffect` in `pptx-viewer-shared`); * this is the fallback used when a preset table lookup misses (unmapped or * absent `presetId`), which happens for decks authored by tools other than * PowerPoint that only emit the SMIL-style filter string. */ effectFilter?: PptxAnimationEffectFilter; } /** * Parsed `p:animEffect/@filter` (+ `@transition`) descriptor. ECMA-376 * describes `@filter` as a free-form string of the form `family(subtype)`, * optionally followed by `;`-separated fallback candidates (only the first * is honoured, per ECMA-376 S19.5.3's "first supported filter wins" rule). * * @example * ```ts * const f: PptxAnimationEffectFilter = { family: 'wipe', subtype: 'up', transition: 'in', raw: 'wipe(up)' }; * ``` */ interface PptxAnimationEffectFilter { /** Filter family name (e.g. `"wipe"`, `"barn"`, `"checkerboard"`), lowercased. */ family: string; /** * Parenthesised subtype/direction token verbatim (e.g. `"up"`, * `"inVertical"`, `"across"`, `"4"`). Absent when the filter has no * subtype (e.g. bare `"dissolve"`). */ subtype?: string; /** * `p:animEffect/@transition`: `"in"` reveals the target (the OOXML * default when the attribute is omitted), `"out"` conceals it, `"none"` * applies the filter without a visibility change (a static filter pass). */ transition?: 'in' | 'out' | 'none'; /** Raw filter string exactly as authored, for round-trip/debugging. */ raw: string; } /** * Single keyframe parsed from a `p:tav` element (CT_TLTimeAnimateValue). * * Each entry in a `p:tavLst` has a time fraction (`@_tm`, in 1000ths of the * total duration; or the literal "indefinite" / "large") and a typed value * child under `p:val/p:strVal|p:boolVal|p:intVal|p:fltVal|p:clrVal`. * * @see ECMA-376 §19.5.30 CT_TLAnimVariantList / §19.5.92 CT_TLTimeAnimateValue */ interface PptxAnimationKeyframe { /** * Time fraction. A finite number is the OOXML `@_tm` integer (0–100000 * for percentage, where 100000 = 100% of duration). A string preserves * special tokens ("indefinite", "large"). */ tm: number | string; /** Decoded keyframe value. */ value: string | boolean | number; /** Discriminant indicating which `p:val` child carried the value. */ valueType: 'str' | 'bool' | 'int' | 'flt' | 'clr'; /** * Optional formula carried on `p:tav/@_fmla`. Preserved for round-trip * fidelity; consumers may use it to drive computed animation values. */ fmla?: string; } /** Color animation data parsed from `p:animClr`. */ interface PptxColorAnimation { /** Color interpolation space: "hsl" or "rgb". */ colorSpace: 'hsl' | 'rgb'; /** Direction for HSL interpolation: "cw" (clockwise) or "ccw". */ direction?: 'cw' | 'ccw'; /** * Optional `p:animClr/@path` value preserved for round-trip. When set, * the colour sweep follows a path-based interpolation rather than the * straight cw/ccw arc. ECMA-376 §19.5.13 documents this attribute as a * companion to `@dir` for HSL colour-space animations. */ path?: string; /** Starting color as hex string. */ fromColor?: string; /** Ending color as hex string. */ toColor?: string; /** * Color delta (for "by" animations) as hex string. For HSL colour-space * animations the value encodes a delta over hue/sat/lum and is preserved * verbatim from the source. */ byColor?: string; /** * Target attribute from `p:attrNameLst` (e.g. "fillcolor", "style.color", * "stroke.color"). Used to determine which CSS property to animate. */ targetAttribute?: string; } /** Text-level animation target from `p:txEl`. */ interface PptxTextAnimationTarget { /** Target type: character range or paragraph range. */ type: 'charRg' | 'pRg'; /** Start index (0-based). */ start: number; /** End index (exclusive). */ end: number; } /** * Event types for animation conditions from `p:cond/@evt`. * * These map directly to OOXML condition event attribute values * (ISO/IEC 29500-1 S19.5.28 CT_TLTimeCondition). */ type AnimationConditionEvent = 'onBegin' | 'onEnd' | 'begin' | 'end' | 'onClick' | 'onMouseOver' | 'onMouseOut' | 'onNext' | 'onPrev' | 'onStopAudio'; /** * Structured representation of a single OOXML animation condition * from `p:cond` elements inside `p:stCondLst` or `p:endCondLst`. * * Conditions control when an animation starts or ends, and can reference * events, time delays, and target time node IDs. * * @example * ```ts * const cond: AnimationCondition = { * event: "onClick", * delay: 0, * targetShapeId: "shape_5", * }; * ``` */ interface AnimationCondition { /** Event that triggers the condition. */ event?: AnimationConditionEvent; /** Delay in milliseconds (from `@_delay`). "indefinite" is represented as -1. */ delay?: number; /** Target time node ID reference (from `@_tn`). */ targetTimeNodeId?: number; /** Target shape ID from `p:tgtEl/p:spTgt/@spid`. */ targetShapeId?: string; /** Whether the condition targets a slide (from `p:tgtEl/p:sldTgt`). */ targetSlide?: boolean; /** Full target choice, including `p:sndTgt` and `p:inkTgt`. */ target?: PptxAnimationTarget; } /** Iteration configuration from `p:iterate`. */ interface PptxAnimationIterate { /** Iteration type: el (element), lt (letter), wd (word). */ type: 'el' | 'lt' | 'wd'; /** Whether to iterate backwards. */ backwards?: boolean; /** Timing interval (percentage of total duration, in 1000ths). */ tmPct?: number; /** Absolute timing interval in ms. */ tmAbs?: number; } /** Build type for text build (paragraph/word/letter) animations from `p:bldP/@build`. */ type PptxTextBuildType = 'allAtOnce' | 'byParagraph' | 'byWord' | 'byChar'; /** Direction for fly-in / fly-out / wipe effects. */ type PptxAnimationDirection = 'fromLeft' | 'fromRight' | 'fromTop' | 'fromBottom' | 'fromTopLeft' | 'fromTopRight' | 'fromBottomLeft' | 'fromBottomRight'; /** Sequence mode for paragraph-level animations. */ type PptxAnimationSequence = 'asOne' | 'byParagraph' | 'byWord' | 'byLetter'; /** Behavior after animation finishes. */ type PptxAfterAnimationAction = 'none' | 'hideOnNextClick' | 'hideAfterAnimation' | 'dimToColor'; /** * High-level animation data associated with a slide element. * * Combines entrance, exit, and emphasis presets with timing and * trigger configuration. Used by the editor’s animation panel * and the `setPptxElementAnimation` tool. * * @example * ```ts * const anim: PptxElementAnimation = { * elementId: "title_1", * entrance: "fadeIn", * durationMs: 600, * order: 1, * trigger: "afterPrevious", * }; * // => { elementId: "title_1", entrance: "fadeIn", durationMs: 600, order: 1, trigger: "afterPrevious" } * ``` */ interface PptxElementAnimation { elementId: string; entrance?: PptxAnimationPreset; exit?: PptxAnimationPreset; emphasis?: PptxAnimationPreset; durationMs?: number; delayMs?: number; order?: number; trigger?: PptxAnimationTrigger; /** Shape ID that triggers this animation when clicked (interactive sequence). */ triggerShapeId?: string; timingCurve?: PptxAnimationTimingCurve; repeatCount?: number; repeatMode?: PptxAnimationRepeatMode; /** Direction for directional effects (fly in/out, wipe, etc.). */ direction?: PptxAnimationDirection; /** Sequence mode — animate as one object or by paragraph/word/letter. */ sequence?: PptxAnimationSequence; /** What happens after the animation finishes playing. */ afterAnimation?: PptxAfterAnimationAction; /** Dim-to color hex (used when afterAnimation is "dimToColor"). */ afterAnimationColor?: string; /** SVG motion path string for custom motion path animations. */ motionPath?: string; /** * Path edit mode for `p:animMotion/@pathEditMode`. Defaults to "relative" * when emitted without an explicit value. */ motionPathEditMode?: string; /** Comma-separated point-types string for `p:animMotion/@ptsTypes`. */ motionPtsTypes?: string; /** Sound relationship ID to play when animation triggers (`p:stSnd`). */ soundRId?: string; /** Resolved sound file path from relationship. */ soundPath?: string; /** Whether to stop any currently playing sound (`p:endSnd`). */ stopSound?: boolean; } //#endregion //#region src/core/types/embedded-font.d.ts interface PptxEmbeddedFontDataId { /** Required relationship identifier from `r:id`. */ relationshipId?: string | null; /** Original leaf retained for unknown attribute preservation. */ rawXml?: XmlObject; } interface PptxEmbeddedFontDescriptor { typeface?: string | null; panose?: string | null; pitchFamily?: string | null; charset?: string | null; rawXml?: XmlObject; } interface PptxEmbeddedFontListEntry { font: PptxEmbeddedFontDescriptor; regular?: PptxEmbeddedFontDataId | null; bold?: PptxEmbeddedFontDataId | null; italic?: PptxEmbeddedFontDataId | null; boldItalic?: PptxEmbeddedFontDataId | null; rawXml?: XmlObject; } interface PptxEmbeddedFontList { fonts: PptxEmbeddedFontListEntry[]; /** Original list retained for unknown attribute and child preservation. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/comment-mentions.d.ts /** * A single `@`-mention inside a modern comment body. * * Offsets index into the comment's FLATTENED plain text: every `a:t` value * below `p188:txBody` concatenated, with paragraphs joined by `\n`. That is the * same string `PptxComment.text` carries, so an edit to `text` invalidates * every offset after the edit point and the serializer re-bases them. * * The markup Office uses for a mention is `CT_Mention` (documented for the * SpreadsheetML `2018/threadedcomments` part): `mentionpersonId`, `mentionId`, * `startIndex` and `length`. The PowerPoint `2018/8/main` schema does not * publish a mention element at all, so `rawXml` is retained and re-emitted * attribute-for-attribute: a producer that spells the attributes differently * still round-trips. * * @example * ```ts * const mention: PptxCommentMention = { * personId: "{2CB2E9D0-D392-EB21-5D46-FBA34C1295E6}", * authorName: "Bob Example", * startIndex: 3, * length: 11, * }; * // => "Hi Bob Example can you check this".slice(3, 14) === "Bob Example" * ``` */ interface PptxCommentMention { /** `mentionId`: GUID identifying this mention instance. */ id?: string; /** `mentionpersonId`: the `p188:author` id of the mentioned person. */ personId: string; /** Display name resolved from the author list at parse time, when known. */ authorName?: string; /** Character offset of the mentioned span in the flattened plain text. */ startIndex: number; /** Character length of the mentioned span. */ length: number; /** * `uri` of the `p188:ext` this mention list was read from. Undefined means * the list is a direct child of `p188:cm`, which is where it is written for * newly authored mentions. */ containerUri?: string; /** Original `p188:mention` node, retained for unknown-attribute preservation. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/metadata.d.ts /** * A slide comment — may be a legacy positional comment or a modern * threaded comment with replies. * * @example * ```ts * const comment: PptxComment = { * id: "c1", * text: "Please update this chart.", * author: "Alice", * createdAt: "2024-06-01T10:00:00Z", * resolved: false, * }; * // => satisfies PptxComment * ``` */ interface PptxComment { id: string; text: string; /** Storage vocabulary used by this comment. Omitted means legacy PresentationML. */ format?: 'legacy' | 'modern'; /** Stable GUID author identifier used by Office 2021 modern comments. */ authorId?: string; /** Optional parent comment id for reply threading metadata. */ parentId?: string; author?: string; createdAt?: string; x?: number; y?: number; /** Whether this comment has been resolved/marked done. */ resolved?: boolean; /** Native p188 status token. */ status?: 'active' | 'resolved' | 'closed'; /** Modern comment classification tags and author IDs that liked the comment. */ tags?: string[]; likes?: string[]; startDate?: string; dueDate?: string; assignedTo?: string[]; /** Task completion in thousandths of a percent, from 0 through 100000. */ complete?: number; priority?: number; title?: string; /** Modern threaded comment support (p15:threadingInfo). */ threadId?: string; /** `@`-mentions, indexed into `text` (see {@link PptxCommentMention}). */ mentions?: PptxCommentMention[]; /** Replies to this comment (for modern threaded comments). */ replies?: PptxComment[]; /** ID of the element this comment is associated with (if any). */ elementId?: string; /** Original `p:cm` subtree, retained for unknown child and extension preservation. */ rawXml?: XmlObject; } /** Office 2021 comment author from the p188 Author part. */ interface PptxModernCommentAuthor { id: string; name: string; initials?: string; userId: string; providerId: string; rawXml?: XmlObject; } /** * A comment author from `ppt/commentAuthors.xml`. * * Stores all attributes needed for lossless round-trip serialization * of the `p:cmAuthor` element (id, name, initials, lastIdx, clrIdx). * * @see ECMA-376 Part 1, §19.4.2 (cmAuthor) * * @example * ```ts * const author: PptxCommentAuthor = { * id: "0", * name: "John Doe", * initials: "JD", * lastIdx: 3, * clrIdx: 0, * }; * // => satisfies PptxCommentAuthor * ``` */ interface PptxCommentAuthor { /** Unique numeric author identifier (`@_id`). */ id: string; /** Author display name (`@_name`). */ name: string; /** Author initials (`@_initials`). */ initials: string; /** Last comment index used by this author (`@_lastIdx`). */ lastIdx: number; /** Colour index assigned to this author (`@_clrIdx`). */ clrIdx: number; /** Original `p:cmAuthor` subtree, retained for unknown attribute preservation. */ rawXml?: XmlObject; } /** * A compatibility warning generated during parse or save when the * file uses features not fully supported by the editor. * * @example * ```ts * const warning: PptxCompatibilityWarning = { * code: "UNSUPPORTED_3D", * message: "3D rotation effects may not render accurately.", * severity: "warning", * scope: "element", * slideId: "slide-1", * elementId: "elem-42", * }; * // => satisfies PptxCompatibilityWarning * ``` */ interface PptxCompatibilityWarning { code: string; message: string; severity: 'info' | 'warning'; scope: 'presentation' | 'slide' | 'element' | 'save'; slideId?: string; elementId?: string; xmlPath?: string; } /** * A single name–value tag from `ppt/tags/*.xml`. * * @example * ```ts * const tag: PptxTag = { name: "CUSTOM_ID", value: "12345" }; * // => satisfies PptxTag * ``` */ interface PptxTag { name: string; value: string; } /** * A collection of tags from a single tags XML part. * * @example * ```ts * const coll: PptxTagCollection = { * path: "ppt/tags/tag1.xml", * tags: [{ name: "CUSTOM_ID", value: "12345" }], * }; * // => satisfies PptxTagCollection * ``` */ interface PptxTagCollection { /** File path within the PPTX archive. */ path?: string; /** Package owner of the tags relationship. New collections default to presentation. */ owner?: 'presentation' | 'slide' | 'part'; /** Source OPC part that owns the relationship, e.g. ppt/slides/slide1.xml. */ sourcePartPath?: string; /** Durable relationship identifier from the owning part. */ relationshipId?: string; /** Tags in this collection. */ tags: PptxTag[]; /** Parsed tag-list XML retained for unknown-node preservation. */ rawXml?: XmlObject; } /** * A custom document property from `docProps/custom.xml`. * * @example * ```ts * const prop: PptxCustomProperty = { * name: "Project", * value: "pptx", * type: "lpwstr", * }; * // => satisfies PptxCustomProperty * ``` */ interface PptxCustomProperty { /** Property name. */ name: string; /** Property value (always stringified). */ value: string; /** Original VT type (e.g. "lpwstr", "i4", "bool", "filetime"). */ type: string; } /** * Core document properties from `docProps/core.xml` (Dublin Core + OOXML). * * @example * ```ts * const core: PptxCoreProperties = { * title: "Q4 Business Review", * creator: "Alice", * created: "2024-01-15T08:00:00Z", * modified: "2024-06-01T12:30:00Z", * lastModifiedBy: "Bob", * }; * // => satisfies PptxCoreProperties * ``` */ interface PptxCoreProperties { /** dc:title */ title?: string; /** dc:subject */ subject?: string; /** dc:creator */ creator?: string; /** cp:keywords */ keywords?: string; /** dc:description */ description?: string; /** cp:lastModifiedBy */ lastModifiedBy?: string; /** cp:revision */ revision?: string; /** dcterms:created (ISO 8601) */ created?: string; /** dcterms:modified (ISO 8601) */ modified?: string; /** cp:category */ category?: string; /** cp:contentStatus */ contentStatus?: string; } /** * Extended (application) properties from `docProps/app.xml`. * * @example * ```ts * const app: PptxAppProperties = { * application: "Microsoft Office PowerPoint", * appVersion: "16.0000", * slides: 24, * words: 1500, * company: "Acme Corp", * }; * // => satisfies PptxAppProperties * ``` */ interface PptxAppProperties { /** Application name (e.g. "Microsoft Office PowerPoint"). */ application?: string; /** Application version string. */ appVersion?: string; /** Presentation format (e.g. "On-screen Show (16:9)"). */ presentationFormat?: string; /** Total number of slides. */ slides?: number; /** Number of hidden slides. */ hiddenSlides?: number; /** Number of notes slides. */ notes?: number; /** Total editing time in minutes. */ totalTime?: number; /** Number of words. */ words?: number; /** Number of paragraphs. */ paragraphs?: number; /** Company name. */ company?: string; /** Manager name. */ manager?: string; /** Template name. */ template?: string; /** Hyperlink base URL. */ hyperlinkBase?: string; /** Document security bitmask (``). */ docSecurity?: number; /** Number of multimedia clips (``). */ mmClips?: number; /** Whether thumbnail images were scaled to fit (``). */ scaleCrop?: boolean; /** Whether hyperlinks are current (``). */ linksUpToDate?: boolean; /** Whether the document is shared (``). */ sharedDoc?: boolean; /** Whether hyperlinks changed since last save (``). */ hyperlinksChanged?: boolean; } //#endregion //#region src/core/types/presentation-print-properties.d.ts type PptxPrintOutput = 'slides' | 'handouts1' | 'handouts2' | 'handouts3' | 'handouts4' | 'handouts6' | 'handouts9' | 'notes' | 'outline'; type PptxPrintColorMode = 'bw' | 'gray' | 'clr'; /** PresentationML `CT_PrintProperties` (`p:prnPr`). */ interface PptxPresentationPrintProperties { printWhat?: PptxPrintOutput | null; colorMode?: PptxPrintColorMode | null; hiddenSlides?: boolean | null; scaleToFitPaper?: boolean | null; frameSlides?: boolean | null; /** Original subtree retained for unknown attributes and `p:extLst`. */ rawXml?: XmlObject; } /** * Resolved hex values for the 12 theme colour slots. * * @example * ```ts * const scheme: PptxThemeColorScheme = { * dk1: "#000000", lt1: "#FFFFFF", * dk2: "#1F497D", lt2: "#EEECE1", * accent1: "#4F81BD", accent2: "#C0504D", * accent3: "#9BBB59", accent4: "#8064A2", * accent5: "#4BACC6", accent6: "#F79646", * hlink: "#0000FF", folHlink: "#800080", * }; * // => satisfies PptxThemeColorScheme * ``` */ interface PptxThemeColorScheme { dk1: string; lt1: string; dk2: string; lt2: string; accent1: string; accent2: string; accent3: string; accent4: string; accent5: string; accent6: string; hlink: string; folHlink: string; } /** * A font-family triplet for major or minor theme fonts. * * Supports Latin, East Asian, and Complex Script font families. * * @example * ```ts * const fonts: PptxThemeFontGroup = { * latin: "Calibri Light", * eastAsia: "MS PGothic", * complexScript: "Arial", * }; * // => satisfies PptxThemeFontGroup * ``` */ interface PptxThemeFontGroup { latin?: string; eastAsia?: string; complexScript?: string; /** * Per-script typeface overrides (`` per ECMA-376 §20.1.4.1.16). Keyed by * the four-letter ISO 15924 script tag from the `script` attribute. * * Phase 4 Stream A / M4. */ byScript?: Record; } /** * Theme font scheme — major (headings) and minor (body) font families. * * @example * ```ts * const scheme: PptxThemeFontScheme = { * majorFont: { latin: "Calibri Light" }, * minorFont: { latin: "Calibri", eastAsia: "MS PGothic" }, * }; * // => satisfies PptxThemeFontScheme * ``` */ interface PptxThemeFontScheme { majorFont?: PptxThemeFontGroup; minorFont?: PptxThemeFontGroup; } /** * A single fill style entry from the theme format scheme. * Each entry is one of: solid, gradient, pattern, or no fill. * The raw XML node is also stored so that `phClr` substitution can happen * at resolution time. * * @example * ```ts * const solidFill: PptxThemeFillStyle = { * kind: "solid", * color: "#4F81BD", * opacity: 1, * }; * * const gradientFill: PptxThemeFillStyle = { * kind: "gradient", * gradientAngle: 90, * gradientType: "linear", * gradientStops: [ * { color: "#4F81BD", position: 0 }, * { color: "#1F497D", position: 1 }, * ], * }; * // => satisfies PptxThemeFillStyle * ``` */ interface PptxThemeFillStyle { /** * Discriminator for the fill type. * * `'group'` corresponds to `` — a fill that inherits the * containing group shape's fill at render time. Captured for round-trip * preservation in `fmtScheme/fillStyleLst`. */ kind: 'solid' | 'gradient' | 'pattern' | 'none' | 'group'; /** Pre-resolved colour (may be `undefined` when `phClr`-dependent). */ color?: string; opacity?: number; /** Gradient-specific fields (only present when `kind === "gradient"`). */ gradientStops?: Array<{ color: string; position: number; opacity?: number; }>; gradientAngle?: number; gradientType?: 'linear' | 'radial'; gradientCss?: string; /** Pattern-specific fields (only present when `kind === "pattern"`). */ patternPreset?: string; patternBackgroundColor?: string; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * A single line style entry from `a:lnStyleLst`. * Provides width, dash, join, cap, and optional fill colour. * * @example * ```ts * const line: PptxThemeLineStyle = { * width: 1.5, * color: "#4F81BD", * dash: "solid", * lineJoin: "round", * lineCap: "flat", * }; * // => satisfies PptxThemeLineStyle * ``` */ interface PptxThemeLineStyle { /** Line width in pixels (converted from EMU). */ width?: number; color?: string; opacity?: number; dash?: string; lineJoin?: 'round' | 'bevel' | 'miter'; lineCap?: 'flat' | 'rnd' | 'sq'; compoundLine?: 'sng' | 'dbl' | 'thickThin' | 'thinThick' | 'tri'; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * A single effect style entry from `a:effectStyleLst`. * Each entry may define shadow, glow, soft-edge, reflection, blur, * and optionally a 3-D scene/shape. * * @example * ```ts * const dropShadow: PptxThemeEffectStyle = { * shadowColor: "#000000", * shadowBlur: 4, * shadowOffsetX: 2, * shadowOffsetY: 3, * shadowOpacity: 0.4, * }; * // => satisfies PptxThemeEffectStyle * ``` */ interface PptxThemeEffectStyle { shadowColor?: string; shadowBlur?: number; shadowOffsetX?: number; shadowOffsetY?: number; shadowOpacity?: number; glowColor?: string; glowRadius?: number; glowOpacity?: number; softEdgeRadius?: number; innerShadowColor?: string; innerShadowOpacity?: number; innerShadowBlur?: number; innerShadowOffsetX?: number; innerShadowOffsetY?: number; reflectionBlurRadius?: number; reflectionStartOpacity?: number; reflectionEndOpacity?: number; reflectionEndPosition?: number; reflectionDirection?: number; reflectionRotation?: number; reflectionDistance?: number; /** 3D scene/camera from `a:scene3d` on the effect style (idx 3 typically). */ scene3d?: Pptx3DScene; /** 3D shape extrusion/bevel from `a:sp3d` on the effect style (idx 3 typically). */ shape3d?: Pptx3DShape; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * The full parsed format scheme from `a:fmtScheme` inside `a:themeElements`. * Contains three fill style lists and one line/effect style list each, * at three intensity levels: subtle (idx 1), moderate (idx 2), intense (idx 3). * * OOXML reference indices: * - fillStyleLst: idx 1-3 (used by `a:fillRef @idx` 1-3) * - lnStyleLst: idx 1-3 (used by `a:lnRef @idx` 1-3) * - effectStyleLst: idx 1-3 (used by `a:effectRef @idx` 1-3) * - bgFillStyleLst: idx 1-3 (used by `a:fillRef @idx` 1001-1003) * * @example * ```ts * const fmt: PptxThemeFormatScheme = { * name: "Office", * fillStyles: [solidFill, gradientFill, intenseFill], * lineStyles: [thinLine, mediumLine, thickLine], * effectStyles: [subtle, moderate, intense], * backgroundFillStyles: [solidBg, gradientBg, intenseBg], * }; * // => satisfies PptxThemeFormatScheme * ``` */ interface PptxThemeFormatScheme { /** The `@name` attribute of the format scheme. */ name?: string; /** Fill styles at indices 1-3 (subtle, moderate, intense). */ fillStyles: PptxThemeFillStyle[]; /** Line styles at indices 1-3. */ lineStyles: PptxThemeLineStyle[]; /** Effect styles at indices 1-3. */ effectStyles: PptxThemeEffectStyle[]; /** Background fill styles at indices 1-3 (referenced via idx 1001-1003). */ backgroundFillStyles: PptxThemeFillStyle[]; } /** * Full parsed theme object available to renderers. * * @example * ```ts * const theme: PptxTheme = { * name: "Office Theme", * colorScheme: { dk1: "#000", lt1: "#FFF", /* … *\/ }, * fontScheme: { * majorFont: { latin: "Calibri Light" }, * minorFont: { latin: "Calibri" }, * }, * }; * // => satisfies PptxTheme * ``` */ interface PptxTheme { /** Theme name from `a:theme @name`. */ name?: string; /** Resolved colour scheme. */ colorScheme?: PptxThemeColorScheme; /** Resolved font scheme. */ fontScheme?: PptxThemeFontScheme; /** Format scheme — fill, line, effect and background fill style matrices. */ formatScheme?: PptxThemeFormatScheme; } //#endregion //#region src/core/types/transition.d.ts /** * Available slide transition effects. * * Maps to the OOXML child element names under `` / ``. * * @example * ```ts * const t: PptxTransitionType = "morph"; * // => "morph" — one of 40+ transition effects * ``` */ type PptxTransitionType = 'none' | 'cut' | 'fade' | 'push' | 'wipe' | 'split' | 'randomBar' | 'blinds' | 'checker' | 'circle' | 'comb' | 'cover' | 'diamond' | 'dissolve' | 'plus' | 'pull' | 'random' | 'strips' | 'uncover' | 'wedge' | 'wheel' | 'zoom' | 'newsflash' | 'morph' | 'conveyor' | 'doors' | 'ferris' | 'flash' | 'flythrough' | 'gallery' | 'glitter' | 'honeycomb' | 'pan' | 'prism' | 'reveal' | 'ripple' | 'shred' | 'switch' | 'vortex' | 'warp' | 'wheelReverse' | 'window' | 'cube' | 'flip' | 'rotate' | 'box' | 'orbit' | 'fallOver' | 'drape' | 'curtains' | 'wind' | 'prestige' | 'fracture' | 'crush' | 'peelOff' | 'pageCurlDouble' | 'pageCurlSingle' | 'airplane' | 'origami'; /** Split orientation from OOXML `@_orient`. */ type PptxSplitOrientation = 'horz' | 'vert'; /** Schema-defined `ST_TransitionSpeed` values. */ type PptxTransitionSpeed = 'slow' | 'med' | 'fast'; /** * Slide transition configuration. * * @example * ```ts * const transition: PptxSlideTransition = { * type: "fade", * durationMs: 700, * advanceOnClick: true, * advanceAfterMs: 5000, * }; * // => { type: "fade", durationMs: 700, advanceOnClick: true, advanceAfterMs: 5000 } * ``` */ /** * Morph granularity (``). * * - `byObject` - match whole shapes (PowerPoint's default) * - `byWord` - additionally morph text word by word * - `byChar` - additionally morph text character by character */ type PptxMorphOption = 'byObject' | 'byWord' | 'byChar'; interface PptxSlideTransition { type: PptxTransitionType; /** Schema-defined transition speed. Defaults to `fast` when omitted. */ speed?: PptxTransitionSpeed; durationMs?: number; direction?: string; advanceOnClick?: boolean; advanceAfterMs?: number; /** Number of spokes for wheel transition (1-8). */ spokes?: number; /** Pattern type for shred transition. */ pattern?: string; /** Through-black flag for blinds/checker (OOXML `@_thruBlk`). */ thruBlk?: boolean; /** Split orientation (horz/vert) parsed from `@_orient`. */ orient?: PptxSplitOrientation; /** * Morph granularity from ``: how finely PowerPoint * matches content between the two slides. Only meaningful when * {@link type} is `morph`; defaults to `byObject` when the attribute is * absent, matching PowerPoint's own default. */ morphOption?: PptxMorphOption; /** Relationship ID of transition sound from `p:sndAc/p:stSnd/@r:embed` when present. */ soundRId?: string; /** Embedded WAV display name from `p:stSnd/p:snd/@name`. */ soundName?: string; /** Whether the transition sound repeats until another sound starts. */ soundLoop?: boolean; /** Resolved transition sound media path within the package. */ soundPath?: string; /** Human-readable sound file name (extracted from soundPath). */ soundFileName?: string; /** * When true, the transition stops the currently-playing sound (OOXML `p:sndAc/p:endSnd`). * Mutually exclusive with `soundRId`/`soundPath` (which use `p:stSnd`). */ stopSound?: boolean; /** Preserved sound-action XML node from `p:sndAc` for lossless round-trip. */ rawSoundAction?: XmlObject; /** Preserved extension-list XML node from `p:extLst` within the transition for lossless round-trip. */ rawExtLst?: XmlObject; /** Original transition node, retained to preserve unknown attributes and children. */ rawTransition?: XmlObject; } //#endregion //#region src/core/types/view-properties.d.ts /** * View properties types parsed from `ppt/viewProps.xml`. * * Models the `p:viewPr` element and its child views: * normalViewPr, slideViewPr, outlineViewPr, notesTextViewPr, * sorterViewPr, notesViewPr. * * @module pptx-types/view-properties */ /** * Scale factor for a view (numerator / denominator percentage). */ interface PptxViewScale { /** Numerator of the scale percentage (e.g. 100 for 100%). */ n: number; /** Denominator of the scale percentage (e.g. 100 for 100%). */ d: number; /** Optional independent vertical scale. When absent, the X scale is used. */ sy?: { n: number; d: number; }; } /** * Origin point for a view (x, y in twips or EMU). */ interface PptxViewOrigin { x: number; y: number; } /** A horizontal or vertical drawing guide in slide coordinates. */ interface PptxViewGuide { orientation?: 'horz' | 'vert'; position?: number; } /** Positive grid spacing from `p:gridSpacing`. */ interface PptxGridSpacing { cx: number; cy: number; } /** * Restored region dimensions for normal view splitter. * Represents `p:restoredLeft` or `p:restoredTop`. */ interface PptxRestoredRegion { /** Size as a percentage of the available space (thousandths of a percent). */ sz: number; /** Whether auto-adjust is enabled. */ autoAdjust?: boolean; } /** * Normal view properties (`p:normalViewPr`). * Controls the splitter positions in normal (editing) view. */ interface PptxNormalViewProperties { /** Whether to show outline icons in the slide panel. */ showOutlineIcons?: boolean; /** Whether the outline/slide panel is snapped closed. */ snapVertSplitter?: boolean; /** Vertical splitter bar state: 'minimized' | 'maximized' | 'restored'. */ vertBarState?: string; /** Horizontal splitter bar state. */ horzBarState?: string; /** Whether to prefer single-slide view in the panel. */ preferSingleView?: boolean; /** Restored left region (slide panel width). */ restoredLeft?: PptxRestoredRegion; /** Restored top region (notes panel height). */ restoredTop?: PptxRestoredRegion; } /** * Common slide view properties shared by slideViewPr, outlineViewPr, * notesTextViewPr, and notesViewPr. */ interface PptxCommonSlideViewProperties { /** Whether snap-to-grid is enabled. */ snapToGrid?: boolean; /** Whether snap-to-objects is enabled. */ snapToObjects?: boolean; /** Whether drawing guides are shown. */ showGuides?: boolean; /** Whether the application may vary the scale automatically. */ variableScale?: boolean; /** Drawing guides shown in this slide view. */ guides?: PptxViewGuide[]; /** View origin (scroll position). */ origin?: PptxViewOrigin; /** View scale. */ scale?: PptxViewScale; } /** * Full view properties from `ppt/viewProps.xml`. */ interface PptxViewProperties { /** Last used view type (`p:viewPr/@lastView`). */ lastView?: string; /** Whether comments are shown (`p:viewPr/@showComments`). */ showComments?: boolean; /** Normal view properties (splitter positions). */ normalViewPr?: PptxNormalViewProperties; /** Slide view properties. */ slideViewPr?: PptxCommonSlideViewProperties; /** Outline view properties. */ outlineViewPr?: PptxCommonSlideViewProperties; /** Notes text view properties. */ notesTextViewPr?: PptxCommonSlideViewProperties; /** Sorter view scale. */ sorterViewPr?: { scale?: PptxViewScale; }; /** Notes view properties. */ notesViewPr?: PptxCommonSlideViewProperties; /** Grid spacing in positive DrawingML coordinates. */ gridSpacing?: PptxGridSpacing; /** Raw XML preserved for lossless round-trip of unparsed attributes. */ rawXml?: Record; } //#endregion //#region src/core/types/presentation.d.ts /** * A customer data reference from `p:custDataLst / p:custData`. * * Enterprise add-ins and integrations store custom data parts in the * package and reference them via relationship IDs in the slide or * presentation XML. * * @see ECMA-376 Part 1, §19.2.1.3 (custDataLst), §19.3.1.6 (custData) */ interface PptxCustomerData { /** Resolved part path inside the package (e.g. `customXml/item1.xml`). */ id?: string; /** Relationship ID referencing the custom data part. */ relId?: string; /** Raw string content of the custom data part (if resolvable). */ data?: string; /** OPC content type for the custom data part. */ contentType?: string; /** Raw `p:custData` XML retained for unknown-node preservation. */ rawXml?: XmlObject; } /** * An ActiveX control reference from `p:controls / p:control`. * * ActiveX form controls (buttons, text boxes, check boxes, combo boxes, etc.) * are embedded via OLE parts and referenced by relationship ID in the slide XML. * * @see ECMA-376 Part 1, §19.3.1.3 (controls), §19.3.1.2 (control) */ interface PptxActiveXControl { /** Relationship ID referencing the ActiveX binary part. */ relId: string; /** Control name from @name attribute. */ name?: string; /** Shape ID this control is linked to (from @spid). */ shapeId?: string; /** X position (px) of the control's fallback picture, if present. */ x?: number; /** Y position (px) of the control's fallback picture, if present. */ y?: number; /** Width (px) of the control's fallback picture, if present. */ width?: number; /** Height (px) of the control's fallback picture, if present. */ height?: number; /** * Relationship ID of the control's static fallback picture * (`mc:AlternateContent > mc:Fallback > p:pic > p:blipFill > a:blip@r:embed`). * Renderers resolve this to an image so a control shows its last static * frame instead of a blank area (the live ActiveX cannot run in a viewer). */ fallbackImageRelId?: string; /** Raw XML for round-trip preservation. */ rawXml?: XmlObject; } /** * Pattern fill on a slide background. * * Mirrors the `` choice inside ``. Renderers should * draw a 2-colour preset pattern (e.g. `dkDnDiag`, `pct50`). * * ECMA-376 §20.1.8.47. * * @example * ```ts * const pattern: PptxSlideBackgroundPattern = { * preset: "ltDnDiag", * fgColor: "#4472C4", * bgColor: "#FFFFFF", * }; * // => satisfies PptxSlideBackgroundPattern * ``` */ interface PptxSlideBackgroundPattern { /** DrawingML preset pattern token (`@_prst`). */ preset: string; /** Foreground colour resolved to `#RRGGBB`. */ fgColor?: string; /** Background colour resolved to `#RRGGBB`. */ bgColor?: string; } /** * A single slide in a parsed PPTX presentation. * * Contains the element tree, background settings, notes, comments, * transition / animation data, and metadata like layout path and section. * * @example * ```ts * const slide: PptxSlide = { * id: "slide1", * rId: "rId2", * slideNumber: 1, * elements: [titleTextBox, subtitleTextBox], * backgroundColor: "#FFFFFF", * notes: "Remember to mention quarterly goals.", * }; * // => satisfies PptxSlide * ``` */ interface PptxSlide { id: string; rId: string; /** * `p:sldIdLst/p:sldId/@id` (ST_SlideId, 256..2147483647): the numeric key * that sections (`p14:sldIdLst/p14:sldId/@id`) and section/summary zooms * name slides by. * * It lives in `presentation.xml`, NOT in the slide part, so it cannot be * recovered from `rawXml`. Without it on the model, code that writes a * section's membership has nothing correct to write and falls back to the * slide NUMBER, which is 1-based and therefore never matches a real deck's * ids: the section reloads with no slides in it. */ slideId?: string; sourceSlideId?: string; /** Optional author-supplied slide name (set via `SlideBuilder.setName`). */ name?: string; layoutPath?: string; layoutName?: string; slideNumber: number; hidden?: boolean; sectionName?: string; sectionId?: string; elements: PptxElement[]; backgroundColor?: string; backgroundImage?: string; backgroundGradient?: string; /** * Pattern fill on the slide background (`` inside ``). * * When present, renderers should draw a real two-colour pattern using * the named DrawingML preset (e.g. `"ltDnDiag"`, `"pct50"`). The flat * `backgroundColor` field is left set to the foreground colour for * fallback rendering paths that don't understand patterns. * * ECMA-376 §20.1.8.47. */ backgroundPattern?: PptxSlideBackgroundPattern; /** * ``: boolean flag instructing the renderer to * shade the background toward the title placeholder colour. Captured * for lossless round-trip only; no renderer applies the visual effect. * Documented as a known no-op in `docs/guide/limitations.md` (legacy * PowerPoint 97-2003 hint, not observed in any real-world corpus file * and not settable from any modern PowerPoint UI). * * ECMA-376 §19.3.1.2 (CT_BackgroundProperties). */ backgroundShadeToTitle?: boolean; transition?: PptxSlideTransition; animations?: PptxElementAnimation[]; /** Native OOXML animation data parsed from `p:timing`. */ nativeAnimations?: PptxNativeAnimation[]; /** Preserved raw `p:timing` XML for lossless round-trip of native animations. */ rawTiming?: XmlObject; notes?: string; /** Rich text segments for the slide notes (preserves formatting). */ notesSegments?: TextSegment[]; /** * Parsed shapes from the notes slide's `/` so the full * notes-page shape tree can be inspected and mutated, not just the body * placeholder text. When undefined, the existing notes XML is left * untouched on save and only `notes` / `notesSegments` are written. */ notesShapes?: PptxElement[]; /** * Per-notes-slide colour map override parsed from `/`. * Captured for lossless round-trip of the notes-slide's colour scheme. */ notesClrMapOverride?: Record; /** Optional `` value of the notes slide, for round-trip. */ notesCSldName?: string; comments?: PptxComment[]; /** Source package metadata for an Office 2021 p188 comment part. */ modernCommentPart?: PptxModernCommentPart; warnings?: PptxCompatibilityWarning[]; rawXml?: XmlObject; /** Per-slide colour map override parsed from `p:clrMapOvr`. */ clrMapOverride?: Record; /** Whether background animations should play (`p:bg/@showAnimation`). */ backgroundShowAnimation?: boolean; /** Whether master slide shapes should be shown on this slide (`p:sld/@showMasterSp`). */ showMasterShapes?: boolean; /** * Whether inherited master placeholder animations should replay on this * slide (`p:sld/@showMasterPhAnim`). Distinct from {@link showMasterShapes}: * this governs animation timing, not shape visibility. Mirrors * `p:sldLayout/@showMasterPhAnim`, ECMA-376 §19.3.1.38. */ showMasterPhAnim?: boolean; /** Drawing guides parsed from slide extension list. */ guides?: PptxDrawingGuide[]; /** When explicitly `false`, the slide is unmodified and save can skip re-serialization. */ isDirty?: boolean; /** Customer data references from `p:custDataLst` on this slide. */ customerData?: PptxCustomerData[]; /** ActiveX control references from `p:controls` on this slide. */ activeXControls?: PptxActiveXControl[]; /** * Shapes parsed from a referenced legacy VML drawing part * (`ppt/drawings/vmlDrawing*.vml`, linked via a `legacyDrawing` * relationship). These are read-only render hints: the VML part itself is * preserved verbatim on save, so this field is not re-serialized. */ legacyVmlElements?: PptxElement[]; /** Per-slide header/footer flags from `` (P-H3). */ headerFooterFlags?: PptxHeaderFooterFlags; /** Server-backed slide synchronization metadata stored in a related OPC part. */ slideSynchronization?: PptxSlideSyncProperties; } interface PptxModernCommentPart { path: string; relationshipId: string; /** Original p188:cmLst root, including unknown attributes and extensions. */ rawXml?: XmlObject; } /** Metadata from a `p:sldSyncPr` slide synchronization data part. */ interface PptxSlideSyncProperties { serverSlideId: string; serverSlideModifiedTime: string; clientInsertedTime: string; extensionList?: XmlObject; rawXml?: XmlObject; partPath?: string; relationshipId?: string; } /** * A slide layout available in the loaded presentation. * * Each entry maps to a `` inside `ppt/slideLayouts/`. * * @example * ```ts * const layout: PptxLayoutOption = { * path: "ppt/slideLayouts/slideLayout2.xml", * name: "Title and Content", * }; * // => satisfies PptxLayoutOption * ``` */ interface PptxLayoutOption { path: string; name: string; /** Standard layout type from `p:sldLayout/@type` (e.g. "obj", "twoColTx", "blank"). */ type?: string; /** ZIP path of the slide master this layout belongs to. */ masterPath?: string; } /** * Header, footer, date-time, and slide-number placeholders. * * Parsed from `ppt/presProps.xml` and individual slide layouts. * * @example * ```ts * const hf: PptxHeaderFooter = { * hasFooter: true, * footerText: "Confidential", * hasSlideNumber: true, * }; * // => satisfies PptxHeaderFooter * ``` */ interface PptxHeaderFooter { hasHeader?: boolean; headerText?: string; hasFooter?: boolean; footerText?: string; hasDateTime?: boolean; dateTimeText?: string; dateTimeAuto?: boolean; /** OOXML date format pattern (e.g. "M/d/yyyy", "dddd, MMMM dd, yyyy"). */ dateFormat?: string; hasSlideNumber?: boolean; } /** * Presentation-level properties parsed from `presentationPr.xml`. * * Controls slideshow behaviour, print settings, custom colours, and grid. * * @example * ```ts * const props: PptxPresentationProperties = { * showType: "presented", * loopContinuously: false, * advanceMode: "useTimings", * }; * // => satisfies PptxPresentationProperties * ``` */ interface PptxPresentationProperties { /** Show type: presented, browsed, kiosk. */ showType?: 'presented' | 'browsed' | 'kiosk'; /** Whether to loop the slideshow continuously. */ loopContinuously?: boolean; /** Whether to show without narration. */ showWithNarration?: boolean; /** Whether to show without animation. */ showWithAnimation?: boolean; /** Advance slides mode: manual click or use stored timings. */ advanceMode?: 'manual' | 'useTimings'; /** Show slides: 'all', a custom show id, or a from-to range. */ showSlidesMode?: 'all' | 'customShow' | 'range'; /** Custom show id to use when showSlidesMode is 'customShow'. */ showSlidesCustomShowId?: string; /** Slide range start (1-based) when showSlidesMode is 'range'. */ showSlidesFrom?: number; /** Slide range end (1-based) when showSlidesMode is 'range'. */ showSlidesTo?: number; /** Whether to show subtitles/captions during presentation mode. */ showSubtitles?: boolean; /** Typed `p:prnPr` settings. Set to null during save to remove the element. */ printProperties?: PptxPresentationPrintProperties | null; /** Most-recently-used colours from the presentation palette. */ mruColors?: string[]; /** Pen colour for presentation mode annotations (from `p:showPr/p:penClr`). */ penColor?: string; /** Kiosk auto-restart interval in milliseconds (from `p:kiosk/@restart`). Only meaningful when showType is "kiosk". */ kioskRestartTime?: number; } /** * Slide dimensions from `p:sldSz` (CT_SlideSize, ECMA-376 §19.2.1.39). * * @example * ```ts * const size: PptxSlideSize = { widthEmu: 9144000, heightEmu: 6858000, type: 'screen4x3' }; * // => satisfies PptxSlideSize * ``` */ interface PptxSlideSize { /** `@cx` in EMU. Omitted or non-positive values leave the loaded width alone. */ widthEmu?: number; /** `@cy` in EMU. Omitted or non-positive values leave the loaded height alone. */ heightEmu?: number; /** * `@type` (ST_SlideSizeType). The schema default is `custom`, which is * why PowerPoint omits the attribute for a non-preset size. */ type?: string; } /** * A named custom slide show (`p:custShowLst / p:custShow`). * * Custom shows define ordered subsets of slides that can be presented * independently of the full deck. * * @example * ```ts * const show: PptxCustomShow = { * name: "Executive Summary", * id: "0", * slideRIds: ["rId2", "rId5", "rId8"], * }; * // => satisfies PptxCustomShow * ``` */ interface PptxCustomShow { /** Custom show name. */ name: string; /** Custom show id. */ id: string; /** Ordered list of slide relationship IDs included in this custom show. */ slideRIds: string[]; /** Original `p:custShow` subtree used to preserve unmodelled attributes and extensions. */ rawXml?: XmlObject; } /** * An ordered section in the presentation (from `p:sectionLst` / `p14:sectionLst`). * * Sections group consecutive slides under a named heading (visible * in the PowerPoint slide sorter). * * @example * ```ts * const section: PptxSection = { * id: "sec_1", * name: "Introduction", * slideIds: ["256", "257"], * }; * // => satisfies PptxSection * ``` */ interface PptxSection { /** Section unique identifier (GUID or synthetic). */ id: string; /** Human-readable section name. */ name: string; /** Ordered list of numeric slide IDs that belong to this section. */ slideIds: string[]; /** Whether the section is collapsed in the slide sorter (from p15:sectionPr). */ collapsed?: boolean; /** Section highlight color hex (from p15:sectionPr/@clr). */ color?: string; /** Original section subtree used to preserve unmodelled attributes and extensions. */ rawXml?: XmlObject; } /** * Write-protection hash data parsed from `p:modifyVerifier` in `presentation.xml`. * * When present, the presentation is marked as "read-only recommended" or * write-protected with a password hash. The hash parameters follow the * ECMA-376 Part 1, section 19.2.1.22 specification. * * @example * ```ts * const verifier: PptxModifyVerifier = { * algorithmName: "SHA-512", * hashData: "base64EncodedHash==", * saltData: "base64EncodedSalt==", * spinValue: 100000, * }; * // => satisfies PptxModifyVerifier * ``` */ interface PptxModifyVerifier { /** Hash algorithm name (e.g. "SHA-512", "SHA-1"). */ algorithmName?: string; /** Base64-encoded hash value. */ hashData?: string; /** Base64-encoded salt value. */ saltData?: string; /** Number of hash iterations (spin count). */ spinValue?: number; /** Legacy algorithm ID extension. */ algIdExt?: string; /** Legacy algorithm ID. */ cryptAlgorithmSid?: number; /** Cryptographic algorithm type (e.g. "typeAny"). */ cryptAlgorithmType?: string; /** Cryptographic provider name. */ cryptProvider?: string; /** Cryptographic provider type (e.g. "providerTypeRsaFull"). */ cryptProviderType?: string; /** Cryptographic algorithm class (e.g. "hash"). */ cryptAlgorithmClass?: string; } /** * Photo album metadata from `p:photoAlbum` in `presentation.xml`. * * Stores settings for presentations created via Insert > Photo Album. * * @see ECMA-376 Part 1, §19.2.1.27 */ interface PptxPhotoAlbum { /** Whether photos are displayed in black-and-white. */ bw?: boolean; /** Whether captions are shown below each photo. */ showCaptions?: boolean; /** Photo album layout (e.g. "1pic", "2pic", "4pic", "fitToSlide"). */ layout?: string; /** Frame style applied to each photo (e.g. "frameStyle1"). */ frame?: string; } /** * East Asian line-break (kinsoku) settings from `p:kinsoku` in `presentation.xml`. * * Defines forbidden start/end characters for a given language so that * line-breaking follows East Asian typographic rules. * * @see ECMA-376 Part 1, §19.2.1.17 */ interface PptxKinsoku { /** Language code (e.g. "ja-JP", "zh-CN"). */ lang?: string | null; /** Characters that cannot begin a line. */ invalStChars?: string; /** Characters that cannot end a line. */ invalEndChars?: string; /** Original leaf retained for unknown attribute preservation. */ rawXml?: XmlObject; } /** * Root data structure returned by {@link PptxHandlerCore.load}. * * Contains every slide, canvas dimensions, theme data, layout options, * metadata, and optional features (custom shows, sections, macros, * digital signatures, embedded fonts). * * @example * ```ts * const data: PptxData = await handler.load(buffer); * console.log(`${data.slides.length} slides, ${data.width}×${data.height}`); * // => e.g. "24 slides, 960×540" * ``` */ interface PptxData { slides: PptxSlide[]; width: number; height: number; /** Slide width in EMU (for save round-trip). */ widthEmu?: number; /** Slide height in EMU (for save round-trip). */ heightEmu?: number; /** Slide size type from `p:sldSz/@type` (e.g. "screen4x3", "screen16x9", "custom"). */ slideSizeType?: string; /** Notes page width in EMU (from `p:notesSz`). */ notesWidthEmu?: number; /** Notes page height in EMU (from `p:notesSz`). */ notesHeightEmu?: number; layoutOptions?: PptxLayoutOption[]; headerFooter?: PptxHeaderFooter; /** Presentation-level properties parsed from `presentationPr.xml`. */ presentationProperties?: PptxPresentationProperties; /** Named custom slide shows from `p:custShowLst`. */ customShows?: PptxCustomShow[]; /** Ordered presentation sections from `p:sectionLst` / `p14:sectionLst`. */ sections?: PptxSection[]; warnings?: PptxCompatibilityWarning[]; /** Map of theme colour scheme keys to resolved hex values. */ themeColorMap?: Record; /** Full parsed theme object with colours, fonts, and name. */ theme?: PptxTheme; /** Available theme parts discovered in `ppt/theme/`. */ themeOptions?: PptxThemeOption[]; /** Parsed table style definitions from `ppt/tableStyles.xml`. */ tableStyleMap?: ParsedTableStyleMap; /** Whether the presentation is password-protected. */ isPasswordProtected?: boolean; /** Embedded font data (name + binary data URL) extracted from the presentation. */ embeddedFonts?: PptxEmbeddedFont[]; /** Typed `p:embeddedFontLst` package metadata, including unresolved variants. */ embeddedFontList?: PptxEmbeddedFontList; /** * `p:presentation/@embedTrueTypeFonts` (ECMA-376 §19.2.1.26): the author's * saved preference that TrueType fonts referenced by the deck be embedded. * `undefined` when the attribute is absent (spec default `false`). * * This is purely declarative in this library: fonts are only ever embedded * when the caller explicitly supplies `embeddedFontList`/`embeddedFonts` * (there is no automatic embed-on-save), so toggling this flag does not * gate any embedding behaviour of its own here - it only round-trips the * author's stated preference, the same way real PowerPoint reads it back * as a checkbox state rather than a trigger. See `@saveSubsetFonts`, * which is a separate, deliberately unimplemented flag (no glyph * subsetting) that does not interact with this one. */ embedTrueTypeFonts?: boolean; /** * Presentation-level default text style (`p:defaultTextStyle`): the * last-resort paragraph/run-property fallback for every shape (placeholder * or not) whose local and inherited cascade leaves a field undefined. * Keyed the same way as {@link PptxMasterTextStyles} categories: `-1` is * `a:defPPr`, `0`-`8` are `a:lvl1pPr`-`a:lvl9pPr`. */ defaultTextStyle?: PptxTextStyleLevels; /** Most-recently-used colour list from presentation properties. */ mruColors?: string[]; /** Parsed notes master data if present in the PPTX. */ notesMaster?: PptxNotesMaster; /** Parsed handout master data if present in the PPTX. */ handoutMaster?: PptxHandoutMaster; /** Structured slide master data for each master in the presentation. */ slideMasters?: PptxSlideMaster[]; /** Parsed tag collections attached to the presentation or slides. */ tags?: PptxTagCollection[]; /** Custom document properties from `docProps/custom.xml`. */ customProperties?: PptxCustomProperty[]; /** Core document properties from `docProps/core.xml`. */ coreProperties?: PptxCoreProperties; /** Extended (application) properties from `docProps/app.xml`. */ appProperties?: PptxAppProperties; /** Whether the presentation contains VBA macros (is a .pptm file). */ hasMacros?: boolean; /** Whether the presentation contains digital signatures (`_xmlsignatures/` parts). */ hasDigitalSignatures?: boolean; /** Number of digital signatures found. */ digitalSignatureCount?: number; /** Presentation-level drawing guides from `p:extLst`. */ presentationGuides?: PptxDrawingGuide[]; /** View properties from `ppt/viewProps.xml`. */ viewProperties?: PptxViewProperties; /** Write-protection verifier from `p:modifyVerifier` in `presentation.xml`. */ modifyVerifier?: PptxModifyVerifier; /** Photo album metadata from `p:photoAlbum` in `presentation.xml`. */ photoAlbum?: PptxPhotoAlbum; /** East Asian line-break settings from `p:kinsoku` in `presentation.xml`. */ kinsoku?: PptxKinsoku; /** Custom XML data parts from `customXml/` in the OPC package. */ customXmlParts?: PptxCustomXmlPart[]; /** Customer data references from `p:custDataLst` in `presentation.xml`. */ customerData?: PptxCustomerData[]; /** Thumbnail image binary data from `docProps/thumbnail.{jpeg,png}`. */ thumbnailData?: Uint8Array; /** Comment authors parsed from `ppt/commentAuthors.xml` for round-trip preservation. */ commentAuthors?: PptxCommentAuthor[]; /** Office 2021 p188 authors from the modern Author part. */ modernCommentAuthors?: PptxModernCommentAuthor[]; /** * OOXML conformance class of the loaded file. * - `'strict'` -- ISO/IEC 29500 Strict (uses `purl.oclc.org` namespace URIs) * - `'transitional'` -- ECMA-376 Transitional (uses `schemas.openxmlformats.org` URIs) * * When saving, if the save option `conformance` is `'preserve'` (default), * the file will be saved using the same conformance class as the original. */ conformance?: 'strict' | 'transitional'; } /** * Target format for slide export. * * @see {@link PptxExportOptions} */ type PptxExportFormat = 'pdf' | 'png' | 'svg'; /** * Options controlling slide export to raster or vector formats. * * @example * ```ts * const opts: PptxExportOptions = { * format: "png", * slideIndices: [0, 2, 4], * dpi: 300, * }; * // => satisfies PptxExportOptions * ``` */ interface PptxExportOptions { /** Target format. */ format: PptxExportFormat; /** Slide indices to export (0-based). If omitted, all slides are exported. */ slideIndices?: number[]; /** Output width in pixels (for PNG). Height is derived from aspect ratio. */ width?: number; /** DPI for raster export (default 150). */ dpi?: number; /** Whether to include hidden slides. */ includeHidden?: boolean; } /** * Embedded font data extracted from a PPTX file. * * Used to register `@font-face` rules so the renderer can display * the correct typeface even when the system font is missing. * * @example * ```ts * const font: PptxEmbeddedFont = { * name: "CustomSans", * dataUrl: "data:font/truetype;base64,AAEAK...", * format: "truetype", * }; * // => satisfies PptxEmbeddedFont * ``` */ /** * A single Custom XML Data Part stored in `customXml/` within the OPC package. * * These parts are used by add-ins, data-binding, and enterprise templates * to store structured data alongside the presentation. * * @see ECMA-376 Part 1, §15.2.5 */ interface PptxCustomXmlPart { /** Item number (e.g. "1" for `customXml/item1.xml`). */ id: string; /** Raw XML string content of the custom XML item. */ data: string; /** Schema target namespace URI from `itemProps` (ds:schemaRef/@ds:uri). */ schemaUri?: string; /** Raw XML string content of the associated `itemProps` file. */ properties?: string; /** Raw XML string content of the OPC relationship file (`customXml/_rels/item{id}.xml.rels`). */ rels?: string; } interface PptxEmbeddedFont { name: string; dataUrl: string; bold?: boolean; italic?: boolean; /** CSS font format hint (e.g. "truetype", "opentype"). */ format?: 'truetype' | 'opentype' | 'woff' | 'woff2'; /** * Deobfuscated (clear-text) font binary data preserved from load * for round-trip re-embedding on save. When present, the save * pipeline will re-obfuscate and write this data back into the ZIP. */ rawFontData?: Uint8Array; /** * Original ZIP path of the font part (e.g. `ppt/fonts/{GUID}.fntdata`). * Preserved from load for round-trip. */ partPath?: string; /** * The GUID used for obfuscation, either from the `fontKey` attribute * or extracted from the part path. Preserved from load for round-trip. */ fontGuid?: string; /** * Relationship ID (e.g. `rId21`) of the font part in * `ppt/_rels/presentation.xml.rels`. Preserved from load so the save * pipeline can reuse the original part/rel instead of minting a new * GUID-named copy alongside the stale original. */ originalRId?: string; /** * Raw bytes of the original obfuscated font part exactly as they were * stored in the source ZIP. When the loader could not determine a * usable GUID (e.g. EOT extraction path), the save pipeline preserves * these bytes verbatim under the original path/rel. */ originalPartBytes?: Uint8Array; } //#endregion //#region ../core/dist/index-DNZI4fvG.d.ts //#region src/core/types/theme-presets.d.ts /** * A complete theme preset that can be applied to a presentation. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const office = THEME_PRESETS.find(p => p.id === "office"); * await handler.switchTheme(office.colorScheme, office.fontScheme, office.name); * ``` */ interface PptxThemePreset { /** Unique identifier for the preset. */ id: string; /** Human-readable display name. */ name: string; /** The 12-colour scheme. */ colorScheme: PptxThemeColorScheme; /** Heading and body font families. */ fontScheme: PptxThemeFontScheme; } //#endregion //#region src/core/builders/sdk/types.d.ts /** Position and size in pixels. Converted to EMU internally when needed. */ interface ElementPosition { x: number; y: number; width: number; height: number; rotation?: number; } type FillInput = { type: 'solid'; color: string; opacity?: number; } | { type: 'gradient'; /** * Gradient direction in the OOXML `a:lin/@ang` convention: degrees * clockwise from the positive x-axis, pointing from the first stop * towards the last (`0` = left to right, `90` = top to bottom). This is * what lands in `ShapeStyle.fillGradientAngle` and what is written back * to the file, NOT a CSS `linear-gradient()` angle. */ angle?: number; gradientType?: 'linear' | 'radial'; stops: Array<{ color: string; position: number; opacity?: number; }>; } | { type: 'pattern'; preset: string; foreground?: string; background?: string; } | { type: 'image'; url: string; mode?: 'stretch' | 'tile'; } | { type: 'none'; }; interface StrokeInput { color?: string; width?: number; dash?: StrokeDashType; opacity?: number; join?: 'round' | 'bevel' | 'miter'; cap?: 'flat' | 'rnd' | 'sq'; } interface ShadowInput { color?: string; blur?: number; offsetX?: number; offsetY?: number; opacity?: number; } interface TextStyleInput { fontSize?: number; fontFamily?: string; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; color?: string; alignment?: 'left' | 'center' | 'right' | 'justify'; verticalAlignment?: 'top' | 'middle' | 'bottom'; lineSpacing?: number; spaceBefore?: number; spaceAfter?: number; } interface TextSegmentInput { text: string; style?: Partial; } interface TextOptions extends Partial { fontSize?: number; fontFamily?: string; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; color?: string; alignment?: 'left' | 'center' | 'right' | 'justify'; verticalAlignment?: 'top' | 'middle' | 'bottom'; lineSpacing?: number; fill?: FillInput; stroke?: StrokeInput; shadow?: ShadowInput; opacity?: number; } interface ShapeOptions extends Partial { fill?: FillInput; stroke?: StrokeInput; text?: string; textStyle?: Partial; adjustments?: Record; shadow?: ShadowInput; opacity?: number; } interface ImageOptions extends Partial { altText?: string; cropLeft?: number; cropTop?: number; cropRight?: number; cropBottom?: number; opacity?: number; } interface TableInput { rows: TableRowInput[]; columnWidths?: number[]; style?: string; bandRows?: boolean; bandColumns?: boolean; firstRow?: boolean; lastRow?: boolean; firstCol?: boolean; lastCol?: boolean; } interface TableRowInput { cells: TableCellInput[]; height?: number; } interface TableCellInput { text: string; style?: Partial; fill?: FillInput; gridSpan?: number; rowSpan?: number; } interface TableOptions extends Partial {} interface ChartSeriesInput { name: string; values: number[]; color?: string; boxWhiskerOptions?: PptxChartBoxWhiskerOptions; histogramOptions?: PptxChartHistogramOptions; waterfallOptions?: PptxChartWaterfallOptions; regionMapOptions?: PptxChartRegionMapOptions; treemapOptions?: PptxChartTreemapOptions; } interface ChartInput { series: ChartSeriesInput[]; categories: string[]; /** ChartEx hierarchy levels in leaf-to-root XML order. */ categoryLevels?: string[][]; title?: string; hasLegend?: boolean; legendPosition?: 't' | 'b' | 'l' | 'r' | 'tr'; grouping?: 'clustered' | 'stacked' | 'percentStacked'; /** Bar series direction (`c:barDir`): vertical columns (default) or horizontal bars. */ barDirection?: 'col' | 'bar'; } interface ChartOptions extends Partial {} interface ConnectorOptions extends Partial { type?: 'straight' | 'bent' | 'curved'; stroke?: StrokeInput; startArrow?: ConnectorArrowType; endArrow?: ConnectorArrowType; from?: { elementId: string; site: number; }; to?: { elementId: string; site: number; }; } interface MediaOptions extends Partial { autoPlay?: boolean; loop?: boolean; volume?: number; trimStartMs?: number; trimEndMs?: number; posterFrame?: string; } interface GroupOptions extends Partial {} type BackgroundInput = { type: 'solid'; color: string; } | { type: 'gradient'; /** * Slide backgrounds are stored as a ready-made CSS gradient string * (`PptxSlide.backgroundGradient`), so this is a CSS * `linear-gradient()` angle: degrees clockwise from "to top" * (`90` = left to right, `180` = top to bottom). Defaults to `180`. */ angle?: number; stops: Array<{ color: string; position: number; }>; } | { type: 'image'; source: string; }; interface TransitionInput { type: PptxTransitionType; duration?: number; direction?: string; advanceAfterMs?: number; } interface AnimationInput { preset: PptxAnimationPreset; trigger?: PptxAnimationTrigger; duration?: number; delay?: number; } interface PresentationOptions { /** Slide width in EMU. Default: 12192000 (16:9 widescreen). */ width?: number; /** Slide height in EMU. Default: 6858000 (16:9 widescreen). */ height?: number; /** Theme configuration. */ theme?: PresentationThemeInput; /** Presentation title (stored in docProps/core.xml). */ title?: string; /** Presentation author. */ creator?: string; /** * Number of blank slides to include in the initial presentation. * Default: 0 (no slides). Slides use the "Blank" layout. */ initialSlideCount?: number; } interface PresentationThemeInput { name?: string; colors?: { dk1?: string; lt1?: string; dk2?: string; lt2?: string; accent1?: string; accent2?: string; accent3?: string; accent4?: string; accent5?: string; accent6?: string; hlink?: string; folHlink?: string; }; fonts?: { majorFont?: string; minorFont?: string; }; } //#endregion //#region src/core/builders/sdk/SlideBuilder.d.ts /** * Fluent builder for a single slide. * * @example * ```ts * const slide = new SlideBuilder(1) * .addText("Hello World", { fontSize: 36, bold: true, x: 100, y: 50 }) * .addShape("roundRect", { fill: { type: "solid", color: "#4472C4" } }) * .setNotes("Remember to mention key points") * .setBackground({ type: "solid", color: "#F5F5F5" }) * .build(); * ``` */ declare class SlideBuilder { private readonly slide; /** * @param slideNumber - 1-based slide number. * @param layoutPath - Optional layout archive path. * @param layoutName - Optional layout display name. */ constructor(slideNumber: number, layoutPath?: string, layoutName?: string); /** Add a text box to the slide. */ addText(text: string | TextSegmentInput[], options?: TextOptions): this; /** Add a shape to the slide. */ addShape(shapeType: string, options?: ShapeOptions): this; /** Add a connector (line) to the slide. */ addConnector(options?: ConnectorOptions): this; /** Add an image to the slide. */ addImage(source: string, options?: ImageOptions): this; /** Add a table to the slide. */ addTable(input: TableInput, options?: TableOptions): this; /** Add a chart to the slide. */ addChart(chartType: PptxChartType, input: ChartInput, options?: ChartOptions): this; /** Add a media element (video or audio) to the slide. */ addMedia(mediaType: 'video' | 'audio', source: string, options?: MediaOptions): this; /** Add a group of elements to the slide. */ addGroup(children: PptxElement[], options?: GroupOptions): this; /** Add a pre-built element directly. */ addElement(element: PptxElement): this; /** Set slide background. */ setBackground(bg: BackgroundInput): this; /** Set slide transition. */ setTransition(input: TransitionInput): this; /** Add an animation to an element on this slide. */ addAnimation(elementId: string, input: AnimationInput): this; /** Set speaker notes. */ setNotes(text: string): this; /** Mark the slide as hidden. */ setHidden(hidden: boolean): this; /** Assign the slide to a section. */ setSection(name: string, id?: string): this; /** * Add a freeform shape from SVG path data. * * Creates a custom-geometry shape element using the provided SVG path * string and appends it to the slide's element list. * * @param pathData - An SVG path data string (e.g. `"M 0 0 L 100 50 L 50 100 Z"`). * @param options - Optional position, styling, and size overrides. * @returns The builder instance for chaining. * * @example * ```ts * new SlideBuilder(1) * .addFreeform("M 0 0 C 33 0 66 100 100 100", { * stroke: { color: "#FF0000", width: 2 }, * }) * .build(); * ``` */ addFreeform(pathData: string, options?: ShapeOptions): this; /** * Add a pre-built element from any element builder (calls `.build()` for you). * * Accepts any object with a `build()` method that returns a {@link PptxElement}, * such as {@link TextBuilder}, {@link ShapeBuilder}, {@link ImageBuilder}, etc. * * @param builder - An element builder with a `.build()` method. * @returns The builder instance for chaining. * * @example * ```ts * const title = TextBuilder.create("Hello").fontSize(36).bold(); * new SlideBuilder(1).addBuilderElement(title).build(); * ``` */ addBuilderElement(builder: { build(): PptxElement; }): this; /** * Remove an element by its ID. * * Filters out the element with the given ID from the slide's element list. * If no element matches, the slide is left unchanged. * * @param elementId - The unique ID of the element to remove. * @returns The builder instance for chaining. * * @example * ```ts * const slide = new SlideBuilder(1) * .addText("temp", { x: 0, y: 0 }) * .removeElement("txt_abc123_1") * .build(); * ``` */ removeElement(elementId: string): this; /** * Get the current list of elements on this slide. * * Returns a readonly view of the elements array. Useful for inspecting * what has been added so far during the build process. * * @returns A readonly array of the slide's current elements. * * @example * ```ts * const builder = new SlideBuilder(1).addText("Hi"); * console.log(builder.getElements().length); // 1 * ``` */ getElements(): readonly PptxElement[]; /** * Get the number of elements on this slide. * * @returns The count of elements currently added to the slide. * * @example * ```ts * const builder = new SlideBuilder(1) * .addText("A").addText("B"); * console.log(builder.elementCount); // 2 * ``` */ get elementCount(): number; /** * Get the last added element (useful for getting its ID for animations). * * Returns `undefined` if the slide has no elements yet. * * @returns The most recently added element, or `undefined`. * * @example * ```ts * const builder = new SlideBuilder(1).addShape("rect"); * const shape = builder.getLastElement(); * if (shape) { * builder.addAnimation(shape.id, { preset: "fadeIn" }); * } * ``` */ getLastElement(): PptxElement | undefined; /** * Set the slide name/title for organizational purposes. * * Stores an arbitrary name string on the slide object. This is useful * for labeling slides in tooling or custom workflows. * * @param name - The display name to assign to the slide. * @returns The builder instance for chaining. * * @example * ```ts * new SlideBuilder(1) * .setName("Introduction") * .addText("Welcome!") * .build(); * ``` */ setName(name: string): this; /** Return the built {@link PptxSlide}. */ build(): PptxSlide; } //#endregion //#region src/core/builders/sdk/PresentationBuilder.d.ts /** Result returned by {@link PresentationBuilder.create}. */ interface PresentationBuilderResult { /** Initialized handler ready for editing and saving. */ handler: PptxHandler; /** Parsed presentation data. */ data: PptxData; /** Convenience slide builder factory. */ createSlide: (layoutName?: string) => SlideBuilder; } //#endregion //#region src/core/builders/fluent/PptxXmlBuilder.d.ts /** * Fluent interface for navigating and mutating a {@link PptxData} structure. * Provides method-chaining access to slides, elements, and notes. */ interface IPptxXmlBuilder { /** Navigate to a slide by zero-based index (Pascal-case alias). */ Slides(index: number): PptxSlideBuilder; /** Navigate to a slide by zero-based index. */ slide(index: number): PptxSlideBuilder; /** Navigate to a slide by zero-based index (plural alias). */ slides(index: number): PptxSlideBuilder; /** Return the underlying presentation data. */ project(): PptxData; } /** * Root builder of the fluent PPTX editing API. * * Wraps a {@link PptxData} object and provides chainable accessors * to navigate into slides, elements, and notes for in-place mutation. */ declare class PptxXmlBuilder implements IPptxXmlBuilder { /** The presentation data being mutated. */ private readonly data; /** @param data - The presentation data to wrap. */ constructor(data: PptxData); /** * Factory method to create a builder from presentation data. * @param data - The presentation data to wrap. * @returns A new {@link PptxXmlBuilder} instance. */ static from(data: PptxData): PptxXmlBuilder; /** @inheritdoc */ Slides(index: number): PptxSlideBuilder; /** * Navigate to a slide by zero-based index. * @param index - Zero-based slide index. * @returns A {@link PptxSlideBuilder} for the requested slide. * @throws Error if index is not an integer or is out of range. */ slide(index: number): PptxSlideBuilder; /** @inheritdoc */ slides(index: number): PptxSlideBuilder; /** Return the underlying {@link PptxData}. */ project(): PptxData; /** Pascal-case alias for {@link project}. */ Project(): PptxData; } /** * Fluent builder scoped to a single slide. * Provides navigation to the slide's elements and notes. */ declare class PptxSlideBuilder { /** The slide being operated on. */ private readonly slideValue; /** Reference back to the root builder for chaining. */ private readonly rootBuilder; /** * @param slideValue - The slide data. * @param rootBuilder - The parent builder. */ constructor(slideValue: PptxSlide, rootBuilder: PptxXmlBuilder); /** Navigate to the slide's notes builder (getter). */ get Notes(): PptxSlideNotesBuilder; /** Navigate to the slide's notes builder. */ notes(): PptxSlideNotesBuilder; /** Navigate to the slide's elements builder. */ elements(): PptxSlideElementsBuilder; /** Return the underlying slide data. */ project(): PptxSlide; /** Pascal-case alias for {@link project}. */ Project(): PptxSlide; /** Navigate back to the root builder. */ done(): PptxXmlBuilder; /** Pascal-case alias for {@link done}. */ Done(): PptxXmlBuilder; } /** * Fluent builder for manipulating the elements array of a single slide. * Supports adding, removing, and updating elements by ID. */ declare class PptxSlideElementsBuilder { private readonly slideValue; private readonly slideBuilder; /** * @param slideValue - The slide whose elements are being modified. * @param slideBuilder - The parent slide builder for chaining. */ constructor(slideValue: PptxSlide, slideBuilder: PptxSlideBuilder); /** * Append an element to the slide's element list. * @param element - The element to add. * @returns This builder for chaining. */ add(element: PptxElement): this; /** * Remove an element from the slide by its ID. * @param elementId - The ID of the element to remove. * @returns This builder for chaining. */ removeById(elementId: string): this; /** * Update an element in-place by ID using a transform function. * @param elementId - The ID of the element to update. * @param updater - A function that receives the current element and returns the replacement. * @returns This builder for chaining. */ updateById(elementId: string, updater: (current: PptxElement) => PptxElement): this; /** Return the current elements array. */ project(): PptxElement[]; /** Navigate back to the slide builder. */ done(): PptxSlideBuilder; } /** * Fluent builder for manipulating speaker notes on a single slide. * Supports adding, setting, clearing, and retrieving notes text. */ declare class PptxSlideNotesBuilder { private readonly slideValue; private readonly slideBuilder; /** * @param slideValue - The slide whose notes are being modified. * @param slideBuilder - The parent slide builder for chaining. */ constructor(slideValue: PptxSlide, slideBuilder: PptxSlideBuilder); /** * Append text to existing notes (separated by newline). * @param text - The text to append. * @returns This builder for chaining. */ add(text: string): this; /** Pascal-case alias for {@link add}. */ Add(text: string): this; /** * Replace all notes with the given text. * @param text - The replacement notes text. Empty string clears notes. * @returns This builder for chaining. */ set(text: string): this; /** Pascal-case alias for {@link set}. */ Set(text: string): this; /** Remove all notes from the slide. */ clear(): this; /** Pascal-case alias for {@link clear}. */ Clear(): this; /** Return the current notes text, or `undefined` if none. */ get(): string | undefined; /** Pascal-case alias for {@link get}. */ Get(): string | undefined; /** Navigate back to the slide builder. */ done(): PptxSlideBuilder; /** Pascal-case alias for {@link done}. */ Done(): PptxSlideBuilder; /** * Synchronize the `notesSegments` array from the plain-text notes string. * Splits text on newlines and creates corresponding {@link TextSegment} entries * with paragraph break markers between lines. */ private syncSegmentsFromNotes; } //#endregion //#region src/core/core/types.d.ts interface PptxHandlerLoadOptions { eagerDecodeImages?: boolean; password?: string; /** * Maximum total uncompressed bytes accepted from the input ZIP archive. * Defaults to 500 MiB. When the sum of `_data.uncompressedSize` across * all archive entries exceeds this cap, `load()` rejects with a * {@link ZipBombError}. A hard cap of 65 536 archive entries also * applies. */ maxUncompressedBytes?: number; /** * When `false` (default), relationship targets that resolve to * `http://` or `https://` URLs are dropped from rendered slides * (image, picture, background). Set to `true` to allow external image * URLs to flow through to ``. * * Disabled by default to mitigate SSRF / privacy-leak vectors in * server-side rendering and headless export pipelines. */ allowExternalImages?: boolean; } /** Output format for the save pipeline. */ type PptxSaveFormat = 'pptx' | 'ppsx' | 'pptm'; interface PptxHandlerSaveOptions { headerFooter?: PptxHeaderFooter; presentationProperties?: PptxPresentationProperties; customShows?: PptxCustomShow[]; sections?: PptxSection[]; coreProperties?: PptxCoreProperties; appProperties?: PptxAppProperties; customProperties?: PptxCustomProperty[]; /** Updated notes master data to save back to notesMaster1.xml. */ notesMaster?: PptxNotesMaster; /** Updated handout master data to save back to handoutMaster1.xml. */ handoutMaster?: PptxHandoutMaster; /** * Updated slide masters to save back to ppt/slideMasters/slideMaster*.xml. * Each entry in the array applies typed mutations (clrMap, hf flags, * background) to the master at its `path`. Masters not listed here pass * through verbatim from the original load. */ slideMasters?: PptxSlideMaster[]; /** * Updated slide layouts to save back to ppt/slideLayouts/slideLayout*.xml. * Each entry applies typed mutations (clrMapOverride, attrs, hf flags, * background) to the layout at its `path`. Layouts not listed here pass * through verbatim from the original load. */ slideLayouts?: PptxSlideLayout[]; /** Updated tag collections to save back to ppt/tags/tag*.xml. */ tags?: PptxTagCollection[]; /** Presentation-level customer data references to author or update. */ customerData?: PptxCustomerData[]; /** Photo album metadata to save back to `p:photoAlbum`. */ photoAlbum?: PptxPhotoAlbum; /** * Slide dimensions to write back to `p:sldSz`. * * Omitting the option preserves the load-time dimensions verbatim, which * is why an edit made through a viewer's Slide Size control has to reach * the save call: nothing else in the pipeline can observe it. * * PowerPoint derives `Presentation.PageSetup.SlideSize` from `@cx`/`@cy` * alone (verified by COM: an A4-typed `p:sldSz` carrying 4:3 dimensions * still reports `ppSlideSizeCustom`), so `type` is written for fidelity * but the dimensions are what actually decide the reported preset. */ slideSize?: PptxSlideSize; /** East Asian line-break settings to save back to `p:kinsoku`. */ kinsoku?: PptxKinsoku | null; /** Write-protection verifier. Set to `null` to remove, `undefined` to preserve existing. */ modifyVerifier?: PptxModifyVerifier | null; /** * `p:presentation/@embedTrueTypeFonts` to write. `undefined` preserves * whatever was loaded (or omits the attribute for a brand-new deck); * purely declarative here, see {@link PptxData.embedTrueTypeFonts}. */ embedTrueTypeFonts?: boolean; /** * Presentation-level default text style edits to save back to * `p:defaultTextStyle`. Only the levels present in the map are touched; * omitted levels and any unmodelled XML on an edited level survive * untouched. See {@link PptxData.defaultTextStyle}. */ defaultTextStyle?: PptxTextStyleLevels; /** View properties to save back to ppt/viewProps.xml. */ viewProperties?: PptxViewProperties; /** * Table style edits to save back to `ppt/tableStyles.xml`. Pass the * `tableStyleMap` from `PptxData` (optionally with edited entries) * to persist user edits. The `def` GUID and any unmodelled XML are * preserved verbatim. Omitting the option round-trips the original * part untouched. */ tableStyles?: ParsedTableStyleMap; /** * Target output format. * - `'pptx'` (default): Standard presentation. * - `'ppsx'`: Slide-show file (opens in presentation mode). * - `'pptm'`: Macro-enabled presentation (requires VBA data). */ outputFormat?: PptxSaveFormat; /** * Embedded fonts to write back (or add) to the saved PPTX. * * Pass the `embeddedFonts` array from `PptxData` to preserve existing * embedded fonts during save. You can also add new fonts by including * entries with `rawFontData` populated. * * When omitted, the save pipeline will automatically re-embed any * fonts that were loaded from the original PPTX and have `rawFontData` * preserved (i.e. the default is lossless round-trip). */ embeddedFonts?: PptxEmbeddedFont[]; /** Typed embedded-font list metadata. Set to null to remove fonts and relationships. */ embeddedFontList?: PptxEmbeddedFontList | null; /** * OOXML conformance class for the saved output. * - `'preserve'` (default): use the same conformance as the loaded file. * - `'strict'`: force Strict Open XML (ISO/IEC 29500) namespace URIs. * - `'transitional'`: force Transitional (ECMA-376) namespace URIs. */ conformance?: 'strict' | 'transitional' | 'preserve'; } interface IPptxHandlerRuntime { /** * Release all resources held by this runtime (Blob URLs, caches, ZIP). * After calling, the runtime cannot be used further. */ dispose(): void; /** * Revoke all Blob URLs created during image loading. */ revokeBlobUrls(): void; getCompatibilityWarnings(): PptxCompatibilityWarning[]; getLayoutOptions(): PptxLayoutOption[]; getLayoutPreview(layoutPath: string): Promise; getLayoutPreviews(layoutPaths?: readonly string[]): Promise; createXmlBuilder(data: PptxData): PptxXmlBuilder; Builder(data: PptxData): PptxXmlBuilder; setTemplateBackground(path: string, backgroundColor: string | undefined): void; setPresentationTheme(themePath: string, applyToAllMasters?: boolean): Promise; getTemplateBackgroundColor(path: string): string | undefined; updateThemeColorScheme(colorScheme: PptxThemeColorScheme): Promise; updateThemeFontScheme(fontScheme: PptxThemeFontScheme): Promise; updateThemeName(name: string): Promise; applyTheme(colorScheme: PptxThemeColorScheme, fontScheme: PptxThemeFontScheme, themeName?: string): Promise; load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; getChartDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; getSmartArtDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; getImageData(imagePath: string): Promise; /** * Extract a media file from the PPTX archive as an ArrayBuffer. * Returns undefined if the file is not found. */ getMediaArrayBuffer(mediaPath: string): Promise; save(slides: PptxSlide[], options?: PptxHandlerSaveOptions): Promise; exportSlides(slides: PptxSlide[], options: PptxExportOptions): Promise>; /** * Get the available slide layouts for a specific slide, based on the * slide's master. Scans the slide master's relationships to find all * layouts that belong to it. * * @param slideIndex - Zero-based slide index. * @param slides - Current slides array. * @returns Array of layout options belonging to the same slide master. */ getAvailableLayoutsForSlide(slideIndex: number, slides: PptxSlide[]): Promise; /** * Resolve the editable template (master + layout) elements a slide * inherits, each carrying a `master-` / `layout-` prefixed id. Excludes * placeholders; returns only decorative shapes/pictures/graphic frames. * * @param slideId - The slide's archive path (`PptxSlide.id`). */ getTemplateElementsForSlide(slideId: string): Promise; /** * Scan the loaded PPTX archive for all theme parts. */ getAvailableThemes(): Promise>; /** * Apply a different layout to an existing slide by updating the slide's * relationship to point to the new layout and re-parsing layout * placeholders / background. * * @param slideIndex - Zero-based slide index. * @param layoutPath - Archive path of the target layout * (e.g. `ppt/slideLayouts/slideLayout2.xml`). * @param slides - Current slides array. * @returns The updated slide with new layout path, name, and background. */ applyLayoutToSlide(slideIndex: number, layoutPath: string, slides: PptxSlide[]): Promise; } //#endregion //#region src/core/core/PptxHandlerRuntimeFactory.d.ts /** * Abstract factory contract for creating {@link IPptxHandlerRuntime} * instances. * * Implement this interface to supply a custom runtime (e.g. a * WASM-backed or test-double runtime) to {@link PptxHandlerCore}. */ interface IPptxHandlerRuntimeFactory { /** Instantiate and return a new runtime implementation. */ createRuntime(): IPptxHandlerRuntime; } //#endregion //#region src/core/utils/ooxml-crypto-types.d.ts /** * Type definitions for OOXML encryption and decryption. * * Contains all interfaces and type aliases used by the OOXML crypto modules. * * @module ooxml-crypto-types */ /** Supported encryption algorithms. */ type EncryptionAlgorithm = 'AES128' | 'AES256'; /** Encryption options for creating encrypted files. */ interface EncryptionOptions { /** The encryption algorithm to use (defaults to AES256). */ algorithm?: EncryptionAlgorithm; /** Number of hash iterations for key derivation (defaults to 100000). Lower values speed up tests. */ spinCount?: number; } //#endregion //#region src/core/PptxHandlerCore.d.ts /** * Dependency injection options for {@link PptxHandlerCore}. * * Provide either `runtime` (an already-constructed runtime) or * `runtimeFactory` (a factory that will be called once). When neither * is supplied the default runtime is created automatically. * * @example * ```ts * // Use the default runtime: * const core = new PptxHandlerCore(); * * // Inject a custom runtime: * const core = new PptxHandlerCore({ runtime: myRuntime }); * * // Supply a factory for lazy creation: * const core = new PptxHandlerCore({ runtimeFactory: myFactory }); * // => PptxHandlerCore instance with injected runtime * ``` */ interface PptxHandlerCoreDependencies { runtime?: IPptxHandlerRuntime; runtimeFactory?: IPptxHandlerRuntimeFactory; } /** * Thin facade over the PPTX runtime implementation. * * All heavy parsing, serialisation, and XML manipulation is delegated to an * {@link IPptxHandlerRuntime}. This surface stays stable and small so that * callers remain decoupled from the runtime internals and host-specific * runtime swaps (e.g. WASM vs Node) can be done transparently. * * @remarks * - Constructed once per open document. * - Errors from encrypted files are caught at `load()` time via * {@link EncryptedFileError}. * - `PptxXmlBuilder` instances returned by `createXmlBuilder()` / `Builder()` * operate directly on the runtime’s in-memory ZIP. * * @example * ```ts * const handler = new PptxHandlerCore(); * const data = await handler.load(arrayBuffer); * // ... mutate slides ... * const out = await handler.save(data.slides); * // => Uint8Array of the modified .pptx file * ``` */ declare class PptxHandlerCore { private readonly runtime; /** * Create a new handler, optionally injecting a custom runtime. * * Resolution order: * 1. `dependencies.runtime` — use as-is. * 2. `dependencies.runtimeFactory` — call `createRuntime()` once. * 3. Fall back to {@link createDefaultPptxHandlerRuntime}. * * @param dependencies - Optional runtime or factory override. * * @example * ```ts * const core = new PptxHandlerCore(); * // => PptxHandlerCore instance with default runtime * ``` */ constructor(dependencies?: PptxHandlerCoreDependencies); /** * Release all resources held by this handler instance. * * Revokes every Blob URL created for images/media, clears all * in-memory caches, and releases the in-memory ZIP archive. * * Call this when the handler is no longer needed (e.g. component * unmount) to free memory immediately rather than waiting for GC. * * After calling `dispose()`, do not call any other methods — create * a new `PptxHandler` instance instead. */ dispose(): void; /** * Return any compatibility warnings detected during the most recent load. * * Warnings indicate features the editor cannot fully represent (e.g. * SmartArt, 3-D effects, embedded OLE objects). * * @returns Array of {@link PptxCompatibilityWarning} objects. */ getCompatibilityWarnings(): PptxCompatibilityWarning[]; /** * Get the slide layout options available in the loaded presentation. * * Each option maps to a `` inside the PPTX archive. * * @returns Array of {@link PptxLayoutOption} entries. */ getLayoutOptions(): PptxLayoutOption[]; /** * Build the artwork thumbnails backing the New Slide / Layout galleries. * * Parsing happens on first request and is memoised afterwards, so opening * the gallery costs one pass over the layout parts and reopening it costs * nothing. Callers that only need one entry should prefer * {@link getLayoutPreview}. * * @param layoutPaths - Restrict the result to these layouts; defaults to * every layout in the presentation. * @returns One {@link PptxLayoutPreview} per resolvable layout. */ getLayoutPreviews(layoutPaths?: readonly string[]): Promise; /** * Build the artwork thumbnail for a single layout. * * @param layoutPath - Archive path of the `p:sldLayout` part. * @returns The preview, or `null` when the presentation has no such layout. */ getLayoutPreview(layoutPath: string): Promise; /** * Create a fluent XML builder scoped to the given presentation data. * * The builder provides a chainable API for constructing and inserting * OpenXML nodes directly into the runtime’s in-memory ZIP. * * @param data - The parsed {@link PptxData} to bind the builder to. * @returns A new {@link PptxXmlBuilder} instance. */ createXmlBuilder(data: PptxData): PptxXmlBuilder; /** * Shorthand alias for {@link createXmlBuilder}. * * @param data - Parsed presentation data. * @returns A {@link PptxXmlBuilder} instance. */ Builder(data: PptxData): PptxXmlBuilder; /** * Register a background image for a specific template layout path. * * @param path - The internal PPTX path (e.g. `ppt/slideLayouts/slideLayout1.xml`). * @param backgroundColor - Optional hex colour to render behind the image. */ setTemplateBackground(path: string, backgroundColor: string | undefined): void; /** * Retrieve the background colour previously set for a template layout. * * @param path - The internal PPTX layout path. * @returns Hex colour string, or `undefined` if none was set. */ getTemplateBackgroundColor(path: string): string | undefined; /** * Replace the presentation’s theme by loading an external `.thmx` file. * * @param themePath - Absolute or relative path to the `.thmx` file. * @param applyToAllMasters - Apply to every slide master (default `true`). * * @example * ```ts * await handler.setPresentationTheme("./themes/corporate.thmx"); * // => void — theme XML replaced in the in-memory ZIP * ``` */ setPresentationTheme(themePath: string, applyToAllMasters?: boolean): Promise; /** * Modify the theme’s colour scheme (accent colours, background, text, etc.). * * @param colorScheme - A {@link PptxThemeColorScheme} with hex colour values. * * @example * ```ts * await handler.updateThemeColorScheme({ * dk1: "#1A1A2E", dk2: "#16213E", * lt1: "#FFFFFF", lt2: "#E8E8E8", * accent1: "#0F3460", accent2: "#533483", * accent3: "#E94560", accent4: "#F0A500", * }); * // => void — colour scheme updated in the in-memory theme XML * ``` */ updateThemeColorScheme(colorScheme: PptxThemeColorScheme): Promise; /** * Update the theme’s font scheme (heading + body typefaces). * * @param fontScheme - A {@link PptxThemeFontScheme} with font family names. * * @example * ```ts * await handler.updateThemeFontScheme({ * majorFont: "Montserrat", * minorFont: "Open Sans", * }); * // => void — font scheme updated in the in-memory theme XML * ``` */ updateThemeFontScheme(fontScheme: PptxThemeFontScheme): Promise; /** * Rename the presentation theme. * * @param name - New display name for the theme. */ updateThemeName(name: string): Promise; /** * Apply a complete theme in one call (colour scheme + font scheme + optional name). * * This is a convenience wrapper over {@link updateThemeColorScheme}, * {@link updateThemeFontScheme}, and {@link updateThemeName}. * * @param colorScheme - Colour definitions. * @param fontScheme - Font definitions. * @param themeName - Optional theme display name. * * @example * ```ts * await handler.applyTheme( * { dk1: "#000", lt1: "#FFF", accent1: "#0066CC", /* … *\/ }, * { majorFont: "Helvetica", minorFont: "Arial" }, * "Corporate 2025", * ); * // => void — colour scheme, font scheme, and name applied atomically * ``` */ applyTheme(colorScheme: PptxThemeColorScheme, fontScheme: PptxThemeFontScheme, themeName?: string): Promise; /** * Switch the presentation's theme, updating both the underlying XML and * re-resolving all element colours in-place. * * This is the high-level API for theme switching: it updates the theme * data in the ZIP, then patches all resolved colours in the provided * `PptxData` so that elements immediately reflect the new colour scheme * without requiring a re-parse. * * @param data - The current parsed presentation data (mutated in-place for * convenience, but a new `PptxData` object is also returned). * @param colorScheme - New colour scheme (12 colours). * @param fontScheme - Optional new font scheme. * @param themeName - Optional theme display name. * @returns The updated PptxData with re-resolved colours. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const ion = THEME_PRESETS.find(p => p.id === "ion")!; * const newData = await handler.switchTheme( * data, * ion.colorScheme, * ion.fontScheme, * ion.name, * ); * // => PptxData with all colours updated to the Ion theme * ``` */ switchTheme(data: PptxData, colorScheme: PptxThemeColorScheme, fontScheme?: PptxThemeFontScheme, themeName?: string): Promise; /** * Apply a built-in theme preset to the presentation. * * Convenience wrapper around {@link switchTheme} that accepts a * {@link PptxThemePreset} directly. * * @param data - The current parsed presentation data. * @param preset - One of the built-in presets from {@link THEME_PRESETS}. * @returns The updated PptxData. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const preset = THEME_PRESETS.find(p => p.id === "facet")!; * const newData = await handler.switchThemePreset(data, preset); * ``` */ switchThemePreset(data: PptxData, preset: PptxThemePreset): Promise; /** * Parse a PPTX file from an `ArrayBuffer` and return structured data. * * If the file is encrypted and a `password` is provided in `options`, * the file will be decrypted before parsing. If no password is provided * for an encrypted file, throws {@link EncryptedFileError}. * * @param data - Raw bytes of the `.pptx` file (may be encrypted OLE2). * @param options - Optional load-time settings, including `password`. * @returns Parsed {@link PptxData} containing slides, theme, layouts, etc. * * @example * ```ts * // Load an unencrypted file: * const pptx = await handler.load(buf.buffer); * * // Load a password-protected file: * const pptx = await handler.load(buf.buffer, { password: "secret" }); * console.log(`${pptx.slides.length} slides loaded`); * ``` */ load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; /** * Extract chart data from a graphic-frame XML node. * * @param slidePath - Internal archive path of the slide (e.g. `ppt/slides/slide1.xml`). * @param graphicFrame - Parsed XML object for the `` node. * @returns Chart data, or `undefined` if the frame is not a chart. */ getChartDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; /** * Extract SmartArt data from a graphic-frame XML node. * * @param slidePath - Internal archive path of the slide. * @param graphicFrame - Parsed XML object for the `` node. * @returns SmartArt data, or `undefined` if the frame is not SmartArt. */ getSmartArtDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; /** * Get the base64-encoded data URL for an embedded image. * * @param imagePath - Archive-relative path (e.g. `ppt/media/image1.png`). * @returns A `data:image/...;base64,...` string, or `undefined` if not found. */ getImageData(imagePath: string): Promise; /** * Extract a media file from the PPTX archive as an ArrayBuffer. * Avoids the 33% base64 overhead of getImageData — prefer this for * audio/video media that will be played via Blob URLs. */ getMediaArrayBuffer(mediaPath: string): Promise; /** * Serialise current slides back into a PPTX byte array. * * @param slides - The (possibly mutated) slide array. * @param options - Optional save-time settings (e.g. thumbnail generation). * @returns `Uint8Array` of the complete `.pptx` file. * * @example * ```ts * const bytes = await handler.save(data.slides); * await fs.writeFile("output.pptx", Buffer.from(bytes)); * // => Uint8Array written to disk as a valid .pptx file * ``` */ save(slides: PptxSlide[], options?: PptxHandlerSaveOptions): Promise; /** * Serialise slides and then encrypt the output with a password. * * This is a convenience method that calls {@link save} followed by * {@link encryptPptx}. The result is an OLE2 container suitable for * opening in Microsoft PowerPoint with a password prompt. * * @param slides - The (possibly mutated) slide array. * @param password - The password to encrypt with. * @param options - Optional save-time and encryption settings. * @returns `Uint8Array` of the encrypted OLE2 file. * * @example * ```ts * const bytes = await handler.saveEncrypted(data.slides, "secret"); * await fs.writeFile("protected.pptx", Buffer.from(bytes)); * // => Encrypted OLE2 file requiring password to open * ``` */ saveEncrypted(slides: PptxSlide[], password: string, options?: PptxHandlerSaveOptions & { encryption?: EncryptionOptions; }): Promise; /** * Get the slide layouts available for a specific slide. * * Returns layouts belonging to the same slide master as the given slide. * This is useful for building a layout picker UI scoped to the current * slide's master. * * @param slideIndex - Zero-based slide index. * @param slides - Current slides array. * @returns Array of {@link PptxLayoutOption} entries for the slide's master. * * @example * ```ts * const layouts = await handler.getAvailableLayoutsForSlide(0, data.slides); * console.log(layouts.map(l => l.name)); * // => ["Title Slide", "Title and Content", "Blank", ...] * ``` */ getAvailableLayoutsForSlide(slideIndex: number, slides: PptxSlide[]): Promise; /** * Resolve the editable template (master + layout) elements a slide * inherits, each carrying a `master-` / `layout-` prefixed id. * * This is the foundation for an "edit template/master" feature. The * returned elements are the decorative master/layout shapes the loader * already merges behind slide-authored content (master shapes behind, * layout shapes on top); placeholders are excluded. The same elements are * shared by every slide inheriting the layout/master, so editing one and * saving updates the shared part. * * To persist an edit, keep the mutated template element inside the * `slide.elements` array passed to {@link save}; the save writer reads * template elements from there and writes their shape XML back into the * owning layout/master `p:spTree`. * * @param slideId - The slide's archive path (the `PptxSlide.id`). * @returns Master + layout elements with prefixed ids (may be empty). * * @example * ```ts * const templateEls = await handler.getTemplateElementsForSlide(slide.id); * const logo = templateEls.find((e) => e.id.startsWith("master-")); * if (logo) { * logo.x += 10; * slide.elements = [...slide.elements, logo]; * await handler.save(data.slides); * } * ``` */ getTemplateElementsForSlide(slideId: string): Promise; /** * Apply a different layout to an existing slide. * * Updates the slide's relationship to point to the new layout and * refreshes layout-derived properties (background, layout name). * The slide's own content elements are preserved. * * @param slideIndex - Zero-based slide index. * @param layoutPath - Archive path of the target layout * (e.g. `ppt/slideLayouts/slideLayout2.xml`). * @param slides - Current slides array (the slide at `slideIndex` * is replaced in-place). * @returns The updated {@link PptxSlide} with new layout metadata. * * @example * ```ts * const updated = await handler.applyLayoutToSlide( * 0, * "ppt/slideLayouts/slideLayout3.xml", * data.slides, * ); * console.log(updated.layoutName); * // => "Two Content" * ``` */ applyLayoutToSlide(slideIndex: number, layoutPath: string, slides: PptxSlide[]): Promise; /** * Scan the loaded PPTX archive for all theme parts (`ppt/theme/theme*.xml`) * and return their paths and display names. */ getAvailableThemes(): Promise>; /** * Export selected slides to a vector or raster format, keyed by slide index. * * **This does not produce PPTX files.** The previous version of this comment * said each entry was "a standalone PPTX with only that slide", named the * option `slideIndexes` (the real field is `slideIndices`), and wrote the * bytes to `slide_N.pptx`. None of that was ever true: the runtime has * always taken a `format` of `svg` / `png` / `pdf`. Per-slide PPTX * extraction is a different operation and is not implemented here. * * Only `svg` works without a host-supplied backend, and it works fully: * the headless {@link SvgExporter} renders it with no DOM. `png` and `pdf` * THROW, because this package carries no rasteriser; use a viewer binding's * browser export pipeline, or override `exportSlides` on the runtime with * your own backend. * * @param slides - Full slide array. * @param options - Export options (`format`, `slideIndices`, `width`, ...). * @returns A `Map` of exported files. Hidden slides * are omitted unless `options.includeHidden` is set, so the map can be * smaller than `options.slideIndices`. * @throws {Error} when `options.format` is `png` or `pdf`. * * @example * ```ts * const exports = await handler.exportSlides(data.slides, { * format: 'svg', * slideIndices: [0, 2], * }); * for (const [idx, bytes] of exports) { * await fs.writeFile(`slide_${idx}.svg`, Buffer.from(bytes)); * } * // => Map: one SVG document per exported slide * ``` */ exportSlides(slides: PptxSlide[], options: PptxExportOptions): Promise>; } //#endregion //#region src/core/PptxHandler.d.ts /** * Public facade for the PPTX editor handler. * * The implementation lives in `PptxHandlerCore` so this surface can stay small, * stable, and easy to replace with alternate implementations in the future. */ declare class PptxHandler extends PptxHandlerCore { /** * Create a new blank PPTX presentation from scratch. * * This is a convenience static method that delegates to * {@link PresentationBuilder.create}. The returned handler is fully * initialized and ready for editing, adding slides, and saving. * * @param options - Optional slide dimensions, theme, and metadata. * @returns Handler, parsed data, and a slide builder factory. * * @example * ```ts * const { handler, data, createSlide } = await PptxHandler.createBlank({ * title: "My Deck", * theme: { colors: { accent1: "#FF6B6B" } }, * }); * * data.slides.push( * createSlide("Blank") * .addText("Hello", { fontSize: 36 }) * .build() * ); * * const bytes = await handler.save(data.slides); * ``` */ static createBlank(options?: PresentationOptions): Promise; /** * Create a new PPTX presentation from scratch. * * Alias for {@link createBlank}. Generates a valid minimal OpenXML * package and returns a fully initialized handler ready for editing, * adding slides, and saving. * * @param options - Optional slide dimensions, theme, metadata, * and initial slide count. * @returns Handler, parsed data, and a slide builder factory. * * @example * ```ts * const { handler, data, createSlide } = await PptxHandler.create({ * title: "Q4 Report", * initialSlideCount: 3, * theme: { colors: { accent1: "#FF6B6B" } }, * }); * * // The presentation already has 3 blank slides * console.log(data.slides.length); // => 3 * * // Add more slides with content * data.slides.push( * createSlide("Blank") * .addText("Hello", { fontSize: 36 }) * .build() * ); * * const bytes = await handler.save(data.slides); * ``` */ static create(options?: PresentationOptions): Promise; /** * Parse a presentation from an `ArrayBuffer`, accepting both `.pptx` * archives and portable `pptx-viewer-json` documents. * * The buffer is sniffed first: JSON documents (leading `{` plus the * `"pptx-viewer-json"` format marker) are routed to {@link loadFromJson}; * everything else goes through the regular ZIP/OLE2 pipeline. */ load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; /** * Load a presentation from `pptx-viewer-json` text. * * A minimal blank package is generated and loaded first so that the * handler keeps a valid in-memory archive (editing and {@link save} keep * working), then the imported model is overlaid on top: imported * presentation fields win, and the slide array is replaced wholesale. */ loadFromJson(text: string, options?: PptxHandlerLoadOptions): Promise; } //#endregion //#region src/converter/SvgExporter.d.ts /** * Options controlling SVG export behaviour. */ interface SvgExportOptions { /** Include hidden slides when exporting all. Default `false`. */ includeHidden?: boolean; /** Slide indices to export (0-based). If omitted, all slides are exported. */ slideIndices?: number[]; /** Default font family when the element does not specify one. */ defaultFontFamily?: string; /** Default font size in points when the element does not specify one. */ defaultFontSize?: number; } //#endregion //#region ../core/dist/index.d.ts //#endregion //#region src/core/utils/accessibility-checker.d.ts type AccessibilityIssueType = 'missingAltText' | 'missingSlideTitle' | 'lowContrast' | 'complexTable' | 'duplicateTitle' | 'blankSlide'; type AccessibilityIssueSeverity = 'error' | 'warning' | 'tip'; interface AccessibilityIssue { type: AccessibilityIssueType; severity: AccessibilityIssueSeverity; slideIndex: number; elementId?: string; message: string; suggestion: string; } //#endregion //#region ../shared/dist/index-BbHsvey8.d.ts //#region src/theme/types.d.ts /** * Theme configuration types for the PowerPoint viewer. * * All color values accept any valid CSS color string: * hex (`#6366f1`), rgb (`rgb(99 102 241)`), hsl (`hsl(239 84% 67%)`), * oklch (`oklch(0.585 0.233 277)`), named colors, etc. * * Framework-agnostic — shared by the React, Vue, and Angular bindings. */ /** * Semantic color tokens for the viewer UI. * * These map to CSS custom properties (`--pptx-`) and drive all * UI component colors. The naming follows the shadcn/ui convention so * that Tailwind + shadcn users get a familiar experience. */ interface ViewerThemeColors { /** Page / root background */ background: string; /** Default text color */ foreground: string; /** Card / panel surface */ card: string; /** Text on card surfaces */ cardForeground: string; /** Popover / dropdown surface */ popover: string; /** Text inside popovers */ popoverForeground: string; /** Primary action color (buttons, active indicators) */ primary: string; /** Text on primary-colored backgrounds */ primaryForeground: string; /** Secondary / subdued action color */ secondary: string; /** Text on secondary backgrounds */ secondaryForeground: string; /** Muted / disabled surface */ muted: string; /** Text on muted surfaces (also used for secondary text) */ mutedForeground: string; /** Accent / hover-highlight surface */ accent: string; /** Text on accent surfaces */ accentForeground: string; /** Destructive / danger action color */ destructive: string; /** Text on destructive backgrounds */ destructiveForeground: string; /** Default border color */ border: string; /** Input field border color */ input: string; /** Focus ring color */ ring: string; } /** * Full viewer theme configuration. * * Every property is optional — unset values fall back to the built-in * dark theme defaults. */ interface ViewerTheme { /** Semantic UI colors. Each key maps to a `--pptx-` CSS custom property. */ colors?: Partial; /** Base border-radius value (e.g. `"0.5rem"`, `"8px"`). */ radius?: string; /** * Escape hatch: arbitrary CSS custom properties to set on the viewer * root element. Keys should include the `--` prefix. * * @example * ```ts * { "--my-custom-shadow": "0 4px 12px rgba(0,0,0,0.5)" } * ``` */ cssVars?: Record; } //#endregion //#region src/theme/defaults.d.ts /** * Default dark-theme color values. * * These correspond to the built-in dark UI of the PowerPoint viewer and * use Tailwind's gray palette as the neutral scale with indigo as the * primary accent. */ declare const defaultThemeColors: ViewerThemeColors; /** Default border-radius. */ declare const defaultRadius = "0.5rem"; //#endregion //#region src/theme/css-vars.d.ts /** * Convert a `ViewerTheme` into a flat `Record` of CSS * custom properties (including the `--` prefix) ready to be spread onto * a `style` attribute. * * Only properties that differ from the built-in defaults are emitted when * `omitDefaults` is true (the default). */ declare function themeToCssVars(theme: ViewerTheme | undefined, omitDefaults?: boolean): Record; /** * Build the complete set of CSS custom properties with all defaults. * Useful for generating a full fallback stylesheet. */ declare function defaultCssVars(): Record; /** Light vermilion theme, ready for the viewer's `theme` prop. */ declare const vermilionLightTheme: ViewerTheme; /** Dark vermilion theme, ready for the viewer's `theme` prop. */ declare const vermilionDarkTheme: ViewerTheme; //#endregion //#region src/theme/theme-catalog.d.ts /** One selectable entry in the viewer chrome's built-in theme picker (File > Options > Appearance). */ interface ThemeCatalogEntry { /** Stable identifier persisted to storage and passed to `onThemeChange`. */ key: string; /** `pptx.*` translation key for the entry's display label. */ labelKey: string; /** The theme to apply, or `undefined` to reset to the built-in default. */ theme: ViewerTheme | undefined; } //#endregion //#region ../shared/dist/index.d.ts //#region src/types.d.ts /** Canvas dimensions in pixels. */ interface CanvasSize { width: number; height: number; } /** Viewer interaction mode: read-only, edit, presentation, or master-view. */ type ViewerMode = 'preview' | 'edit' | 'present' | 'master'; /** * Framework-agnostic imperative API contract for the PowerPoint viewer. * * Each binding (React `forwardRef` handle, Vue `defineExpose`, Angular public * methods) implements this interface so consumers get a consistent progressive * API regardless of framework. */ interface PowerPointViewerAPI { /** Serialise the current presentation to `.pptx` bytes. */ getContent: () => Promise; /** Navigate to a specific slide by zero-based index. */ goTo: (slideIndex: number) => void; /** Navigate to the previous slide. */ goPrev: () => void; /** Navigate to the next slide. */ goNext: () => void; /** Undo the last editing action. No-op when nothing to undo. */ undo: () => void; /** Redo the last undone action. No-op when nothing to redo. */ redo: () => void; /** Whether an undo action is available. */ canUndo: () => boolean; /** Whether a redo action is available. */ canRedo: () => boolean; /** Get the current zoom level (1 = 100%). */ getZoom: () => number; /** Set the zoom level (clamped to min/max bounds). */ setZoom: (level: number) => void; /** Zoom in by one step. */ zoomIn: () => void; /** Zoom out by one step. */ zoomOut: () => void; /** Reset zoom to 100%. */ zoomReset: () => void; /** Get the current viewer mode. */ getMode: () => ViewerMode; /** Switch the viewer mode (e.g. 'edit', 'preview', 'present'). */ setMode: (mode: ViewerMode) => void; /** Get the zero-based active slide index. */ getActiveSlideIndex: () => number; /** Set the active slide by zero-based index (alias of goTo). */ setActiveSlideIndex: (index: number) => void; /** Get the total number of slides. */ getSlideCount: () => number; /** Whether the document has unsaved changes. */ isDirty: () => boolean; /** * Get the full slide array. Returns the actual `PptxSlide[]` from the * internal model with full type information (elements, notes, transitions, * animations, etc.). The returned reference is a snapshot; mutations are * not reflected back unless done via the manipulation methods. */ getSlides: () => readonly PptxSlide[]; /** Get a single slide by zero-based index, or undefined if out of range. */ getSlide: (index: number) => PptxSlide | undefined; /** Get the currently active slide. */ getActiveSlide: () => PptxSlide | undefined; /** Add a blank slide after the given index (or at end if omitted). */ addSlide: (afterIndex?: number) => void; /** Delete slides at the given zero-based indexes. At least one slide is kept. */ deleteSlides: (indexes: number[]) => void; /** Duplicate slides at the given zero-based indexes. */ duplicateSlides: (indexes: number[]) => void; /** Move a slide from one position to another. */ moveSlide: (fromIndex: number, toIndex: number) => void; /** Toggle the hidden flag on slides at the given indexes. */ toggleHideSlides: (indexes: number[]) => void; /** * Get the elements on a slide. Defaults to the active slide when * `slideIndex` is omitted. Returns the full `PptxElement[]` with * all type-specific properties intact. */ getElements: (slideIndex?: number) => readonly PptxElement[]; /** Get a single element by ID from the active slide (or a specified slide). */ getElementById: (elementId: string, slideIndex?: number) => PptxElement | undefined; /** * Update one or more properties of an element by ID on the active slide. * Accepts a `Partial` patch (e.g. `{ x: 100, width: 300 }`). */ updateElement: (elementId: string, updates: Partial) => void; /** Delete elements by their IDs from the active slide. */ deleteElements: (elementIds: string[]) => void; /** * Duplicate an element on the active slide. * Returns the new element's ID, or undefined if the source was not found. */ duplicateElement: (elementId: string) => string | undefined; /** Get the IDs of currently selected elements. */ getSelectedElementIds: () => string[]; /** Programmatically select elements by their IDs. */ selectElements: (ids: string[]) => void; /** Clear the current selection. */ clearSelection: () => void; } /** Collaboration role within a session. */ type CollaborationRole = 'owner' | 'collaborator' | 'viewer'; /** * Collaboration transport. * * - `'websocket'` (default): y-websocket against `serverUrl`. * - `'webrtc'`: y-webrtc peer-to-peer; needs no document server. Peers meet * through the `signaling` servers (WebRTC signaling only, no document data) * and same-browser tabs additionally sync via BroadcastChannel even without * any signaling server, which makes this mode usable from static hosting. */ type CollaborationTransport = 'websocket' | 'webrtc'; /** How the local user entered a collaboration session. */ type CollaborationSessionIntent = 'create' | 'join'; /** * Real-time collaboration configuration. * * The same shape is accepted by every framework binding. */ interface CollaborationConfig { /** Unique identifier for the collaboration room (alphanumeric, hyphens, underscores). */ roomId: string; /** * WebSocket server URL for the Yjs provider (e.g. "wss://collab.example.com"). * Ignored (may be empty) when `transport` is `'webrtc'`. */ serverUrl: string; /** Transport to use. Defaults to `'websocket'`. */ transport?: CollaborationTransport; /** * WebRTC signaling server URLs (only used when `transport` is `'webrtc'`). * Defaults to y-webrtc's built-in public signaling list. Same-browser tabs * sync via BroadcastChannel regardless of signaling availability. */ signaling?: string[]; /** Display name for the local user. */ userName: string; /** Avatar URL for the local user (optional). */ userAvatar?: string; /** Hex colour for the local user's cursor/presence indicator. */ userColor?: string; /** Optional authentication token sent with the WebSocket handshake. */ authToken?: string; /** Role in the session; defaults to `'collaborator'`. */ role?: CollaborationRole; /** * Whether this client created the room or joined an existing room. Providers * do not use this value, but hosts can use it to avoid publishing local file * bytes when handling a join request. Omitted values retain the legacy * create-session behaviour. */ sessionIntent?: CollaborationSessionIntent; /** * Elected-writer write-back callback (Area 3 of the C3 hardening plan). * * When the local user has `role: 'owner'`, the binding debounces changes and * serializes the current Y.Doc state to a PPTX byte array, then calls this * callback so the host can persist the snapshot. Only one writer (the owner) * does this; other collaborators never trigger write-back, eliminating the * last-save-wins problem. */ onWriteBack?: (bytes: Uint8Array) => void; /** * Debounce delay (ms) between the last Y.Doc change and the write-back * invocation. Defaults to 5000 ms. Set to 0 to write back on every change * (not recommended for large documents). */ writeBackDebounceMs?: number; } /** A neutral CSS map (framework `CSSProperties` are structurally compatible). */ type CssStyleMap = Record; /** * Reference to an interactive chart sub-part, carried by the primitives that * represent data marks (bars, dots, slices, series lines). Bindings use it to * make marks clickable/draggable in edit mode and to sync selection with the * chart inspector; primitives without a `part` stay purely decorative. */ interface ChartPartRef { /** 'dataPoint' targets one (series, category) cell; 'series' the whole series. */ role: 'dataPoint' | 'series'; seriesIndex: number; /** Category/point index. Absent when the primitive spans the whole series. */ pointIndex?: number; } //#endregion //#region src/render/animation-authoring.d.ts /** One of the three animation buckets a preset can occupy on an element. */ type AnimationGroup = 'entrance' | 'emphasis' | 'exit'; /** * Normalized staged-reveal mode for a chart graphic frame, derived from the * OOXML `a:bldChart/@bld` (or `p:bldOleChart/@bld`) token: * - `asOne` the whole chart appears at once (`allAtOnce`). * - `bySeries` one data series is revealed per stage (`series`). * - `byCategory` one category is revealed per stage (`category`). * - `byElement` one series/category ELEMENT is revealed per stage * (`seriesElement` / `categoryElement`). */ type ChartBuildMode = 'asOne' | 'bySeries' | 'byCategory' | 'byElement'; /** * Normalized staged-reveal mode for a SmartArt diagram, derived from the OOXML * `a:bldDgm/@bld` or `p:bldDgm/@bld` token: * - `asOne` the whole diagram appears at once (`whole` / `allAtOnce`). * - `byOne` one node is revealed per stage (`one`, and the assorted * `depthBy*` / `breadthBy*` / directional traversals). * - `byLvl` levels are revealed one element at a time (`lvlOne`). * - `byLvlAtOnce` a whole level is revealed per stage (`lvlAtOnce`). */ type DiagramBuildMode = 'asOne' | 'byOne' | 'byLvl' | 'byLvlAtOnce'; /** * Playback-time staged-build state surfaced on {@link ElementAnimationState}. * `progress` is the 0..1 fraction of the build revealed at the current playback * time; a consumer maps it to its own item COUNT (see `revealedStageCount`). */ type ElementBuildState = { kind: 'chart'; mode: ChartBuildMode; progress: number; } | { kind: 'diagram'; mode: DiagramBuildMode; progress: number; }; /** Snapshot of a single element's animation state at a point in the timeline. */ interface ElementAnimationState { /** Whether the element should be visible. */ visible: boolean; /** CSS animation shorthand to apply (undefined = no active animation). */ cssAnimation: string | undefined; /** * Staged-build reveal state, present only when the active animation builds a * chart or SmartArt diagram in stages (`p:bldChart` / `p:bldDgm`) rather than * revealing the whole element at once. A staged renderer multiplies * `build.progress` (0..1) by its own series / category / level COUNT to * decide how many stages are revealed at the current playback time; see * {@link import('./animation-build').revealedStageCount}. Absent for ordinary * whole-element entrances, so existing renderers are unaffected. */ build?: ElementBuildState; /** * True when an active `p:animClr` color animation targets this shape's fill. * A vector renderer should then paint the fill with `fill: inherit` so the * wrapper-level colour keyframes cascade to the SVG path. Absent/false means * no active fill-colour animation. */ animatesFill?: boolean; /** * True when an active `p:animClr` color animation targets this shape's * stroke. A vector renderer should then paint the stroke with * `stroke: inherit`. Absent/false means no active stroke-colour animation. */ animatesStroke?: boolean; } //#endregion //#region src/render/element-align.d.ts /** * Pure geometry helpers for the align / distribute editor operations. * * These functions operate over a list of slide elements (anything carrying the * `{ id, x, y, width, height }` bounding-box fields of {@link PptxElement}) and * return a `Map` keyed by element `id` describing the *new* position(s) for the * elements that need to move. Elements that already sit on the target edge (or * the two outer-most elements during distribution) are still included with * their unchanged coordinate so callers can apply the whole map uniformly — the * map only ever contains the axis that the operation touches. * * The helpers are deliberately framework-agnostic: no DOM, no Vue reactivity, * no side effects. The host wires them into the editor by feeding the current * selection in and applying the returned `Map` via its element-transform * operation (one batched history entry per call). */ /** Edge / centre that {@link alignElements} can snap a selection to. */ type AlignEdge = 'left' | 'centerH' | 'right' | 'top' | 'middle' | 'bottom'; //#endregion //#region src/render/slide-templates/slide-template-types.d.ts /** Discriminated id for every built-in slide template. */ type SlideTemplateId = 'title' | 'titleAndContent' | 'sectionHeader' | 'agenda' | 'twoContent' | 'comparison' | 'quote' | 'timeline' | 'keyMetrics' | 'titleOnly' | 'blank' | 'closing'; /** The unit system used for ruler display. */ type RulerUnit = 'inches' | 'centimetres'; //#endregion //#region src/render/text-field-substitution.d.ts /** * Text-field placeholder substitution, shared by every binding's text * renderer. * * Pure string logic: resolves OOXML field runs (slide number, date/time, * header/footer, document properties, slide title) into their display text. * Extracted from the React `viewer/utils/text-field-substitution` module so * every binding substitutes identically. */ /** Context for substituting field placeholders (slide number, date/time, header/footer, etc.). */ interface FieldSubstitutionContext { slideNumber?: number; dateTimeText?: string; /** OOXML date-format pattern from header/footer settings (e.g. "M/d/yyyy"). */ dateFormat?: string; /** Footer text from PptxHeaderFooter settings. */ footerText?: string; /** Header text from PptxHeaderFooter settings. */ headerText?: string; /** Custom document properties for `docproperty` field substitution (keyed by property name). */ customProperties?: ReadonlyArray<{ name: string; value: string; }>; /** Locale string for date/time formatting (e.g. "en-US"). Falls back to browser default. */ locale?: string; /** Title text extracted from the first title placeholder on the slide. */ slideTitle?: string; } /** * Changes to apply to textStyle from the advanced panel. * Every key maps 1-to-1 to TextStyle fields. */ type TextAdvancedChanges = Partial>; //#endregion //#region src/render/inline-selection-utils.d.ts /** Describes which segments (and offsets within them) are selected. */ interface InlineTextSelection { startSegIdx: number; startOffset: number; endSegIdx: number; endOffset: number; } //#endregion //#region src/render/text-case-transform.d.ts type ChangeCaseMode = 'sentence' | 'lower' | 'upper' | 'capitalize' | 'toggle'; //#endregion //#region src/render/collaboration-presence.d.ts /** Connection lifecycle states for the Yjs WebSocket provider. */ type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error'; /** A single remote collaborator's cursor, in unscaled slide coordinates. */ interface RemoteCursor { /** Stable id for the remote client (awareness clientId or peer id). */ clientId: number | string; /** Display name shown in the label chip. */ userName: string; /** Cursor + chip colour (any CSS colour string). */ color: string; /** Unscaled slide-space X coordinate (px). */ x: number; /** Unscaled slide-space Y coordinate (px). */ y: number; /** Optional ids of elements this user has selected. */ selectionIds?: string[]; } /** * Presence data for a remote collaborator, sanitised from the awareness * protocol. Returned by {@link sanitizePresence}; coordinates are in unscaled * slide pixels, clamped to the supplied canvas bounds. */ interface SanitizedPresence { clientId: number; userName: string; userAvatar?: string; userColor: string; activeSlideIndex: number; cursorX: number; cursorY: number; lastUpdated: string; selectedElementId?: string; role?: CollaborationRole; } /** Everything the Transitions tab's controls currently say. */ interface RibbonTransitionDraft { /** The highlighted gallery entry. */ type: PptxTransitionType; /** Duration field, in seconds. */ durationSec: number; /** Advance Slide > On Mouse Click. */ advanceOnClick: boolean; /** Advance Slide > After (whether the timed advance is armed at all). */ advanceAfter: boolean; /** Advance Slide > After, as the `mm:ss.hh` text the field holds. */ advanceAfterText: string; } /** * A font supplied by the host application. The package never ships fonts: * applications provide a licensed URL, data URL, or blob URL for their users. */ interface ViewerFontSource { family: string; src: string; format?: 'truetype' | 'opentype' | 'woff' | 'woff2'; weight?: string | number; style?: 'normal' | 'italic'; } interface SlideSizeEmu { widthEmu: number; heightEmu: number; /** ST_SlideSizeType token, or `''` for a size with no preset. */ type: string; } //#endregion //#region src/render/presentation-file-kinds.d.ts /** * presentation-file-kinds: the one place that answers "can the viewer open * this file?" and "what should the saved copy be called?". * * ## Why this is a shared decision and not five allow-lists * * The loader reads more formats than any single UI advertises. Legacy binary * `.ppt` (PowerPoint 97-2003) is the sharp example: `PptxHandler.load()` has * detected the OLE compound-file container and converted the binary deck * through the regular pptx pipeline for some time, but the product kept saying * it was unsupported, and a picker that filters the extension out makes a * working loader unreachable in practice. Whenever the loader learns a format, * exactly one list has to change. * * ## Read many, write one * * Input is a superset of output. We READ `.pptx`, `.ppsx`, `.pptm`, `.potx`, * legacy binary `.ppt` and portable `pptx-viewer-json`; we WRITE only the * OpenXML family. That asymmetry is deliberate (PowerPoint itself does the * same: open a 97-2003 deck and Save As offers `.pptx`), and it is why * {@link savedPresentationFileName} always REPLACES the source extension * rather than keeping it. A deck opened as `report.ppt` and saved as * `report.ppt` would be a file whose bytes and whose name disagree, which is * the kind of thing PowerPoint refuses to open. * * This module deliberately imports nothing, so any layer (render, export, a * binding, a host app) can depend on it without risking an import cycle. * * @module render/presentation-file-kinds */ /** * Extensions the built-in file picker offers, in the order it offers them. * * `.ppt` is in the list because the loader genuinely handles it, not as a * courtesy: see `packages/core/src/core/ppt/` and the `ppt-import` integration * suite, which asserts a `.ppt` loads to the same model as the `.pptx` it was * exported from. */ declare const PRESENTATION_OPEN_EXTENSIONS: readonly ['.pptx', '.ppsx', '.pptm', '.potx', '.ppt', '.json']; /** Comma-separated `accept` attribute for a presentation file input. */ declare const PPTX_OPEN_ACCEPT: string; /** * True when a picked / dropped file's name looks like something the loader can * open. Use this instead of a hand-rolled `endsWith` chain: a drop handler that * disagrees with the picker's `accept` list is a format that is supported by * mouse but not by drag, which is how `.ppt` stayed invisible. * * Extension-only, by design. The real answer comes from the container sniff in * `PptxHandler.load()`; this is only the cheap pre-filter a drop target needs * before it hands bytes to the loader. */ declare function isSupportedPresentationFile(name: string | null | undefined): boolean; /** True for the binary PowerPoint 97-2003 family, which we read but never write. */ declare function isLegacyBinaryPresentation(name: string | null | undefined): boolean; /** The formats the save path can produce. Binary `.ppt` is deliberately absent. */ type SavedPresentationFormat = 'pptx' | 'ppsx' | 'pptm'; /** * The stem of a presentation file name: directories and any loadable extension * removed. `C:\decks\report.ppt` becomes `report`; a name with no recognised * extension is kept whole, so `Untitled Presentation` survives intact rather * than losing everything after its last dot. */ declare function presentationBaseName(sourceName: string | null | undefined, fallback?: string): string; /** * The name a saved copy should be offered under: the source stem plus the * extension of the format actually being written. * * This is what turns `report.ppt` into `report.pptx` on Save As. Output is * always an OpenXML package, so keeping the source extension would mislabel * the bytes. */ declare function savedPresentationFileName(sourceName: string | null | undefined, format?: SavedPresentationFormat): string; //#endregion //#region src/render/session-restore.d.ts /** * session-restore: keep the deck a host app has open across a page refresh. * * A host (the demo apps, or any embedder) owns the bytes it hands to the * viewer, so a plain reload drops them and the user lands back on the file * dropzone with their presentation gone. This store remembers the open deck in * IndexedDB and hands it back on the next load. * * Scope is deliberately per-tab: the record is keyed by an id kept in * `sessionStorage`, which survives a reload but NOT a new tab. Refreshing * restores the deck this tab had open, while a second tab opened on the same * origin still starts on the landing page, and two tabs holding different decks * never steal each other's content. * * `restoreSessionDeck` additionally prefers a NEWER autosave snapshot for the * same file (see `./autosave-store`), so a refresh mid-edit comes back with the * edited deck rather than the pristine bytes that were first opened. * * Every operation is best-effort: a blocked IndexedDB, a partitioned * `sessionStorage`, or an exhausted quota degrades to "no restore", never to a * thrown error in the host. */ /** A deck remembered for the current tab. */ interface SessionDeck { /** File name the deck was opened under, used as the autosave key. */ fileName: string; /** The presentation bytes. */ data: Uint8Array; /** When these bytes were remembered (epoch ms). */ timestamp: number; } /** * This tab's session id, or `null` when `sessionStorage` is unavailable (a * sandboxed iframe, or a browser with storage disabled). * * @param create Mint and persist an id when the tab does not have one yet. * Reads pass `false` so a fresh tab never claims another tab's record. */ declare function getSessionTabId(create?: boolean): string | null; /** * Remember `data` as the deck this tab has open, so the next load can restore * it. Resolves `false` when the browser refused to store it; callers treat that * as "no restore available later", never as an error. */ declare function rememberSessionDeck(fileName: string, data: Uint8Array): Promise; /** The deck remembered for this tab, or `null` when there is nothing to restore. */ declare function loadSessionDeck(): Promise; /** Forget this tab's deck (the host closed it, or handed the tab to another flow). */ declare function forgetSessionDeck(): Promise; /** * The deck to reopen on load: this tab's remembered bytes, upgraded to a newer * autosave snapshot of the same file when the viewer wrote one after they were * remembered. Without that upgrade a refresh mid-edit would silently roll the * presentation back to the state it was opened in. */ declare function restoreSessionDeck(): Promise; /** Optional seed values for the share start form. */ interface ShareDefaults { roomId?: string; userName?: string; serverUrl?: string; } //#endregion //#region src/render/presentation-session-types.d.ts interface PresentationSnapshot { slideIndex: number; buildStep: number; sequence: number; blackout: 'none' | 'black' | 'white'; paused: boolean; elapsedMs: number; zoom?: PresentationZoomState; pointer?: PresentationPointerState; inkStrokes?: PresentationInkStroke[]; /** * Whether ink markup is drawn (PowerPoint's Ctrl+M). Undefined means visible, * so existing snapshots keep showing their ink. Hiding never discards the * strokes, which stay in `inkStrokes` and can be kept on exit. */ inkMarkupVisible?: boolean; caption?: string; subtitlesVisible?: boolean; } interface PresentationZoomState { scale: number; originX: number; originY: number; } type PresentationPointerTool = 'none' | 'laser' | 'pen' | 'highlighter' | 'eraser'; interface PresentationPointerState { tool: PresentationPointerTool; x: number; y: number; color: string; } interface PresentationInkPoint { x: number; y: number; } interface PresentationInkStroke { id: string; slideIndex: number; tool: 'pen' | 'highlighter'; color: string; width: number; points: PresentationInkPoint[]; } declare function parsePresentationSessionId(hash: string): string | null; declare function loadPresentationDeck(sessionId: string): Promise; //#endregion //#region src/render/presentation-blackboard.d.ts /** Blackout state, as carried by {@link PresentationSnapshot}. */ type PresentationBlackout = PresentationSnapshot['blackout']; //#endregion //#region src/render/alignment-guides.d.ts /** A single alignment guide, positioned in authored slide pixels. */ interface Guide { id: string; axis: 'h' | 'v'; position: number; } //#endregion //#region src/render/ink-drawing.d.ts /** A 2D point in stage-local coordinates. */ interface InkPoint { x: number; y: number; } /** Options for {@link strokeToInkElement}. */ interface StrokeToInkElementOpts { points: InkPoint[]; color: string; width: number; tool: 'pen' | 'highlighter' | 'freeform'; } //#endregion //#region src/render/mobile-chrome.d.ts /** * Pure helper logic for mobile chrome state, shared by every binding. * * These helpers capture the sheet open/close state machine that governs which * sheet is visible at any given time and which bottom-bar button appears active. * Keeping this logic as pure functions makes it straightforward to test and * trivial to adapt when each binding wires the components together. */ /** All mobile sheets that can be open at one time. */ type MobileSheetKey = 'slides' | 'menu' | 'insert' | 'inspector' | 'comments' | 'notes' | null; //#endregion //#region src/render/gradient-picker.d.ts /** A single gradient stop, mirroring the ShapeStyle array item shape. */ interface GradientStop { color: string; position: number; opacity?: number; } /** The editable gradient state surfaced to the component. */ interface GradientState { type: 'linear' | 'radial'; /** Angle in degrees (only meaningful for linear). */ angle: number; stops: GradientStop[]; } //#endregion //#region src/render/insert-chart.d.ts /** * Dropdown ids for the insert-chart menu. Distinct from `PptxChartType` * because PowerPoint offers Column (vertical) and Bar (horizontal) as two * entries over the same underlying `'bar'` chart type. The six ChartEx ids * (`histogram` through `regionMap`) map one-to-one onto their chart type. */ type InsertChartKind = 'column' | 'bar' | 'line' | 'pie' | 'doughnut' | 'area' | 'scatter' | 'histogram' | 'funnel' | 'treemap' | 'sunburst' | 'boxWhisker' | 'regionMap'; /** In-memory clipboard payload stored when an element is copied or cut. */ interface ElementClipboardPayload { element: PptxElement; isTemplate: boolean; } //#endregion //#region src/render/shape-preset-catalog.d.ts /** * shape-preset-catalog.ts: the Insert > Shape picker catalogue shared by every * binding's toolbar/inspector. * * Pure data: each entry carries the preset geometry `type` (OOXML `a:prstGeom` * value the editor can insert), an English fallback `label`, the shared-i18n * `i18nKey`, and a framework-neutral icon descriptor ({@link ShapePresetGlyph} * name + optional utility-class modifier for rotation/skew). Each binding maps * the glyph name onto its own icon component/SVG. * * Order matters: bindings surface the first 12 entries as the quick "top * shapes" row, so new presets should be appended, not inserted. * * @module render/shape-preset-catalog */ /** Shape preset geometry types offered by the insert picker. */ type ShapePresetType = 'rect' | 'roundRect' | 'ellipse' | 'cylinder' | 'rtArrow' | 'leftArrow' | 'upArrow' | 'downArrow' | 'triangle' | 'rtTriangle' | 'diamond' | 'parallelogram' | 'trapezoid' | 'pentagon' | 'hexagon' | 'octagon' | 'chevron' | 'star5' | 'star6' | 'star8' | 'plus' | 'heart' | 'cloud' | 'sun' | 'moon' | 'pie' | 'plaque' | 'teardrop' | 'line' | 'connector'; interface AutosaveRecord { key: string; data: Uint8Array; timestamp: number; size: number; } /** * Optional hook point for hosts that want to wire a real sign-in flow into * File > Account. Disabled by default: the Account page renders nothing * extra unless a host explicitly opts in by passing `enabled: true`. * See docs/guide for wiring instructions. */ interface AccountAuthConfig { enabled: boolean; onSignIn: () => void; signedInUser?: { name: string; email?: string; avatarUrl?: string; }; } //#endregion //#region src/render/toolbar-actions.d.ts /** * Toolbar action / ribbon-tab visibility: a single, framework-agnostic * catalogue of every top-level toolbar button and ribbon tab a host app can * independently hide. Each binding exposes a `hiddenActions?: ToolbarActionId[]` * prop, threads it down to the relevant render sites, and gates them with * `isActionHidden`. Default (`undefined` / `[]`) hides nothing, matching * today's always-visible behaviour. * * `TOOLBAR_TABS` is also the canonical ribbon-tab list/order, replacing the * copy hand-duplicated in each binding (React's `TOOLBAR_SECTIONS`, Vue's * `ribbon-constants.ts`, Angular's `RIBBON_TABS`, etc.) so the tab set can't * drift between bindings. Per-tab icons stay in each binding (icon libraries * differ per framework); only id + i18n key + order are shared here. */ /** * A single toolbar button/control that can be hidden independently of the * ribbon tab it may also appear inside. `zoom` and `navigation` each cover a * whole control cluster (zoom in/out/fit, prev/next) rather than each button * in it, matching how hosts actually want to hide/keep them as a unit. */ type ToolbarButtonId = 'share' | 'broadcast' | 'export' | 'undo' | 'redo' | 'record' | 'notes' | 'fullscreen' | 'zoom' | 'navigation'; /** A top-level ribbon tab. `record` intentionally shares its id with the quick-access Record button above: both surface the same recording feature, so hiding one hides the other. */ type ToolbarTabId = 'file' | 'home' | 'insert' | 'draw' | 'design' | 'transitions' | 'animations' | 'slideShow' | 'record' | 'review' | 'view' | 'help'; type ToolbarActionId = ToolbarButtonId | ToolbarTabId; type QuickAccessPosition = 'above' | 'below'; interface ViewerQuickAccessOptions { visible: boolean; position: QuickAccessPosition; showCommandLabels: boolean; /** Ordered ids from `QUICK_ACCESS_COMMAND_CATALOG`. */ commandIds: string[]; } //#endregion //#region src/export/handout-layout.d.ts /** * Pure handout layout calculations, shared by every binding's print path. * * Handles distributing slides across pages, computing grid dimensions, and * positioning cells within A4 page space. No DOM/framework dependency: callers * render the resulting rectangles however their view layer prefers. */ /** Supported slides-per-page values. */ type HandoutSlidesPerPage = 1 | 2 | 3 | 4 | 6 | 9; //#endregion //#region src/export/print-document.d.ts /** What to print. */ type PrintWhat = 'slides' | 'handouts' | 'notes' | 'outline'; /** Page orientation for the printed output. */ type PrintOrientation = 'portrait' | 'landscape'; /** Colour mode for the printed output. */ type PrintColorMode = 'color' | 'grayscale' | 'blackAndWhite'; /** Slide range mode. */ type PrintSlideRange = 'all' | 'current' | 'custom'; /** Resolved print settings emitted on confirm. */ interface PrintSettings { printWhat: PrintWhat; orientation: PrintOrientation; colorMode: PrintColorMode; frameSlides: boolean; slidesPerPage: HandoutSlidesPerPage; slideRange: PrintSlideRange; customRangeFrom: number; customRangeTo: number; } //#endregion //#region src/viewer/state/store.d.ts /** * A tiny framework-free reactive store: `get` / `set(patch)` / `subscribe`. * * This is deliberately minimal (a few dozen lines, no external deps). It only * carries the vanilla binding's *view state*; all domain logic (parsing, * styles, geometry, text building) stays in `pptx-viewer-core` and * `pptx-viewer-shared`. */ /** Listener invoked after every state change with the next and previous state. */ type StoreListener = (state: T, previous: T) => void; interface Store { /** Current state snapshot (do not mutate). */ get(): T; /** Shallow-merge a partial patch into the state and notify subscribers. */ set(patch: Partial): void; /** Subscribe to state changes; returns an unsubscribe function. */ subscribe(listener: StoreListener): () => void; } declare function createStore(initial: T): Store; //#endregion //#region src/viewer/i18n/translator.d.ts /** * Minimal i18n for the vanilla binding. * * The other bindings delegate to their framework's i18n runtime (react-i18next, * vue-i18n, ngx-translate) fed by the shared `pptx.*` dictionary. The vanilla * binding has no framework, so this module provides the one missing piece: a * `t(key, params)` lookup with `{{param}}` interpolation over the same shared * English dictionary, plus per-locale overrides supplied by the host. * * Resolution order: host dictionary for the active locale, then the built-in * English dictionary, then a humanised fallback derived from the key (shared * `keyToLabel`), so a missing key never renders as a raw `pptx.*` string. */ /** Translate a dotted `pptx.*` key with optional `{{param}}` interpolation. */ type Translator = (key: string, params?: Record) => string; /** Locale to flat `key: message` dictionary map supplied by the host. */ type TranslationMessages = Record>; /** * Build a {@link Translator} for a locale. `messages[locale]` (when provided) * wins over the built-in English dictionary; English is always the fallback. */ declare function createTranslator(locale?: string, messages?: TranslationMessages): Translator; //#endregion //#region src/viewer/render/types.d.ts /** Discriminant values of the {@link PptxElement} union (`'text'`, `'chart'`, ...). */ type PptxElementType = PptxElement['type']; /** * A selected chart sub-part, scoped to the chart element that owns it. The * vanilla counterpart of React's `ChartPartSelectionContext` / Vue's * `ChartPartSelection` / Angular's `ChartPartSelectionService`: the bridge * between an on-canvas mark click (`chart-editable.ts`) and the chart * inspector (`chart-data-grid.ts` / `chart-point-index.ts`), threaded through * the store rather than component context since vanilla has no such * injection mechanism. */ interface ChartPartSelection { elementId: string; part: ChartPartRef; } /** * Everything an element renderer may need, passed to every renderer call. * * The context is immutable per slide render. Renderers must create DOM through * `context.document` (never the global `document`) so slides can render into * detached documents (tests, export pipelines). */ interface ElementRenderContext { /** Document used for all DOM creation. */ readonly document: Document; /** The slide being rendered. */ readonly slide: PptxSlide; /** Full deck, used by Zoom elements to resolve their target preview. */ readonly slides?: readonly PptxSlide[]; /** Zero-based active slide index for Zoom return navigation. */ readonly currentSlideIndex?: number; /** Presentation-only Zoom tile activation callback. */ readonly onZoomClick?: (targetSlideIndex: number, returnSlideIndex: number) => void; /** Full slide canvas size in CSS px (elements are positioned in this space). */ readonly canvasSize: CanvasSize; /** * The scale the stage is rendered at (1 = 100%). The stage applies it via a * CSS transform, so renderers should lay out in unscaled canvas px; `scale` * is informational (e.g. for raster density decisions). */ readonly scale: number; /** Archive-path to displayable URL map for media + poster frames. */ readonly mediaDataUrls: ReadonlyMap; /** Presentation theme colour scheme used by theme-aware render helpers. */ readonly colorScheme?: PptxThemeColorScheme; /** * Presentation theme font scheme, so table styles resolve their * `a:fontRef@idx` (`minor`/`major`) to a concrete font family. */ readonly fontScheme?: PptxThemeFontScheme; /** Parsed `ppt/tableStyles.xml` definitions used by table band/header styling. */ readonly tableStyleMap?: ParsedTableStyleMap; readonly fieldContext?: FieldSubstitutionContext; /** Shared-dictionary translator (`pptx.*` keys). */ readonly t: Translator; /** * Opt-in flag: render `smartArt` elements as an extruded Three.js scene * instead of flat SVG (see `PptxViewerOptions.smartArt3D`). Defaults to * `false` when the option is unset. */ readonly smartArt3D: boolean; /** * Opt-in flag: render `surface`/`surface3D` charts as an interactive, * camera-orbitable Three.js mesh instead of the static SVG isometric * projection (see `PptxViewerOptions.surfaceChart3D`). Defaults to `false` * when the option is unset. */ readonly surfaceChart3D: boolean; /** * True only for the live presentation stage (real Fullscreen API active): * media renderers use this to autoplay once mounted, matching PowerPoint's * slideshow behaviour. `false` for the editor canvas and thumbnail rail. */ readonly presenting: boolean; /** * True only for the surface the user is authoring on. `false` for every * still of a slide (thumbnail rail, presenter console panes, export raster) * AND for the live show stage, which is driven, not edited. * * Media reads it: a stage that is neither interactive nor presenting is a * still, and a still never carries the browser's native transport (see the * shared `mediaTransportVisible`). Optional so the many hand-built contexts * in tests keep compiling; the stage always sets it. */ readonly interactive?: boolean; readonly onSmartArtNodeTextChange?: (element: PptxElement, nodeId: string, text: string) => void; readonly onSmartArtNodeFillChange?: (element: PptxElement, nodeId: string, fill: string) => void; /** * Commit a table's whole column-width array after a column-boundary drag. * Called once on pointer release (the drag itself only translates the * boundary handle), so one drag is one undo step. Absent on every * non-authoring surface, matching `onSmartArtNodeTextChange`. */ readonly onTableResizeColumns?: (element: PptxElement, widths: number[]) => void; /** Commit one row's new pixel height after a row-boundary drag. */ readonly onTableResizeRow?: (element: PptxElement, rowIndex: number, height: number) => void; /** * Commit a chart data point dragged on the canvas, with the whole updated * `chartData`. Called ONCE on pointer release (the drag itself is a local * preview), so one drag is one undo step. Absent on every non-authoring * surface, which is what keeps thumbnails and the show stage inert. */ readonly onChartPointChange?: (element: PptxElement, chartData: PptxChartData) => void; /** * The current on-canvas chart part selection (shared with the inspector via * the store), or `null`/absent when nothing is selected. Read by * `chart-editable.ts` to seed its highlight on (re)mount, so the ring stays * on the clicked mark across a stage rebuild triggered by an unrelated edit. */ readonly chartPartSelection?: ChartPartSelection | null; /** * A chart part (bar / dot / slice / series line) was pressed on the canvas. * Surfaces the selection to the chart inspector (ring-highlight + scroll in * `chart-data-grid.ts`, point sync in `chart-point-index.ts`). Absent on * every non-authoring surface, matching `onChartPointChange`. */ readonly onChartPartSelect?: (element: PptxElement, part: ChartPartRef) => void; /** * Per-element native-animation playback state, keyed by element id, present * only during a running presentation. Chart / SmartArt renderers read their * element's {@link ElementAnimationState.build} to reveal a staged build * (`p:bldChart` / `p:bldDgm`); shape renderers read `animatesFill` / * `animatesStroke` to relinquish static paint during a `p:animClr` animation. * A stable map instance, mutated in place by the playback controller so a * targeted single-element re-render always reads current state. */ readonly presentationStates?: ReadonlyMap; /** The registry in effect, for renderers that need to inspect it. */ readonly registry: ElementRendererRegistry; /** * Render a child element through the registry (used by the group renderer * for recursion; custom renderers may nest elements the same way). */ renderElement(element: PptxElement, zIndex: number): HTMLElement | SVGElement | null; } /** * Renders one slide element to a DOM node (or `null` to render nothing). * * Contract: * - Position absolutely within the stage: apply the shared * `getContainerStyle(element, zIndex)` map (or equivalent) to the returned * root node. * - Set `dataset.elementId = element.id` on the root node. * - Create all DOM via `context.document`. * - Never mutate `element` or anything on the context. */ type ElementRenderer = (element: PptxElement, zIndex: number, context: ElementRenderContext) => HTMLElement | SVGElement | null; /** * Registry dispatching elements to renderers by their `type` discriminant. * * Additional element types are supported by registering a renderer for the * type; unregistered types fall through to the fallback renderer (a typed * placeholder box by default). See `render/elements/README.md` for the * step-by-step contract. */ interface ElementRendererRegistry { /** Register (or replace) the renderer for an element type. */ register(type: PptxElementType, renderer: ElementRenderer): void; /** Remove the renderer for an element type (falls back afterwards). */ unregister(type: PptxElementType): void; /** The renderer registered for a type, or `undefined`. */ get(type: PptxElementType): ElementRenderer | undefined; /** True when a dedicated (non-fallback) renderer is registered. */ has(type: PptxElementType): boolean; /** Replace the fallback renderer used for unregistered types. */ setFallback(renderer: ElementRenderer): void; /** The renderer to use for a type: registered renderer or fallback. */ resolve(type: PptxElementType): ElementRenderer; /** All types with a dedicated renderer (sorted, for tests/debugging). */ registeredTypes(): PptxElementType[]; } //#endregion //#region src/viewer/render/registry.d.ts /** * Create an empty {@link ElementRendererRegistry}. * * The default fallback renders nothing; `createDefaultRegistry()` (in * `./elements`) installs the built-in renderers plus the typed placeholder * fallback. */ declare function createElementRendererRegistry(): ElementRendererRegistry; //#endregion //#region src/viewer/render/dom.d.ts /** * Apply a shared `CssStyleMap` to an element. The shared builders emit a mix * of camelCase (`backgroundColor`) and kebab-case (`background-color`) keys; * both are normalised to `style.setProperty` calls. */ declare function applyStyleMap(el: HTMLElement | SVGElement, style: CssStyleMap): void; /** Create an element with an optional class and style map. */ declare function createEl(doc: Document, tag: K, className?: string, style?: CssStyleMap): HTMLElementTagNameMap[K]; /** Create a namespaced SVG element with optional attributes. */ declare function createSvgEl(doc: Document, tag: K, attrs?: Record): SVGElementTagNameMap[K]; //#endregion //#region src/viewer/render/slide-stage.d.ts interface SlideStageOptions { document: Document; slide: PptxSlide; canvasSize: CanvasSize; mediaDataUrls: ReadonlyMap; /** Optional presentation theme colour scheme for element renderers. */ colorScheme?: PptxThemeColorScheme; /** Optional presentation theme font scheme for table-style font resolution. */ fontScheme?: PptxThemeFontScheme; /** Optional parsed table-style definitions for theme-aware table rendering. */ tableStyleMap?: ParsedTableStyleMap; fieldContext?: FieldSubstitutionContext; registry: ElementRendererRegistry; t: Translator; /** Scale applied via CSS transform (default 1). */ scale?: number; /** * Grid dot/line spacing in CSS px for the View > Grid overlay (`.pptxv-showGrid`), * derived by the caller from the deck's authored `viewProperties.gridSpacing` * via `computeGridSpacingPx`. Defaults to 10px (this binding's existing grid * step) when omitted. */ gridSpacingPx?: number; /** Opt-in WebGL SmartArt renderer flag; see `PptxViewerOptions.smartArt3D`. */ smartArt3D?: boolean; /** * Opt-in interactive WebGL surface-chart renderer flag; see * `PptxViewerOptions.surfaceChart3D`. */ surfaceChart3D?: boolean; /** True only for the live presentation stage; see `ElementRenderContext.presenting`. */ presenting?: boolean; /** Full deck and active index used by presentation Zoom elements. */ slides?: readonly PptxSlide[]; currentSlideIndex?: number; onZoomClick?: (targetSlideIndex: number, returnSlideIndex: number) => void; onSmartArtNodeTextChange?: (element: PptxElement, nodeId: string, text: string) => void; onSmartArtNodeFillChange?: (element: PptxElement, nodeId: string, fill: string) => void; /** See `ElementRenderContext.onChartPointChange`. */ readonly onChartPointChange?: (element: PptxElement, chartData: PptxChartData) => void; /** See `ElementRenderContext.onTableResizeColumns`. */ readonly onTableResizeColumns?: (element: PptxElement, widths: number[]) => void; /** See `ElementRenderContext.onTableResizeRow`. */ readonly onTableResizeRow?: (element: PptxElement, rowIndex: number, height: number) => void; /** See `ElementRenderContext.chartPartSelection`. */ readonly chartPartSelection?: ChartPartSelection | null; /** See `ElementRenderContext.onChartPartSelect`. */ readonly onChartPartSelect?: (element: PptxElement, part: ChartPartRef) => void; /** * True only for the main (interactive) canvas, never the thumbnail rail. * Marks every rendered element (recursively, including group children) with * `data-pptx-element="true"` and the stage itself with * `role="region" aria-roledescription="slide"` - the framework-neutral e2e * test hooks the React/Vue/Angular bindings also emit. Defaults to `false`. */ interactive?: boolean; /** Whether inherited layout/master nodes participate in editing. */ templateEditing?: boolean; /** Per-element native-animation playback state (presentation mode only). */ presentationStates?: ReadonlyMap; /** * Invoked with the built render context once the stage is assembled. Lets the * presentation playback re-render single elements (staged chart / SmartArt * build, `p:animClr` fill / stroke) in place without rebuilding the stage. */ captureContext?: (context: ElementRenderContext) => void; } /** * Render one slide as a fixed-size stage: the resolved slide background plus * every element dispatched through the registry, scaled with a CSS transform * (`transform-origin: top left`), exactly like the other bindings' stages. * * The returned node is `canvasSize * scale` ON SCREEN but laid out at the * unscaled canvas size, so the caller should wrap it in a box sized to * `canvasSize * scale` (the viewer's stage host and thumbnails both do). */ declare function renderSlideStage(options: SlideStageOptions): HTMLElement; //#endregion //#region src/viewer/render/elements/index.d.ts /** * The registry the viewer uses by default. * * Dedicated renderers: `text`, `shape`, `image`, `picture`, `group`, * `connector`, `table`, `chart`, `smartArt`, `media`, `ink`, `ole`, * `contentPart`, `zoom`, and `model3d`. Only unknown extension elements fall * through to the typed placeholder fallback. */ declare function createDefaultRegistry(): ElementRendererRegistry; //#endregion //#region src/viewer/state/viewer-state.d.ts /** `zoom` is either an explicit scale factor (1 = 100%) or fit-to-viewport. */ type ZoomLevel = number | 'fit'; /** * The Draw ribbon tab's active tool. `'select'` means normal editing gestures * (move/resize/rotate/inline-edit) apply on the stage; every other value * routes stage pointer events to the ink-drawing gesture controller instead * (see `editor-draw-gestures.ts`). */ type DrawTool = 'select' | 'pen' | 'highlighter' | 'eraser' | 'freeform'; /** * The vanilla viewer's reactive view state. Kept intentionally flat and small; * everything here is what the DOM render layer consumes. */ interface ViewerState { /** Parsed slides (image/media URLs already patched in by the load pipeline). */ slides: PptxSlide[]; /** Presentation sections used to group slides in the thumbnail rail. */ sections: PptxSection[]; presentationProperties: PptxPresentationProperties; /** * View properties (`ppt/viewProps.xml`, `p:viewPr`): grid spacing, snap / * guide toggles, last view, splitter state, etc. `gridSpacing` lives here, * NOT on `presentationProperties` -- `p:gridSpacing` is a child of * `p:viewPr`, and a real PowerPoint file never populates it under * `p:presentationPr`. */ viewProperties: PptxViewProperties | undefined; headerFooter: PptxHeaderFooter; coreProperties?: PptxCoreProperties; appProperties?: PptxAppProperties; customProperties: PptxCustomProperty[]; customShows: PptxCustomShow[]; /** * The custom show a started slide show is restricted to, or null for the * whole deck. Custom shows were definable here but nothing could select one, * so picking a show had no effect on what actually presented; this is the * state the show-order rule needs to honour membership (React, Vue and * Angular each hold the same id). */ activeCustomShowId: string | null; embeddedFonts: PptxEmbeddedFont[]; /** * File > Fonts > "Embed fonts in the file". Read by the save path, which * hands it to the shared `embeddedFontSaveOptions`: off passes * `embeddedFontList: null` and strips `p:embeddedFontLst`, the `/font` * relationships and the `.fntdata` parts, while on leaves core's lossless * re-embed alone. Seeded per load from {@link embeddedFonts} (see * `describeFontEmbedding`), because a deck that arrived with embedded fonts * keeps them on save and the switch has to say so. It used to be a private * field on `PptxViewer` that nothing downstream read, so moving it produced a * byte-identical file. */ embedFonts: boolean; hasDigitalSignatures: boolean; digitalSignatureCount: number; isPasswordProtected: boolean; /** * The File > Info > Protect Presentation secret, or null when the deck saves * in the clear. Read by the save path and handed to the shared * `planDeckSave`, which routes a protected save through `saveEncrypted` so * the produced file is an encrypted OLE2 container, not a plain ZIP. * Deliberately separate from {@link isPasswordProtected}, which the LOAD * pipeline also sets for a deck that merely arrived protected. */ presentationPassword: string | null; /** Inherited layout/master elements, separated so interaction can be gated. */ templateElementsBySlideId: Record; /** Parsed slide masters and layouts used by the dedicated master canvas. */ slideMasters: PptxSlideMaster[]; /** Theme parts discovered in the package (inspector THEME card). */ themeOptions: PptxThemeOption[]; /** Parsed notes master and its portrait page size. */ notesMaster?: PptxNotesMaster; notesCanvasSize?: CanvasSize; /** Parsed handout master. */ handoutMaster?: PptxHandoutMaster; /** Whether the loaded package contains a VBA project. */ hasMacros: boolean; /** Active master-workspace tab. */ masterViewTab: MasterViewTab; /** Preview layout used by the handout master workspace. */ handoutSlidesPerPage: number; /** Active master/layout canvas, or null for normal slide view. */ masterViewTarget: { masterIndex: number; layoutIndex: number | null; } | null; /** Slide canvas size in CSS pixels. */ canvasSize: CanvasSize; /** * The deck's `p:sldSz` in EMU, seeded from the parse and re-written by the * inspector's Slide Size preset / orientation controls. * * Held alongside {@link canvasSize} rather than derived from it because the * pixel size is lossy: Ledger is 12179300 EMU (1278.5px), so a round-trip * through an integer pixel would move it 6350 EMU and cost the deck its * `ppSlideSizeLedgerPaper` identity. `resolveSlideSizeSelection` decides * which of the two wins at save time. */ slideSize?: SlideSizeEmu; /** Archive-path to displayable URL map for media + poster frames. */ mediaDataUrls: Map; /** Presentation theme colours used by scheme-based rendering. */ colorScheme?: PptxThemeColorScheme; /** Presentation theme fonts used by table-style font resolution. */ fontScheme?: PptxThemeFontScheme; /** The loaded theme's name (inspector THEME EDITOR card). */ themeName?: string; /** * Families the user registered from a local font file this session * (File > Options > Fonts, off by default). * * Session state, never persisted and never written into the deck: the font * binary is the user's, not ours to store. */ customFontFamilies: string[]; /** Tag collections parsed from `ppt/tags/*.xml` (inspector TAGS card). */ tagCollections: PptxTagCollection[]; /** Parsed presentation table styles keyed by style id. */ tableStyleMap?: ParsedTableStyleMap; /** Zero-based index of the visible slide. */ currentSlide: number; /** Requested zoom (explicit factor or fit-to-viewport). */ zoom: ZoomLevel; /** True while a load is in flight. */ loading: boolean; /** Error message from the last failed load, or null. */ error: string | null; /** True while presentation (fullscreen) mode is active. */ presenting: boolean; /** * True once the show has run past its last slide and the black "End of slide * show" screen is up. It MUST be surfaced: while it is up the next input * either goes nowhere (backward) or ends the show (forward), so a deck that * kept painting its last slide looked stuck and swallowed every advance. */ endOfShow: boolean; /** * True for a single render pass when the show stepped BACKWARD onto the * current slide, so its builds are seeded already-complete (PowerPoint). */ enteringBackward?: boolean; /** True when editing interactions (select/move/resize/...) are enabled. */ editable: boolean; /** Id of the selected element on the current slide, or null. */ selectedElementId: string | null; /** All selected top-level element ids; the primary selection is listed last. */ selectedElementIds: string[]; /** Active table cell for cell-scoped inspector formatting. */ selectedTableCell: { row: number; column: number; } | null; /** Shift-select range of active table cells, including the anchor. */ selectedTableCells: Array<{ row: number; column: number; }>; /** Active rich-text range captured from the inline editor. */ selectedTextRange: InlineTextSelection | null; /** * On-canvas chart part selection (a clicked bar/dot/slice/series line), * surfaced to the chart inspector's data grid + point-index picker. Cleared * whenever the general element selection changes (see `selectionState`). */ chartPartSelection: ChartPartSelection | null; /** Source element id while the one-shot Format Painter is armed. */ formatPainterSourceId: string | null; /** When true, selection and element mutations target inherited template elements. */ editTemplateMode: boolean; /** True when the document has unsaved edits (cleared by a save). */ dirty: boolean; /** * True while a pointer gesture (drag/resize/rotate) is in flight. Thumbnail * re-renders are deferred until the gesture ends. */ interactionActive: boolean; /** * True when the speaker-notes panel body is expanded. Persists across slide * navigation for the life of the viewer instance (in-memory only). */ notesExpanded: boolean; /** True when the right-hand property inspector panel is shown (editing chrome). */ inspectorOpen: boolean; /** Remote collaborators currently in the session (empty when not collaborating). */ remotePresences: SanitizedPresence[]; /** Remote cursors visible on the current slide, projected from `remotePresences`. */ cursors: RemoteCursor[]; /** Client id of the peer the local user is following, or null when free. */ followedClientId: number | null; /** In-memory clipboard payload from the last copy/cut, or null. */ clipboardPayload: ElementClipboardPayload | null; /** Active Draw ribbon tool; `'select'` disables the ink-drawing gesture controller. */ drawTool: DrawTool; /** Stroke colour for the pen/highlighter tools. */ drawColor: string; /** Stroke width (px) for the pen/highlighter tools. */ drawWidth: number; showGrid: boolean; showRulers: boolean; snapToGrid: boolean; snapToShape: boolean; /** * Whether the drawing guides are painted on the stage (View > Guides). * * Visibility only: `guides` keeps the full list either way, so hiding them * neither drops a guide from the saved deck nor stops a drag snapping to * one. Guide visibility and shape snapping are separate settings and each * has its own View-tab control. */ showGuides: boolean; guides: Guide[]; eyedropperActive: boolean; spellCheckEnabled: boolean; } declare function createInitialViewerState(): ViewerState; //#endregion //#region src/viewer/editor/editor-animation-actions.d.ts /** * Element-animation actions for the ribbon's Animations tab. * * Animation data lives on the SLIDE (`PptxSlide.animations`, keyed by * `elementId`), not on the element itself, matching how the presentation * playback state machine reads it (`buildClickGroups(slide.animations)` in * `animation/presentation-playback.ts`). Both actions target the currently * selected element and route through the shared `animation-authoring.ts` * "coarse group preset" model (`applyAnimationPreset` / `removeElementAnimation`), * the same one the Vue ribbon uses: applying a preset sets one of * entrance/emphasis/exit without touching the others; remove drops the whole * entry. */ /** Partial timing/effect-option update for one element's animation entry. */ interface AnimationTimingPatch { durationMs?: number; delayMs?: number; trigger?: PptxAnimationTrigger; direction?: PptxAnimationDirection; sequence?: PptxAnimationSequence; timingCurve?: PptxAnimationTimingCurve; repeatCount?: number; repeatMode?: PptxAnimationRepeatMode | 'none'; triggerShapeId?: string; } interface AnimationActions { addAnimation(group: AnimationGroup, preset: PptxAnimationPreset): void; removeAnimation(): void; /** * Set (or clear with `'none'`) a single effect bucket on the selected * element, React's `AnimationPanel` select model: the entry is created on * first effect and dropped once all three buckets are empty (shared * `setAnimationEntrance`/`setAnimationExit`/`setAnimationEmphasis`). */ setAnimationEffect(group: AnimationGroup, preset: PptxAnimationPreset | 'none'): void; /** * Apply a catalogue motion path to the selected element by preset id. * * `'none'` clears the path; `'custom'` (the inspector's read-only marker for * a hand-dragged path) is deliberately a no-op, so re-picking the marker can * never snap the dragged geometry back to a catalogue entry. */ applyMotionPath(presetId: string): void; /** Replace the selected element's raw path (canvas end-handle drag commit). */ setMotionPathData(path: string): void; setAnimationTiming(elementId: string, patch: AnimationTimingPatch): void; reorderAnimation(elementId: string, direction: 'up' | 'down'): void; moveAnimation(elementId: string, index: number): void; } //#endregion //#region src/viewer/editor/editor-arrange-actions.d.ts /** * Z-order, align, flip, and group/ungroup actions for the ribbon's * Home > Arrange group. * * A single selection aligns against the slide canvas. Shift-click or marquee * multi-selection aligns against the collective bounds, enables distribution * for three or more elements, and enables grouping for two or more elements. */ interface ArrangeActions { bringForward(): void; sendBackward(): void; bringToFront(): void; sendToBack(): void; alignElements(edge: AlignEdge): void; distributeElements(axis: 'horizontal' | 'vertical'): void; flipHorizontal(): void; flipVertical(): void; groupSelected(): void; ungroupSelected(): void; } //#endregion //#region src/viewer/editor/editor-background-actions.d.ts /** * Slide-background actions for the ribbon's Design > Format Background panel. * Solid-colour fill only (matches the docked panel's scope: a single colour * input); clearing removes every background field so the slide falls back to * its layout/master background. Both mutations are history-integrated, * mirroring `editor-slide-actions.ts`. */ interface SlideBackgroundActions { setSlideBackgroundColor(color: string): void; clearSlideBackground(): void; } //#endregion //#region src/viewer/editor/editor-clipboard-actions.d.ts /** * Cut/copy/paste actions for the ribbon's Home > Clipboard group, backed by * the shared `element-clipboard.ts` codec. The in-memory clipboard payload * lives on `ViewerState.clipboardPayload` (not a module-level variable) so * the ribbon's selection sync can reactively enable/disable the Paste button. */ interface ClipboardActions { copy(): void; cut(): void; paste(): void; } //#endregion //#region src/viewer/editor/editor-comment-actions.d.ts interface CommentActions { addComment(text: string, elementId?: string): string | null; /** * Append a reply under a top-level comment (React's `handleSubmitReply` * nested-`replies` model: the reply carries `threadId` = parent id and * inherits the parent's `elementId` anchor). */ addCommentReply(parentId: string, text: string): string | null; /** Update a comment's text in place; recurses into `replies` so nested rows are editable too. */ editComment(id: string, text: string): void; deleteComment(id: string): void; toggleCommentResolved(id: string): void; } //#endregion //#region src/viewer/editor/editor-deck-actions.d.ts /** * Deck-level mutations driven by the no-selection inspector Properties tab * (React's `PresentationPropertiesPanel`): presentation show settings, theme * apply-by-path, per-slide patches (theme override), and slide-canvas resize. * The vanilla counterpart of Vue's `useInspectorDeckActions` plus the * presentation-settings piece of its `PresentationSettingsCard` wiring. */ interface DeckActions { /** Merge a patch into the presentation-level show/print settings. */ updatePresentationSettings(patch: Partial): void; /** Apply a packaged theme part by archive path (React's `handleApplyTheme`). */ applyThemeByPath(themePath: string, allMasters: boolean): void; /** Re-theme the deck from the inspector's THEME EDITOR card. */ applyThemeEdit(payload: { colorScheme: PptxThemeColorScheme; fontScheme: PptxThemeFontScheme; name: string; }): void; /** Replace the deck's `ppt/tags/*.xml` collections (inspector TAGS card). */ updateTagCollections(next: PptxTagCollection[]): void; /** Patch the active slide (inspector THEME OVERRIDE card). */ updateActiveSlide(patch: Partial): void; /** * Set a layout/master's background colour (inspector SLIDE BACKGROUND * card's template rows, shown while `editTemplateMode` is on). Master * Views covers the same ground but requires leaving the slide. */ setTemplateBackground(path: string, backgroundColor: string): void; /** Read a layout/master's current background colour. */ getTemplateBackgroundColor(path: string): string | undefined; /** Resize the slide canvas (the SLIDE SIZE card's raw W/H inputs). */ updateCanvasSize(size: { width: number; height: number; }): void; /** * Adopt an EMU slide size (a preset pick or an orientation flip). Writes the * EMU state AND the pixel canvas, so the stage resizes and the save keeps the * exact authored dimensions. */ updateSlideSize(size: SlideSizeEmu): void; } //#endregion //#region src/viewer/editor/editor-ink-actions.d.ts /** * Ink stroke actions for the Draw ribbon tab: commit a freehand pen/ * highlighter stroke as a new `InkPptxElement`, or erase an existing one. * Both mutations are history-integrated (push -> mutate -> commit), mirroring * `editor-background-actions.ts` and the `insertElement` helper in * `editor-edit-ops.ts`. The pointer-event lifecycle that accumulates a * stroke's points lives separately in `editor-draw-gestures.ts`; this module * only owns the resulting slide/history mutation. */ interface InkActions { /** * Build an `InkPptxElement` from a completed stroke (via the shared * `strokeToInkElement`) and append it to the current slide, selected. * A no-op when not editable, there is no current slide, or the stroke has * fewer than 2 points (shared helper returns `null`, matching a plain tap). */ commitStroke(stroke: StrokeToInkElementOpts): void; /** * Remove the ink element with `id` from the current slide (Draw tab's * eraser tool). Returns `true` when an ink element was found and removed; * `false` for a missing id, a non-ink element, or a read-only viewer (safe * to call from a generic stage hit-test without a pre-check). */ eraseInkElement(id: string): boolean; } //#endregion //#region src/viewer/editor/editor-insert.d.ts /** * Insert-element factories for the vanilla editor. * * The pure builders wrap the framework-agnostic core/shared factories * (`createTextElement`, `createShapeElement`, `createConnectorElement`, * `newTableElement`) and centre the new element on the slide canvas. The one * async helper (`pickImageElement`) owns the file-picker + `FileReader` DOM * side effects needed to turn a chosen image file into a data-URL image * element; it too returns a centred, ready-to-insert element. */ /** The element kinds the Insert ribbon tab can create directly. */ type InsertKind = 'text' | 'table' | 'shape'; //#endregion //#region src/viewer/editor/table-editor-mutations.d.ts interface TableCellPosition { row: number; column: number; } type TableStructureAction = 'insertRowAbove' | 'insertRowBelow' | 'deleteRow' | 'insertColumnLeft' | 'insertColumnRight' | 'deleteColumn'; //#endregion //#region src/viewer/editor/editor-inspector-actions.d.ts /** * Element-type-aware inspector actions: text vertical-align/wrap/autofit, * gradient fill + fill/stroke opacity, image brightness/contrast/saturation + * crop, and table-level header-row/banded-rows/cell-padding. Every method is a * thin `applyToSelected` wrapper around pure builders from `pptx-viewer-shared` * (or the local `patchShapeStyle`), mirroring `editor-text-actions.ts`. * * The corresponding panels live under `ui/inspector/`. */ interface InspectorActions { /** Flip the selected element's lock (writes noMove/noResize; see elementLockTogglePatch). */ toggleElementLock(): void; setTextVerticalAlign(vAlign: NonNullable): void; setTextWrap(wrap: NonNullable): void; setAutoFitMode(mode: NonNullable): void; setTextAdvanced(patch: TextAdvancedChanges): void; setTextStyle(patch: Partial, selection?: InlineTextSelection | null): void; setFillOpacity(opacity: number): void; setStrokeOpacity(opacity: number): void; setGradientFill(state: GradientState): void; addGradientStop(color: string, position: number): void; removeGradientStop(index: number): void; updateGradientStop(index: number, changes: Partial): void; setImageBrightness(value: number): void; setImageContrast(value: number): void; setImageSaturation(value: number): void; setImageCrop(edge: 'left' | 'top' | 'right' | 'bottom', value: number): void; setImageEffects(patch: Partial): void; setElementAction(trigger: 'click' | 'hover', action: ElementAction): void; /** Set the selection's accessibility description (inspector Alt Text field). */ setAltText(text: string): void; setChartData(data: PptxChartData): void; setMediaProperties(patch: Partial): void; setTableHeaderRow(enabled: boolean): void; setTableBandedRows(enabled: boolean): void; setTableCellPadding(padding: number): void; setTableOptions(patch: Partial, cellStyle?: Partial): void; /** Replace the selection's whole table data (inspector data grid). */ setTableData(data: PptxTableData, rawXml?: XmlObject): void; setTableCellStyle(row: number, column: number, patch: Partial): void; setTableCellStyles(cells: TableCellPosition[], patch: Partial): void; mutateTableStructure(cell: TableCellPosition, action: TableStructureAction): void; setTableColumnWidth(column: number, percent: number): void; setTableRowHeight(row: number, height: number): void; mergeTableCells(cells: TableCellPosition[]): void; splitTableCell(cell: TableCellPosition): void; setSmartArtNodeText(nodeId: string, text: string): void; setSmartArtNodeStyle(nodeId: string, patch: Partial): void; mutateSmartArtNode(nodeId: string, action: 'add' | 'addChild' | 'remove' | 'promote' | 'demote'): void; replaceSmartArtData(data: PptxSmartArtData): void; setSmartArtLayout(layout: SmartArtLayoutType): void; setSmartArtColorScheme(scheme: SmartArtColorScheme): void; } //#endregion //#region src/viewer/editor/editor-section-actions.d.ts /** History-aware slide section actions exposed to the rail and Home ribbon. */ interface SectionActions { addSection(name: string, afterSlideIndex?: number): string | null; renameSection(sectionId: string, name: string): void; deleteSection(sectionId: string): void; moveSection(sectionId: string, direction: 'up' | 'down'): void; moveSlidesToSection(slideIndexes: number[], targetSectionId: string): void; toggleSection(sectionId: string): void; } //#endregion //#region src/viewer/editor/editor-slide-actions.d.ts /** * New/duplicate/delete-slide actions for the ribbon's Home > Slides group, * backed by the shared `slide-operations.ts` factory. Every mutation is * history-integrated (matches the element-level actions in `editor-edit-ops`) * and renumbers `slideNumber` across the whole deck so it always matches the * array index (mirrors the Vue `useSlideOperations` contract). * * The layout-driven actions (`insertSlideFromLayout`, `applyLayout`, * `resetSlide`) mirror React's `SlidesGroup` (New Slide dropdown, Layout, and * Reset buttons): they ask the live handler to walk the chosen layout XML and * populate placeholders/background, exactly like React's * `handleInsertSlideFromLayout`. */ interface SlideActions { addSlide(): void; duplicateSlide(): void; deleteSlide(): void; /** Insert a new slide below the current one, keyed to the given layout. */ insertSlideFromLayout(layoutPath: string, layoutName?: string): void; /** Insert a pre-designed starter slide from the shared template catalog. */ insertSlideFromTemplate(templateId: SlideTemplateId): void; /** Resolved deck theme scheme map for template builds and gallery previews. */ getTemplateScheme(): Record; /** Re-key the current slide onto another layout (React's Layout button). */ applyLayout(layoutPath: string): void; /** Reset the current slide to its own layout's defaults (React's Reset). */ resetSlide(): void; } //#endregion //#region src/viewer/editor/editor-text-actions.d.ts /** * Character + paragraph formatting actions for the ribbon's Home > Font and * Home > Paragraph groups. Every method is a thin `applyToSelected` wrapper * around the pure builders in `editor-format-mutations.ts` / * `editor-paragraph-mutations.ts`; this file owns none of the mutation logic * itself, only the selection/history wiring shared by the whole action set. */ interface TextActions { toggleBold(): void; toggleItalic(): void; toggleUnderline(): void; toggleStrikethrough(): void; toggleTextShadow(): void; changeFontSize(delta: number): void; setFontSize(size: number): void; setFontFamily(family: string): void; setTextColor(color: string): void; setHighlightColor(color: string): void; setCharacterSpacing(value: number): void; changeCase(mode: ChangeCaseMode): void; clearFormatting(): void; toggleBulletList(): void; toggleNumberedList(): void; increaseIndent(): void; decreaseIndent(): void; setTextAlign(align: TextStyle['align']): void; setLineSpacing(value: number): void; setTextDirection(direction: TextStyle['textDirection']): void; setColumnCount(count: number): void; } //#endregion //#region src/viewer/editor/editor-transition-actions.d.ts /** * Slide-transition actions for the ribbon's Transitions tab. Playback already * reads `PptxSlide.transition` (see `animation/presentation-playback.ts`), so * this only needs to write that same field, history-integrated like every * other ribbon mutation. * * The ribbon's Transitions tab drives `applyTransitionDraft`, built on the * shared `ribbon-transitions` decision module, so every control on that tab * commits the moment it changes (the matching READ is a plain store read on the * chrome side, because the ribbon is built before this editor exists). * `applyTransition` is the older * single-shot form (type + duration + optional advance fields), kept because it * is part of the published `EditActions` surface: when `applyToAll` is true * every slide gets the same fresh `{ type, durationMs }` transition; otherwise * only the current slide is patched, preserving any advanced fields * (direction, sound, ...) it already carried. */ /** How the deck advances past a slide, from the Transitions tab's Advance Slide group. */ interface TransitionAdvance { /** Advance when the presenter clicks (PowerPoint's default). */ onClick: boolean; /** Auto-advance after this many ms; omitted when the presenter drives it. */ afterMs?: number; } interface TransitionActions { applyTransition(type: PptxTransitionType, durationMs: number, applyToAll: boolean, advance?: TransitionAdvance): void; /** * Commit the Transitions tab's whole draft, so EVERY control on that tab * (preset, duration, advance-on-click, advance-after) takes effect the * moment it changes instead of waiting for the next preset click. * * Apply to All copies the transition the ACTIVE slide ends up with onto * every slide, which is what PowerPoint's own button does; the target list * comes from the shared `ribbonTransitionTargets`. */ applyTransitionDraft(draft: RibbonTransitionDraft, applyToAll: boolean): void; } //#endregion //#region src/viewer/editor/editor-edit-ops.d.ts /** A geometry patch from the inspector (all fields optional). */ interface GeometryPatch { x?: number; y?: number; width?: number; height?: number; rotation?: number; } /** * The full set of formatting / insert / arrange / clipboard / slide actions * exposed to the editing chrome (ribbon, inspector). Composed from the * focused per-concern action files (`editor-text-actions.ts`, * `editor-arrange-actions.ts`, `editor-clipboard-actions.ts`, * `editor-slide-actions.ts`) plus the shape/geometry/insert actions owned * directly here. Every mutating action is history-integrated (push -> mutate * -> commit) via the shared {@link EditorOps}. */ interface EditActions extends TextActions, ArrangeActions, ClipboardActions, SlideActions, SlideBackgroundActions, TransitionActions, AnimationActions, InspectorActions, InkActions, DeckActions { /** Slide section CRUD and ordering actions. */ sections: SectionActions; comments: CommentActions; toggleFormatPainter(): void; setShapeFill(color: string): void; setShapeStroke(color: string): void; setShapeStrokeWidth(width: number): void; setShapeStyle(patch: Partial): void; setShapeType(shapeType: string): void; /** Commit an inspector geometry edit (X/Y/W/H/rotation). */ setGeometry(patch: GeometryPatch): void; insert(kind: InsertKind, shapeType?: ShapePresetType): void; insertImage(): Promise; insertMedia(): Promise; /** Insert a default chart for the given dropdown entry (defaults to Column). */ insertChart(chartKind?: InsertChartKind): void; insertSmartArt(layout: SmartArtLayout, defaultItems: string[]): void; insertEquation(omml: Record): void; updateEquation(id: string, omml: Record): void; insertActionButton(shapeType: string): void; insertField(fieldType: string, value?: string): void; duplicateSelected(): void; deleteSelected(): void; /** Select every interactive element on the active slide (Home > Select > Select All). */ selectAll(): void; toggleViewOption(option: 'showGrid' | 'showRulers' | 'showGuides' | 'snapToGrid' | 'snapToShape'): void; /** * Add an alignment guide. Centred by default (View > H/V Guide); `position` * is supplied when the guide was dragged off a ruler strip, where the shared * `rulerDragToGuidePosition` already resolved the drop point. */ addGuide(axis: Guide['axis'], position?: number): void; activateEyedropper(): void; toggleSpellCheck(): void; replaceSelectedImage(): Promise; resetSelectedImage(): void; } //#endregion //#region src/viewer/editor/editor-find-replace-actions.d.ts /** * Find & Replace actions for the ribbon's Home > Editing group, backed by the * shared `find-replace.ts` helpers. A lean, docked-panel implementation: * `search` reports a match count for the query, `replaceCurrent` replaces the * first match, and `replaceAll` replaces every match; there is no in-canvas * match highlighting or "next/previous match" cursor (that needs per-match * selection/scroll wiring into the renderer, out of scope for this wave). * Every replace is history-integrated like every other editor action. */ interface FindReplaceActions { /** Count occurrences of `query` across all slides (does not mutate). */ search(query: string, matchCase: boolean): number; /** Replace the first match of `query`; returns the number replaced (0 or 1). */ replaceCurrent(query: string, replacement: string, matchCase: boolean): number; /** Replace every match of `query`; returns the number replaced. */ replaceAll(query: string, replacement: string, matchCase: boolean): number; } //#endregion //#region src/viewer/ui/accessibility-panel.d.ts interface AccessibilityPanel { el: HTMLElement; open(issues: readonly AccessibilityIssue[]): void; close(): void; } //#endregion //#region src/viewer/ui/inspector/types.d.ts /** Selection-derived state the inspector reflects, computed by `buildInspectorState`. */ interface InspectorState { hasSelection: boolean; isLocked: boolean; canShape: boolean; canText: boolean; isImage: boolean; isChart: boolean; isMedia: boolean; isTable: boolean; isSmartArt: boolean; smartArtData: PptxSmartArtData | undefined; isGroup: boolean; /** Number of children on a selected group, or undefined when not (yet) known. */ groupChildCount: number | undefined; isOle: boolean; oleObjectType: OleObjectType | undefined; oleFileName: string | undefined; oleIsLinked: boolean; x: number; y: number; width: number; height: number; rotation: number; fillColor: string | undefined; strokeColor: string | undefined; strokeWidth: number; shapeStyle: ShapeStyle | undefined; shapeType: string | undefined; isConnector: boolean; fillOpacity: number; strokeOpacity: number; gradientEnabled: boolean; gradient: GradientState; vAlign: 'top' | 'middle' | 'bottom'; textWrap: 'square' | 'none'; autoFitMode: 'shrink' | 'normal' | 'none'; characterSpacing: number; lineSpacing: number; lineSpacingExactPt: number | null; paragraphSpacingBefore: number; paragraphSpacingAfter: number; paragraphIndent: number; paragraphMarginLeft: number; textDirection: 'horizontal' | 'vertical' | 'vertical270' | 'eaVert' | 'wordArtVert' | 'wordArtVertRtl' | 'mongolianVert'; textRtl: boolean; textStyle: TextStyle | undefined; selectedTextRange: InlineTextSelection | null; imageBrightness: number; imageContrast: number; imageSaturation: number; imageArtisticEffect: string; imageTransparency: number; imageBiLevel: number; imageDuotone1: string; imageDuotone2: string; imageColorWash?: { color: string; opacity: number; }; actionClick?: ElementAction; actionHover?: ElementAction; /** The selected element's alt text (accessibility description), if any. */ altText: string; chartData?: PptxChartData; /** * The on-canvas chart part selection, scoped to the selected chart element: * a `pointIndex` ring-highlights one value cell in the data grid and syncs * the "Data Point Index" picker; series-only (no `pointIndex`) highlights * the series name header. `null` when nothing is selected on canvas, or the * selection belongs to a different chart. */ chartHighlightCell: { seriesIndex: number; pointIndex?: number; } | null; media?: MediaPptxElement; mediaPreviewUrl?: string; mediaPosterUrl?: string; cropLeft: number; cropTop: number; cropRight: number; cropBottom: number; tableHeaderRow: boolean; tableBandedRows: boolean; tableBandedColumns: boolean; tableLastRow: boolean; tableFirstCol: boolean; tableLastCol: boolean; tableRtl: boolean; tableStyleId: string; tableCellBackground: string; tableCellBorder: string; tableCellPadding: number; selectedTableCell: { row: number; column: number; } | null; selectedTableCells: Array<{ row: number; column: number; }>; tableCellStyle: PptxTableCellStyle | undefined; tableColumnWidths: number[]; tableRowHeights: number[]; /** The selected table element itself, feeding the inspector data grid. */ tableElement?: TablePptxElement; } /** * Deck/slide-level state for the inspector's Elements and Comments tabs plus * the no-selection Properties sections (React's `InspectorPane` default view). */ interface InspectorDeckState { slideCount: number; currentSlide: number; canvasSize: { width: number; height: number; }; /** The deck's `p:sldSz` in EMU, which is what a save persists. */ slideSize: SlideSizeEmu | undefined; elements: readonly PptxElement[]; selectedIds: readonly string[]; /** Primary selected element id (docked Animation panel target), if any. */ selectedElementId?: string; comments: readonly PptxComment[]; docTitle: string | undefined; docAuthor: string | undefined; editable: boolean; /** Presentation show/print settings (PRESENTATION card controls). */ presentationProperties: PptxPresentationProperties; /** Theme parts discovered in the package (THEME card dropdown). */ themeOptions: readonly PptxThemeOption[]; /** The visible slide (THEME OVERRIDE card), or undefined on an empty deck. */ activeSlide: PptxSlide | undefined; /** Whether inherited layout/master elements are unlocked for editing. */ editTemplateMode: boolean; /** Slide masters, resolving the active slide's layout/master (SLIDE BACKGROUND card's template rows). */ slideMasters: readonly PptxSlideMaster[]; /** Presentation theme colours used to preview override target slots. */ colorScheme: PptxThemeColorScheme | undefined; /** Presentation theme fonts, seeding the THEME EDITOR card's font pair. */ fontScheme: PptxThemeFontScheme | undefined; /** The loaded theme's name (THEME EDITOR card), when the package has one. */ themeName: string | undefined; /** Tag collections parsed from `ppt/tags/*.xml` (TAGS card). */ tagCollections: readonly PptxTagCollection[]; /** Notes page size in px (NOTES & HANDOUT card), when the package has one. */ notesCanvasSize: { width: number; height: number; } | undefined; /** Notes master placeholder count, or undefined when no notes master. */ notesPlaceholderCount: number | undefined; /** Handout master placeholder count, or undefined when no handout master. */ handoutPlaceholderCount: number | undefined; } interface Inspector { el: HTMLElement; update(state: InspectorState): void; /** Refresh the deck-level tabs (Elements/Comments + no-selection Properties). */ updateDeck(state: InspectorDeckState): void; setEditable(editable: boolean): void; } //#endregion //#region src/viewer/ui/master-view-sidebar.d.ts interface MasterViewSidebarOptions { tab: MasterViewTab; masters: readonly PptxSlideMaster[]; active: { masterIndex: number; layoutIndex: number | null; }; canvasSize: CanvasSize; notesBackground?: string; notesPlaceholders?: readonly { type: string; idx?: string; }[]; notesMasterPresent: boolean; handoutBackground?: string; handoutPlaceholders?: readonly { type: string; idx?: string; }[]; handoutMasterPresent: boolean; handoutSlidesPerPage: number; /** Editing affordances are offered only on an editable deck. */ editable?: boolean; renderStage(slide: PptxSlide, scale: number): HTMLElement; onSelect(masterIndex: number, layoutIndex: number | null): void; onTabChange(tab: MasterViewTab): void; onCollapse(): void; onHandoutSlidesPerPageChange(count: number): void; onMasterBackgroundColorChange(color: string): void; } interface MasterViewSidebar { el: HTMLElement; render(options: MasterViewSidebarOptions): void; setVisible(visible: boolean): void; } //#endregion //#region src/viewer/ui/ribbon/ribbon-types.d.ts /** Ribbon tab identifiers this wave implements (see `ribbon-tabs.ts` for the full future roster). */ type RibbonTabId = 'file' | 'home' | 'insert' | 'draw' | 'design' | 'transitions' | 'animations' | 'slideShow' | 'record' | 'review' | 'view' | 'help'; /** Nav-row handlers: always-visible (both edit and read-only chrome). */ interface RibbonNavHandlers { prev(): void; next(): void; zoomIn(): void; zoomOut(): void; zoomToFit(): void; togglePresentation(): void; /** Return to the normal editing view: exit presentation + close the slide sorter. */ normalView(): void; toggleNotes(): void; openAccessibility(): void; openSettings(tab?: 'general' | 'shortcuts'): void; openHeaderFooter(): void; openCompare(): void; openSelectionPane(): void; openSlideSorter(): void; /** Open PowerPoint's Reading View (windowed deck, not the slide show). */ openReadingView(): void; /** Open PowerPoint's Outline view (the deck as editable indented text). */ openOutlineView(): void; openComments(): void; openHyperlink(): void; toggleTemplateEditing?(): void; toggleMasterView?(): void; /** Show/hide the right-hand property inspector (React's panel toggle). */ toggleInspector?(): void; /** * Drop the current selection. Design > Slide Size needs it: the slide-size * fields live on the inspector's DECK panel, which only renders when nothing * is selected. */ clearSelection?(): void; toggleViewOption(option: 'showGrid' | 'showRulers' | 'showGuides' | 'snapToGrid' | 'snapToShape'): void; addGuide(axis: 'h' | 'v'): void; activateEyedropper(): void; toggleSpellCheck(): void; } /** Primary-row handlers: quick-access undo/redo/save (editing only). */ interface RibbonPrimaryHandlers { undo(): void; redo(): void; save(): void; } /** File tab handlers: save + export actions already implemented by the viewer's export lifecycle. */ interface RibbonFileHandlers { openFile(): void; openRecentFile(key: string): void; createPresentation(templateId: string): void; openSettings(): void; openShare(): void; openDocumentProperties(): void; openFontEmbedding(): void; openDigitalSignatures(): void; openPasswordProtection(): void; openVersionHistory(): void; save(): void; saveAsPpsx(): void; saveAsPptm(): void; packageForSharing(): void; exportPng(): void; copySlideAsImage(): void; exportPdf(): void; exportGif(): void; exportVideo(): void; exportJson(): void; print(): void; } /** Slide Show actions already supported by the viewer and collaboration UI. */ interface RibbonSlideShowHandlers { startFromBeginning(): void; startFromCurrent(): void; openPresenterView(): void; openBroadcast(): void; openSetUp(): void; /** * PowerPoint's Hide Slide: toggle the ACTIVE slide's `hidden` flag, which * makes the show skip it while it stays in the deck, the thumbnail rail and * the sorter. */ toggleHideSlide(): void; startRehearsal(): void; openCustomShows(): void; toggleSubtitles(): void; openSubtitleSettings(): void; /** * The deck's show settings, for the Options cluster's checkbox state (shared * `readSlideShowOption` turns them into ticks). */ showOptions(): PptxPresentationProperties; /** * Commit an Options-cluster change onto the deck's show settings. Routes to * the same history-integrated `updatePresentationProperties` path the Set Up * Show dialog uses, so unticking Use Timings really does stop the show * auto-advancing. */ updateShowOptions(patch: Partial): void; } /** * Transitions tab handlers, expressed in the shared `ribbon-transitions` * vocabulary: what the controls should show for the active slide, and what a * change to any of them commits. * * The read is deliberately NOT an `EditActions` method: the ribbon is built * before the editor controller exists (see `createLazyActions`), and the tab * reads its initial state while it is being constructed. Reading the store * directly is the only thing available that early. */ interface RibbonTransitionHandlers { /** The draft the tab's controls should show for the ACTIVE slide. */ readDraft(): RibbonTransitionDraft; /** Commit the tab's whole draft, onto the active slide or every slide. */ applyDraft(draft: RibbonTransitionDraft, applyToAll: boolean): void; } /** * Design tab handler: swap the viewer chrome's `ViewerTheme` (the shared * light/dark "vermilion" presets, see `theme/presets.ts`), the same mechanism * `PptxViewer.setTheme` already exposes on the public API. This is UI-chrome * styling, not presentation content, so unlike `EditActions` it isn't * history-integrated (same class as the always-available zoom/present nav * actions). */ interface RibbonDesignHandlers { setTheme(theme: ViewerTheme | undefined): void; applyPresentationTheme(presetId: string): void; } /** * Draw tab handlers: switch the active ink tool and the pen/highlighter * stroke colour/width. Plain UI state (like the Design tab's theme swap), * not history-integrated; the ink stroke itself only enters history once a * gesture commits an `InkPptxElement` (see `EditActions.commitStroke`, from * `editor-ink-actions.ts`, wired to the stage in `editor-draw-gestures.ts`). */ interface RibbonDrawHandlers { setTool(tool: DrawTool): void; setColor(color: string): void; setWidth(width: number): void; } /** Draw tab state to reflect (current tool/colour/width). */ interface RibbonDrawState { tool: DrawTool; color: string; width: number; } /** Insert tab handler: build + insert an element of the given kind/shape preset. */ interface RibbonInsertHandlers { insert: EditActions['insert']; insertImage: EditActions['insertImage']; insertMedia: EditActions['insertMedia']; insertChart: EditActions['insertChart']; insertSmartArt: EditActions['insertSmartArt']; insertEquation: EditActions['insertEquation']; insertActionButton: EditActions['insertActionButton']; insertField: EditActions['insertField']; } /** Every handler the ribbon dispatches, grouped by concern. */ interface RibbonHandlers { nav: RibbonNavHandlers; primary: RibbonPrimaryHandlers; file: RibbonFileHandlers; slideShow: RibbonSlideShowHandlers; insert: RibbonInsertHandlers; /** Home tab (clipboard/slides/font/paragraph/arrange/editing) + shape actions. */ edit: EditActions; findReplace: FindReplaceActions; /** Design tab's viewer-chrome theme swap (Format Background routes through `edit`). */ design: RibbonDesignHandlers; /** Transitions tab's draft read/commit pair. */ transitions: RibbonTransitionHandlers; /** Draw tab's tool/colour/width switches (the stroke commit itself routes through `edit`). */ draw: RibbonDrawHandlers; } /** Nav-row state (prev/next/counter/zoom label). */ interface RibbonNavState { current: number; total: number; zoomPercent: number; } /** Primary-row + tab-bar visibility state. */ interface RibbonEditState { editable: boolean; canUndo: boolean; canRedo: boolean; } /** A slide layout the Home > Slides group offers for New Slide / Layout. */ interface LayoutOption { /** Archive path of the layout part (e.g. `ppt/slideLayouts/slideLayout2.xml`). */ path: string; /** Human-readable layout name shown in the dropdown. */ name: string; } /** Selection-derived state the Home tab's Font/Paragraph/Arrange groups reflect. */ interface RibbonSelectionState { hasClipboard: boolean; slideCount: number; selectedCount?: number; formatPainterActive?: boolean; selectedElementId?: string; animations?: readonly PptxElementAnimation[]; /** Available slide layouts for the Slides group's New Slide / Layout menus. */ layouts?: readonly LayoutOption[]; /** Artwork for the layout gallery thumbnails, keyed by layout path. */ layoutPreviews?: ReadonlyMap; /** `layoutPath` of the active slide, marking the current gallery tile. */ currentLayoutPath?: string; /** Theme major/minor latin faces, leading the font dropdown. */ themeFonts?: { heading?: string; body?: string; }; /** Families the deck embeds, offered as their own dropdown group. */ embeddedFontFamilies?: readonly string[]; /** Families registered this session via File > Options > Fonts. */ customFontFamilies?: readonly string[]; } //#endregion //#region src/viewer/ui/mobile-action-sheets.d.ts /** Paint one slide at `scale`, the same callback the desktop rail is given. */ type MobileThumbnailRenderer = (slide: PptxSlide, scale: number) => HTMLElement; interface MobileActionSheets { el: HTMLElement; update(current: number, slides: readonly PptxSlide[], comments: readonly PptxComment[]): void; /** * Hand the Slides sheet the deck renderer, so it can show the slides * themselves rather than a list of their titles (which is all it had, and * nothing like React's thumbnail list). */ setThumbnailRenderer(render: MobileThumbnailRenderer | null, canvasWidth: number, canvasHeight: number): void; toggle(key: Exclude): void; close(): void; setEditable(editable: boolean): void; setNotesExpanded(expanded: boolean): void; } //#endregion //#region src/viewer/ui/mobile-toolbar.d.ts interface MobileToolbar { el: HTMLElement; collaborationHost: HTMLElement; /** * Empty top-right host into which the AI assistant mounts its mobile toggle * (the desktop title-bar toggle is offscreen on phones, so the assistant * would otherwise be unreachable). Populated by `mountAiChat` when `ai` is * configured; stays `display:none` while empty. Hidden when not editable, * matching React's `showEdit && aiEnabled` gating. */ aiHost: HTMLElement; setEditState(state: RibbonEditState): void; } //#endregion //#region src/viewer/ui/notes-panel.d.ts /** State the panel needs to reflect: the slide to read notes from, and whether edits are allowed. */ interface NotesPanelUpdate { slide: PptxSlide | undefined; editable: boolean; /** Notes-master per-level text defaults (font size, indent, etc.) to fall back to when a segment omits them. */ notesStyle?: PptxTextStyleLevels; } interface NotesPanel { el: HTMLElement; /** * Sync the panel to the given slide/editable state. The textarea's value is * only reseeded when the slide id actually changes (never on every render), * so an in-progress edit is never interrupted mid-typing. */ update(update: NotesPanelUpdate): void; /** Expand or collapse the notes body (header stays visible either way). */ setExpanded(expanded: boolean): void; } //#endregion //#region src/viewer/ui/presentation-toolbar.d.ts /** Everything the bar reflects; pushed in as partial patches from two sources. */ interface PresentationToolbarState { /** Zero-based active slide. */ current: number; total: number; tool: PresentationPointerTool; /** Blackout state; with the pen it marks the Blackboard toggle active. */ blackout: PresentationBlackout; hasAnnotations: boolean; presenterViewActive: boolean; } interface PresentationToolbar { el: HTMLElement; update(patch: Partial): void; /** Start/stop the elapsed timer, the auto-hide listeners and the bar itself. */ setPresenting(presenting: boolean): void; /** * PowerPoint's Ctrl+H: flip the bar's visibility. It drives the SAME flag * auto-hide writes, so the shortcut and the countdown cannot disagree about * whether the chrome is up. */ toggleVisible(): void; dispose(): void; } //#endregion //#region src/viewer/ui/presentation-touch-controls.d.ts interface PresentationTouchControls { el: HTMLElement; update(current: number, total: number): void; } //#endregion //#region src/viewer/ui/ribbon/tabs/view-tab.d.ts /** The View > Show toggles, as the viewer state currently holds them. */ interface ViewToggleState { showGrid: boolean; showRulers: boolean; showGuides: boolean; snapToGrid: boolean; snapToShape: boolean; } //#endregion //#region src/viewer/ui/ribbon/ribbon.d.ts interface Ribbon { el: HTMLElement; update(state: RibbonNavState): void; setEditState(state: RibbonEditState): void; setNotesExpanded(expanded: boolean): void; setAutosaveStatus(label: string, kind: 'idle' | 'saving' | 'saved' | 'error'): void; /** Show/hide the whole editing surface (Home/Insert tab content + find/replace). */ setEditable(editable: boolean): void; /** Reflect the current selection across the Home tab's Font/Paragraph/Arrange groups. */ updateSelection(selectedElement: PptxElement | undefined, extra: RibbonSelectionState): void; /** Reflect the current Draw tab tool/colour/width (store-driven). */ setDrawState(state: RibbonDrawState): void; setTemplateEditing(active: boolean): void; /** Reflect the View tab's Show toggles (rulers/grid/guides/snapping). */ setViewOptions(options: ViewToggleState): void; setHasMacros(hasMacros: boolean): void; setSubtitlesVisible(visible: boolean): void; /** Reflect the active slide's `hidden` flag on the Hide Slide toggle. */ setHideSlideActive(active: boolean): void; /** Reflect the inspector panel's open state on the quick-access toggle. */ setInspectorOpen(open: boolean): void; /** * Show or hide the docked Find & Replace panel. Same action as Home > * Editing > Find; exposed so the editor keymap can drive Ctrl/Cmd+F, which * this binding had no shortcut for at all. */ toggleFindReplace(): void; openEquationEditor(id: string, omml: Record): void; /** * Hide ribbon tabs unticked in Options > Customize Ribbon. The File tab * always survives; a hidden active tab falls back to Home (or the first * remaining tab). */ setHiddenOptionTabs(tabIds: readonly string[]): void; /** Apply Options > General ScreenTip style to the tab-bar tooltips. */ applyScreenTips(tip: (label: string) => string | undefined): void; } /** * The tabbed editing ribbon: a React-aligned command row, tab bar, and * swappable tab content. Slide navigation and zoom live in the status bar, * avoiding the duplicate counter row that used to sit above the tabs. * * `hiddenActions` (from the host's `hiddenActions` viewer option) gates DOM * construction, not just visibility: a hidden ribbon tab's content module is * never called, and a hidden button/cluster inside a tab is never built by * that tab's own module (see `ribbon-types.ts` handlers vs. this file). */ declare function createRibbon(doc: Document, t: Translator, handlers: RibbonHandlers, hiddenActions?: readonly ToolbarActionId[], accountAuth?: AccountAuthConfig): Ribbon; //#endregion //#region src/viewer/ui/shortcut-panel.d.ts interface ShortcutPanel { el: HTMLElement; /** Show or hide the panel; bound to "?" and to the Help ribbon entry. */ toggle(): void; close(): void; isOpen(): boolean; } //#endregion //#region src/viewer/ui/status-bar.d.ts /** Autosave lifecycle states pushed into the save-state text. */ type StatusBarSaveKind = 'idle' | 'saving' | 'saved' | 'error'; interface StatusBar { el: HTMLElement; /** Reflect the slide counter and zoom percent. */ update(state: RibbonNavState): void; setNotesExpanded(expanded: boolean): void; /** Push an autosave status label ('' = idle; falls back to All saved/dirty). */ setSaveStatus(label: string, kind: StatusBarSaveKind): void; /** Reflect the unsaved-changes flag in the idle save-state text. */ setDirty(dirty: boolean): void; /** Reflect presentation state so the Normal / Slide Show buttons show the active view (derived, not hardcoded). */ setPresenting(presenting: boolean): void; } //#endregion //#region src/viewer/ui/thumbnail-sections.d.ts interface ThumbnailSectionActions { toggle(sectionId: string): void; rename(sectionId: string, name: string): void; delete(sectionId: string): void; move(sectionId: string, direction: 'up' | 'down'): void; } //#endregion //#region src/viewer/ui/thumbnails.d.ts interface ThumbnailRail { el: HTMLElement; /** Rebuild the rail for a new slide list (uses `renderStage` per slide). */ render(slides: PptxSlide[], canvasSize: CanvasSize, renderStage: (slide: PptxSlide, scale: number) => HTMLElement, sections?: readonly PptxSection[], sectionActions?: ThumbnailSectionActions): void; /** Highlight the active slide and scroll it into view. */ setActive(index: number): void; /** Show or hide the rail. */ setVisible(visible: boolean): void; /** Show or hide the pinned Add Slide footer (editing enabled only). */ setAddSlideVisible(visible: boolean): void; renderMasters(masters: readonly PptxSlideMaster[], canvasSize: CanvasSize, renderStage: (slide: PptxSlide, scale: number) => HTMLElement, onSelect: (masterIndex: number, layoutIndex: number | null) => void, active: { masterIndex: number; layoutIndex: number | null; }): void; } //#endregion //#region src/viewer/ui/title-bar.d.ts /** Autosave lifecycle states the title-bar status text reflects. */ type TitleBarAutosaveKind = 'idle' | 'saving' | 'saved' | 'error'; interface TitleBar { el: HTMLElement; /** Show/hide the editing quick actions + enable/disable undo/redo. */ setEditState(state: RibbonEditState): void; /** Reflect the current autosave lifecycle state in the status text. */ setAutosaveState(state: TitleBarAutosaveKind): void; /** Reflect the unsaved-changes flag in the status text. */ setDirty(dirty: boolean): void; /** Synchronize the AutoSave switch with a host-driven runtime change. */ setAutosaveEnabled(enabled: boolean): void; /** Re-render the Quick Access strip from the current options state. */ refreshQuickAccess(): void; } //#endregion //#region src/viewer/ui/chrome.d.ts /** The viewer's static DOM skeleton plus the mutable overlay controls. */ interface ViewerChrome { /** `.pptxv` root (focusable; keyboard navigation attaches here). */ root: HTMLElement; /** The tabbed ribbon (primary row, nav row, tab bar + File/Home/Insert/View content); null when disabled. */ ribbon: Ribbon | null; /** Top document title and quick-access row; null when the toolbar is hidden. */ titleBar: TitleBar | null; /** Property inspector panel; null when disabled. */ inspector: Inspector | null; /** Cross-slide accessibility checker results, opened from the View ribbon. */ accessibility: AccessibilityPanel; /** Keyboard-shortcut cheat sheet, toggled by "?". */ shortcuts: ShortcutPanel; /** Dedicated Slide/Notes/Handout master workspace navigation. */ masterSidebar: MasterViewSidebar; thumbnails: ThumbnailRail | null; /** Scrollable centring viewport around the stage. */ viewport: HTMLElement; /** Box sized to `canvasSize * scale`; the rendered stage goes inside. */ stageWrap: HTMLElement; /** Collapsible speaker-notes panel docked below the slide area. */ notes: NotesPanel; /** Bottom navigation and zoom bar, matching React's status-bar placement. */ statusBar: StatusBar | null; /** Compact menu/edit/save/present toolbar, visible only on phones. */ mobileToolbar: MobileToolbar | null; mobileActionSheets: MobileActionSheets | null; /** Persistent exit and navigation affordances for touch slide shows. */ presentationTouchControls: PresentationTouchControls; /** Auto-hiding floating show toolbar (desktop slide-show chrome). */ presentationToolbar: PresentationToolbar; setLoading(loading: boolean): void; setError(message: string | null): void; setEmpty(empty: boolean): void; setPresenting(presenting: boolean): void; } //#endregion //#region src/viewer/editor/editor-controller.d.ts interface EditorController { /** (Re)wire listeners + overlay into the current chrome (after mount). */ attachChrome(): void; detachChrome(): void; /** Called by the render controller after every stage render. */ onStageRendered(): void; /** True while editing owns the keyboard (selection or inline editing). */ capturesKeyboard(): boolean; /** Drop history/selection/dirty state (new content loaded). */ reset(): void; setEditable(editable: boolean): void; undo(): void; redo(): void; canUndo(): boolean; canRedo(): boolean; /** File > Options > Advanced > "Maximum number of undos". */ setHistoryDepth(depth: number): void; deleteSelected(): void; duplicateSelected(): string | null; getSelectedElementId(): string | null; selectElements(ids: string[]): void; applyElementPatch(id: string, patch: Partial): void; commitSlides(slides: PptxSlide[], currentSlide?: number): void; /** Switch the Draw ribbon tab's active tool (also clears selection when leaving `'select'`). */ setDrawTool(tool: DrawTool): void; /** Set the pen/highlighter stroke colour used by the next committed stroke. */ setDrawColor(color: string): void; /** Set the pen/highlighter stroke width used by the next committed stroke. */ setDrawWidth(width: number): void; /** The formatting / insert / arrange actions for the editing chrome. */ getEditActions(): EditActions; /** The Find & Replace actions for the ribbon's docked panel. */ getFindReplaceActions(): FindReplaceActions; commitNotes(notes: string, notesSegments?: TextSegment[]): void; setHandoutSlidesPerPage(count: number): void; updateDocumentProperties(core: PptxCoreProperties, app: PptxAppProperties, custom: PptxCustomProperty[]): void; updatePresentationProperties(value: PptxPresentationProperties): void; /** Replace the whole section list as one undoable step (AI deck seam). */ updateSections(value: PptxSection[]): void; updateHeaderFooter(value: PptxHeaderFooter): void; updateCustomShows(value: PptxCustomShow[]): void; save(format?: PptxSaveFormat): Promise; downloadAs(format: PptxSaveFormat, fileName?: string): Promise; packageForSharing(fileName?: string): Promise; downloadPptx(fileName?: string): Promise; destroy(): void; } //#endregion //#region src/viewer/ui/presentation.d.ts interface PresentationController { /** Enter presentation mode (real Fullscreen API on the target element). */ enter(): Promise; /** Exit presentation mode. */ exit(): Promise; /** True while the target is the fullscreen element. */ isActive(): boolean; /** Remove listeners. */ dispose(): void; } //#endregion //#region src/viewer/animation/presentation-playback.d.ts /** Everything the controller needs after a stage (re)render. */ interface SyncStageParams { doc: Document; /** The stage host (position: relative), for layering a transition overlay. */ stageWrap: HTMLElement; /** The freshly rendered, fully-visible main stage node. */ stage: HTMLElement; /** The slide the stage renders, or `undefined` when empty. */ slide: PptxSlide | undefined; /** Zero-based index of the rendered slide. */ slideIndex: number; /** True only when the live (fullscreen) presentation stage is active. */ presenting: boolean; /** Presentation-level switch parsed from `p:showPr`. */ showWithAnimation?: boolean; /** Archive-path to data-URL map, used to resolve action-sound sources. */ mediaDataUrls?: ReadonlyMap; /** * Re-render the given tracked elements in place against the current element * states, so staged chart / SmartArt builds and `p:animClr` fill / stroke * relinquish surface structurally. Supplied by the render controller. */ reRenderElements?: (ids: readonly string[]) => void; /** * Seed the incoming slide as fully built rather than replaying it. Set when * stepping BACKWARD onto a slide, which PowerPoint shows with its builds * already complete. */ seedCompleted?: boolean; } /** * The presentation-mode playback state machine for the vanilla binding, driven * by the shared native-timing {@link PresentationAnimationController} (mirrors * the React / Vue `useAnimationPlayback`, but drives the DOM imperatively). * * The controller builds a timeline engine from the slide's `nativeAnimations` * (expanding staged text builds) and can represent native staged chart / * SmartArt builds (`p:bldChart` / `p:bldDgm`) and colour animations * (`p:animClr`) that the older preset click-group model could not. */ interface PresentationPlayback { /** * Reveal the next click-group. Returns `true` if a group was revealed (stay on * the slide); `false` when the timeline is exhausted or animations are disabled * (the caller should advance to the next slide). */ advance(): boolean; /** True when every click-group on the current slide has been revealed. */ isComplete(): boolean; /** * True while the active slide shows its builds as already complete because * the presenter stepped BACKWARD onto it. The next back press replays it. */ isSeededCompleted(): boolean; /** Replay the active slide's builds from the start. */ replayCurrentSlide(doc: Document): void; /** Sync playback + transitions after a stage (re)render. */ syncStage(params: SyncStageParams): void; /** Cancel any running transition/timers and forget all per-slide state. */ reset(): void; /** * The stable per-element native-animation state map, mutated in place as the * timeline plays. Threaded into the render context so element renderers can * reveal staged builds and relinquish animated fill / stroke. */ readonly elementStates: ReadonlyMap; } //#endregion //#region src/viewer/render-controller.d.ts interface RenderController { /** Re-render everything (used after a chrome rebuild). */ renderAll(): void; /** Re-render the main stage + toolbar counters at the current state. */ renderStage(): void; /** Rebuild the thumbnail rail from the current slide list. */ renderThumbnails(): void; /** * Render one slide statically at `scale` (template elements included), for * surfaces outside the main canvas such as Reading View. Same path the * thumbnail rail draws through, so no surface grows its own element renderer. */ renderSlideNode(slide: PptxSlide, scale: number): HTMLElement; /** Resolve the requested zoom into a concrete scale factor. */ effectiveScale(): number; /** * Scale at which the slide exactly fits the viewport, i.e. the scale that * {@link zoomPercent} reports as 100%. The zoom controls need it because the * stored zoom is an ABSOLUTE scale while the shared zoom step is expressed * relative to fit, exactly as React's is. */ fitScale(): number; /** Display zoom relative to fit-to-viewport (fit === 100%), like React. */ zoomPercent(): number; /** * Presentation-mode animation/transition playback, driven by the stage * rebuild flow. Navigation (`viewer-controls`) consults `advance()` so a * "next" first steps the on-click animation timeline before advancing slides. */ readonly presentationPlayback: PresentationPlayback; } //#endregion //#region ../shared/dist/ai/index.d.ts //#endregion //#region src/ai/change-animator.d.ts /** Host-tunable options for how AI edits are animated on the canvas. */ interface AiChangeAnimationConfig { /** Master switch. Default true. */ enabled?: boolean; /** How long the motion + glow plays, in ms. Default 900. */ durationMs?: number; /** Draw the pulsing glow highlight on changed elements. Default true. */ glow?: boolean; /** Glide old->new bounds and cross-fade colours. Default true. */ tween?: boolean; /** Accent colour (any CSS colour) for the glow/ghosts. Default a blue. */ color?: string; } //#endregion //#region src/ai/config.d.ts /** The UI message shape exchanged with the assistant. Alias of the SDK type. */ type PptxAiUIMessage = UIMessage; /** * Canonical name of every tool the assistant can call. Document tools mirror the * `pptx-viewer-mcp` server exactly (they ARE the same functions, run against the * live deck); the viewer-only tools (navigation, deck outline, element/notes * readers, table merge) have no MCP counterpart. */ type PptxAiToolName = 'get_deck_overview' | 'get_slide' | 'get_element' | 'get_speaker_notes' | 'find_text' | 'get_theme' | 'go_to_slide' | 'select_elements' | 'merge_tables' | 'get_metadata' | 'get_layouts' | 'find_placeholders' | 'get_presentation_properties' | 'run_accessibility_check' | 'convert_to_markdown' | 'export_to_json' | 'add_element' | 'update_element' | 'delete_elements' | 'arrange_elements' | 'clone_element' | 'set_element_animation' | 'group_elements' | 'ungroup_elements' | 'batch_update_elements' | 'update_element_style' | 'replace_geometry' | 'set_element_lock' | 'manage_hyperlinks' | 'replace_text' | 'manage_comments' | 'update_table_cells' | 'manage_table_structure' | 'create_chart' | 'update_chart' | 'add_chart_series' | 'remove_chart_series' | 'update_chart_series_data' | 'manage_smart_art' | 'apply_template' | 'add_slide' | 'duplicate_slide' | 'delete_slides' | 'reorder_slides' | 'update_slide_properties' | 'set_slide_transition' | 'apply_theme_preset' | 'update_theme_colors' | 'update_theme_fonts' | 'set_canvas_size' | 'update_metadata' | 'manage_sections' | 'update_presentation_properties' | 'import_from_json' | 'apply_layout'; type Resolvable = T | (() => T | Promise); /** How the assistant reaches a language model. */ type PptxAiConnection = /** * Post messages to a host backend route (recommended for production so the * provider API key stays server-side). Maps to `DefaultChatTransport`. */ { kind: 'endpoint'; api: string; headers?: Resolvable>; body?: Resolvable>; credentials?: RequestCredentials; fetch?: typeof globalThis.fetch; } | /** * Run a language model in-process in the browser (bring-your-own key / * local model). Maps to a `ToolLoopAgent` behind a `DirectChatTransport`. */ { kind: 'model'; model: LanguageModel; system?: string; maxSteps?: number; } | /** Provide a fully-constructed transport (advanced / testing escape hatch). */ { kind: 'transport'; transport: ChatTransport; }; /** How writes proposed by the assistant reach the document. */ type PptxAiWritePolicy = 'stage' | 'approve' | 'auto'; /** Which deck context is fed to the model with each turn. */ type PptxAiContextStrategy = 'outline' | 'current-slide' | 'none'; /** Optional per-session history persistence hooks. */ interface PptxAiHistoryHooks { load?(id: string): Promise; save?(id: string, messages: PptxAiUIMessage[]): Promise; } /** Complete host configuration for an AI chat session. */ interface PptxAiConfig { connection: PptxAiConnection; /** Extra host instructions appended to the base system prompt. */ systemPromptExtras?: string; tools?: { /** Allowlist. When set, only these tools are exposed. */ enabled?: PptxAiToolName[]; /** Denylist, applied after `enabled`. */ disabled?: PptxAiToolName[]; /** Additional host-defined tools merged into the tool set. */ extra?: ToolSet; }; /** Default `'stage'`. */ writePolicy?: PptxAiWritePolicy; /** Default `'outline'`. */ contextStrategy?: PptxAiContextStrategy; history?: PptxAiHistoryHooks; /** * How AI edits are animated on the canvas so the user can watch them land * (glide old->new, fade/scale in-out, glow-pulse). Omit for the defaults; * set `{ enabled: false }` to turn it off. */ changeAnimation?: AiChangeAnimationConfig; onError?(error: Error): void; } //#endregion //#region src/ai/bridge.d.ts /** Lightweight, model-friendly summary of the whole deck. */ interface PptxAiDeckMeta { /** Total number of slides. */ slideCount: number; /** Zero-based index of the currently active slide. */ activeSlideIndex: number; /** Deck title, when known (first slide title / core properties). */ title?: string; /** Slide canvas width in CSS pixels. */ width: number; /** Slide canvas height in CSS pixels. */ height: number; } /** Severity hint for {@link PptxAiBridge.notify}. */ type PptxAiNotifyLevel = 'info' | 'success' | 'warning' | 'error'; /** * A target the user has scoped the assistant to: either a whole slide or a * single element on a slide. Returned by {@link PptxAiBridge.getFocusedTargets} * so the context builder can tell the model exactly what to focus on. */ type PptxAiFocusedTarget = { kind: 'slide'; slideIndex: number; } | { kind: 'element'; slideIndex: number; elementId: string; }; /** * A pure updater over the deck's slides. It receives a deep clone of the * current slides (mutation-safe) and returns the next slides array. The bridge * commits the returned array as ONE history entry. */ type PptxAiSlidesUpdater = (slides: PptxSlide[]) => PptxSlide[]; /** * A pure updater over the whole parsed deck ({@link PptxData}). Mirrors the * `pptx-viewer-mcp` tool model (data in, mutated data out) so presentation-level * MCP tools (metadata, sections, canvas size, presentation properties, layouts) * can be committed as ONE undoable history entry through {@link * PptxAiBridge.applyDeckData}. Optional: bindings that only track slide/theme * state can omit it, in which case those presentation-level tools report that * they are unavailable in this viewer while every slide/theme tool still works. */ type PptxAiDataUpdater = (data: PptxData) => PptxData; /** Field-level updates for a single element, mirroring the MCP update vocab. */ interface PptxAiElementUpdate { x?: number; y?: number; width?: number; height?: number; rotation?: number; opacity?: number; hidden?: boolean; flipHorizontal?: boolean; flipVertical?: boolean; text?: string; fontSize?: number; fontFamily?: string; fontColor?: string; bold?: boolean; italic?: boolean; underline?: boolean; align?: 'left' | 'center' | 'right' | 'justify'; fillColor?: string; strokeColor?: string; strokeWidth?: number; } /** * Implemented by each binding to expose its live editor to the AI core. * * Read methods must be cheap and synchronous. Write methods must route through * the binding's editor-history layer so AI edits are undoable like manual ones. */ interface PptxAiBridge { /** Return a summary of the whole deck. */ getDeckMeta(): PptxAiDeckMeta; /** Return the deck's slides. Callers must not mutate the returned array. */ getSlides(): PptxSlide[]; /** Return the zero-based index of the active slide. */ getActiveSlideIndex(): number; /** Return the resolved presentation theme, when available. */ getTheme(): PptxTheme | undefined; /** Return the underlying core handler, when the binding exposes one. */ getHandler(): PptxHandler | undefined; /** Navigate the viewer to a slide by zero-based index. */ goToSlide(index: number): void; /** Select the given elements on a slide (empty array clears selection). */ selectElements(slideIndex: number, elementIds: string[]): void; /** * Apply a slides updater as a single, atomic, undoable history entry. The * binding is responsible for cloning current slides before calling * `updater` and for installing the result. */ applySlidesUpdate(updater: PptxAiSlidesUpdater, label: string): void; /** Apply field updates to one element as a single history entry. */ updateElement(slideIndex: number, elementId: string, updates: PptxAiElementUpdate): void; /** Apply partial theme updates as a single history entry. */ applyTheme(updates: Partial): void; /** * Return the full parsed {@link PptxData} for the open deck, with the live * (edited) slides and theme overlaid. Enables `pptx-viewer-mcp` tools that * read presentation-level state (metadata, sections, layouts, presentation * properties). Optional: when absent, the AI core synthesises a minimal * PptxData from slides + dimensions, which is enough for every slide/theme * tool but not for presentation-level reads. */ getDeckData?(): PptxData | undefined; /** * Commit a whole-deck {@link PptxData} mutation as one undoable history entry. * Used to apply presentation-level MCP tool results (metadata, sections, * canvas size, presentation properties, layouts). Optional: when absent, * those tools report they are not supported in this viewer; slide/theme tools * are unaffected (they route through {@link PptxAiBridge.applySlidesUpdate} * and {@link PptxAiBridge.applyTheme}). */ applyDeckData?(updater: PptxAiDataUpdater, label: string): void; /** * Return the slides / elements the user has scoped the assistant to, if any. * When present and non-empty, the context builder tells the model to focus on * exactly these targets. Optional so existing bridges satisfy the contract * without change; a bridge that does not implement it behaves as before (no * focus scoping). */ getFocusedTargets?(): PptxAiFocusedTarget[]; /** Surface a transient message in the host UI (toast / status line). */ notify?(message: string, level?: PptxAiNotifyLevel): void; } //#endregion //#region ../shared/dist/i18n/index.d.ts //#region src/i18n/translations-en.d.ts /** * The canonical English UI-string dictionary for pptx-viewer. None of the * React/Vue/Angular binding packages ship translations themselves - each * only calls its framework's translation function (react-i18next's `t()`, * vue-i18n's `t()`, ngx-translate's `TranslateService`/`translate` pipe) * against dotted `pptx.*` keys. The host app supplies the dictionary; this * module is that dictionary for the demos, shared so all three stay in sync * instead of drifting into three separate copies. */ declare const translationsEn: Record; /** * Every key in the English dictionary. A new locale dictionary typed as * `Record` gets a compile error for any key it's * missing or misspells, so translation contributions stay complete without a * separate parity test. */ type TranslationKey = keyof typeof translationsEn; /** * Convert a dotted translation key to a human-readable label when no * explicit dictionary entry exists yet. Takes the last segment and converts * camelCase to Title Case, e.g. "pptx.slideSorter.zoomIn" -> "Zoom In". */ declare function keyToLabel(key: string): string; //#endregion //#region src/i18n/locale-catalog.d.ts /** One selectable entry in the viewer chrome's built-in language picker (File > Options > Language). */ interface LocaleCatalogEntry { /** BCP-47-ish locale code, e.g. `'en'`, `'fr'`. Matches `pptx-viewer-locales`' exports. */ code: string; /** English display name, used before a translation dictionary for the target locale is loaded. */ label: string; /** The locale's own name for itself, e.g. `'Français'` for `fr`. */ nativeLabel: string; } //#endregion //#region src/viewer/autosave/autosave-controller.d.ts /** * Debounced autosave for the vanilla viewer, layered on the shared IndexedDB * recovery store (`pptx-viewer-shared/autosave-store`). * * A local edit marks the document dirty; a debounce timer then re-serializes the * deck through the shared `saveDeckWithPassword` and persists it under * `filePath` via `saveAutosaveSnapshot` (evicting the oldest record on quota * exhaustion). The recovery blob is a crash-safety net only; **it never clears * the editor's dirty flag** or stands in for the user's real Save. * * The snapshot is written with `recoverySnapshotIntent`, so it is a plain ZIP * even when the deck is password protected: `getAutosaveSnapshot` hands the * bytes back with no password, and an encrypted package would simply refuse to * open (see `deck-save-encryption` in `pptx-viewer-shared` for the reasoning * and the cleartext-at-rest tradeoff it accepts). * * Two decisions are NOT made here, so all five bindings share them: * * - **whether autosave runs at all** comes from the shared * `resolveAutosaveActivation` verdict, read live through * {@link AutosaveControllerDeps.getActivation}. This controller never owns a * raw `enabled` boolean, because the host prop is a ceiling and the user's * toggle is a preference inside it. * - **how long the debounce may defer a snapshot** comes from the shared * `nextAutosaveDelayMs`. A plain re-armed debounce can defer forever while * the user keeps typing; the ceiling keeps the promise that a snapshot lands * no later than one interval after the FIRST unsaved edit. * * Recovery is probed once per loaded deck through the shared * `shouldProbeAutosaveRecovery` / `probeAutosaveRecovery` pair, and surfaced * both to the host callback ({@link AutosaveControllerDeps.onRecovery}) and to * the viewer's own prompt ({@link AutosaveControllerDeps.onRecoveryOffer}). */ type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'error'; //#endregion //#region src/viewer/export/export-types.d.ts /** Per-slide progress callback: `(currentSlideIndex, totalSlides)`. */ type ExportProgress = (current: number, total: number) => void; //#endregion //#region src/viewer/export/export-gif.d.ts /** * Animated-GIF export for the vanilla binding. All slides are captured (one * frame per slide) via the injected `rasterizeSlide`, then encoded with the * shared pure-JS GIF89a encoder (`pptx-viewer-shared` `gif-encoder`: median-cut * quantisation + LZW). Frame timing comes from the shared `planGifFrames` * planner and oversized captures are downscaled via `clampGifDimensions`; only * the DOM capture / canvas scaling / Blob download driver lives here. */ /** Options for the animated-GIF export (all slides, one frame per slide). */ interface ExportGifOptions { /** * Duration each slide is shown, in milliseconds (default 2000). Per-slide * overrides can be supplied via {@link ExportGifOptions.slideTimingsMs}. */ slideDurationMs?: number; /** * Per-slide duration overrides in milliseconds (index maps to slide index, * e.g. rehearsed timings). Flows through the shared `planGifFrames` plan * into per-frame GIF delays. */ slideTimingsMs?: number[]; /** * Cap on the longer side of the encoded frames, in pixels (default 1920). * Captured canvases larger than this are downscaled before quantisation, * keeping encode time and file size manageable. */ maxDimension?: number; /** Capture-phase progress callback: `(currentSlide, totalSlides)`. */ onProgress?: ExportProgress; /** Abort the export early; the capture loop checks this between slides. */ signal?: AbortSignal; } //#endregion //#region src/viewer/export/export-print.d.ts /** * Print for the vanilla binding, assembled entirely from the shared print * module (`pptx-viewer-shared` `print-document`): `validatePrintSettings`, * `computeSlideIndices` / `computeColorFilter`, the `build*Html` body markup * builders, and the DOMPurify-hardened `buildPrintHtmlDocument` assembler. * Only the drivers live here: rasterising the selected slides to data URLs and * writing the document into a print window. Vanilla port of Vue's `usePrint` * raster path (slides / notes / handouts / outline). */ /** * Open a print window for a complete HTML document and trigger printing. * Returns `false` when the window could not be opened (popup blocker). */ type OpenPrintWindow = (htmlDocument: string) => boolean; /** * Options for `print`: any subset of the shared `PrintSettings` (unspecified * fields fall back to `DEFAULT_PRINT_SETTINGS`, i.e. all slides, landscape, * full colour) plus progress/abort and a print-window override. */ interface PrintOptions extends Partial { /** Rasterisation progress callback: `(currentSlide, totalSlidesToPrint)`. */ onProgress?: ExportProgress; /** Abort before the window opens; checked between slide captures. */ signal?: AbortSignal; /** * Override how the assembled document is opened (e.g. write it into a * hidden iframe). Popup-blocker caveat: the default opener uses * `window.open`, which browsers typically only allow inside a user * gesture, so call `print()` from a click handler; when the popup is * blocked the returned promise resolves `false`. */ openPrintWindow?: OpenPrintWindow; } //#endregion //#region src/viewer/export/export-video.d.ts /** * WebM video export for the vanilla binding, driven by the shared `video-plan` * module: `planVideoSegments` (per-slide segment timing + frame counts), * `fpsToFrameIntervalMs` (draw-loop pacing), and `pickSupportedMimeType` over * `WEBM_MIME_CANDIDATES` (MediaRecorder codec selection). Only the browser * driver lives here: capture each slide to a canvas, replay the canvases onto * a recording canvas fed to `captureStream()` + `MediaRecorder`, and download * the resulting Blob. Vanilla port of React's `exportAllSlidesAsVideo`. */ /** Options for the WebM video export (all slides). */ interface ExportVideoOptions { /** Duration each slide is held, in milliseconds (default 3000). */ slideDurationMs?: number; /** Per-slide duration overrides in milliseconds (index maps to slide index). */ slideTimingsMs?: number[]; /** Recording frame rate in frames per second (default 30). */ fps?: number; /** MediaRecorder video bitrate in bits per second (default 5,000,000). */ videoBitsPerSecond?: number; /** Capture-phase progress callback: `(currentSlide, totalSlides)`. */ onProgress?: ExportProgress; /** Recording-phase progress callback: `(currentSlide, totalSlides)`. */ onRecordProgress?: ExportProgress; /** Abort the export early; checked between slides and between frames. */ signal?: AbortSignal; } //#endregion //#region src/viewer/export/export-controller.d.ts /** Options for the multi-slide PDF export (progress + cooperative cancel). */ interface ExportPdfOptions { /** Capture-phase progress callback: `(currentSlide, totalSlides)`. */ onProgress?: ExportProgress; /** Abort the export early; the loop checks this between slides. */ signal?: AbortSignal; } //#endregion //#region src/viewer/export/export-svg.d.ts /** Export one parsed slide as resolution-independent SVG markup. */ declare function exportSlideToSvg(slide: PptxSlide, width: number, height: number, options?: SvgExportOptions): string; /** Export the selected slides in a parsed presentation as SVG markup. */ declare function exportAllSlidesToSvg(data: PptxData, options?: SvgExportOptions): string[]; //#endregion //#region src/viewer/export/render-to-canvas.d.ts /** * A drop-in wrapper around `html2canvas-pro` that first normalises modern CSS * colour functions (oklch/oklab/lch/lab/color()) to sRGB and applies the * shared CSS-preprocessing pass (backdrop-filter, mix-blend-mode, 3D * transforms, unsupported features) to the cloned capture document. Mirrors * the React/Angular `renderToCanvas` wrapper (`packages/react/src/lib/canvas-export.ts`, * `packages/angular/src/lib/canvas-export.ts`); the pure colour/CSS passes * live once in `pptx-viewer-shared`, only this thin html2canvas-pro glue is * per-binding. `html2canvas-pro` is imported dynamically so it stays out of * the main bundle until export is actually used. */ declare function renderToCanvas(element: HTMLElement, options?: Partial): Promise; //#endregion //#region src/viewer/load/source.d.ts /** * Content sources accepted by the vanilla viewer: raw bytes, a Blob/File, or * a URL string to fetch. */ type PptxViewerSource = ArrayBuffer | Uint8Array | Blob | string; //#endregion //#region src/viewer/types.d.ts /** Callbacks mirroring the Vue component's emits. */ interface PptxViewerCallbacks { /** Fired after a presentation loads successfully. */ onLoad?: (info: { slideCount: number; canvasSize: CanvasSize; }) => void; /** Fired when a load fails (message is already localised/best-effort). */ onError?: (message: string, error: unknown) => void; /** Fired when the active slide changes (zero-based index). */ onSlideChange?: (index: number) => void; /** Fired when the effective zoom scale changes (1 = 100%). */ onZoomChange?: (scale: number) => void; /** Fired when presentation (fullscreen) mode is entered or exited. */ onPresentationChange?: (presenting: boolean) => void; /** Fired after any document mutation (move, resize, edit, undo, ...). */ onChange?: () => void; /** Fired when the unsaved-edits flag flips (a save resets it). */ onDirtyChange?: (dirty: boolean) => void; /** Fired when the selected element changes (`null` = no selection). */ onSelectionChange?: (elementId: string | null) => void; /** Fired on every autosave lifecycle transition (`saving`/`saved`/`error`). */ onAutosaveStatus?: (status: AutosaveStatus) => void; /** * Offered a recovery snapshot found in the shared IndexedDB store on start * (a previous session's autosave for the same `autosaveFilePath`). The host * decides whether to restore it, e.g. `viewer.loadFile(record.data)`. */ onAutosaveRecovery?: (record: AutosaveRecord) => void; /** Fired on every collaboration connection-status transition. */ onCollaborationStatus?: (status: ConnectionStatus) => void; } interface PptxViewerOptions extends PptxViewerCallbacks { /** * The presentation to open: raw `.pptx` bytes (ArrayBuffer / Uint8Array), * a Blob/File, or a URL string to fetch. Omit to start empty and call * `loadFile` / `loadUrl` later. */ source?: PptxViewerSource; /** Licensed font sources supplied by the host application. */ fonts?: ViewerFontSource[]; /** Viewer chrome theme (shared `ViewerTheme`: colors, radius, CSS vars). */ theme?: ViewerTheme; /** Display name shown in the PowerPoint-style title bar. */ fileName?: string; /** UI locale (default `'en'`). Dictionaries come from `messages`. */ locale?: string; /** * Per-locale `pptx.*` message dictionaries. English falls back to the * built-in shared dictionary; other locales fall back to English. */ messages?: TranslationMessages; /** * Theme choices offered by File > Options > Appearance (and, by reference * match, highlighted in the Design tab's quick-access gallery). Defaults to * the shared `THEME_CATALOG` (default/light/vermilion light/vermilion dark). */ availableThemes?: readonly ThemeCatalogEntry[]; /** * Language choices offered by File > Options > Language. Defaults to every * locale with a registered `messages` dictionary plus `'en'`. */ availableLocales?: readonly LocaleCatalogEntry[]; /** * Fired when a theme is selected via File > Options > Appearance (or any * `setTheme` call that matches a known catalog entry) with the entry's * `key`. When supplied, the host owns persisting the choice; otherwise the * viewer falls back to `localStorage` (`pptx-viewer-prefs`) automatically. */ onThemeChange?: (key: string) => void; /** * Fired when a language is selected via File > Options > Language (or any * `setLocale` call) with the new locale code. When supplied, the host owns * persisting the choice; otherwise the viewer falls back to `localStorage` * (`pptx-viewer-prefs`) automatically. */ onLocaleChange?: (code: string) => void; /** * Optional hook point wiring a real sign-in flow into File > Account. * Disabled by default: the Account page's sign-in section renders nothing * unless `enabled: true` is passed. */ accountAuth?: AccountAuthConfig; /** Zero-based slide to show after load (default 0). */ initialSlide?: number; /** * Enable editing (default `false`): click to select, drag/resize/rotate, * inline text editing, keyboard shortcuts, undo/redo, and the toolbar * Save button. Toggle later via `setEditable`. */ editable?: boolean; /** * Legacy flag superseded by {@link editable}; kept so existing option * objects stay type-valid. It has no effect. */ readOnly?: boolean; /** Show the toolbar (default `true`). */ showToolbar?: boolean; /** Show the thumbnail sidebar (default `true`). */ showThumbnails?: boolean; /** * Build the editing format toolbar row (bold/fill/insert/z-order); default * `true`. The row is only *visible* while editing is enabled. */ showFormatToolbar?: boolean; /** * Build the property inspector panel (position/size/fill/line); default * `true`. Only *visible* while editing is enabled. */ showInspector?: boolean; /** * Individually hide toolbar buttons and/or ribbon tabs (default `undefined`, * nothing hidden). Each id in {@link ToolbarActionId} controls one button * (e.g. `'share'`, `'undo'`) or one whole control cluster (`'zoom'` covers * zoom in/out/fit as a unit, `'navigation'` covers prev/next as a unit) or * one ribbon tab (e.g. `'insert'`, `'view'`). `'record'` hides both the * quick-access Record control and the Record ribbon tab, since they surface * the same feature. Unlike {@link showToolbar}, this hides individual * pieces rather than the whole chrome. */ hiddenActions?: ToolbarActionId[]; /** * Custom element-renderer registry. Defaults to `createDefaultRegistry()`; * pass your own (or mutate the default via `getRegistry()`) to add or * override element renderers. */ registry?: ElementRendererRegistry; /** * Unit system for the View > Rulers tick labels (default `'inches'`, as * PowerPoint does). Mirrors React's `rulerUnit` prop; the tick generator is * shared with every other binding, so `'centimetres'` labels identically. */ rulerUnit?: RulerUnit; /** * Opt-in WebGL SmartArt renderer (default `false`): renders `smartArt` * elements as an extruded Three.js scene instead of the flat SVG layout. * `three` is an optional peer dependency, lazily imported only when this is * `true`; when it is unavailable or the scene fails to mount, the SVG * renderer is used instead. Set once at construction (no runtime setter, * mirroring the Vue/React/Angular bindings). */ smartArt3D?: boolean; /** * Opt in to the interactive Three.js surface-chart renderer (default * `false`). When `true`, `surface`/`surface3D` charts render as a * camera-orbitable WebGL mesh (drag to rotate, scroll to zoom) instead of * the static SVG isometric projection. Chart marks are not * selectable/draggable in this mode. `three` is an optional peer * dependency, lazily imported only when this is `true`; when it is * unavailable or the chart has no plottable grid, the SVG renderer is used * instead. Set once at construction (no runtime setter, mirroring the * Vue/React/Angular/Svelte bindings). */ surfaceChart3D?: boolean; /** * Whether this application PERMITS recovery autosave (`@default true`). * * This is a policy ceiling, not the user's setting: the title-bar AutoSave * switch (and File > Options > Save > AutoSave) is the preference expressed * inside it, and it defaults to on. * * - `false` turns recovery autosave off AND renders the switch inert, so a * user cannot enable what the application forbade. * - `true`, or omitting it, permits autosave and lets the user decide. * * When active, each local edit re-serializes the deck into the shared * IndexedDB recovery store as a crash-safety net (it never replaces the * user's real Save, and never clears the unsaved-changes flag). The toolbar * shows a small status pill, and a snapshot left by a previous session * raises the built-in recovery prompt as well as * {@link PptxViewerCallbacks.onAutosaveRecovery}. * * Autosave additionally requires an editable viewer and an * {@link PptxViewerOptions.autosaveFilePath} key, since there is otherwise * nothing to write or nowhere to write it. * * @default true */ autosave?: boolean; /** Fired when the title-bar AutoSave control enables or disables recovery autosave. */ onToggleAutosave?: (enabled: boolean) => void; /** * Explicit cadence (ms) between the first unsaved edit and the persisted * snapshot. Like {@link PptxViewerOptions.autosave} this is host policy, so * it OUTRANKS the user's File > Options > Save > "Save AutoRecover * information every N minutes". Omit it to leave the cadence to the user, * which is the default: that Options value decides, falling back to the * shared two-minute AutoRecover default. * * The debounce also has a ceiling: continuous editing still produces a * snapshot once per interval instead of deferring one forever. */ autosaveIntervalMs?: number; /** IndexedDB recovery key for autosave (default `'presentation.pptx'`). */ autosaveFilePath?: string; /** * Start a real-time collaboration session immediately (Yjs over y-websocket * or serverless y-webrtc). Local edits publish to peers and remote edits * merge in granularly; a `role: 'viewer'` config forces read-only. Start or * stop a session later with {@link PptxViewerInstance.startCollaboration} / * {@link PptxViewerInstance.stopCollaboration}. * * Note: media/OLE/3D/ink binary payloads are not carried over the wire (a * shared codec limitation), and a remote update replaces the whole local * slide array, so a joiner's host-provided media can degrade. */ collaboration?: CollaborationConfig; /** * Prefilled values for the built-in Share/Broadcast dialogs' form fields * (e.g. a host-generated display name), mirroring the Vue binding's * `shareDefaults` prop. The broadcast dialog uses `userName` as the * presenter's display name. */ shareDefaults?: ShareDefaults; /** * Opt-in AI assistant (default: off). When supplied, a sparkles toggle * appears in the title bar that opens a right-side chat panel wired to the * framework-agnostic AI core (`pptx-viewer-shared/ai`). The panel and the * optional `ai` SDK peer are dynamically imported only when the panel is * first opened, so a viewer without this option pays nothing. The host owns * the model connection (a backend `endpoint`, an in-browser `model`, or a * custom `transport`); see {@link PptxAiConfig}. */ ai?: PptxAiConfig; } /** The viewer handle returned by `createPptxViewer`. */ interface PptxViewerInstance extends PowerPointViewerAPI { /** Load a presentation from bytes or a Blob/File (replaces the current one). */ loadFile(file: Blob | ArrayBuffer | Uint8Array): Promise; /** Fetch and load a presentation from a URL. */ loadUrl(url: string): Promise; /** Go to the next slide (no-op on the last slide). */ next(): void; /** Go to the previous slide (no-op on the first slide). */ prev(): void; /** Jump to a zero-based slide index (clamped). */ goToSlide(index: number): void; /** Number of slides in the loaded presentation (0 when none). */ getSlideCount(): number; /** Zero-based index of the visible slide. */ getCurrentSlide(): number; /** Effective zoom scale (1 = 100%), after fit resolution. */ getZoom(): number; /** Set an explicit zoom scale, or `'fit'` for fit-to-viewport. */ setZoom(zoom: number): void; zoomIn(): void; zoomOut(): void; zoomToFit(): void; /** Apply a new viewer theme (pass `undefined` to reset to defaults). */ setTheme(theme: ViewerTheme | undefined): void; /** Switch the UI locale (rebuilds the chrome labels). */ setLocale(locale: string): void; /** Enter presentation mode (real Fullscreen API). */ enterPresentation(): Promise; /** Exit presentation mode. */ exitPresentation(): Promise; /** Enable or disable editing at runtime (disabling clears the selection). */ setEditable(editable: boolean): void; /** Enable or disable editing inherited layout/master elements. */ setEditTemplateMode(enabled: boolean): void; /** Undo the last edit (no-op when the undo stack is empty). */ undo(): void; /** Redo the last undone edit (no-op when the redo stack is empty). */ redo(): void; canUndo(): boolean; canRedo(): boolean; /** Serialise the (edited) presentation to `.pptx` bytes and clear dirty. */ save(format?: PptxSaveFormat): Promise; /** Save and download a presentation in a supported OpenXML format. */ downloadAs(format: PptxSaveFormat, fileName?: string): Promise; /** Bundle the current presentation and usage notes in a shareable ZIP. */ packageForSharing(fileName?: string): Promise; /** `save()` + trigger a browser download (default `presentation.pptx`). */ downloadPptx(fileName?: string): Promise; /** Delete the selected element (no-op without a selection). */ deleteSelected(): void; /** Id of the selected element, or `null`. */ getSelectedElementId(): string | null; /** * Export a slide as a PNG download (defaults to the current slide). Renders * the slide off-screen at scale 1 and rasterises it with `html2canvas-pro` * (dynamically imported), so the first call pays a one-time load cost. */ exportSlidePng(index?: number): Promise; /** Copy a slide to the system clipboard as a PNG image. */ copySlideAsImage(index?: number): Promise; /** * Export every slide as a multi-page PDF download (one slide per page). * `jspdf` is dynamically imported on first use. */ exportPdf(options?: ExportPdfOptions): Promise; /** * Export every slide as an animated GIF download (one frame per slide, * `slideDurationMs` per frame). Slides are captured off-screen like * `exportSlidePng` and encoded with the shared pure-JS GIF89a encoder. */ exportGif(options?: ExportGifOptions): Promise; /** * Export every slide as a WebM video download: each captured slide is held * for its configured duration on a canvas stream recorded by * `MediaRecorder` (codec picked from the shared WebM candidates). */ exportVideo(options?: ExportVideoOptions): Promise; /** * Assemble the printable document (slides / notes / handouts / outline) * and open it in a new print window. Resolves `false` when the popup was * blocked: browsers typically only allow `window.open` inside a user * gesture, so call this from a click handler (or pass a custom * `openPrintWindow` that writes into an iframe you own). */ print(options?: PrintOptions): Promise; /** * Serialize the deck to `pptx-viewer-json` and trigger a browser download * (the File > Export > "Export as JSON" backstage card). Synchronous: the * JSON is built from the live viewer state, no rasterisation involved. */ exportJson(): void; /** The element-renderer registry in effect (extension point). */ getRegistry(): ElementRendererRegistry; /** * Escape hatch: the live `pptx-viewer-core` handler for the loaded file * (or `null`). Enables advanced operations (save, markdown conversion, * archive access) without extra APIs here. */ getHandler(): PptxHandler | null; /** * Start (or restart) a real-time collaboration session. Resolves once the * transport is created; connection status arrives via * {@link PptxViewerCallbacks.onCollaborationStatus}. */ startCollaboration(config: CollaborationConfig): Promise; /** Stop the active collaboration session (no-op when none is running). */ stopCollaboration(): void; /** Current collaboration connection status (`'disconnected'` when inactive). */ getCollaborationStatus(): ConnectionStatus; /** Force an immediate autosave snapshot (no-op when autosave is disabled). */ autosaveNow(): Promise; /** Enable or disable recovery autosave without rebuilding the viewer. */ setAutosaveEnabled(enabled: boolean): void; /** Whether recovery autosave is currently enabled. */ isAutosaveEnabled(): boolean; /** Tear down DOM, listeners, Blob URLs, and the core handler. */ destroy(): void; } //#endregion //#region src/viewer/chrome-lifecycle.d.ts /** The mutable pieces `PptxViewer` owns for one chrome mount lifecycle. */ interface ChromeLifecycle { chrome: ViewerChrome; presentation: PresentationController; detachKeyboard: () => void; detachTouchGestures: () => void; /** Remove the presenting click-to-advance listener. */ detachPresentationClick: () => void; resizeObserver: ResizeObserver | null; appliedThemeVars: string[]; } /** The subset of `PptxViewer` needed to build its `MountChromeDeps`. */ interface ChromeHost { doc: Document; container: HTMLElement; t: Translator; options: PptxViewerOptions; store: Store; renderer: RenderController; lifecycle: ChromeLifecycle; /** The viewer's live theme (kept in sync by `setTheme`); read on mount/remount instead of the static `options.theme`. */ currentTheme: ViewerTheme | undefined; editor: { commitNotes(notes: string, notesSegments?: TextSegment[]): void; getEditActions(): EditActions; getFindReplaceActions(): FindReplaceActions; setDrawTool(tool: DrawTool): void; setDrawColor(color: string): void; setDrawWidth(width: number): void; }; prev(): void; next(): void; /** Set an ABSOLUTE stage scale (the pinch gesture's units). */ setZoom(zoom: number): void; zoomIn(): void; zoomOut(): void; zoomToFit(): void; undo(): void; redo(): void; /** Live File > Options > Quick Access Toolbar group driving the strip. */ getQuickAccessOptions(): ViewerQuickAccessOptions; /** ScreenTip-styled tooltip for a strip button (undefined suppresses it). */ quickAccessScreenTip(label: string): string | undefined; toggleAutosave(): boolean; /** The AutoSave switch's state (the user preference within the host ceiling). */ isAutosaveSwitchOn(): boolean; /** Whether the host permits that switch to change anything. */ isAutosaveToggleAvailable(): boolean; downloadPptx(): Promise; downloadAs(format: PptxSaveFormat): Promise; packageForSharing(): Promise; toggleNotes(): void; goToSlide(index: number): void; /** Home: the show's first slide (skips a hidden slide 1 while presenting). */ goToFirstSlide(): void; /** End: the show's last slide (skips trailing hidden slides while presenting). */ goToLastSlide(): void; getSlideCount(): number; enterPresentation(): Promise; openPresenterView(): void; /** Open the presenter console when closed, close it when open. */ togglePresenterView(): void; /** Raise the console's "See All Slides" navigator (Ctrl+S during a show). */ showPresentationAllSlides(): void; /** See {@link MountChromeDeps.classifyPresentationExit}. */ classifyPresentationExit(): 'end-show' | 'restore-show'; exitPresentation(): Promise; getPresenterSnapshot(): PresentationSnapshot; updatePresenterSnapshot(patch: Partial): void; /** Discard the show's ink strokes (E, and the show toolbar's Clear button). */ clearPresentationAnnotations(): void; openBroadcast(): void; openShare(): void; openAccessibility(): void; openSettings(tab?: 'general' | 'shortcuts'): void; openHeaderFooter(): void; openCompare(): void; openSetUpSlideShow(): void; /** PowerPoint's Hide Slide: toggle the active slide's skip-in-show flag. */ toggleHideCurrentSlide(): void; startRehearsal(): void; toggleSubtitles(): void; openSelectionPane(): void; openSlideSorter(): void; openReadingView(): void; openOutlineView(): void; openComments(): void; openHyperlink(): void; openCustomShows(): void; openDocumentProperties(): void; openFontEmbedding(): void; openDigitalSignatures(): void; openPasswordProtection(): void; openVersionHistory(): void; toggleTemplateEditing(): void; toggleMasterNavigation(): void; selectElements(ids: string[]): void; exportSlidePng(): Promise; copySlideAsImage(): Promise; exportPdf(): Promise; exportGif(): Promise; exportVideo(): Promise; exportJson(): void; print(): Promise; openPrintDialog(): void; openFile(): void; openRecentFile(key: string): void; createPresentation(templateId: string): void; setTheme(theme: ViewerTheme | undefined): void; applyPresentationTheme(presetId: string): void; } //#endregion //#region src/viewer/export-lifecycle.d.ts /** The export slice of the public viewer API (see `PptxViewerInstance`). */ interface ViewerExportApi { /** Export a single slide as a PNG download. Defaults to the current slide. */ exportSlidePng(index?: number): Promise; /** Copy a slide to the system clipboard as a PNG image. */ copySlideAsImage(index?: number): Promise; /** Export every slide as a multi-page PDF download (one slide per page). */ exportPdf(options?: ExportPdfOptions): Promise; /** Export every slide as an animated GIF download (one frame per slide). */ exportGif(options?: ExportGifOptions): Promise; /** Export every slide as a WebM video download (MediaRecorder). */ exportVideo(options?: ExportVideoOptions): Promise; /** Open the assembled print document in a print window (`false` = blocked). */ print(options?: PrintOptions): Promise; /** Serialize the deck to `pptx-viewer-json` and trigger the download. */ exportJson(): void; } interface ExportLifecycle extends ViewerExportApi { /** Remove the off-screen capture stage from the DOM. */ destroy(): void; } /** * Base class implementing the export slice of `PptxViewerInstance` by * delegating to the instance's {@link ExportLifecycle}. `PptxViewer` extends * this, so new export formats are wired here (and in the export controller) * without growing the size-capped `PptxViewer.ts`. */ declare abstract class ViewerExportHost implements ViewerExportApi { protected abstract readonly exporter: ExportLifecycle; exportSlidePng(index?: number): Promise; copySlideAsImage(index?: number): Promise; exportPdf(options?: ExportPdfOptions): Promise; exportGif(options?: ExportGifOptions): Promise; exportVideo(options?: ExportVideoOptions): Promise; print(options?: PrintOptions): Promise; exportJson(): void; } //#endregion //#region src/viewer/PptxViewer.d.ts /** * The zero-framework PowerPoint viewer. Construct via {@link createPptxViewer}: * builds chrome inside `container`, loads `options.source` when given, and * re-renders through a tiny reactive store. */ declare class PptxViewer extends ViewerExportHost implements PptxViewerInstance, ChromeHost { readonly container: HTMLElement; readonly doc: Document; readonly options: PptxViewerOptions; readonly store: Store; readonly renderer: RenderController; t: Translator; /** The viewer's live theme (kept in sync by `setTheme`); read by `ChromeHost` on mount/remount. */ currentTheme: ViewerTheme | undefined; private readonly availableThemes; private readonly availableLocales; private currentThemeKey; private currentLocale; lifecycle: ChromeLifecycle; editor: EditorController; protected readonly exporter: ExportLifecycle; private readonly loading; private readonly registry; private readonly sessions; private readonly controls; private destroyed; private presenterChannel; private audienceWindow; private presenterSessionId; private presenterSequence; private presenterSnapshot; private presenterView; private presenterCaptions; /** * Latch telling the fullscreen handler that the next exit is the audience * popup stealing focus, not the presenter leaving. See * `render/presenter-show-lifecycle`. */ private readonly presenterShowGuard; private userFontsStyle; private signatureWarningAcknowledged; private detachSignatureWarning; private annotations; private parityWorkflows; private aiChat; /** Live AI focus controller while `ai` is configured; the canvas menu's AI entries route to it. */ private aiFocus; private contextMenu; /** View > Rulers strips (ticks, labels, drag-out guides) around the stage. */ private rulers; /** File > Options store + option-driven behavior (undo depth, ribbon, etc.). */ private optionsController; constructor(container: HTMLElement, options?: PptxViewerOptions); /** * Mount the optional AI assistant (toggle button + lazy chat panel) when the * host supplied an {@link PptxViewerOptions.ai} config. Re-callable: tears down * a prior mount first, so it can re-attach after a locale-driven chrome * remount. No-op (and never touches the `ai` SDK) when `ai` is absent. */ /** * Mount the canvas right-click menu against the current chrome. Re-callable: * the chrome (and with it `chrome.viewport`, which owns the `contextmenu` * listener) is rebuilt on every locale switch, so the menu must be re-bound * to the new viewport or right-clicking stops opening anything. */ private setupContextMenu; private setupAiChat; /** * Merge AI-proposed theme scheme updates into the live deck and re-render. * Theme scheme state sits outside the slides history snapshot, so this is a * best-effort apply (not a strictly undoable step, unlike slide edits). */ private applyAiThemeUpdates; loadFile(file: Blob | ArrayBuffer | Uint8Array): Promise; openFile(): void; openRecentFile(key: string): void; createPresentation(templateId: string): void; loadUrl(url: string): Promise; next: () => void; prev: () => void; goToSlide: (index: number) => void; /** PowerPoint's Hide Slide: toggle the active slide's skip-in-show flag. */ toggleHideCurrentSlide: () => void; /** Home: the show's first slide (skips a hidden slide 1 while presenting). */ goToFirstSlide: () => void; /** End: the show's last slide (skips trailing hidden slides while presenting). */ goToLastSlide: () => void; getSlideCount: () => number; getCurrentSlide: () => number; getZoom: () => number; setZoom(zoom: number): void; zoomIn: () => void; zoomOut: () => void; zoomToFit: () => void; zoomReset: () => void; goTo: (index: number) => void; goPrev: () => void; goNext: () => void; getContent: () => Promise; getMode: () => ViewerMode; setMode: (mode: ViewerMode) => void; getActiveSlideIndex: () => number; setActiveSlideIndex: (index: number) => void; isDirty: () => boolean; getSlides: () => readonly PptxSlide[]; getSlide: (index: number) => PptxSlide | undefined; getActiveSlide: () => PptxSlide | undefined; getElements: (index?: number) => readonly PptxElement[]; getElementById: (id: string, index?: number) => PptxElement | undefined; updateElement: (id: string, updates: Partial) => void; deleteElements: (ids: string[]) => void; duplicateElement: (id: string) => string | undefined; getSelectedElementIds: () => string[]; selectElements: (ids: string[]) => void; clearSelection: () => void; addSlide: (afterIndex?: number) => void; deleteSlides: (indexes: number[]) => void; duplicateSlides: (indexes: number[]) => void; moveSlide: (fromIndex: number, toIndex: number) => void; toggleHideSlides: (indexes: number[]) => void; private renumber; /** Expand/collapse the speaker-notes panel; persists for the instance's life. */ toggleNotes(): void; /** * Apply a viewer chrome theme (pass `undefined` to reset to defaults). * When `theme` matches a catalog entry's `theme` by reference (the shared * `THEME_CATALOG` presets, or a host's own `availableThemes`), the match * also updates `currentThemeKey` and persists the choice: to * `options.onThemeChange` when the host supplied one, otherwise to * `localStorage` via `writeStoredViewerPrefs`. A theme with no catalog * match (an ad hoc `ViewerTheme` a host passes directly) still applies but * isn't tracked as a "choice", matching how the Design tab's own gallery * already calls this method. */ setTheme(theme: ViewerTheme | undefined): void; applyPresentationTheme(presetId: string): void; /** Run the shared WCAG checks against the live deck and show the results. */ openAccessibility(): void; openSettings(tab?: 'general' | 'shortcuts'): void; openSetUpSlideShow(): void; toggleSubtitles(): void; openHeaderFooter(): void; openCompare(): void; openPrintDialog(): void; startRehearsal(): void; openSelectionPane(): void; openSlideSorter(): void; /** * PowerPoint's Reading View: the deck at full window size with the chrome * reduced to a nav bar. Not a slide show, so no Fullscreen API is involved. */ openReadingView(): void; /** * PowerPoint's Outline view: the deck as an editable indented text document. * Edits route through the normal whole-deck commit, so they undo like any * other structural change. */ openOutlineView(): void; openComments(): void; openHyperlink(): void; openCustomShows(): void; /** Open the document metadata editor backed by the current loaded deck. */ openDocumentProperties(): void; openFontEmbedding(): void; openDigitalSignatures(): void; openPasswordProtection(): void; openVersionHistory(): void; /** * Mount the presenter console on this screen. * * Deliberately does NOT open the audience display. Vanilla used to do both * at once, which is why its `audience` control always read "Close Audience * Window" the moment the console appeared while the other four read "Open". * In every other binding presenter view is the presenter's own surface and * the audience window is a separate, explicit toggle; coupling them also * meant a presenter who only wanted their notes got a popup they had to * dismiss. */ openPresenterView(): void; /** * Open the audience display in its own window and hand it the deck. * * Split out of {@link openPresenterView} so the console and the second * screen are independent, as they are in the other four bindings. */ openAudienceWindow(): void; private getPresenterChannel; private isAudienceWindowOpen; private syncAudience; /** Current presenter snapshot (pointer tool, blackout, ink, captions). */ getPresenterSnapshot(): PresentationSnapshot; updatePresenterSnapshot(patch: Partial): void; /** * Assemble the presenter console's dependencies. * * All of it is read through getters rather than captured: the console * outlives individual slides, snapshots and even chrome remounts, and a * captured slide array is how a console ends up painting a stale deck after * an edit. */ private buildPresenterViewOptions; /** Discard the show's ink strokes (E, and the show toolbar's Clear button). */ clearPresentationAnnotations(): void; /** * Reflect the presenter snapshot on the show toolbar. * * The snapshot is the single source of truth for the active pointer tool and * the show's ink, so the bar never keeps its own copy: every mutation, from a * bar button, a keyboard shortcut or the presenter console, lands here. */ /** * Mark the chrome while the presenter console owns the screen. * * The show toolbar has to stand down: the console carries its own * navigation, annotation tools and End control, and leaving the bar up put a * SECOND set of them over the console, at a higher stacking order. */ private markPresenterConsoleOpen; private syncPresentationToolbar; /** * The show toolbar's presenter-view control: mount the presenter console * when it is closed, tear it down when it is open. Closing the console also * closes the audience display, because the console owns the only control * that could close it afterwards. */ togglePresenterView(): void; /** * PowerPoint's Ctrl+S during a show: raise "See All Slides". * * The navigator is built by the presenter console, so the console comes up * with it rather than a second grid being assembled over the show. The key * resolved in the shared map long before this existed and was then dropped on * the floor, so the show swallowed Ctrl+S and did nothing at all. */ showPresentationAllSlides(): void; /** Start or stop the console's live captions, publishing onto the snapshot. */ private togglePresenterCaptions; /** * Trade places with the audience display, PowerPoint's "Swap displays". * A no-op when no audience window is open, which is why the console's * control is disabled in that state. */ private swapPresenterDisplays; /** * Tell the fullscreen handler what a fullscreen exit during a running show * meant. Delegates to the shared one-shot latch, which only answers * `'restore-show'` for the bounce {@link openPresenterView} armed it for. */ classifyPresentationExit(): 'end-show' | 'restore-show'; private connectAudienceRole; private closeAudienceWindow; /** * Switch the UI locale (rebuilds the chrome labels). Persists the choice * the same way {@link setTheme} does: `options.onLocaleChange` when the * host supplied one, otherwise `localStorage` via `writeStoredViewerPrefs`. */ setLocale(locale: string): void; setEditable(editable: boolean): void; setEditTemplateMode(enabled: boolean): void; toggleTemplateEditing(): void; toggleMasterNavigation(): void; undo: () => void; redo: () => void; /** * Flip the user's AutoSave preference and report the switch's new state. * * Returns the state the switch must actually show, which is NOT always the * requested one: when the host passed `autosave: false` the preference cannot * exceed that policy, so the toggle is inert and the switch snaps back off. */ toggleAutosave(): boolean; /** * Whether the host permits the AutoSave switch to change anything. Falls back * to the raw option while the session controllers are still being built (the * chrome mounts before them). */ isAutosaveToggleAvailable: () => boolean; /** * The title-bar switch state: the user's preference inside the host ceiling. * Deliberately not {@link isAutosaveEnabled}, which also reports the * read-only gate; a read-only deck must not look like the user turned * AutoSave off. Falls back to the option while the session controllers are * still being constructed (the chrome mounts before them). */ isAutosaveSwitchOn: () => boolean; /** * Live Quick Access Toolbar options for the title-bar strip. * * The chrome is mounted BEFORE the options controller is constructed, so the * very first strip render falls back to the shared defaults; the controller's * `applyAll()` re-renders it from the persisted options moments later. */ getQuickAccessOptions(): ViewerQuickAccessOptions; /** ScreenTip-styled tooltip for a Quick Access button; see above for timing. */ quickAccessScreenTip(label: string): string | undefined; canUndo: () => boolean; canRedo: () => boolean; save(format?: PptxSaveFormat): Promise; downloadAs(format: PptxSaveFormat, fileName?: string): Promise; downloadPptx(fileName?: string): Promise; packageForSharing(fileName?: string): Promise; deleteSelected: () => void; getSelectedElementId: () => string | null; enterPresentation(): Promise; exitPresentation(): Promise; getRegistry: () => ElementRendererRegistry; getHandler: () => PptxHandler | null; startCollaboration(config: CollaborationConfig): Promise; stopCollaboration: () => void; getCollaborationStatus: () => ConnectionStatus; autosaveNow(): Promise; setAutosaveEnabled(enabled: boolean): void; isAutosaveEnabled: () => boolean; openBroadcast(): void; openShare(): void; destroy(): void; /** File > Options > Save > "Delete cached files": drop recovery snapshots. */ private clearOptionsCache; private remountChrome; } /** Create a PowerPoint viewer inside `container` (see {@link PptxViewerOptions}). */ declare function createPptxViewer(container: HTMLElement, options?: PptxViewerOptions): PptxViewerInstance; //#endregion //#region src/viewer/styles/inject.d.ts /** The complete viewer stylesheet text (for hosts that self-manage CSS). */ declare function getViewerCss(): string; //#endregion export { type AccountAuthConfig, type AutosaveRecord, type AutosaveStatus, type CollaborationConfig, type CollaborationRole, type CollaborationTransport, type ConnectionStatus, type CssStyleMap, type EditActions, type ElementRenderContext, type ElementRenderer, type ElementRendererRegistry, type ExportGifOptions, type ExportPdfOptions, type ExportProgress, type ExportVideoOptions, type FindReplaceActions, type OpenPrintWindow, PPTX_OPEN_ACCEPT, PRESENTATION_OPEN_EXTENSIONS, type PptxAiBridge, type PptxAiConfig, type PptxAiConnection, type PptxAiContextStrategy, type PptxAiToolName, type PptxAiWritePolicy, type PptxElement, type PptxElementType, type PptxHandler, type PptxSlide, PptxViewer, type PptxViewerCallbacks, type PptxViewerInstance, type PptxViewerOptions, type PptxViewerSource, type PrintOptions, type Ribbon, type RibbonDesignHandlers, type RibbonDrawHandlers, type RibbonDrawState, type RibbonEditState, type RibbonFileHandlers, type RibbonHandlers, type RibbonInsertHandlers, type RibbonNavHandlers, type RibbonNavState, type RibbonPrimaryHandlers, type RibbonSelectionState, type RibbonSlideShowHandlers, type RibbonTabId, type SavedPresentationFormat, type SessionDeck, type SlideStageOptions, type Store, type SvgExportOptions, type ToolbarActionId, type TranslationKey, type TranslationMessages, type Translator, type ViewerState, type ViewerTheme, type ViewerThemeColors, type ZoomLevel, applyStyleMap, createDefaultRegistry, createEl, createElementRendererRegistry, createInitialViewerState, createPptxViewer, createRibbon, createStore, createSvgEl, createTranslator, defaultCssVars, defaultRadius, defaultThemeColors, exportAllSlidesToSvg, exportSlideToSvg, forgetSessionDeck, getSessionTabId, getViewerCss, isLegacyBinaryPresentation, isSupportedPresentationFile, keyToLabel, loadPresentationDeck, loadSessionDeck, parsePresentationSessionId, presentationBaseName, rememberSessionDeck, renderSlideStage, renderToCanvas, restoreSessionDeck, savedPresentationFileName, themeToCssVars, translationsEn, vermilionDarkTheme, vermilionLightTheme }; //# sourceMappingURL=index.d.ts.map