Something went wrong. Try again.
My website. cameron.stream
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333import { UnicodeString } from "@atproto/api";
export const LEAFLET_CONTENT_TYPE = "pub.leaflet.content" as const;
interface BlobRef { $type: "blob"; ref: { $link: string }; mimeType: string; size: number;}
type JsonObject = Record<string, any>;
function escapeAttribute(value: string): string { return value .replace(/&/g, "&") .replace(/"/g, """) .replace(/</g, "<") .replace(/>/g, ">");}
function featureKey(feature: JsonObject): string { return JSON.stringify( Object.fromEntries(Object.entries(feature).sort(([left], [right]) => left.localeCompare(right))), );}
function featureSetKey(features: JsonObject[]): string { return features.map(featureKey).sort().join("\n");}
function normalizeFacets(unicode: UnicodeString, facets: JsonObject[]): JsonObject[] { const sorted = facets .filter((facet) => Number.isInteger(facet.index?.byteStart) && Number.isInteger(facet.index?.byteEnd) && facet.index.byteStart >= 0 && facet.index.byteEnd >= facet.index.byteStart && facet.index.byteEnd <= unicode.length ) .map((facet) => ({ ...facet, index: { ...facet.index }, features: Array.isArray(facet.features) ? [...facet.features] : [], })) .sort((left, right) => left.index.byteStart - right.index.byteStart || right.index.byteEnd - left.index.byteEnd );
for (let index = 0; index < sorted.length; index++) { const facet = sorted[index]; const selected = unicode.slice(facet.index.byteStart, facet.index.byteEnd); if (selected.trim() !== "") continue;
const previous = sorted[index - 1]; const next = sorted[index + 1]; if (previous?.index.byteEnd !== facet.index.byteStart || next?.index.byteStart !== facet.index.byteEnd) { facet.features = []; continue; }
const nextFeatures = new Set(next.features.map(featureKey)); facet.features = previous.features.filter((feature: JsonObject) => nextFeatures.has(featureKey(feature)) ); }
const merged: JsonObject[] = []; for (const facet of sorted) { if (facet.features.length === 0 || facet.index.byteStart === facet.index.byteEnd) continue; const previous = merged.at(-1); if (previous && previous.index.byteEnd === facet.index.byteStart && featureSetKey(previous.features) === featureSetKey(facet.features)) { previous.index.byteEnd = facet.index.byteEnd; continue; } merged.push(facet); } return merged;}
function applyFacets(text: string, facets?: JsonObject[]): string { if (!facets || facets.length === 0) return text;
const unicode = new UnicodeString(text); const sorted = normalizeFacets(unicode, facets);
const starts = new Map<number, JsonObject[]>(); const ends = new Map<number, JsonObject[]>(); for (const facet of sorted) { const start = facet.index.byteStart; const end = facet.index.byteEnd; starts.set(start, [...(starts.get(start) ?? []), facet]); ends.set(end, [...(ends.get(end) ?? []), facet]); }
const marker = (feature: JsonObject): { open: string; close: string } | undefined => { switch (feature.$type) { case "pub.leaflet.richtext.facet#bold": return { open: "**", close: "**" }; case "pub.leaflet.richtext.facet#italic": return { open: "*", close: "*" }; case "pub.leaflet.richtext.facet#code": return { open: "`", close: "`" }; case "pub.leaflet.richtext.facet#link": return { open: "[", close: `](${feature.uri})` }; case "pub.leaflet.richtext.facet#strikethrough": return { open: "~~", close: "~~" }; default: return undefined; } };
const bytePoints = [...new Set([0, unicode.length, ...starts.keys(), ...ends.keys()])] .filter((point) => point >= 0 && point <= unicode.length) .sort((a, b) => a - b); const parts: string[] = [];
for (let index = 0; index < bytePoints.length - 1; index++) { const start = bytePoints[index]; const end = bytePoints[index + 1];
const closing = [...(ends.get(start) ?? [])] .sort((a, b) => b.index.byteStart - a.index.byteStart); for (const facet of closing) { const markers = (facet.features ?? []).flatMap((feature: JsonObject) => { const value = marker(feature); return value ? [value] : []; }); parts.push(...markers.reverse().map((value: { close: string }) => value.close)); }
const opening = [...(starts.get(start) ?? [])] .sort((a, b) => b.index.byteEnd - a.index.byteEnd); for (const facet of opening) { const markers = (facet.features ?? []).flatMap((feature: JsonObject) => { const value = marker(feature); return value ? [value] : []; }); parts.push(...markers.map((value: { open: string }) => value.open)); }
parts.push(unicode.slice(start, end)); }
const finalPoint = bytePoints.at(-1) ?? unicode.length; const closing = [...(ends.get(finalPoint) ?? [])] .sort((a, b) => b.index.byteStart - a.index.byteStart); for (const facet of closing) { const markers = (facet.features ?? []).flatMap((feature: JsonObject) => { const value = marker(feature); return value ? [value] : []; }); parts.push(...markers.reverse().map((value: { close: string }) => value.close)); }
return parts.join("");}
function stripUnresolvedFootnoteReferences( markdown: string, definitions: ReadonlySet<string>,): string { return markdown.replace( /\[\^([A-Za-z0-9_-]+)\](?!:)/g, (reference, label: string) => definitions.has(label) ? reference : "", );}
function footnoteDefinitions(blocks: JsonObject[]): Set<string> { const definitions = new Set<string>(); const collect = (plaintext: unknown) => { for (const match of String(plaintext ?? "").matchAll( /(?:^|\n)[ \t]{0,3}\[\^([A-Za-z0-9_-]+)\]:/g, )) { definitions.add(match[1]); } };
for (const wrapper of blocks) { const block = wrapper.block as JsonObject | undefined; if (!block || block.$type === "pub.leaflet.blocks.code") continue; collect(block.plaintext); for (const item of block.children ?? []) collect(item.content?.plaintext); } return definitions;}
function richTextMarkdown( plaintext: unknown, facets: JsonObject[] | undefined, definitions: ReadonlySet<string>,): string { return stripUnresolvedFootnoteReferences( applyFacets(String(plaintext ?? ""), facets), definitions, );}
function listItemMarkdown( item: JsonObject, definitions: ReadonlySet<string>,): string { const content = item.content ?? {}; return richTextMarkdown(content.plaintext, content.facets, definitions);}
function imageUrl(did: string, cid: string, mimeType: string): string { const extension = mimeType === "image/png" ? "png" : mimeType === "image/webp" ? "webp" : "jpeg"; return `https://cdn.bsky.app/img/feed_fullsize/plain/${did}/${cid}@${extension}`;}
export function leafletContentToMarkdown(content: unknown, did: string): string { if (!content || typeof content !== "object") { throw new Error("Leaflet content must be an object"); } const value = content as JsonObject; if (value.$type !== LEAFLET_CONTENT_TYPE || !Array.isArray(value.pages)) { throw new Error(`Unsupported content type: ${String(value.$type)}`); } if (value.pages.length !== 1) { throw new Error(`Expected one linear Leaflet page, found ${value.pages.length}`); }
const page = value.pages[0] as JsonObject; if (page.$type !== "pub.leaflet.pages.linearDocument" || !Array.isArray(page.blocks)) { throw new Error(`Unsupported Leaflet page type: ${String(page.$type)}`); }
const lines: string[] = []; let inBlockquote = false; const definitions = footnoteDefinitions(page.blocks as JsonObject[]);
const closeBlockquote = () => { if (inBlockquote) { lines.push(""); inBlockquote = false; } };
for (const wrapper of page.blocks) { const block = (wrapper as JsonObject).block as JsonObject | undefined; if (!block) throw new Error("Leaflet block wrapper has no block"); const type = String(block.$type ?? "");
if (type !== "pub.leaflet.blocks.blockquote") closeBlockquote();
switch (type) { case "pub.leaflet.blocks.text": lines.push(richTextMarkdown(block.plaintext, block.facets, definitions), ""); break; case "pub.leaflet.blocks.header": lines.push( `${"#".repeat(Math.min(6, Math.max(1, Number(block.level) || 2)))} ${richTextMarkdown(block.plaintext, block.facets, definitions)}`, "", ); break; case "pub.leaflet.blocks.code": lines.push(`\`\`\`${String(block.language ?? "")}`, String(block.plaintext ?? ""), "```", ""); break; case "pub.leaflet.blocks.blockquote": if (inBlockquote) lines.push(">"); lines.push( richTextMarkdown(block.plaintext, block.facets, definitions) .split("\n") .map((line) => `> ${line}`) .join("\n"), ); inBlockquote = true; break; case "pub.leaflet.blocks.unorderedList": for (const item of block.children ?? []) { lines.push(`- ${listItemMarkdown(item, definitions)}`); } lines.push(""); break; case "pub.leaflet.blocks.orderedList": { const start = Number.isFinite(block.startIndex) ? Number(block.startIndex) : 1; (block.children ?? []).forEach((item: JsonObject, index: number) => { lines.push(`${start + index}. ${listItemMarkdown(item, definitions)}`); }); lines.push(""); break; } case "pub.leaflet.blocks.horizontalRule": lines.push("---", ""); break; case "pub.leaflet.blocks.image": { const blob = block.image as BlobRef | undefined; const cid = blob?.ref?.$link; if (!blob || !cid) throw new Error("Leaflet image block has no blob reference"); const alt = String(block.alt ?? ""); lines.push(`})`, ""); break; } case "pub.leaflet.blocks.bskyPost": { const uri = String(block.postRef?.uri ?? ""); const cid = String(block.postRef?.cid ?? ""); if (!uri || !cid) throw new Error("Bluesky embed is missing a strong reference"); const [, , postDid, , postRkey] = uri.split("/"); const clientHost = String(block.clientHost ?? "bsky.app"); const url = `https://${clientHost}/profile/${postDid}/post/${postRkey}`; lines.push( `<bsky-embed data-url="${escapeAttribute(url)}" data-uri="${escapeAttribute(uri)}" data-cid="${escapeAttribute(cid)}" data-client-host="${escapeAttribute(clientHost)}"></bsky-embed>`, "", ); break; } case "pub.leaflet.blocks.website": { const preview = block.previewImage as BlobRef | undefined; const previewAttributes = preview ? ` data-preview-cid="${escapeAttribute(preview.ref.$link)}" data-preview-mime="${escapeAttribute(preview.mimeType)}" data-preview-size="${preview.size}"` : ""; lines.push( `<leaflet-card data-href="${escapeAttribute(String(block.src ?? ""))}" data-title="${escapeAttribute(String(block.title ?? ""))}" data-description="${escapeAttribute(String(block.description ?? ""))}"${previewAttributes}></leaflet-card>`, "", ); break; } case "pub.leaflet.blocks.iframe": { const ratio = block.aspectRatio as { width?: number; height?: number } | undefined; lines.push( `<iframe src="${escapeAttribute(String(block.url ?? ""))}" height="${Number(block.height) || 300}"${ratio?.width && ratio?.height ? ` data-aspect-width="${ratio.width}" data-aspect-height="${ratio.height}"` : ""} loading="lazy" allowfullscreen></iframe>`, "", ); break; } default: throw new Error(`Unsupported Leaflet block type: ${type || "<missing>"}`); } }
closeBlockquote(); return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();}