Files
openclaw/packages/markdown-core/src/render-plain.ts
Peter Steinberger b5232bc4dd refactor(markdown): add capability-driven fallbacks (#112985)
* refactor(markdown): add capability-driven fallbacks

* fix(markdown): type task-list core rule state

* style(markdown): make list-prefix return explicit

* fix(markdown): honor label-only plain links

* fix(markdown): retain clipped list provenance
2026-07-23 05:47:48 -04:00

40 lines
1.5 KiB
TypeScript

import { applyConstructFallbacks } from "./construct-fallbacks.js";
import type { FormatCapabilityProfile } from "./format-capabilities.js";
import type { MarkdownIR } from "./ir.js";
export type PlainRenderOptions = {
linkStyle?: "label" | "label-and-url";
};
/** Projects Markdown IR to plain text, optionally applying channel capability fallbacks. */
export function renderMarkdownAsPlainText(
ir: MarkdownIR,
options: PlainRenderOptions = {},
profile?: FormatCapabilityProfile,
): string {
const effectiveProfile =
profile && options.linkStyle === "label"
? { ...profile, constructs: { ...profile.constructs, linkLabel: "strip" as const } }
: profile;
const projected = effectiveProfile ? applyConstructFallbacks(ir, effectiveProfile) : ir;
if ((options.linkStyle ?? "label-and-url") === "label" || projected.links.length === 0) {
return projected.text;
}
let output = "";
let cursor = 0;
for (const link of [...projected.links].toSorted((a, b) => a.start - b.start)) {
if (link.start < cursor) {
continue;
}
output += projected.text.slice(cursor, link.end);
const href = link.href.trim();
const label = projected.text.slice(link.start, link.end).trim();
const comparableHref = href.startsWith("mailto:") ? href.slice("mailto:".length) : href;
if (href && label && label !== href && label !== comparableHref) {
output += ` (${href})`;
}
cursor = link.end;
}
return output + projected.text.slice(cursor);
}