const OSC8_CLOSE = "\x1b]8;;\x1b\\"; // SGR, cursor markers, OSC, and other non-printing controls can split marker text. const CONTROL_RE = /\x1b(?:\[[0-?]*[ -/]*[@-~]|[\]_^P][^\x07\x1b]*(?:\x07|\x1b\\)|[()][A-Za-z0-9])/g; function localLink(reference: string | undefined): string | undefined { if (!reference || /[\x00-\x20\x7f]/.test(reference)) return undefined; try { const url = new URL(reference); return url.protocol === "file:" && !url.hostname ? url.href : undefined; } catch { return undefined; } } /** Decorate render output only. Original controls, cursor positions, and editor text stay intact. */ export function renderDraftImageLinks( lines: readonly string[], resolveReference: (marker: string) => string | undefined, ): string[] { return lines.map((line) => { // Do not nest links supplied by another editor. if (line.includes("\x1b]8;")) return line; const offsets: number[] = []; let plain = ""; let position = 0; for (const control of line.matchAll(CONTROL_RE)) { for (; position < control.index; position += 1) { offsets.push(position); plain += line[position]; } position = control.index + control[0].length; } for (; position < line.length; position += 1) { offsets.push(position); plain += line[position]; } let result = ""; let end = 0; for (const match of plain.matchAll(/\[Image #\d+\]/g)) { const reference = localLink(resolveReference(match[0])); if (!reference) continue; const start = offsets[match.index]; const last = offsets[match.index + match[0].length - 1]; if (start === undefined || last === undefined) continue; const stop = last + 1; result += line.slice(end, start) + `\x1b]8;;${reference}\x1b\\` + line.slice(start, stop) + OSC8_CLOSE; end = stop; } return result + line.slice(end); }); }