Plite owns canonical editor JSON, typed value persistence, schema admission,
ContentSlice, and browser DataTransferFormat negotiation. External syntax
such as HTML, Markdown, or DOCX belongs to a framework or application adapter
that knows the document schema.
const document = editor.read.value();
const json = JSON.stringify(document);
editor.read.value() includes primary children, named roots, and persistent
metadata. Saving only editor.read.children() drops roots and metadata.
Load untrusted JSON through the application's schema and migration boundary before publishing it. Persistence must reject malformed, unknown-version, or future-version data without mutating the current editor.
EditorValuePersistence<TValue, TEncoded> maps one live value to its current
canonical JSON representation and owns the stored envelope version.
import type { EditorValuePersistence } from "plitejs";
type Preferences = Readonly<{ compact: boolean }>;
type PreferencesJson = Readonly<{ compact: boolean }>;
const preferencesPersistence: EditorValuePersistence<
Preferences,
PreferencesJson
> = {
decode(value) {
if (
typeof value !== "object" ||
value === null ||
typeof (value as PreferencesJson).compact !== "boolean"
) {
throw new Error("Invalid preferences");
}
return { compact: (value as PreferencesJson).compact };
},
encode(value) {
return { compact: value.compact };
},
legacyDecoders: {
1: (value) => decodeLegacyPreferences(value),
},
version: 2,
};import type { EditorValuePersistence } from "plitejs";
type Preferences = Readonly<{ compact: boolean }>;
type PreferencesJson = Readonly<{ compact: boolean }>;
const preferencesPersistence: EditorValuePersistence<
Preferences,
PreferencesJson
> = {
decode(value) {
if (
typeof value !== "object" ||
value === null ||
typeof (value as PreferencesJson).compact !==
encode must return canonical EditorJsonValue: finite numbers, strings,
booleans, null, arrays, and plain objects. Plite rejects undefined, bigint,
symbols, functions, non-finite numbers, sparse arrays, accessors, cycles, and
exotic prototypes at owned persistence boundaries.
Legacy entries are decode-only. After decoding one, encode the live value with the current declaration to produce canonical current JSON. Never substitute current defaults for an unknown or malformed version.
State fields and effect descriptors use persist for this contract. A state
field without persist is runtime-only.
Plite does not infer HTML or Markdown from arbitrary custom nodes. A format owner must:
A complete file parse produces EditorDocumentValue. An insertion parser
produces ContentSlice. Direct syntax slices should be closed and rootless;
only a native fragment transport can grant authority for open edges or detached
roots.
const result = parseExternalSlice(source);
if (result.ok) editor.update.slice.replace(result.slice);const result = parseExternalSlice(source);
if (result.ok) editor.update.slice.replace(result.slice);The insertion owner fits the slice after the destination parent, root, and selection exist. A detached parser should not invent that context.
Use DataTransferFormat only for browser MIME representations. Register one or
more formats through dataTransferFormats(owner, formats) from plitejs/dom.
import { dataTransferFormats } from "plitejs/dom";
const ProductTransferPlugin = dataTransferFormats("product", [
{
key: "product-json",
mimeType: "application/x-product+json",
decode: ({ data }) => decodeProductSlice(data),
encode: ({ slice }) => encodeProductSlice(slice),
},
]);import { dataTransferFormats } from "plitejs/dom";
const ProductTransferPlugin = dataTransferFormats("product", [
{
key: "product-json",
mimeType: "application/x-product+json",
decode: ({ data }) => decodeProductSlice(data),
encode: ({ slice }) => encodeProductSlice(slice),
},
]);decode returns a ContentSlice and encode returns a string. accept
returning false, or decode or encode returning null, lets the next
format try. The DOM adapter fits a decoded slice at the actual insertion
destination; a slice that does not fit also falls through to the next format.
See Clipboard And Paste for the complete negotiation and insertion pipeline.