@brianmsm/quarto-bento 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +86 -2
- package/bin/qbento-npm.mjs +62 -0
- package/distribution/LICENSE +21 -0
- package/distribution/PACKAGING.md +59 -0
- package/distribution/bin/qbento +5 -0
- package/distribution/bin/qbento.cmd +4 -0
- package/distribution/manifest.json +720 -0
- package/distribution/runtime/LICENSE +21 -0
- package/distribution/runtime/_extensions/qbento/_extension.yml +24 -0
- package/distribution/runtime/_extensions/qbento/capture.lua +21 -0
- package/distribution/runtime/_extensions/qbento/guard.lua +7 -0
- package/distribution/runtime/_extensions/qbento/post.ts +2 -0
- package/distribution/runtime/_extensions/qbento/pre.ts +2 -0
- package/distribution/runtime/_extensions/qbento/producer-activation.js +129 -0
- package/distribution/runtime/src/bridge/browser.js +168 -0
- package/distribution/runtime/src/bridge/isolation.ts +55 -0
- package/distribution/runtime/src/browser/chromium.ts +218 -0
- package/distribution/runtime/src/cli.ts +219 -0
- package/distribution/runtime/src/core/agent.ts +565 -0
- package/distribution/runtime/src/core/annotations.ts +384 -0
- package/distribution/runtime/src/core/capture.ts +203 -0
- package/distribution/runtime/src/core/component-actions.ts +550 -0
- package/distribution/runtime/src/core/component-frames.ts +59 -0
- package/distribution/runtime/src/core/component-sync.ts +290 -0
- package/distribution/runtime/src/core/design-diagnostics.ts +78 -0
- package/distribution/runtime/src/core/design-registry.ts +287 -0
- package/distribution/runtime/src/core/graph-relations.ts +354 -0
- package/distribution/runtime/src/core/graph-scope.ts +63 -0
- package/distribution/runtime/src/core/graph-sync.ts +228 -0
- package/distribution/runtime/src/core/io.ts +27 -0
- package/distribution/runtime/src/core/kpi-appearance.ts +205 -0
- package/distribution/runtime/src/core/layouts.ts +245 -0
- package/distribution/runtime/src/core/project.ts +759 -0
- package/distribution/runtime/src/core/reconcile.ts +384 -0
- package/distribution/runtime/src/core/recovery.ts +1255 -0
- package/distribution/runtime/src/core/slide-actions.ts +158 -0
- package/distribution/runtime/src/core/source-scope.ts +50 -0
- package/distribution/runtime/src/core/sync.ts +2423 -0
- package/distribution/runtime/src/core/widgets.ts +300 -0
- package/distribution/runtime/src/integration/install.ts +98 -0
- package/distribution/runtime/src/integration/invocation.ts +100 -0
- package/distribution/runtime/src/integration/quarto.ts +457 -0
- package/distribution/runtime/src/render/assets.ts +66 -0
- package/distribution/runtime/src/render/capture.lua +8 -0
- package/distribution/runtime/src/render/chart.ts +165 -0
- package/distribution/runtime/src/render/components.ts +199 -0
- package/distribution/runtime/src/render/design.ts +820 -0
- package/distribution/runtime/src/render/diagnostics.ts +36 -0
- package/distribution/runtime/src/render/graphs.ts +747 -0
- package/distribution/runtime/src/render/html.ts +82 -0
- package/distribution/runtime/src/render/images.ts +268 -0
- package/distribution/runtime/src/render/inline.ts +259 -0
- package/distribution/runtime/src/render/kpi.ts +385 -0
- package/distribution/runtime/src/render/lists.ts +183 -0
- package/distribution/runtime/src/render/math.ts +413 -0
- package/distribution/runtime/src/render/measure.ts +185 -0
- package/distribution/runtime/src/render/native-layout.ts +174 -0
- package/distribution/runtime/src/render/producer-capture.ts +319 -0
- package/distribution/runtime/src/render/producer-environment.R +49 -0
- package/distribution/runtime/src/render/producer-environment.ts +96 -0
- package/distribution/runtime/src/render/producer-profile.R +91 -0
- package/distribution/runtime/src/render/producer-site-profile.R +18 -0
- package/distribution/runtime/src/render/project.ts +1806 -0
- package/distribution/runtime/src/render/quarto.ts +127 -0
- package/distribution/runtime/src/render/registry.ts +233 -0
- package/distribution/runtime/src/render/resources.ts +292 -0
- package/distribution/runtime/src/render/shell.ts +110 -0
- package/distribution/runtime/src/render/source-inline.ts +169 -0
- package/distribution/runtime/src/render/table.ts +220 -0
- package/distribution/runtime/src/render/text-overflow.ts +208 -0
- package/distribution/runtime/src/render/types.ts +71 -0
- package/distribution/runtime/src/render/validate.ts +572 -0
- package/distribution/runtime/src/rich/project.ts +117 -0
- package/distribution/runtime/src/rich/table.ts +374 -0
- package/distribution/runtime/src/source/annotations.ts +490 -0
- package/distribution/runtime/src/source/attributes.ts +415 -0
- package/distribution/runtime/src/source/chart.ts +165 -0
- package/distribution/runtime/src/source/code-language.ts +277 -0
- package/distribution/runtime/src/source/component-insertion.ts +160 -0
- package/distribution/runtime/src/source/deletion.ts +278 -0
- package/distribution/runtime/src/source/graphs.ts +517 -0
- package/distribution/runtime/src/source/identities.ts +256 -0
- package/distribution/runtime/src/source/identity-id.ts +6 -0
- package/distribution/runtime/src/source/image.ts +50 -0
- package/distribution/runtime/src/source/kpi.ts +196 -0
- package/distribution/runtime/src/source/mod.ts +876 -0
- package/distribution/runtime/src/source/notes.ts +308 -0
- package/distribution/runtime/src/source/structure.ts +647 -0
- package/distribution/runtime/src/source/table.ts +290 -0
- package/distribution/runtime/src/source/text-conversion.ts +146 -0
- package/distribution/runtime/src/state/components.ts +281 -0
- package/distribution/runtime/src/state/graphs.ts +200 -0
- package/distribution/runtime/src/state/json.ts +162 -0
- package/distribution/runtime/src/state/merge.ts +1034 -0
- package/distribution/runtime/src/state/mod.ts +5 -0
- package/distribution/runtime/src/state/store.ts +226 -0
- package/distribution/runtime/src/state/types.ts +151 -0
- package/distribution/runtime/src/studio/client.js +790 -0
- package/distribution/runtime/src/studio/components.js +188 -0
- package/distribution/runtime/src/studio/index.html +278 -0
- package/distribution/runtime/src/studio/layouts.js +170 -0
- package/distribution/runtime/src/studio/registry.js +283 -0
- package/distribution/runtime/src/studio/relations.js +169 -0
- package/distribution/runtime/src/studio/server.ts +433 -0
- package/distribution/runtime/src/widgets/deck.ts +39 -0
- package/distribution/runtime/src/widgets/plotly.ts +354 -0
- package/distribution/runtime/src/widgets/presenter.ts +367 -0
- package/distribution/runtime/src/widgets/snapshot.ts +116 -0
- package/distribution/runtime/vendor/bento/FONT_NOTICES.md +49 -0
- package/distribution/runtime/vendor/bento/LICENSE +21 -0
- package/distribution/runtime/vendor/bento/THIRD_PARTY_NOTICES.md +51 -0
- package/distribution/runtime/vendor/bento/font-licenses/fraunces-OFL.txt +93 -0
- package/distribution/runtime/vendor/bento/font-licenses/instrument-sans-OFL.txt +93 -0
- package/distribution/runtime/vendor/bento/pin.json +69 -0
- package/distribution/runtime/vendor/bento/provenance/slides.package-lock.json +1928 -0
- package/distribution/runtime/vendor/bento/provenance/slides.package.json +28 -0
- package/distribution/runtime/vendor/bento/runtime-dependencies.json +244 -0
- package/distribution/runtime/vendor/bento/runtime-licenses.json +4038 -0
- package/distribution/runtime/vendor/bento/shell.html +269 -0
- package/distribution/runtime/vendor/bento/slides.schema.json +2379 -0
- package/package.json +40 -4
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/** Plain native speaker notes with a bounded, independently revalidated QMD patch. */
|
|
2
|
+
import {
|
|
3
|
+
hashSource,
|
|
4
|
+
scanSource,
|
|
5
|
+
sliceBytes,
|
|
6
|
+
type SourceBlock,
|
|
7
|
+
type SourceDocument,
|
|
8
|
+
SourceError,
|
|
9
|
+
type SourcePatch,
|
|
10
|
+
} from "./mod.ts";
|
|
11
|
+
import type { Node } from "../render/types.ts";
|
|
12
|
+
|
|
13
|
+
export const NOTES_CODEC_VERSION = 1;
|
|
14
|
+
export interface NotesRequest {
|
|
15
|
+
blockId: string;
|
|
16
|
+
slideId: string;
|
|
17
|
+
expected: string;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
export interface NotesPreview {
|
|
21
|
+
version: 1;
|
|
22
|
+
kind: "speaker-notes";
|
|
23
|
+
sourceHash: string;
|
|
24
|
+
requests: NotesRequest[];
|
|
25
|
+
patches: SourcePatch[];
|
|
26
|
+
result: string;
|
|
27
|
+
resultHash: string;
|
|
28
|
+
}
|
|
29
|
+
function fail(code: string, message: string): never {
|
|
30
|
+
throw new SourceError(code, `${code}: ${message}`);
|
|
31
|
+
}
|
|
32
|
+
const punctuation = /[!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~]/g;
|
|
33
|
+
const escapedPunctuation = /\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/g;
|
|
34
|
+
export function normalizeNotesText(value: string): string {
|
|
35
|
+
const text = value.replace(/\r\n|\r/g, "\n");
|
|
36
|
+
if (/`+(?:\{(?:r|python|julia|ojs)\}|(?:r|python|julia|ojs)\s)/i.test(text)) {
|
|
37
|
+
fail(
|
|
38
|
+
"COMPUTED_NOTES_UNSUPPORTED",
|
|
39
|
+
"Inline execution syntax cannot be introduced through plain notes; execution preprocessors may run before Markdown escaping.",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (
|
|
43
|
+
Array.from(text).some((character) => {
|
|
44
|
+
const point = character.codePointAt(0)!;
|
|
45
|
+
return point < 32 && point !== 10 || point === 127 ||
|
|
46
|
+
point >= 0xd800 && point <= 0xdfff;
|
|
47
|
+
}) ||
|
|
48
|
+
/ {2}|^ | $/m.test(text) || /^\n|\n$|\n{3}/.test(text)
|
|
49
|
+
) {
|
|
50
|
+
fail(
|
|
51
|
+
"UNSUPPORTED_NOTES_WHITESPACE",
|
|
52
|
+
"Plain notes support single spaces, line breaks and one blank line between paragraphs; remove tabs, indentation and extra surrounding whitespace before syncing.",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
/** Native Bento uses a textarea/textContent: richer AST nodes must never become HTML. */
|
|
58
|
+
export function renderPlainNotes(blocks: Node[]): string {
|
|
59
|
+
return blocks.map((block) => {
|
|
60
|
+
if (block.t !== "Para" && block.t !== "Plain") {
|
|
61
|
+
return fail(
|
|
62
|
+
"UNSUPPORTED_NOTES",
|
|
63
|
+
"Speaker notes support plain paragraphs only; rich or structured blocks need explicit source authoring.",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return (block.c as Node[]).map((node) => {
|
|
67
|
+
if (node.t === "Str") return String(node.c);
|
|
68
|
+
if (node.t === "Space") return " ";
|
|
69
|
+
if (node.t === "SoftBreak") return "\n";
|
|
70
|
+
return fail(
|
|
71
|
+
"UNSUPPORTED_NOTES",
|
|
72
|
+
`Speaker notes cannot faithfully project ${node.t} into native plain text.`,
|
|
73
|
+
);
|
|
74
|
+
}).join("");
|
|
75
|
+
}).join("\n\n");
|
|
76
|
+
}
|
|
77
|
+
function opener(block: SourceBlock): string[] | undefined {
|
|
78
|
+
const first = block.raw.split(/\r\n|\n|\r/, 1)[0];
|
|
79
|
+
const match = first.match(/^ {0,3}:{3,}\s*\{([^{}]*)\}\s*$/);
|
|
80
|
+
return match?.[1].trim().split(/\s+/);
|
|
81
|
+
}
|
|
82
|
+
export function sourceNotes(
|
|
83
|
+
doc: SourceDocument,
|
|
84
|
+
blockId: string,
|
|
85
|
+
slideId: string,
|
|
86
|
+
): { block: SourceBlock; text: string } {
|
|
87
|
+
const candidates = doc.blocks.filter((b) => b.id === blockId);
|
|
88
|
+
if (doc.diagnostics.length || candidates.length !== 1) {
|
|
89
|
+
fail(
|
|
90
|
+
"AMBIGUOUS_NOTES",
|
|
91
|
+
"Resolve source diagnostics and use one unique explicit notes identity.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const block = candidates[0], tokens = opener(block);
|
|
95
|
+
const slide = doc.slides.find((s) => s.id === slideId);
|
|
96
|
+
if (
|
|
97
|
+
block.kind !== "div" || !block.explicitId || !block.stable ||
|
|
98
|
+
block.parentId ||
|
|
99
|
+
block.slideId !== slideId || !slide?.stable || !tokens ||
|
|
100
|
+
tokens.length !== 2 || !tokens.includes(".notes") ||
|
|
101
|
+
!tokens.includes(`#${blockId}`)
|
|
102
|
+
) {
|
|
103
|
+
fail(
|
|
104
|
+
"UNMAPPED_NOTES",
|
|
105
|
+
"Speaker notes need one top-level ::: {#notes-id .notes} block under an explicitly identified slide; extra attributes/classes are unsupported.",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const owners = doc.blocks.filter((b) =>
|
|
109
|
+
b.kind === "div" && b.slideId === slideId && opener(b)?.includes(".notes")
|
|
110
|
+
);
|
|
111
|
+
if (owners.length !== 1) {
|
|
112
|
+
fail(
|
|
113
|
+
"AMBIGUOUS_NOTES",
|
|
114
|
+
"Use a single notes block per slide before editing from Bento.",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const children = doc.blocks.filter((b) =>
|
|
118
|
+
b !== block && b.span.start >= block.contentSpan.start &&
|
|
119
|
+
b.span.end <= block.contentSpan.end
|
|
120
|
+
);
|
|
121
|
+
// Execution preprocessors run before Markdown escaping. In particular knitr
|
|
122
|
+
// still recognizes an escaped `r ...` opener, so keep this conservative gate.
|
|
123
|
+
if (children.some((b) => b.origin === "computed" || b.kind === "chunk")) {
|
|
124
|
+
fail(
|
|
125
|
+
"COMPUTED_NOTES_UNSUPPORTED",
|
|
126
|
+
"Computed speaker notes cannot be reverse-written as authored content.",
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (
|
|
130
|
+
children.some((b) =>
|
|
131
|
+
b.parentId !== blockId || !["paragraph", "unsupported"].includes(b.kind)
|
|
132
|
+
)
|
|
133
|
+
) {
|
|
134
|
+
fail(
|
|
135
|
+
"UNSUPPORTED_NOTES",
|
|
136
|
+
"Speaker notes must contain only plain paragraphs.",
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
// Literal source proof is compared with the executed plain AST at projection.
|
|
140
|
+
// Decode only Markdown backslash punctuation, never HTML or arbitrary transforms.
|
|
141
|
+
const text = block.content.replace(/\r\n|\r/g, "\n").replace(/^\n+|\n+$/g, "")
|
|
142
|
+
.replace(escapedPunctuation, "$1");
|
|
143
|
+
normalizeNotesText(text);
|
|
144
|
+
return { block, text };
|
|
145
|
+
}
|
|
146
|
+
export function previewNotes(
|
|
147
|
+
doc: SourceDocument,
|
|
148
|
+
requests: NotesRequest[],
|
|
149
|
+
): NotesPreview {
|
|
150
|
+
if (hashSource(doc.text) !== doc.hash) {
|
|
151
|
+
fail("stale-document", "Notes source snapshot was modified.");
|
|
152
|
+
}
|
|
153
|
+
const fresh = scanSource(doc.text);
|
|
154
|
+
const patches = requests.map((request): SourcePatch => {
|
|
155
|
+
const { block } = sourceNotes(fresh, request.blockId, request.slideId);
|
|
156
|
+
if (block.content !== request.expected) {
|
|
157
|
+
fail("stale-fragment", "The notes fragment changed; review it again.");
|
|
158
|
+
}
|
|
159
|
+
const text = normalizeNotesText(request.text);
|
|
160
|
+
const replacement = text
|
|
161
|
+
? text.replace(punctuation, "\\$&").replace(/\n/g, fresh.newline) +
|
|
162
|
+
fresh.newline
|
|
163
|
+
: "";
|
|
164
|
+
return {
|
|
165
|
+
blockId: block.id,
|
|
166
|
+
span: block.contentSpan,
|
|
167
|
+
expected: block.content,
|
|
168
|
+
replacement,
|
|
169
|
+
};
|
|
170
|
+
}).sort((a, b) => a.span.start - b.span.start);
|
|
171
|
+
for (let i = 1; i < patches.length; i++) {
|
|
172
|
+
if (
|
|
173
|
+
patches[i].span.start < patches[i - 1].span.end ||
|
|
174
|
+
patches[i].blockId === patches[i - 1].blockId
|
|
175
|
+
) fail("overlapping-patches", "Notes patches overlap or repeat a block.");
|
|
176
|
+
}
|
|
177
|
+
let result = doc.text;
|
|
178
|
+
for (const patch of [...patches].reverse()) {
|
|
179
|
+
result = sliceBytes(result, { start: 0, end: patch.span.start }) +
|
|
180
|
+
patch.replacement +
|
|
181
|
+
sliceBytes(result, {
|
|
182
|
+
start: patch.span.end,
|
|
183
|
+
end: new TextEncoder().encode(result).length,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const after = scanSource(result);
|
|
187
|
+
for (const request of requests) {
|
|
188
|
+
if (
|
|
189
|
+
sourceNotes(after, request.blockId, request.slideId).text !==
|
|
190
|
+
normalizeNotesText(request.text)
|
|
191
|
+
) {
|
|
192
|
+
fail(
|
|
193
|
+
"structural-replacement",
|
|
194
|
+
"Notes replacement changed its reviewed plain text.",
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const outside = (document: SourceDocument) =>
|
|
199
|
+
document.blocks.filter((b) =>
|
|
200
|
+
!requests.some((r) => {
|
|
201
|
+
const container = document.blocks.find((c) => c.id === r.blockId)!;
|
|
202
|
+
return b.span.start >= container.contentSpan.start &&
|
|
203
|
+
b.span.end <= container.contentSpan.end;
|
|
204
|
+
})
|
|
205
|
+
).map((b) => [b.kind, b.explicitId, b.parentId, b.level, b.origin]);
|
|
206
|
+
if (JSON.stringify(outside(fresh)) !== JSON.stringify(outside(after))) {
|
|
207
|
+
fail(
|
|
208
|
+
"structural-replacement",
|
|
209
|
+
"Notes edit altered source structure outside the bounded body.",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
version: 1,
|
|
214
|
+
kind: "speaker-notes",
|
|
215
|
+
sourceHash: fresh.hash,
|
|
216
|
+
requests,
|
|
217
|
+
patches,
|
|
218
|
+
result,
|
|
219
|
+
resultHash: hashSource(result),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export function applyNotes(current: string, preview: NotesPreview): string {
|
|
223
|
+
if (
|
|
224
|
+
preview.version !== 1 || preview.kind !== "speaker-notes" ||
|
|
225
|
+
hashSource(current) !== preview.sourceHash
|
|
226
|
+
) fail("stale-source", "QMD changed after the notes review.");
|
|
227
|
+
const verified = previewNotes(scanSource(current), preview.requests);
|
|
228
|
+
if (JSON.stringify(verified) !== JSON.stringify(preview)) {
|
|
229
|
+
fail(
|
|
230
|
+
"invalid-preview",
|
|
231
|
+
"The notes patch or bounds were modified after review.",
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return verified.result;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Stable source identity shared by native, Studio and agent creation. */
|
|
238
|
+
export function notesIdentity(slideId: string): string {
|
|
239
|
+
return `qb-notes-${hashSource(slideId).slice(0, 24)}`;
|
|
240
|
+
}
|
|
241
|
+
export function generatedNotes(
|
|
242
|
+
doc: SourceDocument,
|
|
243
|
+
slideId: string,
|
|
244
|
+
id: string,
|
|
245
|
+
text: string,
|
|
246
|
+
): string {
|
|
247
|
+
if (
|
|
248
|
+
!/^[\p{L}_][\p{L}\p{N}\p{M}_.:-]*$/u.test(id) ||
|
|
249
|
+
doc.blocks.some((block) => block.id === id)
|
|
250
|
+
) {
|
|
251
|
+
fail(
|
|
252
|
+
"duplicate-id",
|
|
253
|
+
"The new notes source identity is invalid or already used.",
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (
|
|
257
|
+
doc.blocks.some((block) =>
|
|
258
|
+
block.slideId === slideId && block.kind === "div" &&
|
|
259
|
+
opener(block)?.includes(".notes")
|
|
260
|
+
)
|
|
261
|
+
) {
|
|
262
|
+
fail(
|
|
263
|
+
"AMBIGUOUS_NOTES",
|
|
264
|
+
"This slide already contains source notes; recalculate before editing them.",
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const normalized = normalizeNotesText(text);
|
|
268
|
+
if (!normalized) {
|
|
269
|
+
fail("UNSUPPORTED_NOTES", "New notes must contain plain text.");
|
|
270
|
+
}
|
|
271
|
+
return (`::: {#${id} .notes}\n${
|
|
272
|
+
normalized.replace(punctuation, "\\$&")
|
|
273
|
+
}\n:::\n\n`).replace(/\n/g, doc.newline);
|
|
274
|
+
}
|
|
275
|
+
export function verifyNotesInsertion(
|
|
276
|
+
before: SourceDocument,
|
|
277
|
+
after: SourceDocument,
|
|
278
|
+
slideId: string,
|
|
279
|
+
id: string,
|
|
280
|
+
text: string,
|
|
281
|
+
): void {
|
|
282
|
+
const binding = sourceNotes(after, id, slideId);
|
|
283
|
+
const outside = (doc: SourceDocument) =>
|
|
284
|
+
doc.blocks.filter((block) =>
|
|
285
|
+
block.id !== id &&
|
|
286
|
+
!(block.span.start >= binding.block.contentSpan.start &&
|
|
287
|
+
block.span.end <= binding.block.contentSpan.end && doc === after)
|
|
288
|
+
).map((
|
|
289
|
+
block,
|
|
290
|
+
) => [
|
|
291
|
+
block.id,
|
|
292
|
+
block.kind,
|
|
293
|
+
block.raw.replace(/\r?\n$/, ""),
|
|
294
|
+
block.origin,
|
|
295
|
+
block.slideId,
|
|
296
|
+
]);
|
|
297
|
+
if (
|
|
298
|
+
binding.text !== normalizeNotesText(text) ||
|
|
299
|
+
JSON.stringify(outside(before)) !== JSON.stringify(outside(after)) ||
|
|
300
|
+
JSON.stringify(before.slides.map((slide) => [slide.id, slide.stable])) !==
|
|
301
|
+
JSON.stringify(after.slides.map((slide) => [slide.id, slide.stable]))
|
|
302
|
+
) {
|
|
303
|
+
fail(
|
|
304
|
+
"invalid-notes-insertion",
|
|
305
|
+
"Notes insertion changed existing source scope or content.",
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|