mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-04 10:51:37 +00:00
* 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
40 lines
1.5 KiB
TypeScript
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);
|
|
}
|