Following a redline through the engine
The path a draft takes from baseline-plus-current text to email-safe redline HTML — the doc section first, then the modules that do the work.
The redline engine
The pipeline entry is buildRedline()
(src/redline/index.ts:51-104), wrapped by the two
workflow entry points in src/redline/workflow.ts
(buildFullDraftRedline, buildRegionRedline).
flowchart TD
A["baseline + current<br/>(HTML or text)"] --> B{"both HTML?<br/>canPreserveFormatting()"}
B -- yes --> C["buildPlainTextMap × 2<br/>htmlPlainMap.ts — HTML↔text index maps"]
B -- no --> D["normalizeForDiff()<br/>normalize.ts"]
C --> E["computeDiff()<br/>diff.ts — word diff + char refinement"]
D --> E
E --> F{"HTML blocks found &<br/>not inlineDiff?"}
F -- yes --> G["buildBlockPreservingRedline()<br/>htmlBlocks.ts — per-block diff,<br/>list grouping, spacer handling"]
F -- no --> H{"index maps available?"}
H -- yes --> I["renderPreservingHtml()<br/>renderPreserving.ts — slice original<br/>HTML segments around changes"]
H -- no --> J["renderRedlineHtml()<br/>render.ts — plain spans,<br/>optional dominant style"]
G --> K["finalizeBodyHtml()<br/>bodyEnvelope.ts — restore Outlook wrapper"]
I --> K
J --> K
K --> L["RedlineResult<br/>{html, cleanText, cleanHtml, changed}"]
Diff granularity is adaptive (src/redline/diff.ts):
the engine uses diffWordsWithSpace from the diff package, then drills down
to diffChars when the change looks localized — texts under 40 chars
(CHAR_DIFF_THRESHOLD), an overall change ratio ≤ 18 %
(LOCALIZED_CHANGE_RATIO), a word-pair typo ratio ≤ 35 %
(WORD_PAIR_TYPO_RATIO), or tokens of ≤ 5 chars
(SHORT_TOKEN_CHAR_DIFF_MAX). This is what turns "Arcadian → Arcadia" into a
one-character strike instead of a whole-word swap
(src/test/diff.test.ts).
Block alignment keeps redline spans from crossing paragraph or list-item
boundaries: blocks are aligned with diffArrays, unmatched removed/added
blocks are paired when their text similarity is within an 18 % change ratio,
and a block split into several is merged back when the deletion ratio stays
under 45 % (src/redline/htmlBlocks.ts:562,637,877).
Formatting preservation works by never re-generating equal text: the
HTML↔plain-text index map records, for every text span, the pre-serialized
inline-formatting context (b/i/span/font/a chain), and the renderer slices
original HTML segments for equal and deleted runs while insertions inherit
the baseline styling at the insertion point
(src/redline/htmlPlainMap.ts:129,294,321).
When no segment covers a range it falls back to the dominant style extracted
by frequency count over the document
(src/redline/dominantStyle.ts:144).
Redline markup is email-safe inline styles only — no classes:
deletions are color:red;text-decoration:line-through, insertions
color:blue;text-decoration:underline
(src/redline/types.ts:28-31). stripRedlineMarkup
removes deletions and unwraps insertions; acceptAllFromParts keeps
equal + insert parts for the clean output
(src/redline/stripRedline.ts:49,
src/redline/acceptAll.ts:5).