/**
 * Utilities for splitting a PreTeXt article into individual sections and
 * merging them back into a complete document.
 *
 * Splitting always works at the `<section>`, `<introduction>`, and
 * `<conclusion>` level inside a top-level `<article>` element.
 */
import type { Asset, SourceFormat } from "./types/editor";
import type { Division, DivisionType, DocumentSection, DocumentSectionType, DocumentSplitResult } from "./types/sections";
/**
 * Replace (or insert) the `<title>` of a section XML string with `newTitle`.
 * Returns the updated XML string.
 */
export declare function updateDivisionTitle(divisionXml: string, newTitle: string): string;
/** Create a new blank `<section>` as a `Division`. */
export declare function createNewSection(title?: string): DocumentSection;
/** Create a blank `<introduction>` division. */
export declare function createIntroduction(): DocumentSection;
/** Create a blank `<conclusion>` division. */
export declare function createConclusion(): DocumentSection;
/**
 * Strip the outer wrapper element from a section XML string, returning just the
 * inner XML content (i.e. everything between `<section>` and `</section>`).
 * Used to show only the section body in the code editor so users can't
 * accidentally edit or delete the enclosing element.
 */
export declare function stripSectionWrapper(sectionXml: string): string;
/**
 * Re-wrap inner XML content (as returned by the code editor) with the correct
 * outer element for the given section type.
 *
 * Because `DocumentSectionType` values are identical to the XML tag names,
 * this is simply `<${type}>inner</${type}>`.
 */
export declare function rewrapSection(innerXml: string, type: DocumentSectionType): string;
/**
 * Ensure the given XML string has the correct outer element for its section
 * type.  If the outer tag is already present it is returned unchanged;
 * otherwise the content is re-wrapped so that accidental deletions in the
 * code editor are recovered gracefully.
 */
export declare function ensureSectionWrapper(content: string, type: DocumentSectionType): string;
export declare function splitDocument(xml: string): DocumentSplitResult;
export declare function mergeDocument(wrapper: string, sections: DocumentSection[]): string;
/**
 * Strip the outer `<chapter>` element from a chapter XML string, returning
 * just its inner content (title, sections, etc.).  Behaves exactly like
 * {@link stripSectionWrapper}: the enclosing element is dropped but all
 * children are kept, so the user edits the chapter body without ever seeing
 * or editing the `<chapter>` division tag itself.
 */
export declare function stripChapterWrapper(chapterXml: string): string;
/**
 * Re-wrap chapter body content (as produced by the code editor) with the
 * original `<chapter>` element, preserving its tag name and all attributes
 * (e.g. `xml:id`, `label`).
 *
 * The attributes are recovered from `originalChapterXml` — the last known
 * full chapter source — so that editing the body never drops them.  String
 * concatenation (rather than re-serialising via xast) keeps this robust to
 * invalid inner XML while the user is mid-edit.
 */
export declare function rewrapChapter(innerXml: string, originalChapterXml: string): string;
export declare function splitLatexDocument(latex: string): DocumentSplitResult;
export declare function mergeLatexDocument(wrapper: string, sections: DocumentSection[]): string;
/**
 * Strip the section-level header from a LaTeX section string so the code
 * editor shows only the body content.
 *
 * - For `\section{…}`-style sections: removes the leading `\section{…}` command.
 * - For `\begin{section}…\end{section}`-style: removes the wrapper tags.
 * - For introduction / conclusion (no header): returns as-is.
 */
export declare function stripLatexSectionWrapper(content: string, type: DocumentSectionType): string;
/**
 * Re-wrap inner LaTeX content (as produced by the code editor) with the
 * correct section header for the given section type and title.
 *
 * - `section` type: prepends `\section{title}`.
 * - `introduction` / `conclusion`: returns inner content unchanged.
 *
 * Pass `originalContent` to detect whether the document uses environment style
 * (`\begin{section}…\end{section}`) so the same style is preserved.
 */
export declare function rewrapLatexSection(inner: string, type: DocumentSectionType, title: string, originalContent?: string): string;
/**
 * Ensure the given LaTeX string has the correct section header/wrapper for its
 * type.  If the header is already present it is returned unchanged; otherwise
 * it is re-wrapped using `rewrapLatexSection` so that accidental deletions in
 * the code editor are recovered gracefully.
 */
export declare function ensureLatexSectionWrapper(content: string, type: DocumentSectionType, title: string, originalContent?: string): string;
/**
 * Replace (or insert) the section title in a LaTeX section string.
 *
 * - For `\section{…}` style: updates the command argument.
 * - For `\begin{section}` style: updates the `\title{…}` inside.
 */
export declare function updateLatexSectionTitle(content: string, newTitle: string): string;
/**
 * Derive a LaTeX section's title directly from its header — the code
 * editor's source-of-truth content — mirroring the two header styles
 * {@link updateLatexSectionTitle} writes. Returns `null` when no header is
 * found (introduction/conclusion have none), so callers leave title as-is.
 */
export declare function extractLatexDivisionTitle(content: string): string | null;
/** Create a new blank LaTeX section as a `Division`. */
export declare function createNewLatexSection(title?: string): DocumentSection;
/** Create a blank LaTeX introduction. */
export declare function createLatexIntroduction(): DocumentSection;
/** Create a blank LaTeX conclusion. */
export declare function createLatexConclusion(): DocumentSection;
/**
 * Merge two adjacent sections into one, keeping the title of the first.
 *
 * - For PreTeXt: parses both sections and concatenates the body children
 *   (skipping the second section's `<title>`).
 * - For LaTeX: strips the second section's header and appends its body.
 *
 * @param a First (absorbing) section.
 * @param b Second section whose content is appended to `a`.
 * @param isLatex Whether the document source is LaTeX.
 */
export declare function mergeTwoSections(a: DocumentSection, b: DocumentSection, isLatex: boolean): DocumentSection;
/**
 * Extract `xml:id` and `label` attributes from the root element of a section
 * content string.  Returns empty strings when the attributes are absent.
 */
export declare function getSectionAttributes(content: string): {
    xmlId: string;
    label: string;
};
/**
 * Derive a division's title, type, `xml:id`, and `label` directly from its
 * full PreTeXt source — the code editor's content, wrapper tag included.
 * Used to keep the TOC in sync when the user edits these directly in the
 * source rather than through the metadata dropdown form.
 *
 * Returns `null` when `content` isn't well-formed XML or its root element
 * isn't a recognised division tag (both common mid-edit), so callers can
 * skip the update rather than clobbering existing metadata with junk.
 */
export declare function extractDivisionMetadata(content: string): {
    title: string;
    type: DivisionType;
    xmlId: string;
    label: string;
} | null;
/**
 * Update the title, tag name (type), `xml:id`, and `label` of a section.
 *
 * Pass `null` for `xmlId` or `label` to remove the attribute entirely.
 * Omit a key (or pass `undefined`) to leave it unchanged.
 *
 * Returns a new `DocumentSection` with updated `content`, `title`, and `type`.
 */
export declare function updateSectionMetadata(section: DocumentSection, changes: {
    title?: string;
    type?: DocumentSectionType;
    xmlId?: string | null;
    label?: string | null;
}): DocumentSection;
/**
 * Update the `<title>`, `xml:id`, and `label` of a chapter XML string.
 *
 * Mirrors {@link updateSectionMetadata} but operates on a raw chapter source
 * string and never changes the element's tag name (a chapter is always a
 * chapter).  Pass `null`/empty for `xmlId` or `label` to remove the
 * attribute; omit a key (or pass `undefined`) to leave it unchanged.
 */
export declare function updateChapterMetadata(chapterXml: string, changes: {
    title?: string;
    xmlId?: string | null;
    label?: string | null;
}): string;
export declare function wrapDocumentAsSection(xml: string, sectionTitle?: string): DocumentSplitResult;
export declare function wrapLatexDocumentAsSection(latex: string, sectionTitle?: string): DocumentSplitResult;
/**
 * Return the ordered list of `xmlId` values referenced by
 * `<plus:* ref="..."/>` placeholders found in `content`.
 *
 * Only direct children are returned — the function does not recurse.
 * Call it for each division in the pool to build the full tree.
 */
export declare function parseDivisionRefs(content: string): string[];
/**
 * Like {@link parseDivisionRefs} but also returns the division type inferred
 * from the tag name (e.g. `<plus:chapter ref="x"/>` → `{ type: "chapter", xmlId: "x" }`).
 * Used to auto-create Division records when new refs appear in edited content.
 *
 * Only tag names in {@link DIVISION_REF_TAGS} are considered — asset
 * placeholders (`plus:image`, `plus:doenet`, ...) are not divisions and are
 * skipped. The generic `<plus:division ref="x"/>` alias falls back to type
 * `"section"`, matching {@link tagToType}'s default for unrecognised tags.
 */
export declare function parseDivisionRefsWithTypes(content: string): {
    xmlId: string;
    type: DivisionType;
}[];
/** A `<plus:image ref="..."/>` / `<plus:doenet ref="..."/>` asset placeholder. */
export interface AssetRef {
    kind: "image" | "doenet";
    ref: string;
}
/**
 * Parse every `<plus:image ref="..."/>` / `<plus:doenet ref="..."/>` asset
 * placeholder out of `content`, in document order, without de-duplicating.
 *
 * Asset placeholders share the `<plus:* ref="..."/>` shape used by division
 * refs (see {@link DIVISION_REF_TAGS}) but are deliberately parsed by a
 * separate, disjoint tag set so the two kinds of include are never conflated.
 */
export declare function parseAssetRefs(content: string): AssetRef[];
/**
 * Create a minimal Division record for a given `xmlId` and `type`.
 * Used when the user types a new `<plus:TYPE ref="id"/>` placeholder into a
 * division's content and no matching Division exists in the pool yet.
 */
export declare function createDivisionWithId(xmlId: string, type: DivisionType, sourceFormat?: SourceFormat): Division;
/**
 * Insert a `<plus:TYPE ref="xmlId"/>` placeholder into `content`.
 *
 * - When `afterXmlId` is `null` the ref is appended just before the closing
 *   tag of the outer element (or at the end of the string if none is found).
 * - When `afterXmlId` is provided the new ref is inserted immediately after
 *   that ref's placeholder.  If the named ref is not found, falls back to
 *   appending.
 */
export declare function insertDivisionRef(content: string, xmlId: string, type: DivisionType, afterXmlId: string | null): string;
/**
 * Remove the `<plus:* ref="xmlId"/>` placeholder for `xmlId` from `content`.
 * The surrounding newline/whitespace is cleaned up so the result stays tidy.
 */
export declare function removeDivisionRef(content: string, xmlId: string): string;
/**
 * Move an existing `<plus:* ref="xmlId"/>` placeholder to a new position.
 *
 * Equivalent to `removeDivisionRef` followed by `insertDivisionRef`, but
 * preserves the original tag's element name (e.g. `plus:section` stays
 * `plus:section` rather than being normalised to `plus:division`).
 *
 * - `afterXmlId === null` moves the ref to the end (before the closing tag).
 * - `afterXmlId` moves it immediately after that ref.
 */
export declare function moveDivisionRef(content: string, xmlId: string, afterXmlId: string | null): string;
/**
 * Rename an existing `<plus:* ref="oldXmlId"/>` placeholder in-place to point
 * at `newXmlId`, also updating the `*` tag name to `newType` if it changed.
 * Unlike {@link moveDivisionRef}, the placeholder's position is left
 * untouched — only its `ref` value and element name are rewritten.
 *
 * Used to keep a parent division's child placeholder in sync when the
 * child's own `xml:id`/type are edited directly in its source, so the
 * rename doesn't orphan the child from its parent.
 *
 * Returns `content` unchanged if no placeholder for `oldXmlId` is found.
 */
export declare function renameDivisionRef(content: string, oldXmlId: string, newXmlId: string, newType: DivisionType): string;
/**
 * Find the division in `divisions` whose content contains a
 * `<plus:* ref="xmlId"/>` placeholder for `xmlId` — i.e. `xmlId`'s parent in
 * the division tree.  Returns `null` if `xmlId` is unplaced (orphaned) or is
 * the root.
 */
export declare function findDivisionParent(divisions: Division[], xmlId: string): Division | null;
/**
 * Rewrite `content` so its `<plus:* ref="..."/>` placeholders appear in the
 * order given by `orderedXmlIds`.
 *
 * Implemented by repeatedly moving each ref to sit immediately after its
 * predecessor in the desired order; because every referenced child is moved,
 * the final relative order of the whole group matches `orderedXmlIds` exactly
 * while non-ref content keeps its position.  Original tag element names are
 * preserved (via `moveDivisionRef`).
 */
export declare function reorderDivisionRefs(content: string, orderedXmlIds: string[]): string;
/**
 * Collapse expanded-empty `<plus:TAG ...></plus:TAG>` placeholders back to the
 * canonical self-closing `<plus:TAG .../>` form.  An XML round-trip through
 * xast expands self-closing elements, so this is applied after editing a
 * division's content to keep the stored source tidy.
 */
export declare function normalizeSelfClosingRefs(content: string): string;
/**
 * Return all divisions in `divisions` that are not reachable from
 * `rootXmlId` (and are not the root itself).
 *
 * Orphaned divisions are shown separately in the TOC so they can be placed
 * inside a parent division.
 */
export declare function getOrphanedDivisions(divisions: Division[], rootXmlId: string): Division[];
/** A division flattened into a depth-first list, annotated for tree rendering. */
export interface DivisionTreeNode {
    division: Division;
    /** Nesting depth: direct children of the start division are depth 0. */
    depth: number;
    /** `xmlId` of the division that references this one. */
    parentXmlId: string;
}
/**
 * Walk the division hierarchy starting from `startXmlId` (exclusive) and return
 * a depth-first–ordered flat list of descendant nodes, each annotated with its
 * `depth` and `parentXmlId`.
 *
 * The start division itself is not included.  Cycles and missing refs are
 * skipped defensively.  Rendering the result as a single list with
 * depth-based indentation reproduces the tree visually while keeping a flat
 * structure that a single dnd `SortableContext` can operate over.
 */
export declare function buildDivisionTree(divisions: Division[], startXmlId: string): DivisionTreeNode[];
/**
 * Return the "roots" of the orphaned (unreachable) divisions: orphans that are
 * not referenced by any other orphan.  Each orphan root heads its own dangling
 * subtree, so the TOC can render unplaced material as trees rather than a flat
 * jumble of every disconnected descendant.
 */
export declare function getOrphanRoots(divisions: Division[], rootXmlId: string): Division[];
/**
 * Resolve the root division and recursively expand every
 * `<plus:* ref="..."/>` placeholder it (transitively) contains, converting
 * any LaTeX/Markdown divisions to PreTeXt along the way. Returns the bare
 * root element (e.g. `<book>...</book>`) — *not* wrapped in `<pretext>` and
 * without `<docinfo>`.
 *
 * This is the body half of a full document. Most callers that want an
 * actual buildable/persistable document should use
 * {@link assembleFullProjectSource} instead; this lower-level function
 * remains for callers (like the division-scoped preview path) that need to
 * compose the resolved body further before wrapping it themselves.
 */
export declare function assembleProjectSource(divisions: Division[], rootXmlId: string, assets?: Asset[]): string;
/**
 * Assemble the complete PreTeXt document for a project: the root division,
 * fully resolved (every `<plus:* ref="..."/>` placeholder expanded and any
 * LaTeX/Markdown divisions converted to PreTeXt), wrapped in the outer
 * `<pretext>` element with `<docinfo>` inserted as its sibling.
 *
 * This is the same shape produced for a root-division preview build, and is
 * what a host application should persist as "the full source" and send to a
 * build server (e.g. `https://build.pretext.plus`) to produce the final
 * rendered document — the `divisions` pool itself is never a valid build
 * input, since it's a flat list of fragments rather than a single document
 * tree.
 */
export declare function assembleFullProjectSource(divisions: Division[], rootXmlId: string, docinfo: string, assets?: Asset[]): string;
/**
 * Wrap a single division's own tagged XML (e.g.
 * `<section xml:id="...">...</section>`) into a standalone PreTeXt fragment
 * document suitable for a build-server preview of just that division.
 *
 * This function itself never expands `<plus:* ref="..."/>` placeholders —
 * the real build server has no notion of that placeholder syntax, so callers
 * must resolve them first (e.g. via {@link assembleProjectSource}) before
 * passing `divisionXml` in. Passing unresolved refs produces invalid PreTeXt
 * and a build failure.
 *
 * `divisionType` determines the minimal wrapper needed around `divisionXml`:
 * root types (`book`/`article`/`slideshow`) need none, `chapter`/`part` are
 * wrapped in a bare `<book>`, and everything else in a bare `<article>`.
 * The PreTeXt schema requires `<book>`/`<article>` to have a `<title>` as
 * their first child, so a wrapper built here uses `wrapperTitle` for that —
 * without it the build server's schema validation rejects the document,
 * produces no output, and 500s.
 * `docinfo` (the full `<docinfo>...</docinfo>` element, or `""`) is inserted
 * as a sibling of the root element inside `<pretext>`, matching real PreTeXt
 * document shape.
 */
export declare function wrapDivisionForPreview(divisionType: DivisionType, divisionXml: string, docinfo: string, wrapperTitle: string): string;
export declare function normalizeDivisionsOnLoad(divisions: Division[], rootDivisionId: string | undefined, projectType: "article" | "book" | undefined): Division[];
