Skip to Content
PackagesFormat Cleanup

Format cleanup

A lint for how a document is built, not what it says. Word documents rot: formatting set by hand instead of through styles, empty paragraphs for spacing, duplicate styles from every paste. The engine finds these, explains each one, and fixes them in one undo step.

Packages

PackageContents
@stesura/format-lintformatLintPlugin, the engine (collectFormatSuggestions, applyFormatSuggestionsTr), commands, types
@stesura/format-lint-reactformatLintUIExtension: Export-tab button, status-bar count, right panel

Setup

import { formatLintPlugin } from "@stesura/format-lint"; import { formatLintUIExtension } from "@stesura/format-lint-react"; // in the mount hook's extraPlugins formatLintPlugin({ debounceMs: 600 }); // in uiExtensions (module level) const uiExtensions = [formatLintUIExtension];

The plugin recollects a debounce after each edit, in the root editor only. Headers, footers, notes and text boxes count for usage (a style used only in a footer is not unused), but paragraph fixes apply to the main story.

Guarantees

Every suggestion says what fixing it does to the page:

GuaranteeMeaning
losslessEvery block renders exactly as before.
toleranceLengths move by at most tolerancePx (default 3 px).
restructureChanges the document on purpose. Hints only, never applied by “Fix all”.

A fix that moves a value into a style keeps everything else rendering as before: paragraphs and child styles that inherited the old value get it as their own.

Rules

RuleSeverityFinds
FMT-1warningCustom styles nothing uses or references
FMT-2warningStyles that resolve identically (Body Text 2, Normal (Web))
FMT-3errorbasedOn/next/link naming a missing style, basedOn loops, blocks on a missing style
FMT-4hintUnused styles that only exist to be inherited from
FMT-5warningIdentical list definitions (counters stay separate after the merge)
FMT-10warningParagraph formatting equal to what the style already gives
FMT-11warningDirect formatting: a value nearly every paragraph on a style sets moves into the style; repeated combinations become a style; near-misses snap back
FMT-12warningEmpty paragraphs used as spacing
FMT-13hintLine breaks used as paragraph breaks, and the reverse
FMT-15hintTyped numbers (1., (a)) next to a real list
FMT-16hintShort bold or large lines that read as headings; heading levels that disagree with their style
FMT-17warningNumbered paragraphs indented a hair off their list
FMT-18hintManual page breaks before headings
FMT-19hintA typed table of contents
FMT-20warningCharacter formatting the style already has
FMT-21warningA font, size or colour on 90% of a style’s text, set by hand
FMT-22hintFonts used in the text but in no style
FMT-23hintWhole paragraphs highlighted (reported, never fixed)

Options: tolerancePx (3), coverageThreshold (0.9), minClusterSize (3), rules (run only these) and ignore (suggestion ids). The defaults come from a survey of real legal documents.

Applying

“Fix all” applies every fixable error and warning in one transaction, in a fixed order (repairs, content, style fields, clusters, inline coverage, redundancy, styles, lists, chains), each pass repeated until it settles: a second collect finds nothing left. Hints apply one click each, or under “Fix all” when named in rules.

Suggestion ids are a hash of the rule and what it is about (a style id, a list, a block), so they survive edits elsewhere. “Ignore” writes the id to doc.attrs.formatLintIgnore, which travels with the document to collaborators.

While track changes is on, applying is refused (trackChangesBlockedReason): every cleanup would be recorded as a tracked formatting change. The panel says so and offers to apply anyway (force: true).

import { applyFormatSuggestions, collectFormatSuggestions } from "@stesura/format-lint"; const suggestions = collectFormatSuggestions(state); applyFormatSuggestions("all")(state, dispatch);
Last updated on