refactoring of cmaps, widgets, etc.

This commit is contained in:
2026-09-02 16:06:26 +02:00
parent 38f255c1a4
commit f0562a06cc
52 changed files with 3626 additions and 2642 deletions
+69
View File
@@ -0,0 +1,69 @@
import { buildBundle } from "./interchange.js";
/** Convert an ArrayBuffer to the base64 representation used in JSON bundles. */
function arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = "";
const chunkSize = 0x8000;
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
}
return btoa(binary);
}
/**
* Export a stored CMap model and its linked resources as a complete JSON bundle.
* CMaps are loaded through their repository. Page loading, bundle construction
* and attachment encoding remain independent of workspace presentation.
*/
export class CmapJsonExporter {
constructor(cmapRepository, loadWikiPage, fetchFile, generator = "Racket Wiki") {
if (!cmapRepository || typeof cmapRepository.load !== "function" ||
typeof loadWikiPage !== "function" || typeof fetchFile !== "function") {
throw new TypeError("A CMap repository, wiki-page loader and file loader are required");
}
this.cmapRepository = cmapRepository;
this.loadWikiPage = loadWikiPage;
this.fetchFile = fetchFile;
this.generator = generator;
}
/**
* Build a validated JSON bundle without changing the source CMaps.
* Linked CMaps are followed up to maxDepth; linked pages and attachments
* are included by the interchange format.
*/
async export(rootMap, maxDepth = 0) {
return buildBundle({
rootMap: this.bundleMap(rootMap),
maxDepth,
generator: this.generator,
loadConceptMap: async (slug) => this.bundleMap(await this.cmapRepository.load(slug)),
loadWikiPage: this.loadWikiPage,
loadAttachment: (url) => this.loadAttachment(url)
});
}
/** Present one stored model through the public interchange record shape. */
bundleMap(storedMap) {
if (!storedMap?.slug || typeof storedMap.toDocument !== "function") {
throw new TypeError("JSON export requires a stored CMap");
}
return {
slug: storedMap.slug,
title: storedMap.title,
document: storedMap.toDocument()
};
}
/** Load and encode one attachment referenced by an exported wiki page. */
async loadAttachment(url) {
const response = await this.fetchFile(url);
if (!response.ok) throw new Error(`Attachment could not be exported: ${url}`);
const content = await response.arrayBuffer();
return {
mimeType: response.headers.get("content-type") || "application/octet-stream",
contentBase64: arrayBufferToBase64(content)
};
}
}