Skip to content

v3.1.0

Choose a tag to compare

@shbernal shbernal released this 09 Aug 21:06
· 904 commits to main since this release

Added

  • The package ships a ts-pptx-upstream skill, and InternalError says where to send a
    report.
    Most code that calls this library is written by an agent working in a repository
    that is not this one, and an agent that hits a library defect writes a workaround instead of
    filing — which is the rational move from where it stands, since the workaround unblocks its
    user today and the tracker belongs to a repo it is not in. So the report never happens and the
    next consumer rediscovers the same defect. Three layers answer three different reasons the
    report dies. InternalError now appends the tracker URL from its constructor rather than from
    its throw sites, so a site added later cannot forget it; it is the only class that does, because
    it is the only one that already declares whose bug it is, and a "report this" banner on every
    malformed package would train callers to skip the line that always means something. The
    ErrorCode TSDoc — and therefore the .d.ts an agent in node_modules actually reads — now
    states the test for the other four: the supported bar is "the output opens cleanly in Microsoft
    PowerPoint"
    , and it reads in both directions, so a PackageReadError on a file PowerPoint opens
    cleanly is our gap, not bad input. And skills/ts-pptx-upstream/ is published in the tarball, so
    npx skills add ./node_modules/@shbernal/ts-pptx works offline and always matches the installed
    version. The skill's load-bearing instruction is the one about the deck: presentations carry
    client names, unreleased strategy and pricing, and the tracker is public, so it spends most of
    its length on reducing a failure to a script that builds its own deck — and passes
    --repo shbernal/ts-pptx on every gh call, since gh in a consumer repo would otherwise file
    our bug into theirs. A third issue form, agent-report.yml, is where the error message's URL
    lands; its attachment dropdown deliberately has no option for a file containing real data, and
    "what should have happened" asks for the reason — an ECMA-376 clause, PowerPoint's own behaviour,
    or the docs — since that is what separates an actionable report from a matter of opinion. No API
    changed. InternalError.message gained a trailing pointer, which is not a contract: the class and
    the code are API and the message never was.

  • TableCell.fillNoFill reads an explicit <a:noFill/> on a cell (#7) — the cell-side
    counterpart of 3.0.0's Shape.fillNoFill, and what TableCell.noFill() has always been
    able to write. hasOwnFill is not this question: it is true for any
    EG_FillProperties child, so it cannot separate a suppressed fill from a gradient, a
    pattern, a picture or an a:grpFill, and every colour accessor (resolvedFill,
    fillColor, fillSchemeColor) reports null for a no-fill cell exactly as it does for
    one inheriting the style's shading. Deriving it as "has an own fill that no accessor
    recognises" also changes meaning silently the day a further fill kind gets an accessor.

Changed

  • Development toolchain: ESLint + Prettier → oxlint + oxfmt, and TypeScript 6 → 7.
    This is a contributor-facing change with no runtime effect on the published package —
    no API was added, removed or altered, and the generated OOXML is byte-identical across
    all 183 parts of the reference decks. Enforcement was held level rather than relaxed: of
    the 90 lint rules previously enabled on src/, 89 carry over (the missing one, no-octal,
    is already a syntax error in an ES module), type-aware linting stays on, and
    no-floating-promises / no-misused-promises now also cover scripts/ and test/,
    where the old configuration could not afford them. See docs/development.md.

  • Published .d.ts files are textually different, though no type changed. TypeScript 7
    prints declarations slightly differently: string literal types use single quotes
    (readonly shapeType: 'autoShape' rather than "autoShape") and redundant parentheses
    are dropped (fontRef?: (StyleFontRef | null) | undefined becomes
    fontRef?: StyleFontRef | null | undefined). Chunk filename hashes shift as a
    consequence. No declaration, signature or export moved, and publint,
    @arethetypeswrong/cli and the packed-package smoke test all pass — but a consumer
    diffing shipped declarations between versions will see churn, which is why it is recorded
    here rather than left as an implementation detail.

    Note that documentation generation deliberately stays on TypeScript 6: TypeScript 7 is
    the native Go compiler and ships no JavaScript compiler API for TypeDoc to import. That
    copy is confined to the private tools/api-docs workspace package and reaches nothing
    that is published.

Fixed

  • fill: { type: 'none' } emits <a:noFill/> instead of nothing at all (#9). The
    option's own name states the shape is transparent, and it was the one call that did not
    produce that state: genXmlColorSelection had no none case, so the fill child was
    omitted entirely and the interior fell back to p:style/a:fillRef or the placeholder —
    a shape carrying a style reference rendered in the theme's accent colour rather than
    transparent. line: { type: 'none' } on the same options object has always emitted its
    a:noFill; the two now agree. This reaches every caller of the shared fill dispatch, so
    a table cell (TableCellProps.fill) can author a transparent cell the same way.
    Round-trip consequence: addShape({ fill: { type: 'none' } }) → save → load now reports
    Shape.fillNoFill === true, where it reported false before. The one behaviour that
    changes for existing decks is that type: 'none' no longer produces the inherit state
    by accident — if you were relying on it to mean "leave the fill to the style", omit the
    fill option instead.

  • readModelToIr carries a line's @cap, and declares a dropped @algn (#8). Both
    legs of the cap mapping already existed — ShapeLineProps.cap authors the attribute and
    3.0.0's Shape.lineCap reads it back — but the script tier's lineOption never consumed
    it, so a deck this library wrote could not survive its own converter. The loss was
    undeclared, which is the part that mattered: the round-trip gate excludes exactly what
    a fidelity note names, so it passed green while the deck changed. @cap extends every
    dash by the stroke width and decides whether each draws as a rectangle or a lozenge, so
    on a thick dashed rule the before and after are visibly different. @algn is readable and
    has no write option, so it now records a line.align note (dropped/unwritable) —
    for algn="in" only, since ctr is what an omitted @algn already renders as.

  • latexToOmml emits accents as <m:acc> rather than <m:limUpp> (#6). \hat,
    \bar, \vec, \dot, \ddot, \tilde, \acute, \grave, \check, \breve,
    \mathring, \H, \dddot and their short-form aliases (\^, \`, \', \",
    \., \=, \u, \v, \r) all landed as over-limits, with limit spacing and
    semantics, because temml emits a bare <mover> (correct for a browser — MathML renderers
    derive accent positioning from the operator dictionary) and mathml2omml has no dictionary
    and keys strictly off accent="true". The pipeline now carries the small dictionary
    subset that closes the gap, and while it is there it swaps temml's spacing modifier for
    the combining mark ECMA-376 §22.1.2.20 says an accPr character should be — so \vec{v}
    gets an arrow accent instead of a full-size arrow hung over the base.

    Scoped to latexToOmml: mathmlToOmml passes hand-written MathML through unchanged,
    because there accent is the caller's to set. Constructs that were already mapping well
    are untouched (\widehat/\overbrace stay m:groupChr, \overline/\underline stay
    m:borderBox, \stackrel stays m:limUpp), and two stay limits by necessity: \utilde
    and other under-accents, since OMML has no under-accent object and the symmetric
    accentunder="true" makes mathml2omml emit an over-accent; and \ddddot, whose
    two-character operator has no single m:chr.

  • A table cell's explicit a:noFill survives read → script → write. Previously a
    suppressed cell fell out of cellFill as "no fill option" and the copy took the table
    style's banding — the opposite of what the source showed. Enabled by the
    TableCell.fillNoFill reader above and the fill: { type: 'none' } writer fix above.