tldraw
Version:
A tiny little drawing editor.
232 lines (231 loc) • 8.14 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var file_exports = {};
__export(file_exports, {
TLDRAW_FILE_EXTENSION: () => TLDRAW_FILE_EXTENSION,
TLDRAW_FILE_MIMETYPE: () => TLDRAW_FILE_MIMETYPE,
isV1File: () => isV1File,
parseAndLoadDocument: () => parseAndLoadDocument,
parseTldrawJsonFile: () => parseTldrawJsonFile,
serializeTldrawJson: () => serializeTldrawJson,
serializeTldrawJsonBlob: () => serializeTldrawJsonBlob
});
module.exports = __toCommonJS(file_exports);
var import_editor = require("@tldraw/editor");
var import_buildFromV1Document = require("../tldr/buildFromV1Document");
const TLDRAW_FILE_MIMETYPE = "application/vnd.tldraw+json";
const TLDRAW_FILE_EXTENSION = ".tldr";
const LATEST_TLDRAW_FILE_FORMAT_VERSION = 1;
const schemaV1 = import_editor.T.object({
schemaVersion: import_editor.T.literal(1),
storeVersion: import_editor.T.positiveInteger,
recordVersions: import_editor.T.dict(
import_editor.T.string,
import_editor.T.object({
version: import_editor.T.positiveInteger,
subTypeVersions: import_editor.T.dict(import_editor.T.string, import_editor.T.positiveInteger).optional(),
subTypeKey: import_editor.T.string.optional()
})
)
});
const schemaV2 = import_editor.T.object({
schemaVersion: import_editor.T.literal(2),
sequences: import_editor.T.dict(import_editor.T.string, import_editor.T.positiveInteger)
});
const tldrawFileValidator = import_editor.T.object({
tldrawFileFormatVersion: import_editor.T.nonZeroInteger,
schema: import_editor.T.numberUnion("schemaVersion", {
1: schemaV1,
2: schemaV2
}),
records: import_editor.T.arrayOf(
import_editor.T.object({
id: import_editor.T.string,
typeName: import_editor.T.string
}).allowUnknownProperties()
)
});
function isV1File(data) {
try {
if (data.document?.version) {
return true;
}
return false;
} catch {
return false;
}
}
function parseTldrawJsonFile({
json,
schema
}) {
let data;
try {
data = tldrawFileValidator.validate(JSON.parse(json));
} catch (e) {
try {
data = JSON.parse(json);
if (isV1File(data)) {
return import_editor.Result.err({ type: "v1File", data });
}
} catch {
}
return import_editor.Result.err({ type: "notATldrawFile", cause: e });
}
if (data.tldrawFileFormatVersion > LATEST_TLDRAW_FILE_FORMAT_VERSION) {
return import_editor.Result.err({
type: "fileFormatVersionTooNew",
version: data.tldrawFileFormatVersion
});
}
let migrationResult;
let storeSnapshot;
try {
const records = pruneUnusedAssets(data.records);
storeSnapshot = Object.fromEntries(records.map((r) => [r.id, r]));
migrationResult = schema.migrateStoreSnapshot({ store: storeSnapshot, schema: data.schema });
} catch (e) {
return import_editor.Result.err({ type: "invalidRecords", cause: e });
}
if (migrationResult.type === "error") {
return import_editor.Result.err({ type: "migrationFailed", reason: migrationResult.reason });
}
try {
return import_editor.Result.ok(
(0, import_editor.createTLStore)({
snapshot: { store: storeSnapshot, schema: data.schema },
schema
})
);
} catch (e) {
return import_editor.Result.err({ type: "invalidRecords", cause: e });
}
}
function pruneUnusedAssets(records) {
const usedAssets = /* @__PURE__ */ new Set();
for (const record of records) {
if (record.typeName === "shape" && "assetId" in record.props && record.props.assetId) {
usedAssets.add(record.props.assetId);
}
}
return records.filter((r) => r.typeName !== "asset" || usedAssets.has(r.id));
}
async function serializeTldrawJson(editor) {
const records = [];
for (const record of editor.store.allRecords()) {
switch (record.typeName) {
case "asset":
if (record.type !== "bookmark" && record.props.src && !record.props.src.startsWith("data:")) {
let assetSrcToSave;
try {
let src = record.props.src;
if (!src.startsWith("http")) {
src = await editor.resolveAssetUrl(record.id, { shouldResolveToOriginal: true }) || "";
}
assetSrcToSave = await import_editor.FileHelpers.blobToDataUrl(await (await (0, import_editor.fetch)(src)).blob());
} catch {
assetSrcToSave = record.props.src;
}
records.push({
...record,
props: {
...record.props,
src: assetSrcToSave
}
});
} else {
records.push(record);
}
break;
default:
records.push(record);
break;
}
}
return JSON.stringify({
tldrawFileFormatVersion: LATEST_TLDRAW_FILE_FORMAT_VERSION,
schema: editor.store.schema.serialize(),
records: pruneUnusedAssets(records)
});
}
async function serializeTldrawJsonBlob(editor) {
return new Blob([await serializeTldrawJson(editor)], { type: TLDRAW_FILE_MIMETYPE });
}
async function parseAndLoadDocument(editor, document, msg, addToast, onV1FileLoad, forceDarkMode) {
const parseFileResult = parseTldrawJsonFile({
schema: editor.store.schema,
json: document
});
if (!parseFileResult.ok) {
let description;
switch (parseFileResult.error.type) {
case "notATldrawFile":
editor.annotateError(parseFileResult.error.cause, {
origin: "file-system.open.parse",
willCrashApp: false,
tags: { parseErrorType: parseFileResult.error.type }
});
reportError(parseFileResult.error.cause);
description = msg("file-system.file-open-error.not-a-tldraw-file");
break;
case "fileFormatVersionTooNew":
description = msg("file-system.file-open-error.file-format-version-too-new");
break;
case "migrationFailed":
if (parseFileResult.error.reason === import_editor.MigrationFailureReason.TargetVersionTooNew) {
description = msg("file-system.file-open-error.file-format-version-too-new");
} else {
description = msg("file-system.file-open-error.generic-corrupted-file");
}
break;
case "invalidRecords":
editor.annotateError(parseFileResult.error.cause, {
origin: "file-system.open.parse",
willCrashApp: false,
tags: { parseErrorType: parseFileResult.error.type }
});
reportError(parseFileResult.error.cause);
description = msg("file-system.file-open-error.generic-corrupted-file");
break;
case "v1File": {
(0, import_buildFromV1Document.buildFromV1Document)(editor, parseFileResult.error.data.document);
onV1FileLoad?.();
return;
}
default:
(0, import_editor.exhaustiveSwitchError)(parseFileResult.error, "type");
}
addToast({
title: msg("file-system.file-open-error.title"),
description,
severity: "error"
});
return;
}
(0, import_editor.transact)(() => {
editor.loadSnapshot(parseFileResult.value.getStoreSnapshot());
editor.clearHistory();
const bounds = editor.getCurrentPageBounds();
if (bounds) {
editor.zoomToBounds(bounds, { targetZoom: 1, immediate: true });
}
});
if (forceDarkMode) editor.user.updateUserPreferences({ colorScheme: "dark" });
}
//# sourceMappingURL=file.js.map