Word counts

word-counts-by-section(body, level: 1, count-captions: false)
one row per heading at level: (top-level by default), the words found under it. render-report(...) calls this for you — reach for it directly only if a project needs the numbers outside the usual report layout.
extract-text(node, count-captions: false)
the structural walk underneath it — content in, plain text out, with every reference, citation, and label already stripped.
count-words(s)
splits on whitespace, nothing fancier.

Three exclusions are always in effect, none of them configurable off:

  1. Citations and cross-references. A resolved @smith2020 becomes a bracketed number once Typst lays a page out — real text, easy to miscount as prose. extract-text never sees that: it walks the manuscript’s content before layout, captured by instrument() (Quickstart) at the exact moment contexture.bundle hands it to template:, where a citation is still a real, ignorable ref/cite node rather than rendered text.
  2. Table cells. A table()‘s cells have no space/paragraph-break between them in the content tree the way a paragraph’s words do — concatenating them as if they were prose merges neighbouring cells into nonsense tokens ("A", "B" becoming "AB", one word instead of two, or worse for multi-word cells). Never counted, in any mode — table data was never prose to begin with.
  3. Figure captions, by default — count-captions: true opts them in, passed to report(...).
Note
colophon reads through palimpsest’s own marks too — add/del/rep/passage/touched all render their actual output wrapped in a context block, even in clean mode, which is structurally invisible to colophon’s pre-layout walk the same way a citation’s rendered bracket would be. Rather than trying to see through that opacity, colophon reads each function’s own plain, non-context metadata directly — the exact content clean mode shows, no more and no less.
// Regression test for the Phase 4 fix documented in CLAUDE.md
// ("Phase 4" -> "Correction du 2026-09-13"): colophon's word count
// must reflect exactly what palimpsest's *clean* compile shows, even
// though passage()/add()/del()/rep() all render through a `context`
// block this package's pre-layout walk can't otherwise see into.
//
// Expected total: 6 + 7 + 7 + 1 = 21 words.
// - "This sentence has and stays short." (6) -- del()'d words gone.
// - "The old wording is now different today." (7) -- rep()'s `new`
//   only, never `old`.
// - "Plain touched text with six words total." (7) -- an ordinary
//   passage/touched(), no add/del/rep at all.
// - the figure's caption (1, "Caption.") -- proves the figure
//   inventory (Phase 2, query()-based) already saw content nested
//   inside a mark correctly, unaffected by this whole problem.

#import "../../lib.typ" as colophon
#import "/typst-palimpsest/lib.typ" as palimpsest
#import "../../../typst-contexture/lib.typ" as contexture

#let my-template(body) = {
  set page(width: 16.6cm, height: auto, margin: 12pt)
  set text(size: 10.5pt)
  body
}

#show: contexture.bundle.with(
  template: colophon.instrument(template: my-template),
  documents: (colophon.report(count-captions: true),),
)

= Methods

#palimpsest.passage(none)[
  This sentence has #palimpsest.del[four deleted words] and stays short.
]

#palimpsest.passage(none)[
  The old wording #palimpsest.rep[was here before][is now different] today.
]

#palimpsest.touched(none)[
  Plain touched text with six words total.
]

#palimpsest.passage(none)[
  #palimpsest.add[
    #figure(rect(), caption: [Caption.]) <fig-in-mark>
  ]
]

The count matches exactly what the clean, submitted manuscript would show: the deleted words gone, the replacement’s new wording only, the untouched passage counted normally, and — since this example passes count-captions: true — the figure’s own caption too:

Table cells versus captions, side by side

The same manuscript that introduces the figure/table inventory also makes the table-cell exclusion visible: its results table holds the literal text “A” and “B”, and the total word count is unaffected by them either way count-captions is set — worth keeping in mind when a word count seems low for a manuscript that’s mostly tables and figures:

← QuickstartThe abstract →