@openpresentation/opf-editor 0.25.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/index.d.ts +8 -0
- package/dist/index.js +17 -0
- package/dist/persistence.js +2 -2
- package/dist/source-pane.d.ts +19 -1
- package/dist/source-pane.js +77 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,8 @@ The release after 0.14.2 moves the editor onto core 0.15 (a breaking, clean spec
|
|
|
40
40
|
- **Save and export embed.** `prepareSave`/`saveDocument`, `serializeOpfTransfer` (presentation and slide scope) and `convert` (`/export`) call core's `embed`; the playground's OPF download and PPTX export do too.
|
|
41
41
|
- **Paste** (`prepareOpfImport`, insert mode) uses core's `copySlides`: groups match by `source`, identical records are reused, and the result's `renamed` (`custom-conflict`, `catalog-revision`, `group-name`) and `addedGroups` tell the user what changed.
|
|
42
42
|
- **Update from catalog** is an explicit action: `checkCatalogUpdates(presentation, { catalogs })` lists the embedded records whose catalog record differs, and `applyCatalogUpdate(editor, { refs })` applies only the approved ones as one undoable step. The design panel's `catalog` section runs both and lists the `opf/unresolved-reference` and `opf/undeclared-catalog` findings (`referenceFindings(editor.validation)`).
|
|
43
|
-
- **Editing a catalog's record forks it.** An edit of a layout, theme, colour scheme or font scheme embedded under `catalogs.default` or a named group (from a panel, Properties, Source or any `applyPatch`) moves it into `catalogs.custom` as `<id>-custom` (or the id in `meta.forkIds["<group>:<kind>:<id>"]`), rewrites every reference to it and drops the original, in the same undo step. The change and the session event carry `forked` and a `notice` ("… is now this presentation's own; it no longer receives catalog updates"); approved catalog updates and `moveToCustom` change records in place (`meta.catalogRecordEdit: "in-place"`). `moveToCustom(editor, { group, kind, id }, { id })` is the explicit form, offered for `opf/catalog-record-not-in-source`.
|
|
43
|
+
- **Editing a catalog's record forks it.** An edit of a layout, theme, colour scheme or font scheme embedded under `catalogs.default` or a named group (from a panel, Properties, Source or any `applyPatch`) moves it into `catalogs.custom` as `<id>-custom` (or the id in `meta.forkIds["<group>:<kind>:<id>"]`), rewrites every reference to it and drops the original, in the same undo step. The change and the session event carry `forked` and a `notice` ("… is now this presentation's own; it no longer receives catalog updates"); approved catalog updates and `moveToCustom` change records in place (`meta.catalogRecordEdit: "in-place"`). `moveToCustom(editor, { group, kind, id }, { id })` is the explicit form, offered for `opf/catalog-record-not-in-source`. Opening a document is not an edit: use `replaceDocument` (next bullet).
|
|
44
|
+
- **Opening a document: `replaceDocument`.** `editor.replaceDocument(input, meta?)` puts exactly `input` (a parsed document, JSON text or bytes) in the session as ONE undoable change: Undo restores the previous document, Redo the new one, and nothing under `catalogs.default` or a named group is forked into `catalogs.custom`, even when the two documents embed the same record ids with different content (two examples published at different times). Use it for a host's example menu, File > Open, "start over" and restoring a stored copy; keep `applyPatch` for edits, because an `applyPatch` that replaces the root IS read as an edit of the records the documents share and forks them. It validates like any edit (`meta.rejectInvalid` or the session option refuses an invalid document and changes nothing), accepts the usual `meta` (`source` defaults to `"replace-document"`; the `patch` event carries `meta.replaceDocument: true`) and returns the change; it always records one undo step, also for an equal document. The editor's own File > Open (the playground's Import dialog, "Open as a presentation"), the gallery handoff and `createPersistence`'s `restore()` use it.
|
|
44
45
|
- **Backgrounds** take the flat image form `{ type: "image", src, alt, fit, focus, opacity, recolor, overlay }` (fits `cover`, `contain`, `stretch`, `tile`) and the image-source shorthand, on the deck and on a slide. **Image blocks** carry their own fit, focus, treatments and placement (`/image-options`, the panel's `image` section): a full-bleed photo is a background, a picture beside the content is a placed image block. `design.imageFit` is the default fit of image blocks.
|
|
45
46
|
|
|
46
47
|
Editor 0.15.0 uses core `^0.15.1` and the optional renderer peer `^0.15.0`. Install renderer and PPTX 0.15 together for the coordinated canvas and export workflow. The shared APIs are core's `validate`, `Finding`, `resolveSlideContext` and the catalog helpers above, and the renderer's `loadFonts()` handle passed as `fonts` to the canvas, export, composition and pagination. Historical version changes are recorded in `CHANGELOG.md`.
|
|
@@ -109,8 +110,9 @@ const pane = createSourcePane(container, {
|
|
|
109
110
|
pane.setFormat('yaml'); pane.focusPath('/slides/1/title'); pane.getText(); pane.destroy();
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
Methods: `setFindings(list | null)`, `setFormat(format)`, `focusPath(pointer)` (moves the cursor to the line of a JSON Pointer in any format; exact for JSON, best effort for YAML and Markdown), `getText()`, `refresh()`, `setName`, `setDensity`, `setReadOnly` and `destroy()`. A finding is `{ path, message, severity, line? }` (core `Finding` objects fit). Rows show the path in mono and the line at the right; a click moves the cursor and calls `onFindingSelect`.
|
|
113
|
+
Methods: `replaceDocument(document, { source?, name?, meta? })` (below), `setFindings(list | null)`, `setFormat(format)`, `focusPath(pointer)` (moves the cursor to the line of a JSON Pointer in any format; exact for JSON, best effort for YAML and Markdown), `getText()`, `refresh()`, `setName`, `setDensity`, `setReadOnly` and `destroy()`. A finding is `{ path, message, severity, line? }` (core `Finding` objects fit). Rows show the path in mono and the line at the right; a click moves the cursor and calls `onFindingSelect`.
|
|
113
114
|
|
|
115
|
+
- **Opening a document: `pane.replaceDocument(document, { source, name })`.** Loading an example or a file into the pane (never by writing its text into the code editor, which the pane applies as a typed edit and forks the catalog records the two documents share) calls `editor.replaceDocument`: one undoable change, no catalog record forked. `document` is a parsed document or JSON text; `source` is the JSON text it was read from (shown byte for byte; `document` itself when that is text) and `name` the file name. In YAML or Markdown the pane shows the document written in that format. Undo and Redo bring back each document's text exactly in the format shown (the opened JSON as written, the replaced document's text as it was, including YAML comments), so one Undo returns to the document and the text the visitor had. The pane keeps the last 8 such texts. A refused document throws (`invalid-opf-edit`, with `issues`) and changes nothing, and works in a read-only pane. A diff typed or pasted into the pane is still an edit, and editing a catalog record in it forks the record.
|
|
114
116
|
- **JSON is canonical** and keeps the author's bytes: a YAML or Markdown edit rewrites only the changed JSON tokens (`exact-source`), and Undo restores the exact spelling.
|
|
115
117
|
- **YAML and Markdown** are core's browser-safe converters, `@openpresentation/opf/yaml` and `/markdown`. Switching format writes the session's document in the new format. An edit parses back to the document and reaches the session as one `replace` patch, after a short delay (`applyDelay`, default 250 ms) so one burst of typing is one undo step. A syntax error, or an OPF rule the session refuses, is listed with its line and never reaches the document; Undo, Redo and outside edits replace a draft that did not parse.
|
|
116
118
|
- **Nothing is lost silently.** Markdown writes what its dialect has no syntax for (a `design`, region keys, nested groups) as YAML in `opf-slide` and `opf-block` fences; each such part is a warning finding with its JSON Pointer and reason. Comments in YAML cannot survive a rewrite from the document: Format and outside edits drop them and say how many. A document that is not valid OPF cannot be written as YAML or Markdown; the pane stays in JSON and says so.
|
|
@@ -951,7 +953,7 @@ input.onchange = async () => {
|
|
|
951
953
|
- **Findings.** The conversion's findings for YAML and Markdown, in core's `Finding` shape: `path` (a JSON Pointer such as `/slides/1/bogus`), `message`, `severity` and `location` (line and column in the file). A deck that reads but has OPF errors is returned with them, so a host can show them; JSON gives none (the editor session validates it).
|
|
952
954
|
- **Errors.** Text that cannot be read at all throws `DeckFileError` with a `code` and a clear message: `unreadable`, `file-too-large` (over 20 MB), `empty-file`, `unsupported-file` (a .pptx or other binary file), `invalid-json`, `invalid-yaml`, `invalid-markdown` (with the syntax `findings` and the line) and `not-a-deck` (JSON without a `slides` list, such as one slide or a block, which a host can send to its paste path as the playground does).
|
|
953
955
|
|
|
954
|
-
The source pane has no file-opening path of its own, so it keeps no second copy of this. The playground's Import dialog uses `readDeckFile` and `DECK_FILE_ACCEPT` for its file tab and for dropped files (`examples/transfer-controls.js`). Icons in the presenter button are inlined [Lucide](https://lucide.dev) paths (ISC license).
|
|
956
|
+
Put the opened document in the session with `editor.replaceDocument(document)` (or `pane.replaceDocument(document, { source })` when a source pane is mounted), not `applyPatch`: opening is one undoable change that forks no catalog record. The source pane has no file-opening path of its own, so it keeps no second copy of this. The playground's Import dialog uses `readDeckFile` and `DECK_FILE_ACCEPT` for its file tab and for dropped files (`examples/transfer-controls.js`). Icons in the presenter button are inlined [Lucide](https://lucide.dev) paths (ISC license).
|
|
955
957
|
|
|
956
958
|
## Optional React Bindings
|
|
957
959
|
|
package/dist/index.d.ts
CHANGED
|
@@ -91,6 +91,14 @@ export interface EditorSession {
|
|
|
91
91
|
setGroupComposition(path: string, composition: Composition, meta?: Record<string, unknown>): EditorChange;
|
|
92
92
|
setCatalog(path: string | string[], catalogKind: string, id: string, meta?: Record<string, unknown>): EditorChange;
|
|
93
93
|
applyPatch(operations: JsonPatchOperation[], meta?: Record<string, unknown>): EditorChange;
|
|
94
|
+
/**
|
|
95
|
+
* Open a different document in the session: the whole document is replaced as one undoable change, and the replacement is never
|
|
96
|
+
* read as an edit of the catalog records the two documents share, so nothing under `catalogs.default` or a named group is forked
|
|
97
|
+
* into `catalogs.custom`. `input` is a parsed document, JSON text or bytes. It is validated like any edit (`meta.rejectInvalid` or
|
|
98
|
+
* the session option refuses an invalid document and changes nothing). It always records its one change, also for an equal document.
|
|
99
|
+
* `meta.source` defaults to `"replace-document"`; the `patch` event carries `meta.replaceDocument: true`.
|
|
100
|
+
*/
|
|
101
|
+
replaceDocument(input: unknown, meta?: Record<string, unknown>): EditorChange;
|
|
94
102
|
/** The undo and redo stacks as plain data (oldest entry first), for hosts that persist work. */
|
|
95
103
|
exportHistory(): EditorHistory;
|
|
96
104
|
/** Replace the document and, optionally, the history in one step (one `restore` event). A history that does not replay against the document throws `invalid-history` before anything changes. */
|
package/dist/index.js
CHANGED
|
@@ -314,6 +314,23 @@ export function createEditorSession(input, options = {}) {
|
|
|
314
314
|
return setCatalogId(editor, path, catalogKind, id, meta);
|
|
315
315
|
},
|
|
316
316
|
applyPatch: commitPatch,
|
|
317
|
+
/**
|
|
318
|
+
* Open a different document in the session (a host's example menu, File > Open, "start over"): the whole document is replaced as ONE
|
|
319
|
+
* undoable change (Undo restores the previous document, Redo the new one). Unlike an `applyPatch` that replaces the root, this is
|
|
320
|
+
* never read as the user editing the catalog records the two documents share: records under `catalogs.default` or a named group
|
|
321
|
+
* keep the ids and content the new document carries, and nothing is forked into `catalogs.custom`. `input` is a parsed document,
|
|
322
|
+
* JSON text or bytes. The document is validated like any edit (`meta.rejectInvalid` or the session option refuses an invalid one
|
|
323
|
+
* and changes nothing). It always records its one change, also when the new document equals the open one (Undo then returns to the
|
|
324
|
+
* same document, as for any other replace). `meta.source` defaults to `"replace-document"`; the event is a `patch` whose `meta.replaceDocument` is true.
|
|
325
|
+
*/
|
|
326
|
+
replaceDocument(input, meta = {}) {
|
|
327
|
+
return commitPatch([{ op: "replace", path: "", value: parseInput(input) }], {
|
|
328
|
+
...meta,
|
|
329
|
+
source: meta.source ?? "replace-document",
|
|
330
|
+
replaceDocument: true,
|
|
331
|
+
catalogRecordEdit: "in-place"
|
|
332
|
+
});
|
|
333
|
+
},
|
|
317
334
|
/**
|
|
318
335
|
* The undo and redo stacks as plain data (oldest entry first): `{ undo: [{ patches, inversePatches, meta }], redo: [...] }`.
|
|
319
336
|
* Together with `presentation` this is everything `restoreState` needs to bring a session back, for hosts that persist work.
|
package/dist/persistence.js
CHANGED
|
@@ -268,7 +268,7 @@ export function createPersistence(editor, options = {}) {
|
|
|
268
268
|
}
|
|
269
269
|
} else {
|
|
270
270
|
// The user already worked in this session: bring the copy back as one undoable change instead of replacing their history.
|
|
271
|
-
editor.
|
|
271
|
+
editor.replaceDocument(structuredClone(record.presentation), { source: "restore" });
|
|
272
272
|
}
|
|
273
273
|
touched = true;
|
|
274
274
|
baseline = record.dirty === false ? jsonOf(editor.presentation) : null;
|
|
@@ -315,7 +315,7 @@ export function createPersistence(editor, options = {}) {
|
|
|
315
315
|
let record;
|
|
316
316
|
try { record = await adapter.get(earlierKey); } catch { return false; }
|
|
317
317
|
if (!validRecord(record)) return false;
|
|
318
|
-
try { editor.
|
|
318
|
+
try { editor.replaceDocument(structuredClone(record.presentation), { source: "restore" }); }
|
|
319
319
|
catch { return false; }
|
|
320
320
|
pending = undefined;
|
|
321
321
|
touched = true;
|
package/dist/source-pane.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EditorSession } from "./index.js";
|
|
1
|
+
import type { EditorChange, EditorSession } from "./index.js";
|
|
2
2
|
import type { JsonCatalogContext } from "./json-options.js";
|
|
3
3
|
import type { JsonCodeEditor } from "./json-editor.js";
|
|
4
4
|
|
|
@@ -64,8 +64,26 @@ export interface SourcePaneOptions {
|
|
|
64
64
|
onError?(error: unknown): void;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
export interface ReplaceDocumentOptions {
|
|
68
|
+
/** The JSON text the document was read from. Used when it parses to the document, so its spelling is kept. */
|
|
69
|
+
source?: string;
|
|
70
|
+
/** The file name without extension, as `setName` (the document's own name is not used). */
|
|
71
|
+
name?: string;
|
|
72
|
+
/** Extra `meta` for the session change (`source` defaults to `"replace-document"`). */
|
|
73
|
+
meta?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
export interface SourcePane {
|
|
68
77
|
element: HTMLElement;
|
|
78
|
+
/**
|
|
79
|
+
* Open a different document in the session and the pane (a host's example menu, File > Open): `editor.replaceDocument`, so it is one
|
|
80
|
+
* undoable change and no catalog record is forked into `catalogs.custom` (a diff typed or pasted into the pane is an edit; this is not).
|
|
81
|
+
* `document` is a parsed document or JSON text. The pane shows `options.source` (the JSON text the document was read from, kept byte for
|
|
82
|
+
* byte; `document` itself when it is text) in JSON and the document written in YAML or Markdown. Undo and Redo bring back each
|
|
83
|
+
* document's text exactly, in the format that is shown. A document the session refuses throws (`invalid-opf-edit` with `issues`) and
|
|
84
|
+
* changes nothing. Works in a read-only pane.
|
|
85
|
+
*/
|
|
86
|
+
replaceDocument(document: unknown, options?: ReplaceDocumentOptions): EditorChange;
|
|
69
87
|
/** Re-read the document from the session. */
|
|
70
88
|
refresh(): void;
|
|
71
89
|
/** Replace the host findings; `null` returns to the session's own validation. */
|
package/dist/source-pane.js
CHANGED
|
@@ -2,7 +2,7 @@ import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
|
|
|
2
2
|
import { fromYaml, scanYamlComments, toYaml } from "@openpresentation/opf/yaml";
|
|
3
3
|
import { fromMarkdown, toMarkdown } from "@openpresentation/opf/markdown";
|
|
4
4
|
import { mountJsonCodeEditor } from "./json-editor.js";
|
|
5
|
-
import { createSourceMemory, findDuplicateKey, updateJsonSource } from "./exact-source.js";
|
|
5
|
+
import { MAX_EXACT_SOURCE_LENGTH, createSourceMemory, findDuplicateKey, updateJsonSource } from "./exact-source.js";
|
|
6
6
|
import { lineForPath, lineOfOffset, offsetOfLine } from "./source-lines.js";
|
|
7
7
|
import { errorText, normalizeFinding, rejectionFindings as findingsOfError } from "./source-findings.js";
|
|
8
8
|
|
|
@@ -127,6 +127,17 @@ export function createSourcePane(container, options = {}) {
|
|
|
127
127
|
// Whether the findings include an error, and what the editor's `aria-invalid` currently says (null: not yet set).
|
|
128
128
|
let invalid = false, shownInvalid = null;
|
|
129
129
|
const lineCache = { key: "", values: new Map() };
|
|
130
|
+
// The exact text of the documents `replaceDocument` replaced and opened, per format, so Undo and Redo show it again byte for byte
|
|
131
|
+
// (a YAML comment, the author's JSON spelling). Keyed by format and document; the oldest entries go first.
|
|
132
|
+
const exactTexts = new Map();
|
|
133
|
+
const rememberText = (target, value, text) => {
|
|
134
|
+
if (typeof text !== "string" || text.length > MAX_EXACT_SOURCE_LENGTH) return;
|
|
135
|
+
const key = `${target}|${JSON.stringify(value)}`;
|
|
136
|
+
exactTexts.delete(key);
|
|
137
|
+
exactTexts.set(key, text);
|
|
138
|
+
while (exactTexts.size > 8) exactTexts.delete(exactTexts.keys().next().value);
|
|
139
|
+
};
|
|
140
|
+
const exactText = (target, value) => exactTexts.get(`${target}|${JSON.stringify(value)}`);
|
|
130
141
|
|
|
131
142
|
// ---- DOM ----------------------------------------------------------------------------------------------------------
|
|
132
143
|
const root = el("div", "opf-source opf-ui", { "data-density": density });
|
|
@@ -315,6 +326,12 @@ export function createSourcePane(container, options = {}) {
|
|
|
315
326
|
const before = control.api.getValue(), comments = scanYamlComments(before).count;
|
|
316
327
|
if (comments > 0 && written.text !== before) transientNotes = [normalizeFinding({ message: `${plural(comments, "comment")} in the YAML ${comments === 1 ? "was" : "were"} dropped when the document changed outside the source.`, severity: "warning" }, "format")];
|
|
317
328
|
}
|
|
329
|
+
// A document `replaceDocument` opened or replaced comes back with the exact text it had.
|
|
330
|
+
const exact = exactText(format, deck());
|
|
331
|
+
if (exact !== undefined && exact !== written.text) {
|
|
332
|
+
try { if (same(readText(format, exact, catalogs()).deck, deck())) { written = { ...written, text: exact }; if (format === "json") jsonText = exact; } }
|
|
333
|
+
catch { /* The remembered text no longer reads as this document: use the generated one. */ }
|
|
334
|
+
}
|
|
318
335
|
noteFindings = written.notes;
|
|
319
336
|
if (format !== "json") syncJsonSafe();
|
|
320
337
|
if (written.text !== control.api.getValue()) show(written.text);
|
|
@@ -332,6 +349,60 @@ export function createSourcePane(container, options = {}) {
|
|
|
332
349
|
fromSession();
|
|
333
350
|
});
|
|
334
351
|
|
|
352
|
+
// ---- Replacing the whole document ----------------------------------------------------------------------------------
|
|
353
|
+
/**
|
|
354
|
+
* Open `input` (a document, or JSON text) as the whole document with `editor.replaceDocument`: one undoable change, no catalog record
|
|
355
|
+
* forked. The text shown is `options.source` (or `input` when it is text) in JSON, the document written in YAML or Markdown, and Undo
|
|
356
|
+
* and Redo bring each document's text back exactly.
|
|
357
|
+
*/
|
|
358
|
+
function replaceDocument(input, replaceOptions = {}) {
|
|
359
|
+
if (destroyed) throw new Error("The source pane was destroyed.");
|
|
360
|
+
flush();
|
|
361
|
+
const before = deck();
|
|
362
|
+
const beforeText = dirty ? undefined : control.api.getValue();
|
|
363
|
+
const authored = typeof input === "string" ? input : replaceOptions.source;
|
|
364
|
+
transientNotes = [];
|
|
365
|
+
let change;
|
|
366
|
+
applying = true;
|
|
367
|
+
try {
|
|
368
|
+
// Remember the spelling of the document being replaced, then open the new one.
|
|
369
|
+
syncJson(before);
|
|
370
|
+
change = editor.replaceDocument(input, { format, ...replaceOptions.meta });
|
|
371
|
+
} catch (error) {
|
|
372
|
+
rejectionFindings = error?.issues?.length ? findingsOfError(error) : [normalizeFinding({ message: errorText(error), kind: "rejected" })];
|
|
373
|
+
renderFindings();
|
|
374
|
+
throw error;
|
|
375
|
+
} finally { applying = false; }
|
|
376
|
+
clearTimeout(timer);
|
|
377
|
+
timer = null;
|
|
378
|
+
for (const key of Object.keys(drafts)) delete drafts[key];
|
|
379
|
+
syntaxFindings = [];
|
|
380
|
+
rejectionFindings = [];
|
|
381
|
+
dirty = false;
|
|
382
|
+
version++;
|
|
383
|
+
const opened = deck();
|
|
384
|
+
let written;
|
|
385
|
+
let authoredJson = null;
|
|
386
|
+
if (typeof authored === "string") { try { if (same(JSON.parse(authored), opened) && !findDuplicateKey(authored)) authoredJson = authored; } catch { /* Not the document's JSON: write it. */ } }
|
|
387
|
+
if (replaceOptions.name !== undefined) { name = sanitizeName(replaceOptions.name) || "deck"; updateBar(); }
|
|
388
|
+
try { written = format === "json" && authoredJson !== null ? { text: authoredJson, notes: [] } : write(format, opened); }
|
|
389
|
+
catch (error) {
|
|
390
|
+
syncJsonSafe();
|
|
391
|
+
rejectionFindings = [normalizeFinding({ message: `The document cannot be written as ${formatOf(format).label}: ${errorText(error)}. The text below is the last version that could be.`, kind: "rejected" })];
|
|
392
|
+
renderFindings();
|
|
393
|
+
return change;
|
|
394
|
+
}
|
|
395
|
+
if (authoredJson !== null) jsonText = authoredJson; else syncJsonSafe();
|
|
396
|
+
rememberText(format, opened, written.text);
|
|
397
|
+
rememberText(format, before, beforeText);
|
|
398
|
+
noteFindings = written.notes;
|
|
399
|
+
if (written.text !== control.api.getValue()) show(written.text); else baseline = written.text;
|
|
400
|
+
renderedKey = "";
|
|
401
|
+
renderFindings();
|
|
402
|
+
syncAttributes();
|
|
403
|
+
return change;
|
|
404
|
+
}
|
|
405
|
+
|
|
335
406
|
// ---- Format -------------------------------------------------------------------------------------------------------
|
|
336
407
|
function setFormatInternal(next, notify) {
|
|
337
408
|
const target = formatOf(next);
|
|
@@ -627,6 +698,11 @@ export function createSourcePane(container, options = {}) {
|
|
|
627
698
|
|
|
628
699
|
return {
|
|
629
700
|
element: root,
|
|
701
|
+
/**
|
|
702
|
+
* Open a different document: `editor.replaceDocument` as one undoable change that forks no catalog record, with the text shown in
|
|
703
|
+
* the current format. `options.source` is the JSON text the document was read from (kept byte for byte); `options.name` renames the file.
|
|
704
|
+
*/
|
|
705
|
+
replaceDocument,
|
|
630
706
|
/** Re-read the document from the session (for example after the host changed it without events). */
|
|
631
707
|
refresh() { version++; fromSession(false); },
|
|
632
708
|
/** Replace the host findings; `setFindings(null)` returns to the session's own validation. */
|